In production AI applications, network timeouts, client SDK retries, and distributed system failures can trigger duplicate API calls—each potentially charging your account and generating conflicting outputs. This comprehensive guide walks through battle-tested idempotency patterns that keep your data consistent, your costs predictable, and your users happy.
Quick Comparison: HolySheep vs Official APIs vs Relay Services
| Feature | HolySheep AI | Official OpenAI/Anthropic | Third-Party Relays |
|---|---|---|---|
| Idempotency Key Support | ✅ Native | ✅ Native | ⚠️ Varies |
| Price (GPT-4.1) | $8.00/MTok | $8.00/MTok | $8.50-$15/MTok |
| Price (Claude Sonnet 4.5) | $15.00/MTok | $15.00/MTok | $16-$25/MTok |
| Price (Gemini 2.5 Flash) | $2.50/MTok | $2.50/MTok | $3-$5/MTok |
| Price (DeepSeek V3.2) | $0.42/MTok | N/A (relay only) | $0.50-$0.80/MTok |
| Cost Efficiency | ¥1=$1 (85%+ savings vs ¥7.3) | Market rate | Markup + risk |
| Latency | <50ms overhead | Direct | 100-500ms |
| Payment Methods | WeChat/Alipay/银行卡 | Credit card only | Limited |
| Free Credits | ✅ On signup | ❌ | ❌ |
Sign up here to access all these AI models with built-in idempotency support and significant cost savings.
Why Idempotency Matters for AI APIs
When I integrated AI completions into a high-traffic content platform last year, I discovered a painful reality: the default retry logic in our HTTP client was generating 3-7 duplicate requests per failed call. With GPT-4.1 at $8/MTok, those retries were silently burning through budgets while producing conflicting response IDs that broke our caching layer.
AI APIs differ from traditional REST endpoints in critical ways:
- Non-deterministic outputs: The same prompt may return different completions on each call
- Token-based pricing: Duplicate calls = duplicate charges
- Rate limits: Retries can trigger 429 errors, cascading failures
- State mutation: Some calls create resources (fine-tuning, assistants)
Core Idempotency Pattern: The Idempotency Key
An idempotency key is a unique string (typically UUID v4) that clients attach to requests. When the server receives a duplicate request with the same key, it returns the cached response from the original request instead of reprocessing.
Pattern Architecture
+-------------+ +------------------+ +------------------+
| Client |---->| Idempotency |---->| AI API |
| |<----| Store (Redis) |<----| Provider |
+-------------+ +------------------+ +------------------+
| |
| 1st Request: |
| KEY="abc123" |
| |
| 2nd Request: |
| KEY="abc123" |
| |
v v
Cache Miss Cache Hit
Execute API Return Cached
Store Response Response
Implementation: HolySheep AI SDK with Idempotency
#!/usr/bin/env python3
"""
HolySheep AI API Client with Built-in Idempotency
Install: pip install requests redis hashlib
"""
import hashlib
import json
import time
import uuid
import redis
import requests
from typing import Optional, Dict, Any
from dataclasses import dataclass, field
from datetime import timedelta
@dataclass
class HolySheepConfig:
api_key: str
base_url: str = "https://api.holysheep.ai/v1"
redis_host: str = "localhost"
redis_port: int = 6379
redis_db: int = 0
idempotency_ttl: int = 86400 # 24 hours default
class HolySheepIdempotentClient:
"""
HolySheep AI client with automatic idempotency handling.
Prevents duplicate API calls from causing data inconsistency or double-charging.
"""
def __init__(self, config: HolySheepConfig):
self.config = config
self.redis = redis.Redis(
host=config.redis_host,
port=config.redis_port,
db=config.redis_db,
decode_responses=True
)
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {config.api_key}",
"Content-Type": "application/json"
})
def _get_cache_key(self, idempotency_key: str) -> str:
"""Generate consistent cache key for idempotency storage."""
return f"idempotency:{idempotency_key}"
def _get_request_hash(self, prompt: str, model: str, **kwargs) -> str:
"""Create deterministic hash of request parameters."""
params = {"prompt": prompt, "model": model, **kwargs}
serialized = json.dumps(params, sort_keys=True)
return hashlib.sha256(serialized.encode()).hexdigest()[:16]
def complete(
self,
prompt: str,
model: str = "gpt-4.1",
max_tokens: int = 1000,
temperature: float = 0.7,
idempotency_key: Optional[str] = None,
**kwargs
) -> Dict[str, Any]:
"""
Execute AI completion with automatic idempotency protection.
Args:
prompt: The input prompt for the model
model: Model identifier (gpt-4.1, claude-sonnet-4.5, etc.)
max_tokens: Maximum tokens in response
temperature: Sampling temperature (0-1)
idempotency_key: Unique key for deduplication (auto-generated if None)
**kwargs: Additional model parameters
Returns:
Dict containing the API response with metadata
"""
# Auto-generate idempotency key if not provided
if not idempotency_key:
request_hash = self._get_request_hash(prompt, model, max_tokens=max_tokens, temperature=temperature, **kwargs)
idempotency_key = f"{uuid.uuid4()}-{request_hash}"
cache_key = self._get_cache_key(idempotency_key)
# Check for cached response (idempotency protection)
cached = self.redis.get(cache_key)
if cached:
print(f"[Idempotency HIT] Returning cached response for key: {idempotency_key[:20]}...")
return json.loads(cached)
# Execute fresh API request
print(f"[Idempotency MISS] Executing new request for key: {idempotency_key[:20]}...")
request_body = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": max_tokens,
"temperature": temperature,
**kwargs
}
start_time = time.time()
try:
response = self.session.post(
f"{self.config.base_url}/chat/completions",
json=request_body,
timeout=60
)
response.raise_for_status()
result = response.json()
# Calculate latency and cost
latency_ms = (time.time() - start_time) * 1000
usage = result.get("usage", {})
prompt_tokens = usage.get("prompt_tokens", 0)
completion_tokens = usage.get("completion_tokens", 0)
# Add metadata for tracking
result["_idempotency"] = {
"key": idempotency_key,
"latency_ms": round(latency_ms, 2),
"cost_usd": self._calculate_cost(model, prompt_tokens, completion_tokens),
"cached": False
}
# Cache the response
self.redis.setex(
cache_key,
timedelta(seconds=self.config.idempotency_ttl),
json.dumps(result)
)
return result
except requests.exceptions.RequestException as e:
# On failure, DO NOT cache - allow retry with same idempotency key
raise IdempotencyError(f"API request failed: {e}") from e
def _calculate_cost(self, model: str, prompt_tokens: int, completion_tokens: int) -> float:
"""Calculate cost in USD based on 2026 HolySheep pricing."""
pricing = {
"gpt-4.1": (8.00, 8.00), # $8/MTok prompt + completion
"gpt-4.1-mini": (3.00, 12.00),
"claude-sonnet-4.5": (15.00, 15.00),
"claude-haiku-4": (3.00, 3.00),
"gemini-2.5-flash": (2.50, 2.50),
"deepseek-v3.2": (0.42, 0.42),
}
if model not in pricing:
return 0.0
prompt_rate, completion_rate = pricing[model]
total_cost = (prompt_tokens / 1_000_000 * prompt_rate +
completion_tokens / 1_000_000 * completion_rate)
return round(total_cost, 6)
def invalidate(self, idempotency_key: str) -> bool:
"""Manually invalidate a cached idempotency response."""
cache_key = self._get_cache_key(idempotency_key)
return bool(self.redis.delete(cache_key))
class IdempotencyError(Exception):
"""Raised when idempotency-protected request fails."""
pass
=== Usage Example ===
if __name__ == "__main__":
config = HolySheepConfig(
api_key="YOUR_HOLYSHEEP_API_KEY", # Replace with your key
base_url="https://api.holysheep.ai/v1",
redis_host="localhost",
redis_port=6379
)
client = HolySheepIdempotentClient(config)
# First call - executes API request
print("=== First Call (API execution) ===")
response1 = client.complete(
prompt="Explain quantum entanglement in simple terms.",
model="deepseek-v3.2", # Most cost-effective at $0.42/MTok
max_tokens=500,
temperature=0.7
)
print(f"Response ID: {response1.get('id')}")
print(f"Latency: {response1['_idempotency']['latency_ms']}ms")
print(f"Cost: ${response1['_idempotency']['cost_usd']}")
# Second call with SAME idempotency key - returns cached response
print("\n=== Second Call (Idempotency Hit) ===")
idempotency_key = response1['_idempotency']['key']
response2 = client.complete(
prompt="Explain quantum entanglement in simple terms.",
model="deepseek-v3.2",
max_tokens=500,
temperature=0.7,
idempotency_key=idempotency_key # Same key = cache hit
)
print(f"Response ID: {response2.get('id')}")
print(f"Cached: {response2['_idempotency']['cached']}")
Production-Grade Implementation: Distributed Idempotency with Redis Cluster
#!/usr/bin/env python3
"""
Advanced Idempotency Implementation for Distributed Systems
Supports: Redis Cluster, PostgreSQL fallback, automatic retry with backoff
"""
import asyncio
import hashlib
import json
import logging
import time
import uuid
from contextlib import asynccontextmanager
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable, Dict, Optional
from collections.abc import AsyncIterator
import aiohttp
import redis.asyncio as aioredis
from tenacity import (
retry,
stop_after_attempt,
wait_exponential,
retry_if_exception_type
)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class IdempotencyStatus(Enum):
PROCESSING = "processing"
COMPLETED = "completed"
FAILED = "failed"
@dataclass
class DistributedIdempotencyManager:
"""
Production-grade idempotency manager with distributed locking.
Features:
- Distributed locking to prevent race conditions
- Automatic cleanup of stale entries
- PostgreSQL fallback for Redis failures
- Metrics collection for monitoring
"""
redis_url: str
pg_pool: Any = None # Optional asyncpg Pool
ttl_seconds: int = 86400
lock_timeout: int = 30
request_timeout: int = 120
def __post_init__(self):
self._redis: Optional[aioredis.Redis] = None
async def __aenter__(self):
self._redis = await aioredis.from_url(
self.redis_url,
encoding="utf-8",
decode_responses=True,
max_connections=50
)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self._redis:
await self._redis.close()
def _generate_idempotency_key(
self,
endpoint: str,
method: str,
params: Dict[str, Any]
) -> str:
"""Generate deterministic idempotency key from request parameters."""
key_data = f"{method}:{endpoint}:{json.dumps(params, sort_keys=True)}"
hash_value = hashlib.sha256(key_data.encode()).hexdigest()[:32]
return f"idem:{hash_value}"
@asynccontextmanager
async def distributed_lock(
self,
lock_key: str,
timeout: int = 30
) -> AsyncIterator[bool]:
"""
Acquire distributed lock using Redis SET NX with expiration.
Prevents multiple workers from processing the same request.
"""
lock_acquired = False
try:
lock_acquired = await self._redis.set(
f"lock:{lock_key}",
"1",
nx=True,
ex=timeout
)
if lock_acquired:
logger.info(f"Lock acquired for key: {lock_key[:20]}...")
yield True
else:
logger.info(f"Lock held by another worker for key: {lock_key[:20]}...")
yield False
finally:
if lock_acquired:
await self._redis.delete(f"lock:{lock_key}")
async def execute_with_idempotency(
self,
endpoint: str,
method: str,
params: Dict[str, Any],
api_call: Callable,
api_base_url: str = "https://api.holysheep.ai/v1",
api_key: str = None
) -> Dict[str, Any]:
"""
Execute API call with full idempotency protection.
Flow:
1. Check if result exists in cache
2. If not, acquire distributed lock
3. Double-check cache (another worker may have completed)
4. Execute API call
5. Store result with idempotency metadata
6. Release lock
"""
idempotency_key = self._generate_idempotency_key(endpoint, method, params)
cache_key = f"cache:{idempotency_key}"
status_key = f"status:{idempotency_key}"
# Step 1: Check for existing cached result
cached = await self._redis.get(cache_key)
if cached:
logger.info(f"Returning cached result for {idempotency_key[:20]}...")
result = json.loads(cached)
result["_from_cache"] = True
return result
# Step 2: Try to acquire lock
async with self.distributed_lock(idempotency_key, self.lock_timeout) as acquired:
if not acquired:
# Another worker is processing - wait and fetch result
return await self._wait_for_result(status_key, cache_key)
# Step 3: Double-check cache (race condition protection)
cached = await self._redis.get(cache_key)
if cached:
result = json.loads(cached)
result["_from_cache"] = True
return result
# Step 4: Mark as processing
await self._redis.setex(status_key, self.ttl_seconds, IdempotencyStatus.PROCESSING.value)
try:
# Step 5: Execute the actual API call
start_time = time.time()
result = await api_call(endpoint, method, params, api_base_url, api_key)
# Add idempotency metadata
result["_idempotency"] = {
"key": idempotency_key,
"latency_ms": round((time.time() - start_time) * 1000, 2),
"timestamp": time.time(),
"status": "success"
}
# Step 6: Cache the result
await self._redis.setex(cache_key, self.ttl_seconds, json.dumps(result))
await self._redis.setex(status_key, self.ttl_seconds, IdempotencyStatus.COMPLETED.value)
logger.info(f"Successfully cached result for {idempotency_key[:20]}...")
result["_from_cache"] = False
return result
except Exception as e:
# Mark as failed but keep status for debugging
await self._redis.setex(
status_key,
self.ttl_seconds,
f"{IdempotencyStatus.FAILED.value}:{str(e)}"
)
raise
async def _wait_for_result(
self,
status_key: str,
cache_key: str,
poll_interval: float = 0.5,
max_wait: float = 60.0
) -> Dict[str, Any]:
"""Poll for result when another worker is processing."""
start = time.time()
while time.time() - start < max_wait:
# Check if processing completed
status = await self._redis.get(status_key)
if status == IdempotencyStatus.COMPLETED.value:
cached = await self._redis.get(cache_key)
if cached:
result = json.loads(cached)
result["_from_cache"] = True
return result
await asyncio.sleep(poll_interval)
raise TimeoutError(f"Timeout waiting for result of {status_key}")
async def make_holysheep_api_call(
endpoint: str,
method: str,
params: Dict[str, Any],
base_url: str,
api_key: str
) -> Dict[str, Any]:
"""
Make actual API call to HolySheep AI with retry logic.
"""
url = f"{base_url}/{endpoint}"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_exception_type(aiohttp.ClientError)
)
async def _call():
timeout = aiohttp.ClientTimeout(total=120)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.request(method, url, json=params, headers=headers) as resp:
resp.raise_for_status()
return await resp.json()
return await _call()
=== Production Usage ===
async def main():
"""Example: Content generation with full idempotency protection."""
async with DistributedIdempotencyManager(
redis_url="redis://localhost:6379/0",
ttl_seconds=86400 # 24 hours
) as idempotency_mgr:
async def generate_content(prompt: str, model: str = "gpt-4.1"):
"""Generate content - safe to call multiple times."""
params = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 1000,
"temperature": 0.7
}
return await idempotency_mgr.execute_with_idempotency(
endpoint="chat/completions",
method="POST",
params=params,
api_call=make_holysheep_api_call,
api_base_url="https://api.holysheep.ai/v1",
api_key="YOUR_HOLYSHEEP_API_KEY"
)
# Simulate concurrent duplicate requests (e.g., from retry logic)
tasks = [
generate_content("Write a haiku about artificial intelligence"),
generate_content("Write a haiku about artificial intelligence"),
generate_content("Write a haiku about artificial intelligence"),
]
results = await asyncio.gather(*tasks)
print(f"Executed {len(results)} concurrent requests")
print(f"First result ID: {results[0].get('id')}")
print(f"All IDs identical: {results[0].get('id') == results[1].get('id') == results[2].get('id')}")
print(f"From cache: {[r.get('_from_cache') for r in results]}")
if __name__ == "__main__":
asyncio.run(main())
HTTP Header Implementation for Native Idempotency
Many AI providers support idempotency keys directly via HTTP headers. HolySheep AI follows the OpenAI-compatible standard using the OpenAI-Idempotency-Key header:
#!/bin/bash
HolySheep AI API calls with idempotency headers
Save as: idempotent-api-call.sh
HOLYSHEEP_API_KEY="YOUR_HOLYSHEEP_API_KEY"
BASE_URL="https://api.holysheep.ai/v1"
Generate idempotency key (UUID v4)
generate_idempotency_key() {
cat /proc/sys/kernel/random/uuid
}
Execute completion with idempotency
call_with_idempotency() {
local prompt="$1"
local model="${2:-deepseek-v3.2}" # Default to most cost-effective model
local idempotency_key=$(generate_idempotency_key)
echo "Request ID (for tracking): $idempotency_key"
curl -X POST "${BASE_URL}/chat/completions" \
-H "Authorization: Bearer ${HOLYSHEEP_API_KEY}" \
-H "Content-Type: application/json" \
-H "OpenAI-Idempotency-Key: ${idempotency_key}" \
-d "{
\"model\": \"${model}\",
\"messages\": [
{\"role\": \"user\", \"content\": \"${prompt}\"}
],
\"max_tokens\": 500,
\"temperature\": 0.7
}" \
--silent --show-error | jq '.'
}
Example: Generate product descriptions
echo "=== Product Description Generation (Idempotent) ==="
IDEM_KEY=$(generate_idempotency_key)
echo "Tracking ID: $IDEM_KEY"
First call
echo -e "\n--- First Request ---"
curl -X POST "${BASE_URL}/chat/completions" \
-H "Authorization: Bearer ${HOLYSHEEP_API_KEY}" \
-H "Content-Type: application/json" \
-H "OpenAI-Idempotency-Key: ${IDEM_KEY}" \
-d '{
"model": "gemini-2.5-flash",
"messages": [{"role": "user", "content": "Write a compelling 2-sentence product description for wireless noise-canceling headphones priced at $299."}],
"max_tokens": 100
}' | jq '{id: .id, model: .model, content: .choices[0].message.content, usage: .usage}'
Simulate retry with SAME idempotency key (returns cached response)
echo -e "\n--- Retry Request (Same Idempotency Key) ---"
curl -X POST "${BASE_URL}/chat/completions" \
-H "Authorization: Bearer ${HOLYSHEEP_API_KEY}" \
-H "Content-Type: application/json" \
-H "OpenAI-Idempotency-Key: ${IDEM_KEY}" \
-d '{
"model": "gemini-2.5-flash",
"messages": [{"role": "user", "content": "Write a compelling 2-sentence product description for wireless noise-canceling headphones priced at $299."}],
"max_tokens": 100
}' | jq '{id: .id, model: .model, content: .choices[0].message.content, usage: .usage}'
Cost comparison with different models
echo -e "\n=== Cost Comparison (1000-token response) ==="
echo "Model Pricing (2026 HolySheep rates):"
echo " DeepSeek V3.2: \$0.42/MTok = \$0.00042 per 1K tokens"
echo " Gemini 2.5 Flash: \$2.50/MTok = \$0.00250 per 1K tokens"
echo " GPT-4.1: \$8.00/MTok = \$0.00800 per 1K tokens"
echo " Claude Sonnet 4.5: \$15.00/MTok = \$0.01500 per 1K tokens"
echo ""
echo "DeepSeek V3.2 offers 96% savings over Claude Sonnet 4.5!"
Database Schema for Persistent Idempotency Records
-- PostgreSQL schema for persistent idempotency storage
-- Use when Redis is unavailable or for audit compliance
CREATE TABLE IF NOT EXISTS idempotency_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
idempotency_key VARCHAR(255) UNIQUE NOT NULL,
request_hash VARCHAR(64) NOT NULL,
endpoint VARCHAR(255) NOT NULL,
method VARCHAR(10) NOT NULL,
request_params JSONB NOT NULL,
response_data JSONB,
status VARCHAR(20) NOT NULL DEFAULT 'processing',
error_message TEXT,
retry_count INTEGER DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '24 hours'),
CONSTRAINT valid_status CHECK (status IN ('processing', 'completed', 'failed'))
);
-- Indexes for fast lookups
CREATE INDEX idx_idempotency_key ON idempotency_records(idempotency_key);
CREATE INDEX idx_request_hash ON idempotency_records(request_hash);
CREATE INDEX idx_expires_at ON idempotency_records(expires_at) WHERE status = 'processing';
-- Function to auto-cleanup expired records
CREATE OR REPLACE FUNCTION cleanup_expired_idempotency_records()
RETURNS INTEGER AS $$
DECLARE
deleted_count INTEGER;
BEGIN
DELETE FROM idempotency_records
WHERE expires_at < NOW();
GET DIAGNOSTICS deleted_count = ROW_COUNT;
RETURN deleted_count;
END;
$$ LANGUAGE plpgsql;
-- Schedule cleanup (run daily via pg_cron or external scheduler)
-- SELECT cleanup_expired_idempotency_records();
-- Python implementation with asyncpg
import asyncio
import json
from typing import Optional, Dict, Any
from dataclasses import dataclass
import hashlib
import asyncpg
@dataclass
class PostgresIdempotencyStore:
"""
PostgreSQL-backed idempotency store.
Provides durability and ACID guarantees for critical operations.
"""
dsn: str
pool: Optional[asyncpg.Pool] = None
ttl_seconds: int = 86400
async def __aenter__(self):
self.pool = await asyncpg.create_pool(
self.dsn,
min_size=5,
max_size=20
)
# Initialize table
await self.pool.execute('''
CREATE TABLE IF NOT EXISTS idempotency_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
idempotency_key VARCHAR(255) UNIQUE NOT NULL,
request_hash VARCHAR(64) NOT NULL,
endpoint VARCHAR(255) NOT NULL,
method VARCHAR(10) NOT NULL,
request_params JSONB NOT NULL,
response_data JSONB,
status VARCHAR(20) NOT NULL DEFAULT 'processing',
error_message TEXT,
retry_count INTEGER DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '24 hours')
)
''')
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.pool:
await self.pool.close()
def _hash_request(self, endpoint: str, method: str, params: Dict) -> str:
"""Create deterministic hash for duplicate detection."""
data = f"{method}:{endpoint}:{json.dumps(params, sort_keys=True)}"
return hashlib.sha256(data.encode()).hexdigest()
async def get_or_execute(
self,
idempotency_key: str,
endpoint: str,
method: str,
params: Dict[str, Any],
executor: callable
) -> Dict[str, Any]:
"""
Get cached result or execute the function.
Uses PostgreSQL advisory locks for distributed locking.
"""
request_hash = self._hash_request(endpoint, method, params)
# Try to get existing record
record = await self.pool.fetchrow(
'''
SELECT * FROM idempotency_records
WHERE idempotency_key = $1
''',
idempotency_key
)
if record:
if record['status'] == 'completed':
return record['response_data']
elif record['status'] == 'processing':
# Wait for the other worker
return await self._wait_for_completion(idempotency_key)
else:
# Failed - retry
pass
# Acquire advisory lock for distributed safety
lock_id = int(hashlib.md5(idempotency_key.encode()).hexdigest()[:8], 16)
async with self.pool.acquire() as conn:
# Use advisory lock to prevent race conditions
locked = await conn.fetchval(
'SELECT pg_try_advisory_lock($1)',
lock_id
)
try:
if not locked:
# Another transaction holds the lock
return await self._wait_for_completion(idempotency_key)
# Double-check (another worker may have completed)
record = await conn.fetchrow(
'SELECT * FROM idempotency_records WHERE idempotency_key = $1',
idempotency_key
)
if record and record['status'] == 'completed':
return record['response_data']
# Create or update processing record
record = await conn.fetchrow('''
INSERT INTO idempotency_records
(idempotency_key, request_hash, endpoint, method, request_params, status, expires_at)
VALUES ($1, $2, $3, $4, $5, 'processing', NOW() + $6 * INTERVAL '1 second')
ON CONFLICT (idempotency_key) DO UPDATE
SET status = 'processing', updated_at = NOW(), retry_count = idempotency_records.retry_count + 1
RETURNING *
''', idempotency_key, request_hash, endpoint, method, json.dumps(params), self.ttl_seconds)
# Execute the actual operation
try:
result = await executor(endpoint, method, params)
# Update with successful result
await conn.execute('''
UPDATE idempotency_records
SET status = 'completed', response_data = $2, updated_at = NOW()
WHERE idempotency_key = $1
''', idempotency_key, json.dumps(result))
return result
except Exception as e:
# Update with error
await conn.execute('''
UPDATE idempotency_records
SET status = 'failed', error_message = $2, updated_at = NOW()
WHERE idempotency_key = $1
''', idempotency_key, str(e))
raise
finally:
await conn.execute('SELECT pg_advisory_unlock($1)', lock_id)
async def _wait_for_completion(self, idempotency_key: str, timeout: float = 60) -> Dict:
"""Poll for completion when another worker is processing."""
import time
start = time.time()
while time.time() - start < timeout:
record = await self.pool.fetchrow(
'SELECT status, response_data, error_message FROM idempotency_records WHERE idempotency_key = $1',
idempotency_key
)
if record['status'] == 'completed':
return record['response_data']
elif record['status'] == 'failed':
raise RuntimeError(f"Previous request failed: {record['error_message']}")
await asyncio.sleep(0.5)
raise TimeoutError(f"Timeout waiting for idempotent request: {idempotency_key}")
Best Practices for AI API Idempotency
- Always use idempotency keys for any mutation that costs money or changes state
- Set appropriate TTLs: 24-48 hours covers most retry windows
- Include request fingerprints: Hash the actual parameters to detect parameter-level duplicates
- Monitor cache hit rates: Low hit rates may indicate broken retry logic
- Handle partial failures: If API returns partial success, idempotency key should prevent re-execution
- Use distributed locking: Prevents thundering herd when cache misses
- Log idempotency metrics: Track cost savings from duplicate prevention
Common Errors and Fixes
Error 1: "Idempotency key already exists with different result"
Symptom: API returns 409 Conflict or similar error when retrying