diff --git a/.env.example b/.env.example index 5123284..8b41182 100644 --- a/.env.example +++ b/.env.example @@ -53,3 +53,14 @@ RAG_COLLECTION_NAME_TEMPLATE={subject} {grade} RAG_RETRIEVAL_TOP_K=4 # RAG_FORCE_MOCK=1 یعنی حتی با وجودِ کلیدها هم مسیرِ RAG خاموش بمونه. RAG_FORCE_MOCK= + +# ----------------------------------------------------------------------- +# تنظیماتِ مقیاس‌پذیری (همه دیفالتِ امن دارن) +# ----------------------------------------------------------------------- +# persistent DB connection (ثانیه). با SQLite بی‌اثر؛ بعد از Postgres مهم. +DB_CONN_MAX_AGE=60 +# کشِ کوتاهِ ردیفِ کاربر در auth تا هر request یک SELECT نزنه. 0 = خاموش +# (پیش‌فرض). توصیه‌ی پروداکشن با Redis: 30. با هر save/delete روی User باطل می‌شه. +AUTH_USER_CACHE_TTL=0 +# سقفِ ارزانِ خواندن روی endpointهای پرترافیکِ GET (home/leaderboard/summary/...). +READ_THROTTLE_RATE=120/min diff --git a/config/settings.py b/config/settings.py index ea185e3..7183732 100644 --- a/config/settings.py +++ b/config/settings.py @@ -149,6 +149,10 @@ DATABASES = { 'OPTIONS': { 'timeout': 20, }, + # کانکشنِ DB به‌ازای هر request باز/بسته نشه؛ تا این تعداد ثانیه + # persistent بمونه. با SQLite بی‌اثرِ عملی ولی بی‌ضرره؛ بعد از + # مهاجرت به Postgres جلوی هزینه‌ی TCP+auth در هر request رو می‌گیره. + 'CONN_MAX_AGE': int(os.environ.get('DB_CONN_MAX_AGE', '60')), } } @@ -235,9 +239,11 @@ REST_FRAMEWORK = { # Http404/PermissionDenied) به‌جای پاسخِ JSON، صفحه‌ی HTML عمومیِ ۵۰۰ # جنگو رو برمی‌گردونه (نگاه کنید به config/exceptions.py). 'EXCEPTION_HANDLER': 'config.exceptions.custom_exception_handler', + # BrowsableAPIRenderer فقط در dev؛ در پروداکشن هم overhead داره هم یک + # مرورگرِ HTMLِ API رو در معرض دید می‌ذاره. 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', - 'rest_framework.renderers.BrowsableAPIRenderer', + *(['rest_framework.renderers.BrowsableAPIRenderer'] if DEBUG else []), ], 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.ScopedRateThrottle', @@ -255,6 +261,10 @@ REST_FRAMEWORK = { 'bazaar_verify': '10/hour', 'ai_ask': '20/hour', 'rag_ingest': '30/hour', + # سقفِ ارزانِ خواندن روی endpointهای پرترافیکِ GET (home/leaderboard/ + # summary/notifications). فقط جلوی کلاینتِ خراب/اسکرپر رو می‌گیره؛ + # به UX عادی دست نمی‌زنه. + 'read': os.environ.get('READ_THROTTLE_RATE', '120/min'), 'purchase': '10/hour', 'token_refresh': '30/hour', 'question_answer': '60/min', @@ -295,6 +305,14 @@ SPECTACULAR_SETTINGS = { ACCESS_TOKEN_TTL_SECONDS = 3600 REFRESH_TOKEN_TTL_DAYS = 30 +# کشِ کوتاهِ ردیفِ کاربر در TadrisinoTokenAuthentication تا هر request +# authenticated یک SELECT روی accounts_user نزنه. ۰ = خاموش (رفتارِ فعلی، و +# پیش‌فرض تا وقتی Redis در پروداکشن هست). توصیه‌ی پروداکشن: ۳۰. با هر save/ +# delete روی User، کلیدِ کاربر باطل می‌شه (accounts/receivers.py)، پس تنها +# پنجره‌ی کهنگی برای تغییری که از مسیرِ دیگری (مثلاً shell/DB) اعمال شده، +# همین چند ثانیه‌ست. +AUTH_USER_CACHE_TTL = int(os.environ.get('AUTH_USER_CACHE_TTL', '0')) + # سقفِ گیرنده‌ی اکشنِ ادمینِ send_sms_to_selected؛ این اکشن synchronous توی # همون requestه (بدون صفِ پس‌زمینه)، پس این سقف جلویِ تایم‌اوت/overloadِ # provider رو می‌گیره — بیشتر از سقف یعنی هیچ پیامکی ارسال نمی‌شه.