Course contentsShow
← Databases & SQL
Lesson 1301 of 1,95312. Key-Value and Caching EnginesFree lesson

Idempotent Request Tracking

Prevent duplicate processing of critical transactions using cache-based keys.

In this lesson, you will learn how to use a cache-based key to ensure a transaction is processed exactly once, even if a client sends the same request multiple times due to network retries.

The Problem: The "Double-Click" Dilemma

In distributed systems, a client might send a payment request, but a network hiccup causes the response to be lost. The client, assuming the request failed, sends the exact same request again. Without a guardrail, your database might process the payment twice, leading to unhappy customers and inconsistent ledger states.

The Solution: Idempotent Keys

We use a cache (like Redis) as a gatekeeper to track these "in-flight" requests using a unique identifier, often called an Idempotency Key. This key is typically generated by the client and included in the request header.

The workflow looks like this:

  1. Check: Before processing, the server checks the cache for the existence of the key.
  2. Lock: If the key doesn't exist, we set it in the cache with a short Time-To-Live (TTL) (e.g., 30 minutes).
  3. Execute: If the key was successfully set, proceed with the database transaction.
  4. Ignore: If the key already exists, the server returns the cached result of the previous attempt or a "409 Conflict" status, preventing redundant database writes.

Implementation Pattern

Using a simple string key in Redis, the logic follows this pattern:

# Pseudo-code for an idempotent transaction
key = "idempotency_key:" + request.header['x-idempotency-key']

# Attempt to set the key only if it doesn't exist (NX)
if redis.set(key, "processing", nx=True, ex=1800):
    try:
        # Perform the actual SQL transaction
        result = db.execute("INSERT INTO payments...")
        redis.set(key, result) # Store success for future retries
        return result
    except:
        redis.delete(key) # Allow retry on failure
        raise
else:
    # Key exists; return the previously cached result
    return redis.get(key)

By leveraging the atomicity of cache operations, we create a barrier that ensures critical state-changing operations are strictly idempotent. The cache doesn't replace your primary SQL database; it protects it from accidental duplication by acting as a high-speed filter for concurrent or retry-heavy request streams.

Key Takeaway: Use a unique idempotency key and a cache-based "check-and-set" pattern to prevent duplicate processing of critical transactions.