Skip to content

feat: idempotency TTL sweeper with pressure metrics#478

Merged
thlpkee20-wq merged 2 commits into
Veritasor:mainfrom
Mkalbani:feat/idempotency-sweeper
Jul 8, 2026
Merged

feat: idempotency TTL sweeper with pressure metrics#478
thlpkee20-wq merged 2 commits into
Veritasor:mainfrom
Mkalbani:feat/idempotency-sweeper

Conversation

@Mkalbani

Copy link
Copy Markdown
Contributor

Summary

Adds a cooperative TTL sweeper for the idempotency key store and exposes
storage-pressure metrics so SREs can tune retention.

The in-memory store previously only pruned during set() when
MAX_MEMORY_STORE_SIZE was reached, leaving idle entries to accumulate
otherwise. Redis relied on PEXPIRE for self-eviction but had no gauge
to plot pressure. This change closes both gaps.

Changes

  • IdempotencyStore interface gains optional sweepExpired() and count().
  • inMemoryIdempotencyStore implements both; overflow prune, manual delete
    and TTL sweep now emit idempotency_evictions_total{reason=…}.
  • RedisIdempotencyStore adds a SCAN-based count() (configurable MATCH);
    sweepExpired() is a no-op since PEXPIRE handles it.
  • New IdempotencySweeper: unref'd interval, single-flight runOnce(),
    swallows store errors with structured logging and a metric, awaits
    in-flight cycle on stop().
  • New metrics:
    • idempotency_keys_count (gauge, backend)
    • idempotency_evictions_total (counter, backend+reason)
    • idempotency_sweep_runs_total (counter, backend+outcome)
  • startIdempotencySweeper() / stopIdempotencySweeper() wired into
    startServer() and the shutdown onCleanup hook.
  • IDEMPOTENCY_SWEEP_INTERVAL_MS env var (default 60s, hard floor 1s).
  • README documents TTL semantics, eviction model, and the new metrics.

Tests

  • tests/unit/middleware/idempotency.sweeper.test.ts — 35 new tests
    covering lifecycle, single-flight, error swallowing, interval floor,
    env parsing, backend selection, and in-memory / Redis store
    integration.
  • All 277 middleware tests pass. No existing tests modified.

Security & Correctness Notes

  • Sweeper interval is unref()'d: it cannot keep the event loop alive
    on its own, so process shutdown is never gated on the sweeper.
  • runOnce() swallows store errors and emits
    idempotency_sweep_runs_total{outcome="error"}; a transient Redis
    blip cannot wedge the loop. The next cycle resumes once ioredis
    reconnects.
  • stop() awaits the in-flight cycle, so shutdown handlers can rely
    on a quiescent state.
  • The in-memory store's overflow prune path is unchanged in behavior
    — the only addition is the eviction metric.
  • No changes to existing middleware or routes; the
    RedisIdempotencyStore constructor is backwards compatible (the
    new options arg is optional).

Closes #448

Adds a cooperative IdempotencySweeper that periodically evicts
TTL-expired keys from the in-memory store and exposes storage-
pressure metrics so SREs can tune retention.

- IdempotencyStore gains optional sweepExpired() and count() hooks.
- RedisIdempotencyStore.count() walks the keyspace via SCAN with the
  configurable MATCH pattern; sweepExpired() is a no-op since Redis
  self-evicts via PEXPIRE.
- inMemoryIdempotencyStore.sweepExpired() prunes expired entries and
  emits idempotency_evictions_total{reason="expired"}; set() overflow
  pruning now emits reason="overflow"; delete() emits reason="manual".
- New metrics: idempotency_keys_count (gauge, backend),
  idempotency_evictions_total (counter, backend+reason),
  idempotency_sweep_runs_total (counter, backend+outcome).
- IdempotencySweeper: unref'd interval, single-flight runOnce, error
  swallowing with structured logging and metric, awaits in-flight
  cycle on stop(). Wired into startServer() and the shutdown
  onCleanup hook. Interval is IDEMPOTENCY_SWEEP_INTERVAL_MS
  (default 60s, hard floor 1s).
- README documents TTL semantics and the new metrics.
- Adds tests/unit/middleware/idempotency.sweeper.test.ts (35 tests).
@drips-wave

drips-wave Bot commented Jun 29, 2026

Copy link
Copy Markdown

@Mkalbani Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Mkalbani

Copy link
Copy Markdown
Contributor Author

@thlpkee20-wq hello please review and merge!!

@thlpkee20-wq
thlpkee20-wq merged commit a93240d into Veritasor:main Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add idempotency-key TTL sweeper and storage-pressure metrics

2 participants