The Importance of Idempotent APIs

hai nguyenhai nguyen
3 min read

Idempotence is a concept where clients receive the same response for multiple identical requests. With idempotence, calling endpoints multiple times results in the action happening only once. This is important because it ensures that if a client repeatedly sends the same request, the server state stays the same after the first call.

Idempotent properties are important in API design because they reduce system load and prevent data redundancy. However, achieving idempotence is not easy because it depends on many components and factors.

The table below highlights the key advantages and disadvantages of Idempotent APIs:

Non-idempotent APIIdempotent API's advantagesIdempotent API's disadvantages
Overloads the server by repeatedly requesting for same resources (multiple requests)Reduces load on the serverExtra storage is required to cache the responses and keys of the responses
Causes data redundancy by accepting all the requests and sending the same data repeatedlyEliminates data redundancyCustom scheduling job (custom code) is required to define TTL and then clear the data from storage
Due to the repetition of the same requests, networks can get busy, and congestedAvoids network congestionIn case of using third-party scheduling jobs (TTL and clearing storage jobs), it adds the overhead of integrating them
These are resource wastages because they’re occupied in responding to the same requests again and again.Efficient resource utilization

In real life, especially in a distributed system, "retry" is a popular and crucial technique to enhance system robustness. However, retries inevitably lead to duplicate messages or requests. If our API is not idempotent, this can result in severe bugs, such as duplicating bank balances, transferring money multiple times, or creating identical records.

Using RestAPI as an example, POST requests are not idempotent by default, so we need to design our system to make POST requests idempotent.

To achieve this, we need a way to identify duplicate requests. Each request should have a unique ID that the client generates. On the server side, we maintain a list of processed IDs to track which requests have been handled and to ignore duplicates.

The diagram looks simple enough, but you can see almost all distributed system problems in it. Let's take some questions.

  1. What is the format of the request_id?

    If the request_id is too short, another client might generate the same ID, causing bugs. If the request_id is too long, it wastes network bandwidth to send it and requires more storage on the server, which costs money.

  2. How long should the cache keep the request_id? 5 minutes, 1 hour, or several days?

    We can't keep the request ID in the cache forever because it would be too costly. But how long should we keep it? It depends on the business needs; there is no one-size-fits-all answer.

  3. Fallback strategy. In step 5, what happens if the API server can’t connect to the cache to check if the request_id has been processed? What should we do in this situation? Should we inform the client that we can't process their request, or should we continue processing and risk creating duplicates?

It's easy to implement the "happy case," but even a simple system with just a few components can introduce many of the common problems found in large distributed systems like those at Amazon, Google, or in banking systems.

References:

0
Subscribe to my newsletter

Read articles from hai nguyen directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

hai nguyen
hai nguyen