Tail-batch P2 — lifecycle + concurrency + streaming proxy + lock-free emit¶
Motivation¶
Four small but cumulative wins, batched into one commit so the surface area is easy to review together:
frontend_proxy._clientwas never closed on uvicorn shutdown.monitor_cacheandstorage_usage_cachespawned a brand-newthreading.Threadfor every background refresh — under SSE + multiple monitor routes that meant 10+ thread spawns per dashboard tick.event_emitter._get_clientpaid a lock acquire on everyemit()even after the singleton was already cached.aks/openapi proxybuffered the entire upstream response withupstream.contentbefore returning; large BLAST result files multiplied through the api sidecar's RAM under concurrent clients.
User-facing change¶
None. Lower steady-state thread/RAM footprint; clean keep-alive shutdown for uvicorn reloads.
API / IaC diff¶
api/routes/frontend_proxy.pyaddsclose_client(), called fromapi/main.py::_lifespan's finally block.api/services/monitor_cache.pyandapi/services/storage_usage_cache.pyroute every_start_refresh_threadsubmission through a sharedThreadPoolExecutor(default 8 / 4 workers, env-overridable). Pools are torn down viaatexit. Fallback daemon-thread path keeps the contract during the shutdown window.api/services/event_emitter.py::_get_clientadds a lock-free cached-singleton fast path; double-checked locking inside.api/routes/aks/openapi.py::aks_openapi_proxyswitches toclient.build_request+await client.send(req, stream=True)and returns aStreamingResponse(_body_iter())that closes the upstream response and the temporary AsyncClient in itsfinally.
Validation¶
uv run pytest -q api/tests/test_security_audit_bundle.py api/tests/test_monitor_cache.py api/tests/test_storage_usage_cache.py api/tests/test_event_emitter.py api/tests/test_external_blast_api.py— passes.uv run ruff checkon all four modules +api/main.py— clean.