hedge-fetch
v0.9.0
Published
An adaptive, speculative request-hedging library that automatically cuts p95 tail latency.
Maintainers
Readme
hedge-fetch
Adaptive, speculative request hedging for the modern web. hedgehog-fetch is a high-performance wrapper around the Fetch API designed to eliminate "tail latency" (the slow P95/P99 requests). By intelligently firing a second "speculative" request when the first one takes too long, Hedgehog ensures your users never wait on a stray slow server.
Key Features
- Adaptive P95 Delay: No hardcoded timeouts. Hedgehog learns your network's latency and hedges exactly when a request is statistically "late."
- Idempotency Safety: Automatically handles
Idempotency-Keyheaders forPOSTrequests to prevent duplicate server-side actions. - Zero Leakage: Uses modern
AbortSignal.any()to ensure that once a winner is found, the loser is aborted immediately—no dangling connections. - Plugin-Ready Buckets: Ship with a local token bucket, or plug in Redis to coordinate hedging budgets across a global cluster.
- Developer Visibility: Built-in hooks and response decoration (
res.isHedged) for deep observability.
Installation
npm install hedge-fetchQuick Start
import { HedgedContext, LocalTokenBucket, LatencyTracker } from 'hedge-fetch';
// 1. Initialize the context
const hedge = new HedgedContext(
new LocalTokenBucket(10), // Allow 10% hedging overhead
new LatencyTracker() // Adaptive learning
);
// 2. Use it just like native fetch
const response = await hedge.fetch('https://api.example.com/data', {
timeoutMs: 5000, // Global safety net
onHedge: () => console.log('Hedging triggered!')
});
// 3. Check if the hedge saved the day
if ((response as any).isHedged) {
console.log('Speculative request won!');
}
Deep Dive: How it Works
1. The Adaptive Delay (P95 Algorithm)
Instead of guessing a timeout (e.g., "wait 200ms"), Hedgehog uses the LatencyTracker. It maintains a sliding window of recent request durations and calculates the 95th percentile. If your primary request hasn't responded by the P95 mark, it is statistically likely to be a "tail latency" request, and Hedgehog fires the speculative request.
2. Idempotency & Safety
Hedging POST or PATCH requests is usually dangerous. Hedgehog makes it safe:
- Safe Methods:
GET,HEAD,OPTIONSare hedged by default. - Unsafe Methods:
POSTis only hedged ifforceHedge: trueis passed. - Auto-Key: If enabled, Hedgehog generates a
UUIDand attaches it to theIdempotency-Keyheader, ensuring your backend doesn't process the same action twice.
3. Distributed Budgets (Redis)
To prevent your fleet of servers from DDOSing your own backend during a slowdown, Hedgehog uses a Token Bucket. You can implement the IHedgeBucket interface to sync this budget across multiple instances using Redis.
class RedisBucket implements IHedgeBucket {
async canHedge() {
const tokens = await redis.get('hedge_tokens');
return parseInt(tokens) > 0;
}
// ...
}
Technical Reference
HedgeFetchOptions
Extends the standard RequestInit with:
| Option | Type | Description |
| --- | --- | --- |
| timeoutMs | number | The global safety net. Aborts everything if no response in X ms. |
| forceHedge | boolean | Bypass safety checks for non-idempotent methods. |
| onHedge | () => void | Callback when the speculative request is fired. |
| onPrimaryWin | (ms) => void | Callback when the first request succeeds. |
| onSpeculativeWin | (ms) => void | Callback when the second request succeeds. |
Response Decoration
Successful responses from a speculative request are decorated with a non-enumerable property:
const res = await hedge.fetch(...);
console.log(res.isHedged); // true if the speculative request won
Best Practices
- Backend Support: Ensure your backend ignores duplicate
Idempotency-Keyheaders for the best experience. - Budgeting: Start with a 5-10% budget (
LocalTokenBucket) to improve latency without significantly increasing server cost. - Global Timeouts: Always set a
timeoutMsto prevent "hanging" UI states in extreme network failure scenarios.
Contributing
Contributions are welcome! If you have ideas for new resilience patterns (like Rate Limiting or Timeouts), feel free to open an issue or a PR.
License
MIT © Ali nazari
Built with ❤️ for the Node.js community. Star this repo if it helped you sleep better at night! ⭐
