Parent
#27
What to build
Redesign start_handling_loop() to dispatch multiple handler invocations concurrently, gated by a configurable semaphore.
Concurrent polling model: The loop runs multiple concurrent poll-dispatch cycles. Each cycle: acquire the semaphore → await poll_ki_call() → on HANDLE, spawn an asyncio.create_task that runs the handler, posts the response back to the SC, and releases the semaphore. The loop immediately starts a new cycle (acquiring the semaphore again). This bounds both in-flight polls and running handlers to max_concurrent_handlers.
Configuration: start_handling_loop() accepts a max_concurrent_handlers: int = 10 parameter that sets the semaphore size.
Error handling: When a handler task raises an exception: log the error (including KI name and request context), post an empty binding set back to the SC (to prevent it from hanging), release the semaphore, and continue. The handling loop must not crash due to a single handler failure.
Graceful shutdown: When any poll returns PollResult.EXIT, stop issuing new polls and await all in-flight handler tasks to completion before returning from start_handling_loop(). No timeout — handlers run to completion.
Event loop reference: Store a reference to the running event loop (asyncio.get_running_loop()) on the KnowledgeBase instance when start_handling_loop() begins. This will be used by the sync bridge in a subsequent slice.
The loops parameter for limiting iteration count (used in testing) should be preserved with semantics adapted for the concurrent model.
Acceptance criteria
Blocked by
Parent
#27
What to build
Redesign
start_handling_loop()to dispatch multiple handler invocations concurrently, gated by a configurable semaphore.Concurrent polling model: The loop runs multiple concurrent poll-dispatch cycles. Each cycle: acquire the semaphore →
await poll_ki_call()→ on HANDLE, spawn anasyncio.create_taskthat runs the handler, posts the response back to the SC, and releases the semaphore. The loop immediately starts a new cycle (acquiring the semaphore again). This bounds both in-flight polls and running handlers tomax_concurrent_handlers.Configuration:
start_handling_loop()accepts amax_concurrent_handlers: int = 10parameter that sets the semaphore size.Error handling: When a handler task raises an exception: log the error (including KI name and request context), post an empty binding set back to the SC (to prevent it from hanging), release the semaphore, and continue. The handling loop must not crash due to a single handler failure.
Graceful shutdown: When any poll returns
PollResult.EXIT, stop issuing new polls andawaitall in-flight handler tasks to completion before returning fromstart_handling_loop(). No timeout — handlers run to completion.Event loop reference: Store a reference to the running event loop (
asyncio.get_running_loop()) on theKnowledgeBaseinstance whenstart_handling_loop()begins. This will be used by the sync bridge in a subsequent slice.The
loopsparameter for limiting iteration count (used in testing) should be preserved with semantics adapted for the concurrent model.Acceptance criteria
start_handling_loop()acceptsmax_concurrent_handlersparameter (default 10)max_concurrent_handlersin-flightloopsparameter still works for testinguv run ruff check .passesBlocked by