Security re-check and load test 2026-10-04: 32 threads, data file permissions, patched deps
- gunicorn gthread 8 -> 32 threads per worker: each assistant stream holds a thread for the whole answer; with 3x8, about 20 concurrent asks already queued ordinary page loads for seconds (up to 23 s at 50 asks locally, 7 s at 20 asks on the live server). With 3x32, 75 concurrent asks kept page loads under 110 ms (laptop, simulated model and Qdrant) - deploy.sh: new step closes the project dir to other accounts and sets the database and backups to 0600/0700 on every release; user `nobody` on the shared server could read db.sqlite3 and every backup - requirements: python-dotenv 1.2.2, urllib3>=2.8.0 (known CVEs in the installed versions), gunicorn pinned to the server's version (was only installed by hand) - security-before-production.md: items 19-23 (file permissions, DEBUG=1 still in the systemd unit, dependencies, frontend headers, guest AI cap per shared IP) and the reproduced X-Forwarded-For spoof through the origin IP - model-prices.md: 0.183 cents per answer from 50 answers in the live load test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
18
deploy.sh
18
deploy.sh
@@ -171,6 +171,24 @@ if 'LocMem' in settings.CACHES['default']['BACKEND'] and not settings.DEBUG:
|
|||||||
print(' ⚠ بدونِ Redis محدودیتِ نرخ بینِ workerها مشترک نیست (CACHE_URL).')
|
print(' ⚠ بدونِ Redis محدودیتِ نرخ بینِ workerها مشترک نیست (CACHE_URL).')
|
||||||
PYCHECK
|
PYCHECK
|
||||||
|
|
||||||
|
# ─── ۳.۶ مجوزِ فایلهای داده ───────────────────────────────────────────────
|
||||||
|
# این ماشین بینِ چند تیم مشترک است. تا ۲۰۲۶-۱۰-۰۴ دیتابیس و همهی بکاپها 0644 بودند و
|
||||||
|
# پوشهها قابلِ پیمایش؛ کاربرِ nobody روی سرور توانست سرِ db.sqlite3 و یک بکاپ را بخواند
|
||||||
|
# (شمارهها، refresh tokenهای بیهش، گفتگوها). در هر انتشار دوباره بسته میشود، چون بکاپِ
|
||||||
|
# دستیِ تازه دوباره با umaskِ پیشفرض (0644) ساخته میشود. nginx فقط به سوکتِ /run وصل
|
||||||
|
# میشود و از این پوشه چیزی نمیخواند (sites-enabled/mathapp، همان روز خوانده شد)؛ اگر
|
||||||
|
# سرویسِ دیگری این دیتابیس را مستقیم میخواند، از این به بعد خطای دسترسی میگیرد.
|
||||||
|
step "مجوزِ فایلهای داده"
|
||||||
|
chmod o-rwx . || warn "مجوزِ پوشهی پروژه عوض نشد."
|
||||||
|
for f in db.sqlite3 db.sqlite3-wal db.sqlite3-shm db.sqlite3-journal; do
|
||||||
|
if [ -e "$f" ]; then chmod 600 "$f"; fi
|
||||||
|
done
|
||||||
|
if [ -d .deploy-backups ]; then
|
||||||
|
chmod 700 .deploy-backups
|
||||||
|
find .deploy-backups -type f -exec chmod 600 {} +
|
||||||
|
fi
|
||||||
|
printf ' دیتابیس و بکاپها فقط برای %s خواندنیاند.\n' "$(id -un)"
|
||||||
|
|
||||||
# ─── ۴. دیتابیس ────────────────────────────────────────────────────────────
|
# ─── ۴. دیتابیس ────────────────────────────────────────────────────────────
|
||||||
step "اعمالِ مهاجرتهای دیتابیس"
|
step "اعمالِ مهاجرتهای دیتابیس"
|
||||||
python manage.py migrate --noinput || fail "migrate شکست خورد؛ سرویس ریاستارت نشد."
|
python manage.py migrate --noinput || fail "migrate شکست خورد؛ سرویس ریاستارت نشد."
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
| ۲۰۲۶-۰۹-۲۶ | میانگینِ همهی جوابهای سنجش | `gpt-4.1-mini` | ۰٫۰۹۶ سنت | میانگینِ `costUsd`ِ ۵۴۱ فراخوانی (۱) |
|
| ۲۰۲۶-۰۹-۲۶ | میانگینِ همهی جوابهای سنجش | `gpt-4.1-mini` | ۰٫۰۹۶ سنت | میانگینِ `costUsd`ِ ۵۴۱ فراخوانی (۱) |
|
||||||
| ۲۰۲۶-۱۰-۰۱ | یک جوابِ دستیار، پرامپتِ `tutor-v11-math-only`، ۱۰۳ مسئلهی کتاب | `gpt-4.1-mini` | ۰٫۱۶۲ سنت (دبستان ۰٫۱۲۹، دبیرستان ۰٫۲۲۵) | توکنِ ورودی تخمینی (۰٫۳۸۷ توکن به ازای هر حرف)، خروجی از جوابِ واقعی، ضربدر قیمتِ بالا |
|
| ۲۰۲۶-۱۰-۰۱ | یک جوابِ دستیار، پرامپتِ `tutor-v11-math-only`، ۱۰۳ مسئلهی کتاب | `gpt-4.1-mini` | ۰٫۱۶۲ سنت (دبستان ۰٫۱۲۹، دبیرستان ۰٫۲۲۵) | توکنِ ورودی تخمینی (۰٫۳۸۷ توکن به ازای هر حرف)، خروجی از جوابِ واقعی، ضربدر قیمتِ بالا |
|
||||||
| ۲۰۲۶-۱۰-۰۱ | یک سوالِ زنده در اپ (۱۲ سوال) | `gpt-4.1-mini` | ۰٫۲۲۵ سنت | فراخوانیِ واقعی |
|
| ۲۰۲۶-۱۰-۰۱ | یک سوالِ زنده در اپ (۱۲ سوال) | `gpt-4.1-mini` | ۰٫۲۲۵ سنت | فراخوانیِ واقعی |
|
||||||
|
| ۲۰۲۶-۱۰-۰۴ | یک جوابِ دستیار در لودتستِ سرورِ زنده (۵۰ جواب، استریم، سوالهای تصادفیِ بانکِ سوالِ ده پایه) | `gpt-4.1-mini` | ۰٫۱۸۳ سنت (میانگین: ۳٬۱۸۱ توکنِ ورودی، ۳۴۸ خروجی) | `prompt_tokens`/`completion_tokens`ِ ذخیرهشده روی پیامها ضربدر قیمتِ بالا؛ داشبوردِ GapGPT خوانده نشد |
|
||||||
|
|
||||||
عددِ برنامهریزیِ قیمتگذاری **۰٫۲۰ سنت برای هر سوالِ دستیار** است و حدودِ ۷۰٪ِ آن ورودی است.
|
عددِ برنامهریزیِ قیمتگذاری **۰٫۲۰ سنت برای هر سوالِ دستیار** است و حدودِ ۷۰٪ِ آن ورودی است.
|
||||||
هزینهی embedding جدا اندازه گرفته نشده است. قیمتِ رسمیِ `text-embedding-3-small` بسیار کمتر از
|
هزینهی embedding جدا اندازه گرفته نشده است. قیمتِ رسمیِ `text-embedding-3-small` بسیار کمتر از
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# پیش از پروداکشنِ واقعی: چیزهایی که عمداً باز ماندهاند
|
# پیش از پروداکشنِ واقعی: چیزهایی که عمداً باز ماندهاند
|
||||||
|
|
||||||
**تاریخِ ثبت: ۲۰۲۶-۰۹-۲۵ (۳ مهر ۱۴۰۵)، کامیتِ پایه `66066cb`.**
|
**تاریخِ ثبت: ۲۰۲۶-۰۹-۲۵ (۳ مهر ۱۴۰۵)، کامیتِ پایه `66066cb`.** بازبینیِ دوباره با سرورِ زنده:
|
||||||
|
۲۰۲۶-۱۰-۰۴ (مواردِ ۱۹ تا ۲۳ و بازتولیدِ مورد ۸).
|
||||||
|
|
||||||
این پروژه تا این تاریخ در حالتِ **دمو و ارائهی کارآموزی** است، نه محصولِ نهایی. چند چیز
|
این پروژه تا این تاریخ در حالتِ **دمو و ارائهی کارآموزی** است، نه محصولِ نهایی. چند چیز
|
||||||
عمداً باز گذاشته شده تا توسعهدهندهها، فرانتکار و ارائه کار کنند. هر کدام برای یک سرورِ
|
عمداً باز گذاشته شده تا توسعهدهندهها، فرانتکار و ارائه کار کنند. هر کدام برای یک سرورِ
|
||||||
@@ -38,6 +39,11 @@
|
|||||||
| ۱۶ | فرانت توکن را در `localStorage` نگه میدارد | کم | فرانت |
|
| ۱۶ | فرانت توکن را در `localStorage` نگه میدارد | کم | فرانت |
|
||||||
| ۱۷ | ~~شمارهی موبایل نرمال نمیشود؛ سقفهای per-phone با املای دیگر دور زده میشوند~~ **بسته شد ۲۰۲۶-۰۹-۲۵** | متوسط | کد |
|
| ۱۷ | ~~شمارهی موبایل نرمال نمیشود؛ سقفهای per-phone با املای دیگر دور زده میشوند~~ **بسته شد ۲۰۲۶-۰۹-۲۵** | متوسط | کد |
|
||||||
| ۱۸ | `/api/payment/orders/{id}/pay` بدونِ ورود و بدونِ سنجشِ مالکیت | کم | کد |
|
| ۱۸ | `/api/payment/orders/{id}/pay` بدونِ ورود و بدونِ سنجشِ مالکیت | کم | کد |
|
||||||
|
| ۱۹ | دیتابیس و بکاپها برای همهی کاربرانِ ماشینِ مشترک خواندنی بودند | بالا | کد آماده (`deploy.sh`)؛ اعمال با دیپلوی |
|
||||||
|
| ۲۰ | یونیتِ systemd هنوز `DJANGO_DEBUG=1` و کلیدِ ناامن دارد؛ فقط یک خطِ `.env` جلوی DEBUG را گرفته | بالا (نهفته) | ادمینِ سرور |
|
||||||
|
| ۲۱ | `urllib3 2.7.0` و `python-dotenv 1.0.1` آسیبپذیریِ شناختهشده داشتند | کم | کد آماده؛ اعمال با دیپلوی |
|
||||||
|
| ۲۲ | سایتِ فرانت هیچ هدرِ امنیتی ندارد (CSP، frame-ancestors، HSTS) | کم | nginx/فرانت |
|
||||||
|
| ۲۳ | سقفِ دستیار برای مهمانها بر اساسِ IP مشترک است؛ کلاسِ پشتِ یک IP بعد از ۲۰ سوال در ساعت ۴۲۹ میگیرد | متوسط (دسترسپذیری) | تصمیمِ محصولی |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -138,6 +144,12 @@
|
|||||||
- **رفع (ادمینِ سرور):** پورتِ ۸۰/۴۴۳ فقط برای رنجهای ArvanCloud، یا `set_real_ip_from` +
|
- **رفع (ادمینِ سرور):** پورتِ ۸۰/۴۴۳ فقط برای رنجهای ArvanCloud، یا `set_real_ip_from` +
|
||||||
`real_ip_header` در nginx.
|
`real_ip_header` در nginx.
|
||||||
|
|
||||||
|
- **بازتولید شد ۲۰۲۶-۱۰-۰۴:** `curl -H 'Host: mathapi.tadrisino2.ir' -H 'X-Forwarded-For: 1.2.3.4'
|
||||||
|
http://78.38.35.249/api/health` جواب داد `clientIp: 1.2.3.4`. یعنی هر سقفِ IP-محور (ورودِ ادمین
|
||||||
|
۱۰ در ۱۵ دقیقه، درخواست و ورودِ OTP، ساختِ مهمان، دستیارِ مهمان) با عوض کردنِ این هدر در هر
|
||||||
|
درخواست دور زده میشود، و حفاظتِ DDoS/WAFِ آروان هم برای این مسیر وجود ندارد. سقفهای
|
||||||
|
per-phone و per-code که در دیتابیس شمرده میشوند متأثر نیستند. پورتهای ۸۰ و ۴۴۳ هر دو باز بودند.
|
||||||
|
|
||||||
## ۹. نبودِ کشِ مشترک (`CACHE_URL`)
|
## ۹. نبودِ کشِ مشترک (`CACHE_URL`)
|
||||||
|
|
||||||
- **چیست:** بدونِ `CACHE_URL` هر worker کشِ `LocMemCache` خودش را دارد (۳ worker).
|
- **چیست:** بدونِ `CACHE_URL` هر worker کشِ `LocMemCache` خودش را دارد (۳ worker).
|
||||||
@@ -204,6 +216,8 @@
|
|||||||
`docker` و `lxd`، روی ماشینی که Gitea و ایمیلِ شرکت هم رویش است. رمز یک بار در گفتگو با
|
`docker` و `lxd`، روی ماشینی که Gitea و ایمیلِ شرکت هم رویش است. رمز یک بار در گفتگو با
|
||||||
agent گذاشته شده و روی دسکتاپِ صاحبِ پروژه در یک فایلِ متنی نگه داشته میشود.
|
agent گذاشته شده و روی دسکتاپِ صاحبِ پروژه در یک فایلِ متنی نگه داشته میشود.
|
||||||
- **خطر:** هر نشتِ رمز یعنی دسترسیِ root به کلِ ماشینِ شرکت، نه فقط این پروژه.
|
- **خطر:** هر نشتِ رمز یعنی دسترسیِ root به کلِ ماشینِ شرکت، نه فقط این پروژه.
|
||||||
|
۲۰۲۶-۱۰-۰۴: `PasswordAuthentication yes` در `sshd_config` و پورتِ ۲۲ از اینترنت باز است؛ پس رمز
|
||||||
|
از هر جای اینترنت حدسزدنی است (fail2ban سنجیده نشد).
|
||||||
- **رفع (ادمینِ سرور):** کلیدِ SSH بهجای رمز، تعویضِ رمز، حسابِ جدا برای این پروژه با
|
- **رفع (ادمینِ سرور):** کلیدِ SSH بهجای رمز، تعویضِ رمز، حسابِ جدا برای این پروژه با
|
||||||
دسترسیِ فقط به `~/mathapp-v2` و یک خطِ `NOPASSWD` فقط برای
|
دسترسیِ فقط به `~/mathapp-v2` و یک خطِ `NOPASSWD` فقط برای
|
||||||
`systemctl restart zareian-mathapp`؛ پاک کردنِ فایلِ رمز از دسکتاپ (Keychain جایگزین است).
|
`systemctl restart zareian-mathapp`؛ پاک کردنِ فایلِ رمز از دسکتاپ (Keychain جایگزین است).
|
||||||
@@ -252,6 +266,63 @@
|
|||||||
- **رفع:** در `paymentUrl` یک توکنِ امضاشدهی کوتاهعمر (`signing`) با `order_id` بگذارید و
|
- **رفع:** در `paymentUrl` یک توکنِ امضاشدهی کوتاهعمر (`signing`) با `order_id` بگذارید و
|
||||||
همانجا بسنجید؛ یا شناسهی غیرترتیبی.
|
همانجا بسنجید؛ یا شناسهی غیرترتیبی.
|
||||||
|
|
||||||
|
## ۱۹. دیتابیس و بکاپها برای همهی کاربرانِ ماشین خواندنی بودند (۲۰۲۶-۱۰-۰۴)
|
||||||
|
|
||||||
|
- **چیست:** `/home/administrator` با مجوزِ 755، `~/mathapp-v2` و `.deploy-backups` با 775، و
|
||||||
|
`db.sqlite3` و هر ۳۵ بکاپ با 644. `.env` درست 600 بود.
|
||||||
|
- **بازتولید شد:** `sudo -u nobody head -c 15 ~/mathapp-v2/db.sqlite3` و همین برای یک بکاپ، هر دو
|
||||||
|
`SQLite format 3` چاپ کردند. هر حسابِ دیگرِ این ماشین (aria، artina، behdad، zahra، git، gitea،
|
||||||
|
metabase) یا هر سرویسِ نفوذشدهی دیگری شمارهها، گفتگوهای کودکان و refresh tokenهای بیهش (مورد ۱۱،
|
||||||
|
یعنی ورود بهجای هر کاربر) را میخواند.
|
||||||
|
- **رفع در کد:** مرحلهی «مجوزِ فایلهای داده» در `deploy.sh`: پوشهی پروژه بیدسترسیِ others،
|
||||||
|
دیتابیس 600، پوشهی بکاپ 700 و فایلهایش 600، در هر انتشار (بکاپِ دستیِ تازه دوباره 644 ساخته
|
||||||
|
میشود). خودِ `/home/administrator` دست نخورد چون nginx فرانت را از `~/mathapp_frontend_new/dist`
|
||||||
|
میخواند. بعد از دیپلوی باید دوباره با `sudo -u nobody` سنجیده شود. هش کردنِ refresh token (مورد ۱۱)
|
||||||
|
هنوز باز است.
|
||||||
|
|
||||||
|
## ۲۰. یونیتِ systemd: DEBUG و کلیدِ ناامن (۲۰۲۶-۱۰-۰۴)
|
||||||
|
|
||||||
|
- **چیست:** `systemctl cat gunicorn-mathapp` هنوز `Environment="DJANGO_DEBUG=1"` و
|
||||||
|
`Environment="DJANGO_SECRET_KEY=django-insecure-…"` دارد. `EnvironmentFile=.env` بعد از آن میآید و
|
||||||
|
`.env` خطِ `DJANGO_DEBUG=0` دارد (شمرده شد، مقدار چاپ نشد)؛ برای همین الان DEBUG خاموش است.
|
||||||
|
- **خطر:** اگر روزی آن یک خط از `.env` پاک شود (مثلاً با این فکر که «پیشفرضِ DEBUG خاموش است»)،
|
||||||
|
بعد از ریاستارت DEBUG روشن میشود و کدِ `0000` برای هر شمارهای پذیرفته میشود. `deploy.sh` این را
|
||||||
|
نمیبیند، چون تنظیماتِ مؤثر را با محیطِ شِل میخواند نه محیطِ یونیت.
|
||||||
|
- **رفع (ادمینِ سرور، با بکاپِ یونیت):** هر دو خطِ `Environment=` را از یونیت بردارید، `daemon-reload`،
|
||||||
|
ریاستارت. حذفِ خطِ کلید بهتنهایی کسی را بیرون نمیاندازد اگر `.env` همان کلید را داشته باشد؛
|
||||||
|
عوض کردنِ خودِ کلید (مورد ۱) جداست.
|
||||||
|
|
||||||
|
## ۲۱. کتابخانههای آسیبپذیر (۲۰۲۶-۱۰-۰۴)
|
||||||
|
|
||||||
|
- **چیست:** پایگاهِ OSV برای ۴۸ بستهی نصبشده: `urllib3 2.7.0` (GHSA-8988-9cw3-xx77 و
|
||||||
|
GHSA-vxq7-64xx-v4gw با شدتِ بالا، GHSA-gh4c-6fx4-qh6g متوسط) و `python-dotenv 1.0.1`
|
||||||
|
(GHSA-mf9w-mj56-hr94، فقط در `set_key` که پروژه صدا نمیزند). اثرِ عملی کم است: سناریوی هر سهی
|
||||||
|
urllib3 پاسخِ مخربِ سرورِ بالادستی یا پراکسی است (بازار، زرینپال، کاوهنگار، Qdrant). `npm audit` روی
|
||||||
|
وابستگیهای اجراییِ فرانت صفر.
|
||||||
|
- **رفع در کد:** `python-dotenv==1.2.2`، `urllib3>=2.8.0`، و `gunicorn==26.2.0` (تا امروز فقط دستی
|
||||||
|
روی سرور نصب بود). هر سه با پایتونِ ۳.۱۰ِ سرور سازگارند و سرور به PyPI دسترسی دارد (سنجیده شد).
|
||||||
|
|
||||||
|
## ۲۲. هدرهای امنیتیِ فرانت (۲۰۲۶-۱۰-۰۴)
|
||||||
|
|
||||||
|
- **چیست:** `https://math.tadrisino2.ir/` فقط `x-xss-protection` دارد؛ نه CSP، نه `X-Frame-Options`/
|
||||||
|
`frame-ancestors`، نه HSTS، نه `X-Content-Type-Options`. API همهی اینها را جز HSTS و CSP دارد.
|
||||||
|
- **خطر:** صفحهی اپ را میشود در iframeِ سایتِ دیگری گذاشت (clickjacking)؛ و چون توکن در
|
||||||
|
`localStorage` است (مورد ۱۶)، نبودِ CSP اثرِ هر XSSِ احتمالی را کامل میکند. در خودِ فرانت سینکِ XSS
|
||||||
|
پیدا نشد (تنها `dangerouslySetInnerHTML` روی SVGِ ثابتِ شکلکهاست).
|
||||||
|
- **رفع:** در nginxِ `mathapp-front` یا در آروان: `Content-Security-Policy` با `frame-ancestors 'none'`
|
||||||
|
و `connect-src` به دامنهی API، و `X-Content-Type-Options: nosniff`.
|
||||||
|
|
||||||
|
## ۲۳. سقفِ دستیار برای مهمانها روی IPِ مشترک (۲۰۲۶-۱۰-۰۴)
|
||||||
|
|
||||||
|
- **چیست:** `GuestAwareAIThrottle` برای مهمانها سطلِ `ai_ask` (۲۰ در ساعت) را بر اساسِ IP میسازد تا
|
||||||
|
ساختنِ مهمانِ تازه سقف را دور نزند.
|
||||||
|
- **بازتولید شد:** ۲۲ مهمانِ متفاوت از یک IP، هر کدام اولین سوالش: ۲۰ تا ۲۰۰، ۲۱اُم و ۲۲اُم ۴۲۹.
|
||||||
|
دانشآموزانِ مهمانِ یک کلاس پشتِ وایفایِ مدرسه یا CGNAT با هم ساعتی ۲۰ سوال دارند (روی سرور با ۳
|
||||||
|
workerِ بیRedis تا حدودِ ۶۰).
|
||||||
|
- **چرا تصمیمِ محصولی است:** سقفِ IP جلوی «مهمان بساز، سه سوالِ رایگان بگیر» را میگیرد. از
|
||||||
|
۲۰۲۶-۱۰-۰۳ سقفِ سراسریِ `AI_GLOBAL_DAILY_MAX` (۳۰۰۰ در روز، حدودِ ۶ دلار) هزینه را جدا محدود
|
||||||
|
میکند، پس میشود سطل را per-user کرد و فقط ساختِ مهمان را محدود نگه داشت. تا تصمیم، دست نخورد.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## چیزهایی که بسته شدهاند (برای اینکه دوباره باز نشوند)
|
## چیزهایی که بسته شدهاند (برای اینکه دوباره باز نشوند)
|
||||||
|
|||||||
@@ -932,7 +932,7 @@ fetch ← `npm ci` ← بررسیِ نوعها ← build در `dist.next` ←
|
|||||||
|
|
||||||
### ۱۴-۳ مقیاسِ اجرا: کجا گلوگاه است و بعد چه
|
### ۱۴-۳ مقیاسِ اجرا: کجا گلوگاه است و بعد چه
|
||||||
|
|
||||||
وضعِ فعلی: ۲۴ درخواستِ همزمان؛ درخواستِ عادی چند ده میلیثانیه، جوابِ دستیار دهها ثانیه. توکن stateless است، پس هر تعداد نسخهی دیگر از بکاند پشتِ nginx بیتغییرِ کد کار میکند — به شرطِ دیتابیس و کشِ مشترک.
|
وضعِ فعلی: ۳ worker × ۳۲ نخ = ۹۶ درخواستِ همزمان (تا ۲۰۲۶-۱۰-۰۴ ۸ نخ بود)؛ درخواستِ عادی چند ده میلیثانیه، جوابِ دستیار روی سرور میانهی ۵ تا ۸ ثانیه و گاهی ۴۰ ثانیه، که تمامش یک نخ را نگه میدارد. لودتستِ همان روز: با ۸ نخ از حدودِ ۲۰ پرسشِ همزمان صفحههای عادی چند ثانیه صف کشیدند؛ با ۳۲ نخ تا ۷۵ پرسش نه. توکن stateless است، پس هر تعداد نسخهی دیگر از بکاند پشتِ nginx بیتغییرِ کد کار میکند — به شرطِ دیتابیس و کشِ مشترک.
|
||||||
|
|
||||||
| # | گلوگاه | راهحلِ سادهی بعدی | نیاز |
|
| # | گلوگاه | راهحلِ سادهی بعدی | نیاز |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|||||||
@@ -14,10 +14,18 @@ gthread هر worker را چند نخی میکند: ۳ worker × ۸ نخ = ۲
|
|||||||
کدِ جنگو برای نخ امن است و SQLite با `OPTIONS.timeout=20` نوشتنِ همزمان را
|
کدِ جنگو برای نخ امن است و SQLite با `OPTIONS.timeout=20` نوشتنِ همزمان را
|
||||||
تحمل میکند. timeout در gthread به ضربانِ خودِ worker است، نه طولِ هر
|
تحمل میکند. timeout در gthread به ضربانِ خودِ worker است، نه طولِ هر
|
||||||
درخواست، پس استریمِ طولانی دیگر کشته نمیشود.
|
درخواست، پس استریمِ طولانی دیگر کشته نمیشود.
|
||||||
|
|
||||||
|
۸ → ۳۲ نخ (۲۰۲۶-۱۰-۰۴، لودتست): هر جوابِ دستیار یک نخ را تا آخرِ جواب نگه میدارد
|
||||||
|
(روی سرور میانه ۵ و p90 ۱۳ ثانیه). با ۳×۸ از حدودِ ۲۰ پرسشِ همزمان، صفحههای عادیِ اپ
|
||||||
|
پشتِ استریمها صف کشیدند (تا ۲۳ ثانیه در ۵۰ پرسش؛ فرانت بعد از ۱۵ ثانیه خطا میدهد)، چون
|
||||||
|
gthread اتصال را حتی وقتی همهی نخهایش مشغولاند میپذیرد و workerها یکنواخت پر نمیشوند.
|
||||||
|
با ۳×۳۲ تا ۷۵ پرسشِ همزمان صفحهها زیرِ ۱۱۰ میلیثانیه ماندند. این روی لپتاپ با مدل و
|
||||||
|
Qdrantِ شبیهسازیشده سنجیده شد؛ CPUِ مشترکِ سرور و رفتارِ GapGPT زیرِ این بار جدا
|
||||||
|
سنجیده نشدهاند. نخِ منتظرِ شبکه تقریباً هزینه ندارد؛ هر نخ اتصالِ SQLiteِ خودش را دارد.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
worker_class = 'gthread'
|
worker_class = 'gthread'
|
||||||
threads = 8
|
threads = 32
|
||||||
timeout = 120
|
timeout = 120
|
||||||
graceful_timeout = 30
|
graceful_timeout = 30
|
||||||
keepalive = 5
|
keepalive = 5
|
||||||
|
|||||||
@@ -4,8 +4,14 @@ drf-spectacular==0.30.0
|
|||||||
django-cors-headers==4.9.0
|
django-cors-headers==4.9.0
|
||||||
requests==2.33.1
|
requests==2.33.1
|
||||||
redis==5.2.1
|
redis==5.2.1
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.2.2
|
||||||
whitenoise==6.11.0
|
whitenoise==6.11.0
|
||||||
|
# همان نسخهای که سرور با آن اجرا میشود (یونیتِ systemd آن را از venv صدا میزند). تا
|
||||||
|
# ۲۰۲۶-۱۰-۰۴ در این فایل نبود و فقط دستی روی سرور نصب شده بود.
|
||||||
|
gunicorn==26.2.0
|
||||||
|
# وابستگیِ غیرمستقیمِ requests و qdrant-client. ۲.۷.۰ سه آسیبپذیریِ شناختهشده داشت
|
||||||
|
# (GHSA-8988-9cw3-xx77، GHSA-vxq7-64xx-v4gw، GHSA-gh4c-6fx4-qh6g؛ OSV، ۲۰۲۶-۱۰-۰۴).
|
||||||
|
urllib3>=2.8.0
|
||||||
|
|
||||||
# RAG / AI tutor (اپ rag). کلاینتها lazy import میشن؛ نبودِ اینها فقط
|
# RAG / AI tutor (اپ rag). کلاینتها lazy import میشن؛ نبودِ اینها فقط
|
||||||
# مسیرِ RAG رو از کار میندازه، نه بقیهی پروژه رو.
|
# مسیرِ RAG رو از کار میندازه، نه بقیهی پروژه رو.
|
||||||
|
|||||||
Reference in New Issue
Block a user