Why Network Requests Are Inherently Uncertain
In distributed system architectures, communication between clients and servers can never be guaranteed to be perfectly stable. When a user clicks a "Pay" button, if a minor network latency or interruption occurs, the client often cannot confirm whether the request was successfully received and processed by the server. In such cases, automatic retry mechanisms become the developer's first line of defense, but they also introduce the significant risk of "duplicate execution."
If a payment request is processed successfully on the server side, but the network drops while sending the confirmation message, the client—receiving no response—will initiate a second request. Without defensive mechanisms, this easily leads to duplicate charges or data inconsistency. This characteristic, where the system state remains the same regardless of how many times a request is executed, is known as "Idempotency" in software engineering.
The Underlying Mechanism of Idempotency
The core of idempotency lies in how the server identifies "the same request." In the HTTP protocol, GET, PUT, and DELETE methods are theoretically idempotent by nature, as the resource state should not change regardless of how many times they are called. However, POST requests do not possess this property. To achieve idempotency in POST requests, we must introduce an "Idempotency Key."
Identifier Generation Logic
The identifier is typically a unique UUID or a business sequence number generated by the client when initiating the request. Upon receiving the request, the server first checks if this identifier exists in a cache (such as Redis). If it exists, the server returns the previously stored result instead of performing the business logic again. This mechanism decouples "execution" from "querying," ensuring transaction safety.
Judging Idempotency Across HTTP Methods
Not every API requires strict idempotency design; developers should evaluate based on business scenarios. The following table outlines the idempotency characteristics and application scenarios for different HTTP methods:
| Method | Idempotent | Scenario |
|---|---|---|
| GET | Yes | Data retrieval |
| PUT | Yes | Full update or overwrite |
| DELETE | Yes | Delete specific resource |
| POST | No | Create new resource, payments |
| PATCH | No | Partial update |
Implementation Strategy: From Interception to State Caching
Before writing code, it is recommended to establish a standard interceptor flow. First, the API Gateway or Middleware layer must be able to parse the Idempotency-Key field in the request header. If this field is missing for non-idempotent methods, the system should reject the request with a 400 Bad Request to prevent accidental duplicate operations.
Implementation Checklist
- Define the Idempotency-Key request header format.
- Build a locking mechanism (Distributed Lock) for the key on the server side.
- Check if the processing status of the key exists in Redis.
- If the status is "Processing," reject the new request or wait for the lock to release.
- If the status is "Completed," return the cached result directly.
- If no record exists, execute the business logic and store the result and status.
Common Development Pitfalls
Many developers mistakenly believe that "checking the database for duplicates" is equivalent to idempotency. This is a major misconception. Database queries involve high I/O costs, and relying solely on the database in high-concurrency scenarios will create severe performance bottlenecks. The correct approach is to use a high-performance in-memory database (like Redis) as the first line of defense.
Another common error is confusing "Idempotency" with "Transactions." Transactions ensure the atomicity of an operation, while idempotency ensures the safety of repeating that operation. In distributed systems, they are complementary, not substitutes. If you only implement transactions without idempotency, the system remains vulnerable to duplicate transactions during network instability.
Exception Handling and State Consistency
What happens if the server crashes while executing business logic, before the status record is written to Redis? This relates to retry strategy design. We recommend a "reserve-then-update" strategy: write the key and mark it as `PENDING` before executing the business logic. Even if the server crashes, when the client retries, the server can identify that the task is currently in progress, thus avoiding resource conflicts.
Taking the Next Step in System Resilience
When an API achieves robust idempotency, the system's resilience is significantly improved. Developers should focus on handling "idempotency failures": for example, when the server returns 409 Conflict indicating that the key is currently being processed, how should the client implement an exponential backoff algorithm to avoid hammering the server during high load? This is not just a technical detail but a core competence in building modern, stable APIs.