Docs: Redis cache behaviour during an outage, what was tested live, CACHE_KEY_PREFIX
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
18
CLAUDE.md
18
CLAUDE.md
@@ -441,7 +441,7 @@ preferred setup, because there is then no key to leak. **Never let the server ru
|
||||
`django-insecure-…` fallback from `config/settings.py`: that string is committed here, so
|
||||
anyone who can read this repo can forge a token for any user.**
|
||||
|
||||
## وضعیت ۲۰۲۶-۱۰-۰۷ — روی سرور (`4c60108`، ریاستارت ۱۳:۵۹)
|
||||
## وضعیت ۲۰۲۶-۱۰-۰۷ — روی سرور (`2779270`، ریاستارت ۱۴:۱۶)
|
||||
|
||||
بکاپهای همان روز در `.deploy-backups/`: `env-before-secretkey-redis-20261007-135850`،
|
||||
`unit-gunicorn-mathapp-before-20261007-135850.service`، `db-before-deploy-20261007-13*.sqlite3`.
|
||||
@@ -453,9 +453,19 @@ anyone who can read this repo can forge a token for any user.**
|
||||
دیتابیس است، نه امضاشده، پس نشستها نمیمیرند و فرانت بیصدا توکنِ تازه میگیرد؛ این را با ورودِ واقعی
|
||||
**نسنجیدم**.
|
||||
- **`CACHE_URL=redis://127.0.0.1:6379/1`** (Redisِ مشترکِ ماشین، بدونِ رمز، `maxmemory 0`؛ دیتابیسِ ۰ مالِ
|
||||
دیگران است و دست نخورد). سقفِ نرخ حالا مشترک است: رگبارِ ۶۰۰ درخواستی ۱۲۸ تا `200` داد (پیش از آن
|
||||
۳۶۰ = ۳ worker × ۱۲۰). ⚠️ اگر این Redis بخوابد، هر مسیرِ throttleدار خطا میدهد؛ برگشت: خطِ `CACHE_URL` را
|
||||
از `.env` خالی کنید و ریاستارت.
|
||||
دیگران است و دست نخورد). کلیدها پیشوندِ `tadrisino-mathapp` دارند و کش `config.cache.ResilientRedisCache`
|
||||
است: خطای Redis ۵۰۰ نمیشود، خطای ERRORِ لاگشده (هر ۳۰ ثانیه یک بار) و **سقفهای مبتنی بر کش اعمال
|
||||
نمیشوند** (fail-open: throttleهای DRF، پنجرهی ساعتیِ OTP، سقفِ ورودِ ادمین، قفلِ بازسنجیِ پرداخت)،
|
||||
`/api/health` ۵۰۳ میدهد؛ آنچه در دیتابیس شمرده میشود (cooldown و سقفِ روزانهی پیامک، حدسِ هر کد) میماند.
|
||||
Timeoutِ اتصال و خواندن ۱ ثانیه. آزموده شد: ۱۱ تستِ واحد (از جمله redis-serverِ محلی که وسطِ کار میمیرد و
|
||||
بدونِ ریاستارتِ برنامه برمیگردد)، تمرینِ قطعی روی خودِ سرور (پروسهی جدا با کش روی پورتِ بسته: همه ۲۰۰،
|
||||
۰٫۳ ثانیه برای ۷ درخواست، health ۵۰۳)، و رگبارِ ۶۰۰ درخواستی روی سایتِ زنده (۱۲۴ تا ۲۰۰، پیشتر ۳۶۰).
|
||||
**آزموده نشد:** قطعیِ واقعیِ Redisِ مشترک (مالِ ما نیست)، و رفتارِ آن زیرِ فشارِ حافظه. برگشت: خطِ
|
||||
`CACHE_URL` را در `.env` خالی کنید و ریاستارت.
|
||||
- **آزمونِ زنده با کاربرِ آزمایشی** (۳۲ از ۳۲ سبز؛ کاربر و دادهاش پاک شد، شمارشها با پیش از آزمون برابر):
|
||||
ورود با OTP، توکنِ تازه، تمدید (توکنِ تمدیدِ قدیمی ۴۰۱)، جزیرهی قفل ۴۰۳، بیجواب ۴۰۹، ادعای کمتر از
|
||||
تعدادِ سوال ۴۰۹، پایانِ جزیره و درس بعد از جواب، Idempotency-Key، و خروج. رابطِ کاربریِ فرانت در مرورگر
|
||||
رانده **نشد** (همان فراخوانیها با API زده شد).
|
||||
- پایانِ درس/جزیره بیجواب و روی جزیرهی قفل پذیرفته نمیشود، ۱۵ مسیر سقفِ نرخ گرفتند، `deploy.sh`
|
||||
پیش از `migrate` بکاپ میگیرد و سلامت را از سوکتِ gunicorn میسنجد.
|
||||
- نسخهی کتابخانههای `requirements.txt` قفل شد (نسخههای روی سرور).
|
||||
|
||||
@@ -111,7 +111,8 @@ curl https://mathapi.tadrisino2.ir/api/health
|
||||
| `DJANGO_SECRET_KEY` | یک کلیدِ تصادفیِ قوی ساخته و در `.django_secret_key` (مجوز `0600`) ذخیره میشود |
|
||||
| `PUBLIC_BASE_URL` · `FRONTEND_URL` | دامنهی فعلی (`mathapi.tadrisino2.ir` / `math.tadrisino2.ir`). بقیهی تنظیمهای دامنه از همین دو ساخته میشوند؛ بخشِ «عوض کردنِ دامنه» |
|
||||
| `DJANGO_ALLOWED_HOSTS` · `CSRF_TRUSTED_ORIGINS` · `CORS_ALLOWED_ORIGINS` | از دو آدرسِ بالا ساخته میشوند؛ مقدارِ env فقط دامنهی *اضافه* است |
|
||||
| `CACHE_URL` | سرویس بالا میآید ولی نرخ بینِ workerها مشترک نیست؛ `/api/health` هشدار میدهد |
|
||||
| `CACHE_URL` | سرویس بالا میآید ولی نرخ بینِ workerها مشترک نیست؛ `/api/health` هشدار میدهد. روی سرور `redis://127.0.0.1:6379/1` است (Redisِ مشترکِ ماشین). قطعیِ Redis API را ۵۰۰ نمیکند ولی تا برگشتنش سقفهای مبتنی بر کش اعمال نمیشوند و health ۵۰۳ میدهد (`config/cache.py`). برگشت: خالیکردنِ این خط و ریاستارت |
|
||||
| `CACHE_KEY_PREFIX` | `tadrisino-mathapp`؛ پیشوندِ کلیدهای ما روی Redisِ مشترک |
|
||||
| `KAVENEGAR_API_KEY` | `POST /api/auth/otp/request` خطای صریحِ `503` میدهد، نه موفقیتِ ساختگی |
|
||||
| `ZARINPAL_MERCHANT_ID` | مسیرِ پرداخت fail-closed میماند (`503`) |
|
||||
| `gunicorn.conf.py` (در ریپو، نه env) | gunicorn خودش از پوشهی کاری میخواندش: workerهای `gthread` با ۸ نخ و timeoutِ ۱۲۰. بدونِ آن، سه استریمِ همزمانِ دستیار کلِ API را قفل میکرد |
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
| ساختنِ دستیِ `OTPCode` و `Session` در پنل فقط برای ابرکاربر (کارمندِ عادی ۴۰۳) | `accounts/tests_security_review.py` (`StaffCannotMintLoginsTests`) |
|
||||
| پایانِ درس و پایانِ آزمونِ جزیره جوابِ ثبتشده به همهی سوالها میخواهند (۴۰۹ `answers_missing`)، روی جزیرهی قفل ۴۰۳ `node_locked` میدهند (همان قاعدهی نقشه)، جزیره یا درسِ بیسوال چیزی برای جوابدادن ندارد و اگر باز است بیامتیاز رد میشود (قفل بودنش هنوز چک میشود)، و تعدادِ سوالِ آزمون را کلاینت نمیتواند کمتر از آزمونِ استانداردِ ۴ تایی بگوید (ستارهی دروغین) | `content/tests_finish_requires_answers.py`، `content/tests_node_completion.py` |
|
||||
| کلیدِ امضای توکن تصادفی و فقط روی سرور است (`.django_secret_key`، ۶۰۰)؛ نه در `.env`، نه در یونیت؛ توکنِ ساختهشده با کلیدِ داخلِ ریپو `401` میگیرد؛ `DJANGO_DEBUG` هم در یونیت نیست | دستی (۲۰۲۶-۱۰-۰۷): توکنِ جعلی `200` ← `401`؛ تستِ واحد ندارد |
|
||||
| کشِ مشترک (Redis، دیتابیسِ ۱) سقفِ نرخ را بینِ workerها مشترک میکند | دستی (۲۰۲۶-۱۰-۰۷): رگبارِ ۶۰۰ درخواستی ۱۲۸ تا `200` (پیشتر ۳۶۰)؛ تستِ واحد ندارد |
|
||||
| کشِ مشترک (Redis، دیتابیسِ ۱) سقفِ نرخ را بینِ workerها مشترک میکند و قطعیِ Redis API را ۵۰۰ نمیکند (fail-open، ERROR در لاگ، health ۵۰۳؛ شرحِ کامل در `config/cache.py`) | `config/tests_cache_resilience.py` (۱۱ تست، با redis-serverِ محلی که وسطِ کار میمیرد)؛ رگبارِ زنده: ۱۲۴ تا `200` |
|
||||
| کتابخانهها بدونِ آسیبپذیریِ شناختهشده (`urllib3>=2.8.0`، `python-dotenv 1.2.2`) | پایگاهِ OSV روی `pip freeze` |
|
||||
| actionهای لغو/بازپرداختِ سفارش، علامتِ «بررسیشده» و لینکِ بازار اجازهی تغییر میخواهند؛ کارمندِ فقطخواندنی (Supervisor) اجرایشان نمیکند | `payment/tests_security_review.py`، `content/tests_security_review.py` |
|
||||
| ورودِ فایلِ سوال/برنامهی درسی اجازهی افزودن (و برای جایگزینی، حذف) میخواهد؛ مرورگرِ محتوا اجازهی دیدنِ سوال | `content/tests_security_review.py` |
|
||||
|
||||
Reference in New Issue
Block a user