61 lines
5.4 KiB
Markdown
61 lines
5.4 KiB
Markdown
# لودتستِ دستیار
|
||
|
||
سه اسکریپت، برای اینکه «چند دانشآموزِ همزمان» هر بار با یک روش سنجیده شود. خروجیِ هر اجرا یک
|
||
JSON است با زمانِ هر پرسش و هر درخواستِ صفحهی عادی؛ جمعبندی در خودِ خروجیِ ترمینال چاپ میشود.
|
||
|
||
| فایل | کار |
|
||
|---|---|
|
||
| `mkusers.py` | کاربرانِ آزمایشیِ غیرمهمان (شمارهی جعلیِ `0900…`، بیپیامک، بیسیگنال) با توکن و سه سوالِ متفاوت از بانکِ سوال برای هر کدام |
|
||
| `loadtest.py` | N نفر همزمان میپرسند (استریم، دقیقاً مثلِ فرانت) و B نفرِ دیگر در خانه/آزمون/خلاصه/سهمیه میچرخند |
|
||
| `fake_upstream.py` | جایگزینِ GapGPT و Qdrant برای آزمایشگاه: embedding، جوابِ استریم با زمانِ لاگنرمال (پیشفرض: میانهی ۵٫۲ و صدکِ ۹۰ِ ۱۳ ثانیه، از جوابهای واقعیِ سرور) و جستوجوی Qdrant |
|
||
|
||
## آزمایشگاه (بیهزینه، روی لپتاپ)
|
||
|
||
روی یک کپی از پروژه اجرا کنید تا دیتابیسِ اصلیِ لوکال پر نشود. Qdrant هم شبیهسازی میشود، چون
|
||
از لپتاپ پهنای باندِ بالارفتن (هر بردار ~۳۰ کیلوبایت) جستوجوها را سریالی نشان میدهد؛ از خودِ
|
||
سرور ۲۰ جستوجوی همزمان ۰٫۲۶ ثانیه است.
|
||
|
||
```bash
|
||
python tools/loadtest/fake_upstream.py & # :8901 (FAKE_SIGMA=0 یعنی همیشه ۵٫۲ ثانیه)
|
||
export DJANGO_DEBUG=0 CACHE_URL= NUM_PROXIES=0 AI_DAILY_QUOTA_FREE=1000 HEARTS_ENFORCED=0 RAG_FORCE_MOCK=0 \
|
||
DJANGO_ALLOWED_HOSTS=127.0.0.1 OPENAI_BASE_URL=http://127.0.0.1:8901/v1 OPENAI_API_KEY=x \
|
||
QDRANT_URL=http://127.0.0.1:8901 QDRANT_API_KEY=x
|
||
LT_N=150 python manage.py shell -c "exec(open('tools/loadtest/mkusers.py').read())" | grep ^LTJSON | cut -c7- > users.json
|
||
gunicorn --bind 127.0.0.1:8800 config.wsgi:application & # همان gunicorn.conf.py ِ سرور
|
||
python tools/loadtest/loadtest.py http://127.0.0.1:8800 users.json 50 10 0 out.json --writes
|
||
```
|
||
|
||
پیش از هر اجرا gunicorn را از نو بالا بیاورید (کشِ جوابِ هر worker خالی شود) و مطمئن شوید پروسهی
|
||
قبلی واقعاً رفته است؛ وگرنه سوالهای تکراری از کش در ۰٫۱ ثانیه جواب میگیرند و عدد دروغ میگوید.
|
||
|
||
## سرورِ اصلی (هزینهی واقعی: ~۰٫۱۸ سنت برای هر جواب)
|
||
|
||
۱. بکاپ از دیتابیس (`docs/operations.md`). ۲. کاربرانِ آزمایشی را روی سرور با `mkusers.py` بسازید
|
||
(`LT_PREFIX` یک پیشوندِ جعلی که روی سرور نیست) و خروجی را فقط در یک فایلِ 600 نگه دارید.
|
||
۳. `loadtest.py` را با `https://mathapi.tadrisino2.ir` و بدونِ `--writes` اجرا کنید.
|
||
۴. زمانِ داخلِ GapGPT برای هر پیام در `ai_tutor.Message.latency_ms` است؛ اختلافِ آن با زمانِ کلاینت
|
||
یعنی کارِ سرورِ ما و صفِ انتظار برای نخِ آزاد. ۵. `manage.py delete_users --ids … --yes` و پاک کردنِ
|
||
فایلِ توکنها.
|
||
|
||
سقفِ ساعتیِ دستیار برای مهمانها بر اساسِ IP است؛ با کاربرِ مهمان از یک ماشین بیش از ~۲۰ پرسش در
|
||
ساعت ممکن نیست، برای همین `mkusers.py` کاربرِ غیرمهمان میسازد.
|
||
|
||
## بازیکنها (نه دستیار): `mkplayers.py`، `play.py`، `agg.py`
|
||
|
||
هر کاربرِ مجازی خانه ← آزمونِ یک جزیره ← ۴ جواب ← درس ← خلاصه را میچرخد، با مکثِ تصادفیِ ۱ تا THINK_MAX ثانیه.
|
||
کاربرها اشتراکِ آزمایشیِ یکروزه میگیرند (بی دیوارِ پرداخت و بی کمبودِ قلب). روی سرور، مستقیم روی سوکتِ gunicorn:
|
||
|
||
```bash
|
||
cd ~/mathapp-v2 && mkdir -m 700 .lt # اول بکاپِ دیتابیس
|
||
LT_N=1000 LT_PREFIX=0900770 venv/bin/python manage.py shell -c "exec(open('tools/loadtest/mkplayers.py').read())" \
|
||
| grep ^LTJSON | cut -c7- > .lt/users.json
|
||
for p in $(seq 0 9); do venv/bin/python tools/loadtest/play.py /run/gunicorn/mathapp.sock .lt/users.json .lt/out-$p.json \
|
||
"300:40,600:40,1000:60" 5 40 $p 10 & done; wait
|
||
venv/bin/python tools/loadtest/agg.py ".lt/out-*.json" "300:40,600:40,1000:60"
|
||
```
|
||
|
||
**مولد را چندپروسهای اجرا کنید (آخرین دو آرگومان).** یک پروسهی httpx با ۶۰۰ اتصال خودش گلوگاه شد: کلاینت ۲۱ ثانیه
|
||
دید درحالیکه لاگِ اپ برای همان درخواستها ۰٫۲ تا ۰٫۵ ثانیه میگفت (۲۰۲۶-۱۰-۱۳). همیشه `duration_ms`ِ `logs/app.log`
|
||
(به وقتِ UTC) را با عددِ کلاینت مقایسه کنید. پاک کردن: `delete_users` از راهِ پایتون روی `phone__startswith=LT_PREFIX`
|
||
(فهرستِ id از `manage.py shell` با خطِ هشدار قاطی میشود)، کشِ رتبهبندی، و `rm -rf .lt`.
|