Skip to content

Benchmark

The benchmark script measures the cost of the interceptor and the latency difference between first execution and replay.

Scenarios

ScenarioDescription
A) POST, no idempotencyPlain NestJS handler without the interceptor.
B) First request, MemoryStorageHeader parsing, stable fingerprint, create lock, run handler, capture response.
C) Replay, MemoryStorageLookup, fingerprint match, replay cached response. Handler is skipped.
D) First request, RedisStorageSame as B with Redis-backed shared storage.
E) Replay, RedisStorageSame as C with Redis-backed shared storage.
F) First request, PostgresStorageSame as B with Postgres-backed shared storage.
G) Replay, PostgresStorageSame as C with Postgres-backed shared storage.

Test setup

  • NestJS test application with a small JSON handler.
  • Raw http.request() client to avoid supertest assertion overhead.
  • Unique run id to prevent key collisions across repeated benchmark runs.
  • Warmup iterations are offset from measured iterations.
  • Redis and Postgres scenarios run only when their connection URLs are provided.

Running locally

bash
# Memory only: A, B, C
npx ts-node bench/idempotency.bench.ts

# Longer local run
npx ts-node bench/idempotency.bench.ts --iterations 1000 --warmup 100

Run Redis scenarios:

bash
docker run -d --name redis-bench -p 6379:6379 redis:7-alpine
npx ts-node bench/idempotency.bench.ts \
  --iterations 1000 \
  --warmup 100 \
  --redis-url redis://localhost:6379
docker stop redis-bench
docker rm redis-bench

Run Postgres scenarios:

bash
docker run -d --name idem-pg-bench \
  -e POSTGRES_USER=test \
  -e POSTGRES_PASSWORD=test \
  -e POSTGRES_DB=idempotency_bench \
  -p 5432:5432 \
  postgres:16-alpine

npx ts-node bench/idempotency.bench.ts \
  --iterations 1000 \
  --warmup 100 \
  --postgres-url postgresql://test:test@localhost:5432/idempotency_bench

docker stop idem-pg-bench
docker rm idem-pg-bench

Example results

These numbers are from a previous local run on Windows 11, Node.js 20, Redis 7 in Docker, localhost. Treat them as an order-of-magnitude reference, not a promise for your hardware or network.

ScenarioAvgP50P95P99
A) POST, no idempotency0.28ms0.25ms0.39ms0.57ms
B) First request, MemoryStorage0.32ms0.30ms0.41ms0.53ms
C) Replay, MemoryStorage0.25ms0.24ms0.33ms0.44ms
D) First request, RedisStorage1.67ms1.61ms2.02ms2.34ms
E) Replay, RedisStorage0.64ms0.61ms0.82ms1.08ms

The docs do not publish a canonical Postgres number yet. The source benchmark includes Postgres scenarios F and G; run them against your own database topology because pool location, TLS, disk, and network latency dominate the result.

Interpretation

MemoryStorage overhead is small. In the example run, first-request overhead is roughly the cost of stable JSON fingerprinting, storage record creation, and response capture.

Replay can be faster than baseline. Replayed requests skip the controller handler entirely. The more work your handler does, such as database writes or external API calls, the more replay helps.

Redis and Postgres are dominated by storage latency. First execution needs create and complete operations. Replay usually needs only the storage read plus JSON/header replay.

The benchmark is not a substitute for a production load test. Measure with your adapter, your deployment topology, realistic response sizes, and representative handler work.

What to watch

MetricWhy it matters
First-request overheadAdded latency on successful non-duplicate writes.
Replay latencyUser-perceived retry speed when a client times out and retries.
409 conflict rateToo many in-flight duplicates may indicate aggressive client retries or a too-long handler.
422 mismatch rateReused keys with different payloads may indicate client key generation bugs.
complete_error eventsStorage instability can prevent replay and should be investigated.
stale eventsProcessing TTL may be too short for the endpoint's real runtime.

Released under the MIT License.