fix: stability improvements on event loop

This commit is contained in:
Alex committed 2026-09-14 12:52:15 +01:00
1 parent e889392eba
commit fb66a0b230
14 files changed
+322 -134

No files matched your search

+22 -1
View File
@@ -3,12 +3,14 @@
from __future__ import annotations
import logging
from typing import Any, AsyncIterable, Awaitable, Callable, Optional
from typing import Any, AsyncIterable, AsyncIterator, Awaitable, Callable, Optional
import anyio
from starlette.responses import StreamingResponse
from starlette.types import Receive, Scope, Send
from docsgpt.streaming.sse_leases import StreamLease, hold_lease
logger = logging.getLogger(__name__)
# Upper bound on each cleanup step. Cleanup is shielded from the cancellation
@@ -16,6 +18,15 @@ logger = logging.getLogger(__name__)
# hold the request task, and a graceful shutdown, indefinitely.
_CLEANUP_TIMEOUT_SECONDS = 5.0
# Headers for the browser-facing SSE streams (notifications, chat reconnect).
SSE_HEADERS = {
"Cache-Control": "no-store",
"X-Accel-Buffering": "no",
"Connection": "keep-alive",
# Marks the response as served by the event loop. Purely diagnostic.
"X-SSE-Transport": "async",
}
class ClosingStreamingResponse(StreamingResponse):
"""``StreamingResponse`` that always finalizes its stream.
@@ -50,6 +61,16 @@ class ClosingStreamingResponse(StreamingResponse):
await _bounded(self._on_close, "response on_close hook")
def sse_response(stream: AsyncIterator[str], lease: Optional[StreamLease]) -> ClosingStreamingResponse:
"""Serve ``stream`` as SSE, refreshing ``lease`` between frames and releasing it however the response ends."""
return ClosingStreamingResponse(
hold_lease(stream, lease),
media_type="text/event-stream",
headers=SSE_HEADERS,
on_close=lease.release if lease is not None else None,
)
async def _bounded(step: Callable[[], Awaitable[None]], what: str) -> None:
"""Run one cleanup step shielded from cancellation, giving up after the cleanup timeout."""
with anyio.move_on_after(_CLEANUP_TIMEOUT_SECONDS, shield=True) as scope: