Skip to content

Benchmark

Measures the overhead added by the idempotency interceptor and the speed of response replay.

What We Measure

BenchmarkDescription
A) POST — no idempotency (baseline)Plain NestJS handler without the interceptor
B) First request — MemoryStorageFull path: header parsing + fingerprint + record creation + handler + response capture
C) Replay — MemoryStorageCache hit: record lookup + fingerprint match + cached response replay (handler skipped)
D) First request — RedisStorageSame as B with Redis (4 round trips: get + create + hgetall + complete)
E) Replay — RedisStorageSame as C with Redis (1 round trip: get only)

Test Setup

  • NestJS: Test app with @nestjs/testing, trivial JSON handler
  • Redis: Docker redis:7-alpine, localhost
  • Iterations: 1,000 per scenario
  • Warmup: 100 iterations (discarded)
  • HTTP client: Raw http.request() (no supertest overhead)

Running Locally

bash
# Memory only (A, B, C)
npx ts-node bench/idempotency.bench.ts --iterations 1000 --warmup 100

# With Redis (A~E)
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

Results

Measured on Windows 11, Node.js 20, Redis 7 (Docker), localhost. Your results will vary.

BenchmarkAvgP50P95P99
A) POST — no idempotency (baseline)0.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

Interpretation

MemoryStorage overhead is negligible. First request (B) adds ~0.04ms to baseline (A) — the cost of SHA-256 fingerprinting, record creation, and response capture. Replay (C) is actually faster than baseline because the handler is skipped entirely.

RedisStorage is dominated by network round trips. First request (D) requires 4 Redis round trips (get → create → hgetall → complete), while replay (E) requires only 1 (get). The ~1ms difference between D and E reflects 3 saved round trips.

Replay saves real handler cost. In this benchmark the handler is trivial, so the savings are modest. In a real application where the handler performs database writes, external API calls, or complex computation, the replay savings scale proportionally — the handler is completely bypassed.

MetricValue
MemoryStorage first-request overhead (B − A)~0.04ms
MemoryStorage replay vs baseline (A − C)~0.03ms faster
RedisStorage network cost (D − B)~1.35ms
RedisStorage replay savings (D − E)~1.03ms (3 RTTs)

Methodology

  • performance.now() for sub-millisecond timing
  • Raw http.request() to avoid supertest assertion overhead
  • Unique run ID (Date.now().toString(36)) prevents key collisions across repeated executions
  • Warmup indices offset from measurement indices to avoid key namespace collision
  • Each scenario runs sequentially (no concurrent interference)

Released under the MIT License.