Idempotency
Idempotency
Section titled “Idempotency”Request deduplication for safe retries
Extension URN: urn:mesh:ext:idempotency
Overview
Section titled “Overview”The idempotency extension ensures that retrying a request doesn’t cause duplicate side effects. Servers cache results keyed by the idempotency key and return cached results for duplicate requests.
When to Use
Section titled “When to Use”Idempotency SHOULD be used for:
- Payment processing
- Order creation
- Resource provisioning
- Any operation with side effects
- Network-unreliable environments
Idempotency is NOT needed for:
- Read operations (naturally idempotent)
- Operations without side effects
Options (Request)
Section titled “Options (Request)”| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique key for deduplication |
ttl | object | No | Requested cache duration |
Key Format
Section titled “Key Format”Keys SHOULD include enough context to be unique per logical operation:
| Pattern | Example |
|---|---|
| Entity + timestamp | order_cust42_1702500000 |
| Entity + operation | charge_order123_attempt1 |
| Combined entities | reserve_sku123_order456 |
| UUID | 550e8400-e29b-41d4-a716-446655440000 |
Data (Response)
Section titled “Data (Response)”| Field | Type | Description |
|---|---|---|
key | string | Echoed idempotency key |
status | string | processed, cached, or processing |
original_request_id | string | Request ID that first processed this key |
cached_at | string | When result was cached (if replayed) |
expires_at | string | When cached result expires |
Behavior
Section titled “Behavior”First Request
Section titled “First Request”When a new idempotency key is received:
- Server MUST hash key with function + version
- Server MUST process the request normally
- Server MUST cache the result
- Server MUST return
status: "processed"
Duplicate Request (Cached)
Section titled “Duplicate Request (Cached)”When the same idempotency key is received again:
- Server MUST NOT reprocess the request
- Server MUST return the cached result
- Server MUST return
status: "cached" - Server MUST include
original_request_id
Concurrent Request (Processing)
Section titled “Concurrent Request (Processing)”When duplicate key received while original is processing:
- Server MUST NOT start new processing
- Server MUST return
IDEMPOTENCY_PROCESSINGerror - Client SHOULD retry after suggested delay
Conflict Detection
Section titled “Conflict Detection”When same key is used with different arguments:
- Server MUST NOT process the request
- Server MUST return
IDEMPOTENCY_CONFLICTerror - Server SHOULD include hash of original arguments
Examples
Section titled “Examples”Request with Idempotency
Section titled “Request with Idempotency”{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_001", "call": { "function": "payments.charge", "version": "1", "arguments": { "amount": 100, "currency": "USD", "customer_id": "cust_123" } }, "extensions": [ { "urn": "urn:mesh:ext:idempotency", "options": { "key": "charge_order456_v1" } } ]}First Response (Processed)
Section titled “First Response (Processed)”{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_001", "result": { "charge_id": "ch_abc", "status": "succeeded" }, "extensions": [ { "urn": "urn:mesh:ext:idempotency", "data": { "key": "charge_order456_v1", "status": "processed", "original_request_id": "req_001", "expires_at": "2024-03-16T10:30:00Z" } } ]}Retry Response (Cached)
Section titled “Retry Response (Cached)”Same idempotency key, different request ID:
{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_002", "result": { "charge_id": "ch_abc", "status": "succeeded" }, "extensions": [ { "urn": "urn:mesh:ext:idempotency", "data": { "key": "charge_order456_v1", "status": "cached", "original_request_id": "req_001", "cached_at": "2024-03-15T10:30:00Z", "expires_at": "2024-03-16T10:30:00Z" } } ]}Conflict Error
Section titled “Conflict Error”Same key with different arguments:
{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_003", "result": null, "errors": [{ "code": "IDEMPOTENCY_CONFLICT", "message": "Idempotency key already used with different arguments", "retryable": false, "details": { "key": "charge_order456_v1", "original_arguments_hash": "sha256:abc123..." } }], "extensions": [ { "urn": "urn:mesh:ext:idempotency", "data": { "key": "charge_order456_v1", "status": "conflict", "original_request_id": "req_001" } } ]}Processing Error
Section titled “Processing Error”Concurrent request while original is processing:
{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_004", "result": null, "errors": [{ "code": "IDEMPOTENCY_PROCESSING", "message": "Previous request with this key is still processing", "retryable": true, "details": { "key": "charge_order456_v1", "retry_after": { "value": 1, "unit": "second" } } }]}ID vs Idempotency Key
Section titled “ID vs Idempotency Key”These serve different purposes:
| Field | Scope | Purpose |
|---|---|---|
id | Per-attempt | Correlate request↔response |
key | Per-operation | Deduplicate retries |
Three retries of the same operation:
Attempt 1: id="req_001", key="idem_abc" → Server processesAttempt 2: id="req_002", key="idem_abc" → Server returns cachedAttempt 3: id="req_003", key="idem_abc" → Server returns cachedDifferent id values enable logging/tracing of each attempt.
Same key prevents duplicate processing.
Server Implementation
Section titled “Server Implementation”Storage Requirements
Section titled “Storage Requirements”Servers MUST:
- Store results keyed by hash(key + function + version)
- Include request arguments hash for conflict detection
- Set TTL on cached entries (recommended: 24 hours)
Recommended Schema
Section titled “Recommended Schema”idempotency_cache { key_hash: string (primary) function: string version: string arguments_hash: string result: json status: enum(processing, completed, failed) created_at: timestamp expires_at: timestamp}Error Codes
Section titled “Error Codes”| Code | Retryable | Description |
|---|---|---|
IDEMPOTENCY_CONFLICT | No | Key reused with different arguments |
IDEMPOTENCY_PROCESSING | Yes | Previous request still processing |