- 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>
37 lines
2.8 KiB
Python
37 lines
2.8 KiB
Python
"""
|
||
تنظیماتِ gunicorn — خودِ gunicorn این فایل را از پوشهی کاری
|
||
(WorkingDirectory=~/mathapp-v2 در unitِ systemd) خودکار میخواند، پس بدونِ
|
||
دست زدن به /etc با همان deploy.sh به سرور میرسد. آرگومانهای خطِ فرمانِ
|
||
unit (مثلِ `--workers 3` و `--bind`) بر این فایل مقدماند.
|
||
|
||
چرا لازم شد (۲۰۲۶-۰۹-۲۴): unit با workerهای sync و timeoutِ پیشفرضِ ۳۰
|
||
ثانیه اجرا میشد. هر استریمِ «از پی بپرس» یک worker را تا پایانِ جواب (~۱۰
|
||
ثانیه، چون GapGPT خودش تا آماده شدنِ کلِ جواب نگه میدارد) اشغال میکرد؛ سه
|
||
دانشآموزِ همزمان با دستیار یعنی بقیهی اپ — نقشه، آزمون، همهچیز — در صف.
|
||
جوابِ بالای ۳۰ ثانیه هم وسطِ کار کشته میشد.
|
||
|
||
gthread هر worker را چند نخی میکند: ۳ worker × ۸ نخ = ۲۴ درخواستِ همزمان.
|
||
کدِ جنگو برای نخ امن است و SQLite با `OPTIONS.timeout=20` نوشتنِ همزمان را
|
||
تحمل میکند. timeout در gthread به ضربانِ خودِ worker است، نه طولِ هر
|
||
درخواست، پس استریمِ طولانی دیگر کشته نمیشود.
|
||
|
||
۸ → ۳۲ نخ (۲۰۲۶-۱۰-۰۴، لودتست): هر جوابِ دستیار یک نخ را تا آخرِ جواب نگه میدارد
|
||
(روی سرور میانه ۵ و p90 ۱۳ ثانیه). با ۳×۸ از حدودِ ۲۰ پرسشِ همزمان، صفحههای عادیِ اپ
|
||
پشتِ استریمها صف کشیدند (تا ۲۳ ثانیه در ۵۰ پرسش؛ فرانت بعد از ۱۵ ثانیه خطا میدهد)، چون
|
||
gthread اتصال را حتی وقتی همهی نخهایش مشغولاند میپذیرد و workerها یکنواخت پر نمیشوند.
|
||
با ۳×۳۲ تا ۷۵ پرسشِ همزمان صفحهها زیرِ ۱۱۰ میلیثانیه ماندند. این روی لپتاپ با مدل و
|
||
Qdrantِ شبیهسازیشده سنجیده شد؛ CPUِ مشترکِ سرور و رفتارِ GapGPT زیرِ این بار جدا
|
||
سنجیده نشدهاند. نخِ منتظرِ شبکه تقریباً هزینه ندارد؛ هر نخ اتصالِ SQLiteِ خودش را دارد.
|
||
"""
|
||
|
||
worker_class = 'gthread'
|
||
threads = 32
|
||
timeout = 120
|
||
graceful_timeout = 30
|
||
keepalive = 5
|
||
|
||
# worker بعد از چند هزار درخواست تازه میشود تا نشتِ حافظهی احتمالی جمع
|
||
# نشود؛ jitter نمیگذارد هر سه با هم ریاستارت شوند.
|
||
max_requests = 2000
|
||
max_requests_jitter = 200
|