feat: isolate live snapshot section deadlines (Closes #1429)
This commit is contained in:
1 parent
065cc46553
commit
dc9e64e98e
2 files changed
+115
-16
No files matched your search
+34
-16
@@ -286,6 +286,7 @@ app = FastAPI(title="Stackchain Dashboard", lifespan=lifespan)
|
||||
app.add_middleware(RequestBodyLimitMiddleware, limit_for=request_body_limit)
|
||||
app.add_middleware(NegotiatedGZipMiddleware, minimum_size=1_024)
|
||||
CONTEXT_TIMEOUT_SECONDS = 5.0
|
||||
LIVE_SNAPSHOT_DEADLINE_GRACE_SECONDS = 0.1
|
||||
EVENT_STREAM_TIMEOUT_SECONDS = 5.0
|
||||
READINESS_TIMEOUT_SECONDS = 5.0
|
||||
READINESS_INTERVAL_SECONDS = max(
|
||||
@@ -4832,28 +4833,43 @@ async def _load_context_for_user(user_data: dict) -> dict:
|
||||
async def _build_live_snapshot(sections: set[str] | None = None) -> dict:
|
||||
requested = set(sections or LIVE_SNAPSHOT_SECTIONS)
|
||||
results: dict[str, object] = {}
|
||||
user_data: dict | None = None
|
||||
user_task: asyncio.Task | None = None
|
||||
if requested & {"context", "events"}:
|
||||
user_task = asyncio.create_task(current_user())
|
||||
|
||||
async def load_user_section(section: str) -> object:
|
||||
assert user_task is not None
|
||||
user_data = await asyncio.shield(user_task)
|
||||
if not isinstance(user_data, dict) or not user_data.get("login"):
|
||||
raise ContextPayloadError("Gitea current-user response was invalid")
|
||||
if section == "context":
|
||||
return await _load_context_for_user(user_data)
|
||||
return await activity_events(user_data)
|
||||
|
||||
async def load_before_deadline(load: Awaitable[Any]) -> object:
|
||||
try:
|
||||
user_data = await current_user()
|
||||
if not isinstance(user_data, dict) or not user_data.get("login"):
|
||||
raise ContextPayloadError("Gitea current-user response was invalid")
|
||||
async with asyncio.timeout(CONTEXT_TIMEOUT_SECONDS):
|
||||
return await load
|
||||
except Exception as exc:
|
||||
for section in requested & {"context", "events"}:
|
||||
results[section] = exc
|
||||
return exc
|
||||
|
||||
loads: dict[str, Awaitable[Any]] = {}
|
||||
if "context" in requested and "context" not in results:
|
||||
assert user_data is not None
|
||||
loads["context"] = _load_context_for_user(user_data)
|
||||
if "events" in requested and "events" not in results:
|
||||
assert user_data is not None
|
||||
loads["events"] = activity_events(user_data)
|
||||
if "context" in requested:
|
||||
loads["context"] = load_user_section("context")
|
||||
if "events" in requested:
|
||||
loads["events"] = load_user_section("events")
|
||||
if "notifications" in requested:
|
||||
loads["notifications"] = notifications()
|
||||
if loads:
|
||||
loaded = await asyncio.gather(*loads.values(), return_exceptions=True)
|
||||
results.update(zip(loads, loaded))
|
||||
try:
|
||||
if loads:
|
||||
loaded = await asyncio.gather(
|
||||
*(load_before_deadline(load) for load in loads.values())
|
||||
)
|
||||
results.update(zip(loads, loaded))
|
||||
finally:
|
||||
if user_task is not None and not user_task.done():
|
||||
user_task.cancel()
|
||||
await asyncio.gather(user_task, return_exceptions=True)
|
||||
|
||||
context_result = results.get("context")
|
||||
events_result = results.get("events")
|
||||
@@ -4892,7 +4908,9 @@ async def _build_live_snapshot(sections: set[str] | None = None) -> dict:
|
||||
|
||||
|
||||
async def _build_live_snapshot_before_deadline(sections: set[str]) -> dict:
|
||||
async with asyncio.timeout(CONTEXT_TIMEOUT_SECONDS):
|
||||
async with asyncio.timeout(
|
||||
CONTEXT_TIMEOUT_SECONDS + LIVE_SNAPSHOT_DEADLINE_GRACE_SECONDS
|
||||
):
|
||||
return await _build_live_snapshot(sections)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user