Payments are experiencing issues due to temporary restrictions in Russia. If your payment does not go through, please submit a support request.Our support team is available 24/7 — we are always here to help with hosting and server issues.We are now accepting requests for dedicated server rental and colocation services in our data center.Reminder: we recommend enabling backups for additional data protection.A new VPS/VDS lineup with NVMe storage and improved performance is now available.Maintenance work on some servers has been completed. All services are operating normally.
Article5 min readViews0

Three Attempts Turned into 27: How to Limit API Request Retries

Retries in modules, libraries, and middleware handlers can multiply. We calculate a sample scenario and analyze the total operation duration, delays, and conditions for resending.

Comments 0

A brass bell and its many reflections in mirrors: one call generates repetitions
In this article

On failure, the store makes up to three request attempts. Its library also supports request retries. Between them sits another handler with the same configuration. The diagram looks reassuring: each level attempts to survive a transient error. For the lower service, this care turns into additional load precisely when it is already struggling.

Start by auditing retries across the entire chain: who retries, which errors are considered transient, how many attempts are made, and what is the total time budget for the operation. A single setting in the store module does not describe the behavior of the HTTP client, the queue, and the external SDK simultaneously.

Where the 27 Requests Come From

The educational model has three nested levels. Each allows three attempts in total: the initial one plus two retries. Each attempt at the upper level triggers up to three attempts at the middle level; each of those triggers up to three attempts at the lower level. If all calls fail and retries are fully exhausted, we get 3 × 3 × 3 = 27 calls to the lower service.

This is the upper bound of the chosen simplified model, not a measurement of a real store. With early success, a total execution time limit, or a different error policy, the number will be lower. A local brute-force check of three nested loops confirmed 27. If retries are kept only at one level with the same limit, this model yields no more than three calls.

It is especially easy to confuse the phrases 'three repetitions' and 'three attempts'. The first usually means three additional requests after the initial one, totaling four; the second may include the initial request. The parameter name must be verified in the documentation for the specific library. For example, the AWS SDK documentation defines the maximum number of attempts including the first request; this is not a naming convention for all other clients.

Attempt Limits and Operation Duration Are Needed Simultaneously

Suppose one attempt takes two seconds, and there are waits of one and two seconds between the three attempts. In our sequential example, this results in 2 + 1 + 2 + 2 + 2 = 9 seconds without additional time for preparation. A two-second timeout does not mean the user will receive a final response within two seconds.

The overall operation deadline limits the entire wait chain. Before the next attempt, check the remaining time, and pass the allowable remainder to the nested call if the protocol and library support it. The gRPC documentation describes deadline propagation between calls. For an arbitrary HTTP client, this capability does not arise automatically: its existence and implementation must be verified separately.

A timeout on the client side does not prove that the work was cancelled on the server. Therefore, the order creation operation still faces the issue of an indeterminate result and the need for duplicate protection. A global deadline limits the wait time but does not replace the idempotency key or a method to determine the actual outcome.

Randomized Delay Does Not Replace a Budget

If a thousand clients wait exactly one second after a failure, they may return almost simultaneously. Increasing delays by adding random jitter helps spread out retry attempts. Google SRE and AWS retry mechanism documentation describe this practice. Specific intervals are chosen based on API conditions and the allowable operation time.

However, randomness changes the time distribution, not the configured maximum number of attempts. Twenty-seven allowed requests do not become three just because the pauses differ. Therefore, it is useful to separately limit retries for a single operation and the overall flow of retries to a dependent service.

During design, select the level that knows enough about the error meaning and remaining time to manage the retry. Other levels must align their policy with it. Do not simply disable all lower-level retries in a production system based on general advice: first, verify library capabilities and behavior in the test environment.

Retry Only What Has a Chance to Change

An invalid required field will not be fixed by waiting. Access denial also requires investigating the cause, not endlessly sending the same request. A temporary network failure may allow a retry, but for an operation with side effects, this is insufficient: the contract must allow safely determining or repeating its result.

The status 429 indicates a rate limit. The response may include Retry-After; the absence of this header does not mean you are immediately allowed to proceed. If the provider specifies a wait time, consider it alongside the total operation duration. When waiting within the current request is no longer possible, a predefined deferred processing workflow or an explicit failure is required, not an attempt to bypass the limit.

Error rules are derived from the specific service documentation, including its machine-readable codes. Do not automatically equate any response from a single HTTP class to a single retry scenario. Also, do not change the operation key after every timeout: this is a way to turn an uncertain result into multiple independent commands.

What to Measure During Acceptance Testing

In a safe test environment, it is useful to simulate a temporary failure, a persistent input error, and a slow response. For a single business operation, count all actual requests, the total time, and the reason for the stop. Separately verify that the total duration timeout does not trigger a new chain with a full budget.

The result should be a clear boundary: this operation may access a dependency no more than an agreed number of times, wait no longer than an agreed duration, and retry only under the specified conditions. The numbers provided are illustrative; the actual stack was not tested here. Such a boundary helps survive a failure without requiring each layer to independently strengthen its resilience.

Discussion 0

Share your experience and ask questions. Comments without links appear after editorial review.

No comments yet. Start the discussion.