Skip to content

First-Class Idempotency-Key Semantics for All Mutating Endpoints with Race-Safe Concurrent Execution Guarantees #342

Description

@Christopherdominic

Summary

The codebase has deduplication scaffolding (src/deduplication/{fingerprint,service,middleware}.rs) but no end-to-end idempotency contract for mutating endpoints. A client that times out and retries POST /tips, a refund, or a scheduled-tip creation can double-execute the operation. For money-adjacent APIs, first-class Idempotency-Key semantics (à la Stripe) are table stakes.

Task

  1. Implement an Idempotency-Key middleware for all mutating routes (POST/PUT/PATCH/DELETE, opt-in per route via router layer):
    • key: client-supplied UUID header, scoped per authenticated principal + route
    • first request: execute, persist (key, request_fingerprint, response_status, response_body_hash + body) with TTL (default 24h)
    • retry with same key + same fingerprint: replay the stored response (identical status/body) without re-executing
    • same key + different request body: 422 Unprocessable Entity with a typed error
  2. Concurrency correctness — the hard part: two in-flight requests with the same key must not both execute. Use a Redis SET NX lock (or Postgres advisory lock) so the second request either waits for and replays the first result, or receives 409 Conflict with Retry-After — choose, justify, and document the semantics.
  3. Storage: Redis primary with Postgres fallback table (new migration) so idempotency survives Redis eviction for financial endpoints; response bodies capped and compressed.
  4. Integrate with the existing deduplication module or explicitly retire it — no two half-overlapping systems may remain.
  5. Metrics: replay hits, conflict rejections, lock contention histogram.
  6. Tests: replay returns byte-identical response; fingerprint mismatch → 422; concurrent duplicate execution race test (spawn N parallel identical requests, assert exactly one execution via a side-effect counter); TTL expiry allows re-execution; Redis-down fallback path.

Acceptance criteria

  • Exactly-one execution under concurrent duplicates, proven by a race test
  • Stored-response replay is byte-identical (status, headers subset, body)
  • Key reuse with different payload rejected with a typed 422
  • Single deduplication system remains in the codebase
  • All checks pass

PR requirements (mandatory)

  • ✅ Your PR must pass all checks — PRs with failing or skipped checks will not be merged.
  • 📸 You must attach a screenshot in the PR description demonstrating the result (for this issue: the race test output proving single execution under N concurrent retries).
  • 🔗 You must link this issue number in your PR description (e.g. Closes #<issue-number>). PRs without a linked issue will not be reviewed.

Metadata

Metadata

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26architectureStructural or architectural changedifficulty: very hardRequires deep expertise and substantial effort

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions