Postgres connection pool, clean 503 under overload, time indexes, 4 workers
Postgres here allows 60 connections, but each of the 96 gunicorn threads held its own persistent one: at 100 concurrent users (local Postgres with the same limit) 21% of requests were 500 "remaining connection slots are reserved". Now each worker has a psycopg pool (8), so the same load gives 0 errors and 714 req/s (was 460). - A full pool answers 503 + Retry-After (DRF handler and middleware), not 500. - The AI tutor and the OTP SMS call return their connection before the network wait. - statement_timeout 30 s, idle-in-transaction 60 s; bad pool settings and the old DB_CONN_MAX_AGE stop startup instead of failing every request. - /api/health dbPool: per-worker pool stats and workers x pool + 6 vs max_connections. - Indexes on the columns global counters filter by time (weekly leaderboard, global SMS cap, global AI cap); the leaderboard recomputes once per expiry. - Worker count moves into gunicorn.conf.py (4 on a shared 8-core box). - security doc: Redis item closed (server uses our own Redis since 2026-10-08). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -109,11 +109,6 @@ class WeeklyReportView(APIView):
|
||||
return Response(WeeklyReportResponseSerializer(weekly_report(request.user.id)).data)
|
||||
|
||||
|
||||
# کوتاه تا XPی تازه زود دیده شود، بلند تا هزار کاربرِ همزمان هر بار جدول را
|
||||
# از نو حساب نکنند.
|
||||
LEADERBOARD_CACHE_TTL = 60
|
||||
|
||||
|
||||
class LeaderboardView(APIView):
|
||||
"""
|
||||
GET /api/leaderboard?period= — رتبهبندی هفتگی/ماهانه
|
||||
@@ -142,12 +137,8 @@ class LeaderboardView(APIView):
|
||||
# زنده از فعالیتِ واقعی، با کشِ کوتاه (progress/leaderboard.py). قبلاً از
|
||||
# جدولِ LeaderboardEntry خوانده میشد که فقط با اجرای دستیِ
|
||||
# rebuild_leaderboard پر میشد، پس XPی کاربرِ تازه هیچوقت دیده نمیشد.
|
||||
from django.core.cache import cache
|
||||
|
||||
from .leaderboard import compute_scores
|
||||
scores = cache.get_or_set(
|
||||
f'leaderboard:{period}', lambda: compute_scores(period), LEADERBOARD_CACHE_TTL,
|
||||
)
|
||||
from .leaderboard import cached_scores
|
||||
scores = cached_scores(period)
|
||||
# نام کاربر از accounts.User خونده میشه (فقط برای نمایش، بدون FK واقعی).
|
||||
from accounts.models import User as AccountUser
|
||||
names = dict(
|
||||
|
||||
Reference in New Issue
Block a user