Skip to content

Ephemeral consumer leak: thousands of never-delivered consumers accumulate on production (5,099 created within seconds) #33

Description

@majkelx

Observation (production OCA broker, 2026-07-22)

JetStream account state: 8,838 consumers, including 6,556 on the tic_status stream alone. Creation-date histogram of tic_status consumers (from /jsz?consumers=true):

2026-06-24: 5,099   ← burst, created within seconds (~21:45:15)
2026-06-17:   276
2026-06-25:   230
2026-05-20:   138
...

5,099 consumers created in one burst on 2026-06-24 — the day the halina service (serverish 2.0.5 client) was restarted. This looks like a creation retry storm: consumer-create requests being retried in a loop where each attempt actually succeeds server-side (response lost/slow?), each retry registering a fresh ephemeral consumer. Pattern-wise a cousin of #30's "retry forever on 4xx" (fixed), but for the create path + non-idempotent creates.

These consumers show Last Delivery: never and survive for a month+. Note: consumers created by current serverish 2.0.5 carry inactive_threshold: 300s (observed on a live one), so month-old leftovers either predate that default or were created through a path that doesn't set it — worth auditing both.

Impact

  • 6.5k consumers on one stream degrade JS API responsiveness for every client of that broker (slow consumer-create/info responses → more client-side timeouts → more retries — self-reinforcing).
  • Operationally invisible until someone lists consumers.

Suggested fixes

  1. Idempotent creates: name ephemeral consumers deterministically per reader instance (client-generated name), so a retried create with the same name is a no-op server-side instead of a new consumer (JetStream treats same-name create as idempotent update).
  2. Ensure every consumer-creation path sets inactive_threshold (bounded, e.g. 300 s), so even leaked ones self-destruct.
  3. MsgReader.close() should explicitly delete its ephemeral consumer (best effort) instead of relying on inactivity GC.
  4. One-off ops cleanup script for existing brokers: delete consumers with delivered.last == never older than N days (happy to contribute).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions