Originally created by: fu351
WebhookAuditSink (src/doberman/storage/sinks.py, [#317]) starts a daemon worker thread whose loop is while True with no stop signal. Nothing crashes — daemon threads die at interpreter exit — but there is no way to stop or reconfigure a sink without killing the process, and every test that constructs an active sink leaks a live worker thread for the rest of the pytest run (roughly 15 of the 41 tests in tests/unit/test_webhook_audit_sink.py do).
What to do
close(): set a threading.Event the worker checks each loop iteration (a small timeout on queue.get lets the worker notice it), then join with a bounded timeout. Idempotent; emit() after close is a no-op, never an error.test_webhook_audit_sink.py.test_drop_oldest_when_queue_full and test_drop_counter_increments_on_each_overflow sync on time.sleep(0.05); the same Event lets them wait deterministically instead of racing CI load.Thread.start() raises inside __init__, the failure isn't cached, so every later decision retries the config read and thread spawn. Caching the failure as an inert sink keeps resource pressure from turning into a retry storm.The decision-path contract doesn't change: emit() stays non-blocking, sink failures stay swallowed, and close() is never called from the decision path.
Ticket changed by: fu351