Reference solution
The findings below are ordered by priority.
Findings
1. Session capacity is released immediately after the handshake
Location: src/subscription-gateway.ts:23-56
The acquired permit is released as soon as the connection is accepted and registered. Long-lived sessions therefore consume no capacity, so the gateway can exceed its 500-session limit even though every handshake individually acquired a permit.
Hold the permit for the entire session lifetime and release it exactly once in the common cleanup path after the client is removed and its forwarding resources are joined.
2. Every client receives an unbounded outbound queue
Location: src/subscription-gateway-types.ts:30-32 and src/subscription-gateway.ts:33-37
The queue factory treats an omitted capacity as unbounded, and connect calls it without one. Broker callbacks can therefore keep appending while a slow client blocks in send. The callback also ignores push's overflow result, so it cannot invoke the required slow-client cleanup even if the queue were bounded.
Create each queue with the required capacity of 64. When push returns full, close that connection with the slow-client reason and run its normal subscription, queue, forwarder, and permit cleanup without affecting others.
3. Broadcast holds the clients lock while waiting for network delivery
Location: src/subscription-gateway.ts:69-76
The exclusive lock remains held through every awaited connection.send. One slow client blocks registration, cleanup, shutdown snapshots, and delivery to every later matching client. Direct sends also bypass the per-client queue's ordering and overflow policy.
Take a stable snapshot of matching sessions under the lock, release it, and enqueue to each session outside the critical section. If an enqueue reports full, invoke that client's slow-client cleanup independently.
4. Abrupt disconnect or forwarding failure skips session cleanup
Location: src/subscription-gateway.ts:46,58-66,89-93
Removal, unsubscribe, queue closure, and forwarder joining occur only when close.orderly is true. An abrupt network loss therefore retains the session. A rejected forwarding task is also not observed while connect waits for connection.closed, allowing an unhandled rejection and retaining the same owned resources until some unrelated close.
Observe both transport closure and forwarding completion, initiate the appropriate connection close when forwarding fails, and run one idempotent cleanup path in a finally block for every outcome. Close the queue and consume the forwarder's result before releasing capacity.
5. Shutdown misses handshakes and does not join forwarding work
Location: src/subscription-gateway.ts:23-55,79-93
The shutdown snapshot contains only registered clients. A connection can pass the early stopping check, remain in accept, and register after that snapshot, escaping shutdown entirely. For captured sessions, shutdown only awaits transport close calls; it never closes queues, unsubscribes, joins forwarding work, or enforces shutdownTimeoutMilliseconds.
Track handshakes from permit acquisition, recheck shutdown before registration, and include accepted or handshaking sessions in the shutdown join. Initiate the common cleanup for every owned session and await connection closure and every forwarder within the five-second deadline.
6. The test covers only an already-orderly close
Location: src/subscription-gateway.test.ts:17-49
The connection's close promise and empty queue complete immediately. The test cannot detect early permit release, queue overflow, a slow broadcast under lock, abrupt disconnect leaks, or shutdown with a blocked forwarder.
Add controlled tests that keep sessions open, fill a 64-entry queue, block one client send while another progresses, resolve an abrupt close, and hold forwarding work across shutdown and its deadline.
Reasonable non-findings
- The handshake failure path unsubscribes, closes the queue, and releases its permit before rethrowing.
- Rechecking
stopping after acquiring capacity correctly prevents a waiter from becoming a new session during shutdown.
- Filtering broadcasts by subscribed topic is required and correctly implemented.
- Sequential sends within one client's forwarding loop preserve that client's message order.
- The transport safely serializes concurrent
send calls by contract; the issue is waiting for sends under the shared clients lock.