Spike readiness: AI gets its own slice of threads, cheaper hot paths, classroom-friendly login
- /api/ai/ask: at most AI_MAX_CONCURRENT_PER_WORKER (12 of 32 threads) answers per worker; beyond that 503 assistant_busy + Retry-After before any quota is reserved. Without it a burst of questions (3-40 s each) took every thread and the map and quizzes queued behind. The slot is held until the stream is actually finished or closed. - Answer submission 21 -> 14 queries, 15.4 -> 11.0 ms: one query for all choices, locked get_or_create for node progress and streak, and today's streak recorded once per day via the cache instead of a transaction per answer. - /api/home 12.4 -> 8.6 ms: the user's completions are read by user_id instead of an IN list of ~600 lesson ids; the dict is returned directly (serializer stays the Swagger contract, HomeMatchesItsSerializerTests keeps them equal). - otp_login per-IP 30 -> 300/hour: a class of 30 on one school Wi-Fi filled it; guessing is bounded per code and per phone. - /api/health aiConcurrency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,8 +20,7 @@ def record_activity(user_id, today=None):
|
||||
"""امروز را روزِ فعال ثبت میکند و استریک را جلو میبرد. idempotent در یک روز."""
|
||||
today = today or timezone.localdate()
|
||||
with transaction.atomic():
|
||||
StreakRecord.objects.get_or_create(user_id=user_id)
|
||||
streak = StreakRecord.objects.select_for_update().get(user_id=user_id)
|
||||
streak, _ = StreakRecord.objects.select_for_update().get_or_create(user_id=user_id)
|
||||
|
||||
last = (
|
||||
ActiveDay.objects.filter(streak=streak)
|
||||
|
||||
Reference in New Issue
Block a user