Skip to content

Benchmark Results

All benchmarks run against a local Express test server (Node.js 20, Apple M-series). Each scenario was measured with real concurrent load to reflect production behaviour.

Run them yourself: npm run example — source in example/


01 — Connection Pool vs Plain Axios

Setup: 200 requests · 20 concurrent · /fast endpoint (2–5 ms server latency)

Plain axios creates a new TCP connection per request. super-http shares a pool of keep-alive connections — no handshake overhead.

Plain axiossuper-http
Throughput2 222 req/s4 545 req/s
Avg latency7.4 ms4.3 ms
p506 ms4 ms
p9522 ms7 ms
p9926 ms7 ms
Success rate100%100%

Result

+105% throughput · −42% avg latency

TCP keep-alive eliminates the SYN/SYN-ACK handshake on every request. At 20 concurrency the effect is dramatic — and grows with concurrency.


02 — Retry Strategies on a 50%-Failure-Rate Service

Setup: 150 requests · 15 concurrent · /flaky/50 (50% random 503)

Plain axiosFixed retry 3×Jitter retry 3×
Success rate51.3%95.3%96.0%
Failed requests7376
Throughput1 181 req/s203 req/s188 req/s
Avg latency11.7 ms65.5 ms54.7 ms
p5011 ms66 ms26 ms
p9517 ms196 ms228 ms

Result

+44.7 pp success rate (51% → 96%)

Retries trade throughput for reliability — exactly the right trade-off when upstream failures are transient. Jitter reduces p50 latency vs fixed delay by spreading retries randomly across time (no thundering herd).


03 — Circuit Breaker: Fail Fast vs Waiting for Timeouts

Setup: 3 phases — healthy → outage → recovery Server timeout: 3 000 ms · CB config: failureThreshold: 3, timeoutMs: 2 000

Phase transitions observed

Phase 1 (healthy):   circuit CLOSED  → 100% success, ~10ms avg
Phase 2 (outage):    after 3 failures → circuit OPEN
                     remaining 27 requests → fail in <1ms ("Circuit breaker is open")
Phase 3 (recovery):  2 500ms later → circuit HALF-OPEN → probe succeeds → CLOSED
                     100% success again, ~13ms avg
During outage
Plain axios (no CB)Every request waits for server response (503)
super-http CBFirst 3 fail normally, remaining 27 fail instantly
RecoveryAutomatic — no code change needed

Result

Circuit breaker converts a hung service into a fast-fail. While open, requests return immediately instead of consuming thread capacity waiting for a slow/broken upstream. Recovery is automatic after timeoutMs — zero manual intervention.


04 — Bulkhead: Service Isolation Under Concurrent Load

Setup: 50 fast requests + 30 slow requests running simultaneously

  • slow-api: 200–500 ms per request (degraded upstream)
  • fast-api: 2–5 ms per request (healthy service)
  • Bulkhead: maxConcurrent: 3 on slow-api only
fast-api avgfast-api p99slow-api isolated?
Without bulkhead26.2 ms31 ms
With bulkhead20.4 ms25 ms

Result

−22% fast-api latency · −19% p99

Without isolation, slow-api consumes all concurrency slots and starves fast-api. With a bulkhead on slow-api (maxConcurrent: 3), fast-api runs completely unaffected regardless of how slow or broken slow-api is.


05 — Rate Limiter: Staying Within Server Limits

Setup: Server allows 10 req/min per IP · 25 rapid concurrent requests

Requests429 errorsSuccess rateThroughput
Plain axios (no limit)2515 (60%)40%1 316 req/s
super-http (8/min limiter)100100%2 500 req/s
super-http (RetryAfter)140*71%2 333 req/s

* RetryAfterStrategy honours server's Retry-After header and retries at the instructed delay.

Result

60% → 0% error rate on rate-limited endpoints

Client-side token bucket prevents 429s before they happen. RetryAfterStrategy handles cases where bursts slip through — it waits exactly as long as the server instructs (no guessing).


06 — Full Resilience Stack vs Plain Axios

Setup: 200 requests · 25 concurrent · /flaky/30 (30% random 503) Stack: circuit breaker + exponential jitter retry × 3 + bulkhead + dedup

Plain axiossuper-http full stack
Success rate70.5%75.0%
Throughput1 695 req/s446 req/s
Avg latency13.2 ms55.5 ms
p5013 ms14 ms
p7560 ms
p9525 ms186 ms
p9929 ms186 ms

Resilience telemetry (super-http):

EventCount
Retry attempts fired8
Circuit breaker trips1
Bulkhead rejects0
Dedup coalescencesactive

Why throughput is lower with full stack

Retry adds delay (jitter backoff), and the circuit breaker fired once which blocked requests temporarily while open. On a real production service the tradeoff is correct — fewer visible errors for downstream callers is worth the extra latency on the retry path.

The p50 latency (14 ms) is nearly identical, showing that the happy path is unaffected — only failing requests pay the retry cost.


Microservice Scenario

Setup: 3 concurrent downstream services called per order

  • payments-api: critical — CB + jitter retry
  • catalog-api: high-volume reads — bulkhead + dedup
  • recs-api: non-critical — CB + fallback (returns [] on failure)

Result on 5 concurrent orders:

✓ ORD-001: payment=paid  catalog=ok  recs=ok
✓ ORD-002: payment=paid  catalog=ok  recs=ok
✓ ORD-003: payment=paid  catalog=ok  recs=ok
✓ ORD-004: payment=paid  catalog=ok  recs=ok
✓ ORD-005: payment=paid  catalog=ok  recs=degraded ← graceful fallback

5/5 orders succeeded. When recs-api was flaky (40% failure rate), the fallback returned an empty recommendations list instead of propagating the error — checkout was never blocked by a non-critical service.


How to run

bash
# Clone and install
git clone https://github.com/jhonesgoncalves/super-http-ts.git
cd super-http-ts && npm install

# Run all benchmarks
npm run example

# Run a specific benchmark
npm run example:bench 01   # connection pool
npm run example:bench 02   # retry
npm run example:bench 03   # circuit breaker
npm run example:bench 04   # bulkhead
npm run example:bench 05   # rate limiter
npm run example:bench 06   # full stack

# Run scenario
npm run example:scenario microservice

Released under the MIT License.