Overview
Idempotency keys ensure that retrying a request multiple times has the same effect as making it once. This is critical for operations like creating resources or processing payments, where duplicate requests could cause unintended side effects. Key benefits:- Safely retry failed requests without creating duplicates
- Handle network timeouts and transient errors gracefully
- Guarantee exactly-once semantics for critical operations
When Idempotency is Required
Validation logic (from
services/proxy.service.ts:66):
How to Use Idempotency Keys
Header Method (Recommended)
Provide the idempotency key as a request header:Body Field Method
Alternatively, include it in the request body:Precedence: If both the header and body field are present, the header value takes precedence. See
routes/proxy.ts:48.Idempotency Key Format
- Type: String
- Length: 1-255 characters
- Scope: Per-agent (different agents can use the same key without collision)
- Recommendations:
- Use a unique identifier that represents the operation (e.g.,
order-{orderId}-{timestamp}) - Include a retry counter for clarity (e.g.,
payment-123-retry-2) - Avoid special characters that might require URL encoding
- Use a unique identifier that represents the operation (e.g.,
How Idempotency Works
Request Hashing
GaiterGuard creates a SHA-256 hash of the request to detect changes:- HTTP method
- Target URL (including query parameters)
- Request body
State Machine
Each idempotency key goes through a lifecycle:Implementation Details
The idempotency logic is implemented inservices/idempotency.service.ts:31:
Cache Hit Behavior
When a request matches an existing completed idempotency key:- Gateway looks up cached response from
idempotency_keystable - Returns the original response status, headers, and body
- No request is made to the target service
- Response includes
X-Idempotency-Status: processedheader
Handling Concurrent Requests
If multiple requests arrive with the same idempotency key while the first is still processing: Request 1 (arrives first):- Creates idempotency record with status=
processing - Forwards to target service
- Updates status to
completedwhen done
- Finds existing record with status=
processing - Returns
409 Conflicterror immediately - Client should wait and retry
Recommended Retry Strategy
Transaction Isolation
Idempotency checks use READ COMMITTED transaction isolation to prevent race conditions:- First transaction creates the record with status=
processing - Second transaction sees the existing record and returns 409
- No duplicate requests are forwarded to the target service
TTL and Expiration
Idempotency keys have a 24-hour TTL:- Cached responses are no longer available
- The same idempotency key can be reused for a new operation
- Expired records may be cleaned up by a background job (implementation pending)
The TTL starts from when the idempotency key is first created, not when the request completes.
Database Schema
Idempotency keys are stored in theidempotency_keys table:
- Unique constraint on
(agent_id, key)ensures one record per agent per key - Indexed on
expires_atfor efficient cleanup queries - Cascading delete when agent is deleted
Completing and Failing Idempotency Keys
On Success
When the proxied request completes successfully:On Failure
When the proxied request fails:Best Practices
Do’s
- Use descriptive, unique keys that represent the operation
- Include identifiers that make the operation traceable (order ID, user ID, etc.)
- Retry 409 errors with exponential backoff
- Keep the same idempotency key when retrying a failed request
- Use the header method for cleaner request bodies
Don’ts
- Don’t reuse idempotency keys for different operations
- Don’t change the request body/URL when retrying with the same key
- Don’t use random UUIDs (defeats the purpose - retries need the same key)
- Don’t use extremely short keys that might collide
- Don’t rely on idempotency for GET requests (use them, but they’re already safe)