uvicorn: ограничить graceful shutdown, чтобы SSE не вешал reload

После запуска dev сайт уходил в вечную загрузку без единой ошибки в логе. Открытая
вкладка держит SSE-поток /api/events, и сервер его сам не закрывает. При любой правке
.py (pull, переключение ветки, мёрж) uvicorn --reload останавливает старый процесс,
а тот в graceful shutdown ждёт закрытия всех соединений. Лимита по умолчанию нет,
поэтому ожидание длится вечно: новый процесс не стартует, слушающий сокет остаётся
у reloader'а, соединения принимаются в backlog и никем не обслуживаются. Запрос
/api/users/me висит, RequireAuth крутит спиннер, в логе только "Reloading...".

Воспроизведено тем же способом, каким запускает run.ps1 (uvicorn в отдельном окне):
при открытом SSE и тронутом .py /api/health не отвечал, хотя TCP-соединение
устанавливалось за 12 мс. Сервер ожил ровно в момент закрытия SSE.

Теперь uvicorn запускается с --timeout-graceful-shutdown: по истечении лимита он
отменяет висящие задачи запросов и доводит перезапуск до конца. В dev лимит 2 с, и
тот же сценарий отвечает 200 примерно через 4 с после правки файла. В entrypoint
лимит 10 с: в контейнере тот же механизм держал остановку до SIGKILL по
stop_grace_period (30 с), и lifespan-shutdown не выполнялся. Команды ручного запуска
в README дополнены тем же флагом.

#47

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
This commit is contained in:
2026-09-13 12:18:25 +03:00
co-authored by Claude Opus 5
parent aabcc106d3
commit b4663796a3
5 changed files with 23 additions and 6 deletions
+4 -1
View File
@@ -112,9 +112,12 @@ switch ($appEnv) {
}
Write-Host "Starting backend (uvicorn --reload) and frontend (vite) in separate windows..." -ForegroundColor Green
# --timeout-graceful-shutdown: an open tab keeps the SSE stream /api/events forever,
# and on reload the old process waits for ALL connections to close - with no limit
# the reload never finishes and the site hangs on loading with nothing in the log.
Start-Process powershell -ArgumentList @(
"-NoExit", "-Command",
"Set-Location '$backend'; & '$py' -m uvicorn app.main:app --reload"
"Set-Location '$backend'; & '$py' -m uvicorn app.main:app --reload --timeout-graceful-shutdown 2"
)
Start-Process powershell -ArgumentList @(
"-NoExit", "-Command",