fix(webhook): avoid synchronous SQLite writes in async event route #95
Labels
No labels
blocked
bug
enhancement
high-priority
low-priority
needs-info
needs-triage
ready-for-agent
ready-for-human
referenced
research
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gabogg/hikcentral#95
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
PR #94 review found that the async
/api/event/webhookroute callsDoorStateManager.handle_webhook_event, which performs synchronous SQLite writes (including door transition persistence). Those writes block the event loop during webhook bursts. This predates #94; its new transition write increases the work done on that path.Approach
Move the webhook persistence and broadcast path to async repository methods, or isolate all synchronous work in a worker thread and schedule the WebSocket emission back on the main loop. Preserve the existing event ordering, one-broadcast-per-persisted-transition rule, and error handling.
Verification
Add an ASGI webhook integration test with real SQLite and a concurrent event-loop task to prove webhook persistence does not block the loop. Keep external HikCentral calls stubbed.
Found in PR #94 review.
gabogg referenced this issue2026-09-25 18:14:27 +00:00
PR #112 second-pass review follow-ups (minor; keep this issue open after merge):
app/controllers/webhook_controller.py:36now builds the overview withasyncio.to_thread.get_door_overview()iteratesself.doors.items()whilesync_doors_async()can insert a newly discovered door into that dict. A deterministic reproduction raisesRuntimeError: dictionary changed size during iteration; the webhook then returns{"code":"1"}and omits itsdoor_eventoverview, although its transition has already been persisted and broadcast. Snapshot the door cache or serialize overview construction with cache writes, and add a concurrent polling/webhook test._cardholder_name(), whoseperson_directory.request()requires a running event loop and silently returnsFalsein the worker thread. A card-only webhook therefore does not start lookup during its immediate overview. The periodic monitor can start it later. Schedule the lookup on the event loop and cover the ASGI webhook route with a card-only event.The main #95 event-loop responsiveness and cancellation/broadcast requirements are covered by PR #112.