project-nomad/admin/app
Chris Sherwood e51ead616f fix(queue): singleton QueueService to stop ioredis connection leak
Every static call site instantiated a fresh QueueService (24 call sites
across 8 files). QueueService.getQueue() opens a BullMQ Queue per call
when not cached, and each Queue opens two ioredis connections (one for
commands, one blocking). Because every static call constructed a new
QueueService, its internal `queues` cache was never shared, every call
opened a fresh pair, and none were ever closed.

In normal operation this leaked a few connections per API hit. During
multi-batch ZIM ingestion after PR #872 (where EmbedFileJob.handle()
dispatches the next batch every 50 articles), every batch completion
opened two new connections. On NOMAD3 at ~one batch every 4s sustained,
that's ~1800 leaked connections/hour. Redis hit its 10,000-maxclient
ceiling in ~5 hours and the admin container fell into an EPIPE flood
that required a restart to recover.

Fix: collapse QueueService to a true process-wide singleton with a
private constructor and getInstance() accessor. The existing per-queue
Map is now shared across every dispatch / status / cleanup call, so each
queue's underlying connections are opened exactly once for the lifetime
of the process. close() now clears the map so the singleton can be torn
down cleanly if a graceful-shutdown hook is ever wired up.

Validated on NOMAD3 (RTX 5060, v1.32.0-rc.4 + this patch hot-applied):
under sustained multi-batch wikipedia_en_simple_all_nopic ingestion,
connected_clients held flat at 21-22 across a 5-minute window. Pre-fix
the same scenario climbed to 10,000+ over hours.
2026-05-13 13:48:21 -07:00
..
controllers fix(AI): rewrite RAG query on first follow-up (off-by-one in skip-rewrite threshold) 2026-05-12 20:34:30 -07:00
exceptions fix(Docs): documentation renderer fixes 2025-12-23 16:00:33 -08:00
jobs fix(queue): singleton QueueService to stop ioredis connection leak 2026-05-13 13:48:21 -07:00
middleware fix(API): skip compression for Server-Sent Events (#798) 2026-04-27 19:00:31 -07:00
models feat(Content): custom ZIM library sources with pre-seeded mirrors (#593) 2026-05-04 11:30:59 -07:00
services fix(queue): singleton QueueService to stop ioredis connection leak 2026-05-13 13:48:21 -07:00
utils fix(Downloads): treat missing Content-Type as octet-stream (#848) 2026-05-11 21:09:40 -07:00
validators feat(Content): custom ZIM library sources with pre-seeded mirrors (#593) 2026-05-04 11:30:59 -07:00