In-flight request deduplication to prevent duplicate operations.
This module prevents duplicate requests from causing duplicate side effects (e.g., double charges, duplicate orders) by tracking in-flight requests and sharing responses with identical concurrent requests.
How It Works
- Request Fingerprinting - Each request gets a hash based on method + URL + body
- In-Flight Tracking - First request executes normally, subsequent identical requests wait
- Response Sharing - When the first request completes, all waiting requests receive the same response
- Automatic Cleanup - Tracking data is automatically removed after configurable TTL
Use Cases
- Prevent double charges from double-clicks on payment buttons
- Prevent duplicate orders from retry storms or race conditions
- Ensure idempotency for critical mutations (POST/PUT/DELETE)
Configuration
# Global configuration
config :httpower, :deduplicate,
enabled: true,
ttl: 5_000 # 5 seconds - how long to track in-flight requests
# Per-request configuration
HTTPower.post(url,
body: payment_data,
deduplicate: true
)
# Or with options
HTTPower.post(url,
body: payment_data,
deduplicate: [
enabled: true,
ttl: 10_000,
wait_timeout: 60_000, # How long waiters block for in-flight request (default: 30s)
key: "custom-dedup-key" # Optional: override hash generation
]
)States
:in_flight- Request currently executing, other identical requests will wait:completed- Brief period after completion to catch race conditions (100-500ms)
Thread Safety
Uses ETS for thread-safe storage and GenServer for coordination.
Summary
Functions
Returns a specification to start this module under a supervisor.
Feature callback for the HTTPower pipeline.
Starts the request deduplicator GenServer.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Feature callback for the HTTPower pipeline.
Checks for duplicate requests and either executes, waits, or returns cached response.
Returns:
{:ok, request}with dedup info stored in private (first occurrence){:halt, response}if cached response available (short-circuit)- Waits and returns
{:halt, response}for duplicate in-flight requests
Examples
iex> request = %HTTPower.Request{method: :post, url: "https://api.example.com/charge", body: "..."}
iex> HTTPower.Dedup.handle_request(request, [enabled: true])
{:ok, modified_request}
Starts the request deduplicator GenServer.