feat(admin): показывать зависшие проверки в панели отладки
All checks were successful
Deploy / test (push) Successful in 3m44s
Deploy / deploy (push) Successful in 8m39s

Нашлись 4 задачи в статусе processing, висящие с 30 мая: пользователь видит
вечное «обрабатывается», а в системе никаких следов. Теперь задачи без
движения дольше 2 часов попадают в /admin/debug рядом с зависшими прогонами
заливки, с указанием, сколько часов они стоят.

tasks.created_at/updated_at — timestamptz, поэтому сравнение идёт с
осведомлённым о зоне временем: naive utcnow() дал бы TypeError в рантайме.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-09-05 19:13:01 +05:00
parent f4092e5331
commit 39951d6a0f

View File

@@ -9,7 +9,7 @@ import io
import logging
import os
import uuid
from datetime import datetime, timedelta
from datetime import UTC, datetime, timedelta
from pathlib import Path
import httpx
@@ -925,6 +925,25 @@ async def debug_snapshot(db: AsyncSession = Depends(get_db)) -> dict:
)
).scalars().all()
# Зависшая проверка: числится в работе, но воркер давно её не трогал.
# Пользователь видит вечное «обрабатывается» — молча и без следов в логах,
# поэтому такие задачи должны быть видны в отладке (нашлись экземпляры,
# висевшие с мая).
# tasks.created_at/updated_at — timestamptz, поэтому сравниваем с
# осведомлённым о зоне временем (naive utcnow() дал бы TypeError)
task_stale_cutoff = datetime.now(UTC) - timedelta(hours=2)
stale_tasks = (
await db.execute(
select(Task)
.where(
Task.status == "processing",
func.coalesce(Task.updated_at, Task.created_at) < task_stale_cutoff,
)
.order_by(Task.created_at.desc())
.limit(20)
)
).scalars().all()
return {
"generated_at": datetime.utcnow().isoformat(timespec="seconds"),
"celery": celery_state,
@@ -956,6 +975,17 @@ async def debug_snapshot(db: AsyncSession = Depends(get_db)) -> dict:
}
for t in failed_tasks
],
"stale_tasks": [
{
"public_id": t.public_id,
"type": t.type,
"created_at": t.created_at,
"stuck_hours": round(
(datetime.now(UTC) - (t.updated_at or t.created_at)).total_seconds() / 3600, 1
),
}
for t in stale_tasks
],
# Ключи бэкендов задаются в .env (генерируется из Infisical) и влияют на
# поведение воркеров — при разборе «почему не так считает» нужны первыми
"config": {