19 views
Retries are not edge cases — they are the product. API Gateway clients time out and retry, EventBridge at-least-once delivers twice, Step Functions redrive, and your deploy swaps code mid-flight. Powertools idempotency is only an unfair advantage when the DynamoDB key is stable across those realities and the persistence window outlives the retry storm.
⚡ TL;DR: Hash a business idempotency key (not the whole event). Scope by function alias/version when payloads are version-sensitive. Set expiry past your max client retry + DLQ redrive window. Use IN_PROGRESS locking so concurrent duplicates wait instead of double-charging. Pair with Bedrock Agents: Idempotent Tool Calls and Lambda Warm Pools.
Lambda Powertools Idempotency: production guidance
typescript
import { makeIdempotent, IdempotencyConfig } from "@aws-lambda-powertools/idempotency";
import { DynamoDBPersistenceLayer } from "@aws-lambda-powertools/idempotency/dynamodb";
const persistence = new DynamoDBPersistenceLayer({ tableName: process.env.IDEMP_TABLE! });
// GOOD: stable across redelivery jitter (new eventId, same orderId)
const config = new IdempotencyConfig({
eventKeyJmesPath: "detail.orderId",
expiresAfterSeconds: 60 * 60 * 24, // 24h > client retry + DLQ
useLocalCache: true,
throwOnNoIdempotencyKey: true,
});
async function charge(event: { detail: { orderId: string; amount: number } }) {
return { charged: event.detail.amount, orderId: event.detail.orderId };
}
export const handler = makeIdempotent(charge, { persistenceStore: persistence, config });
typescript
✅ JMESPath to // BAD: whole-event hash — EventBridge envelope changes → duplicate charge
const badConfig = new IdempotencyConfig({
eventKeyJmesPath: "@", // unique every delivery
expiresAfterSeconds: 30, // shorter than client retry budget
});
void badConfig;
orderId / Idempotency-Key header.❌ Hashing timestamps,
eventId, or request context that changes on retry.
API Gateway header keys that clients can control
typescript
Reject missing keys with 400 at the edge. Document that retries must reuse the same import type { APIGatewayProxyEventV2 } from "aws-lambda";
const httpConfig = new IdempotencyConfig({
eventKeyJmesPath: 'headers."idempotency-key"',
expiresAfterSeconds: 60 * 15,
throwOnNoIdempotencyKey: true,
});
export const apiHandler = makeIdempotent(
async (event: APIGatewayProxyEventV2) => {
const body = JSON.parse(event.body ?? "{}");
return { statusCode: 200, body: JSON.stringify(await createPayment(body)) };
},
{ persistenceStore: persistence, config: httpConfig },
);
Idempotency-Key.
Survive deploys and concurrent duplicates
typescript
During blue/green, const deploySafe = new IdempotencyConfig({
eventKeyJmesPath: "detail.orderId",
// Include payload hash when handler semantics change across versions
payloadValidationJmesPath: "detail",
expiresAfterSeconds: 86_400,
});
payloadValidationJmesPath prevents returning a cached response from an old code path when the body shape changed. Concurrent invokes: Powertools writes IN_PROGRESS; second caller either waits or gets ThereIsAlreadyAExecutionInProgress — handle that as 409/retry, never as “charge again.”
DynamoDB table shape that will not surprise you
typescript
Watch // PK: id (idempotency key), attrs: expiration, status, data_hash, response_data
// Enable TTL on expiration attribute. Provision enough WCU for peak retry storms.
ConditionalCheckFailed and latency on the idempotency table during incidents — a hot partition on a popular orderId prefix needs random suffixing only if you control both writer and key design (usually you should not shard the business key).
Closing checklist
- [ ] Idempotency key = business id or client
Idempotency-Key, not full event - [ ] TTL/expiry > max client retry + DLQ/redrive window
- [ ]
throwOnNoIdempotencyKeyfor mutating APIs - [ ] Payload validation enabled across versioned deploys when needed
- [ ] Table TTL on; alarms on ConditionalCheckFailed and throttle
- [ ] Concurrent in-progress treated as retry/409, never double side-effect
Related reading
- Bedrock Agents: Idempotent Tool Calls Against DynamoDB Writes
- Lambda Warm Pools: Low-Latency Backends for Coding Agent Tools
- Cross-Account Bedrock Access: Platform Teams Without Shared Keys
- OpenTelemetry for LLMs: Trace Prompt Latency Across Microservices
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- Css Animation Performance: Performance-Optimized Motion for Modern UIs
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
