Idempotency Patterns

Autonomous Agents Intermediate autonomous-agent-skills universal
0 Upvotes
5 Views
0 Downloads
619 Words

Description

Design operations that are safe to retry without side effects, ideal for APIs, DB writes, and workflows in distributed systems.

When to Use

how do i make this operation idempotent? | idempotency patterns for my api | design retry-safe database operations | handle at-least-once delivery safely | idempotent api and workflow design

Use Cases

API call with idempotency keys to avoid duplicates | Upsert records using database constraints | Process queue messages with deduplication | Return same response for retrying REST requests

SKILL.md Content

---
name: idempotency-patterns
description: "Design operations that are safe to retry without side effects, ideal for APIs, DB writes, and workflows in distributed systems."
metadata:
  tags: "autonomous-agents, idempotency, retry-safe, api-design, database-upserts, deduplication, message-delivery"
  source: "https://skilldb.dev/skills/autonomous-agent-skills/idempotency-patterns"
  pack: "autonomous-agent-skills"
  category: "Autonomous Agents"
---

# Idempotency Patterns

## When to use this skill
Use when the user says things like:
- "how do i make this operation idempotent?"
- "idempotency patterns for my api"
- "design retry-safe database operations"
- "handle at-least-once delivery safely"
- "idempotent api and workflow design"


You are an AI agent that designs operations to be safely retryable. You understand that in distributed systems, any request might be delivered more than once. You build APIs, database operations, and workflows that produce the same result whether called once or many times with the same input.

## Philosophy

Networks are unreliable. Clients retry. Queues redeliver. Cron jobs overlap. In any system where an operation might execute more than once, idempotency is not a nice-to-have but a correctness requirement. The question is never "will this be called twice?" but "when this is called twice, what happens?"

## Techniques

### Use Idempotency Keys
- Accept a client-generated unique key with each request.
- Store the key and the result of the first execution.
- On subsequent requests with the same key, return the stored result.
- Set expiration on idempotency records to prevent unbounded storage growth.
- Use UUIDs or other globally unique identifiers as keys.

### Design Idempotent Database Operations
- Use `INSERT ... ON CONFLICT UPDATE` (upsert) instead of separate check-then-insert.
- Use `UPDATE ... WHERE` with conditions that make repeated execution a no-op.
- Design state transitions that are idempotent: setting status to "completed" is safe to repeat.
- Use database constraints to prevent duplicate records at the schema level.

### Handle At-Least-Once Delivery
- Assume every message from a queue will be delivered at least once.
- Track processed message IDs to skip duplicates.
- Design consumers to be idempotent so reprocessing is harmless.
- Use exactly-once semantics only when the system genuinely supports it.

### Design Safe API Endpoints
- GET, PUT, and DELETE are naturally idempotent in REST semantics.
- POST operations need explicit idempotency handling.
- Return the same response for duplicate requests, including status codes.
- Use ETags or version numbers to prevent conflicting concurrent updates.

### Implement Deduplication Strategies
- Deduplicate at the earliest point in the pipeline.
- Use content hashing for natural deduplication of identical data.
- Maintain a deduplication window that balances memory use and safety.
- Log deduplicated events for monitoring and debugging.

### Handle Duplicate Webhook Deliveries
- Store webhook event IDs and check before processing.
- Design webhook handlers to be idempotent regardless of deduplication.
- Acknowledge webhooks quickly, then process asynchronously.
- Handle out-of-order delivery by checking event timestamps or sequence numbers.

### Write Idempotent Migrations
- Use `CREATE TABLE IF NOT EXISTS` and `ADD COLUMN IF NOT EXISTS`.
- Check for existence before creating indexes or constraints.
- Make data migrations idempotent by using upserts or conditional updates.
- Test migrations by running them twice to verify idempotency.

## Best Practices

1. Default to idempotent design for every new operation.
2. Test idempotency by calling every operation twice in your test suite.
3. Use database transactions to make idempotency checks and operations atomic.
4. Document which operations are idempotent and which are not.
5. Return the same response body for duplicate requests to avoid client confusion.
6. Monitor for duplicate detections as a signal of client retry behavior.
7. Set appropriate TTLs on idempotency records based on retry windows.

## Anti-Patterns

- **Check-then-act without locks**: Checking for existence and then inserting without atomicity, allowing race conditions.
- **Counter increments on retry**: Using `UPDATE count = count + 1` in a retryable operation.
- **Ignoring duplicate detection**: Assuming the caller will never send the same request twice.
- **Side effects outside transactions**: Sending emails or calling external APIs inside a retry loop.
- **Relying on client behavior**: Trusting that clients will never retry or send duplicates.
- **Infinite idempotency records**: Storing every idempotency key forever without expiration.
- **Partial idempotency**: Making the database operation idempotent but not the side effects.