feat: bound bulk notification fan-out (#145)
This commit is contained in:
1 parent
955343a428
commit
36d7fb9ce8
2 files changed
+110
-6
No files matched your search
+32
-6
@@ -53,6 +53,8 @@ EVENT_STREAM_TIMEOUT_SECONDS = 5.0
|
||||
READINESS_TIMEOUT_SECONDS = 5.0
|
||||
REVIEW_DETAIL_TIMEOUT_SECONDS = 5.0
|
||||
NOTIFICATION_MUTATION_TIMEOUT_SECONDS = 5.0
|
||||
BULK_NOTIFICATION_CONCURRENCY = 5
|
||||
BULK_NOTIFICATION_DEADLINE_SECONDS = 6.0
|
||||
LIVE_SNAPSHOT_FRESHNESS_SECONDS = 8.0
|
||||
FRONTEND_DIR = Path(__file__).resolve().parent.parent / "frontend"
|
||||
_live_snapshot_task: asyncio.Task | None = None
|
||||
@@ -444,13 +446,37 @@ async def _mark_notification_read_result(thread_id: int) -> tuple[int, bool]:
|
||||
@app.patch("/api/v1/notifications/read")
|
||||
async def read_notifications(batch: NotificationReadBatch) -> JSONResponse:
|
||||
thread_ids = list(dict.fromkeys(batch.ids))
|
||||
results = await asyncio.gather(
|
||||
*(_mark_notification_read_result(thread_id) for thread_id in thread_ids)
|
||||
|
||||
semaphore = asyncio.Semaphore(BULK_NOTIFICATION_CONCURRENCY)
|
||||
|
||||
async def mark_within_limit(thread_id: int) -> tuple[int, bool]:
|
||||
async with semaphore:
|
||||
return await _mark_notification_read_result(thread_id)
|
||||
|
||||
tasks = [asyncio.create_task(mark_within_limit(thread_id)) for thread_id in thread_ids]
|
||||
done, pending = await asyncio.wait(
|
||||
tasks, timeout=BULK_NOTIFICATION_DEADLINE_SECONDS
|
||||
)
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
if pending:
|
||||
await asyncio.gather(*pending, return_exceptions=True)
|
||||
|
||||
succeeded_ids = {
|
||||
thread_id
|
||||
for task in done
|
||||
if not task.cancelled() and task.exception() is None
|
||||
for thread_id, succeeded in [task.result()]
|
||||
if succeeded
|
||||
}
|
||||
failed = [thread_id for thread_id in thread_ids if thread_id not in succeeded_ids]
|
||||
return JSONResponse(
|
||||
{
|
||||
"marked": [thread_id for thread_id in thread_ids if thread_id in succeeded_ids],
|
||||
"failed": failed,
|
||||
},
|
||||
headers={"Retry-After": "1"} if failed else None,
|
||||
)
|
||||
return JSONResponse({
|
||||
"marked": [thread_id for thread_id, succeeded in results if succeeded],
|
||||
"failed": [thread_id for thread_id, succeeded in results if not succeeded],
|
||||
})
|
||||
|
||||
|
||||
@app.patch("/api/v1/notifications/{thread_id}/read")
|
||||
|
||||
Reference in new issue
Block a user