HTTPower.Middleware.Dedup (HTTPower v0.23.0)

Copy Markdown View Source

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

  1. Request Fingerprinting - Each request gets a hash based on method + URL + body
  2. In-Flight Tracking - First request executes normally, subsequent identical requests wait
  3. Response Sharing - When the first request completes, all waiting requests receive the same response
  4. 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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

handle_request(request, config)

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}

start_link(opts \\ [])

Starts the request deduplicator GenServer.