How to Implement Retries
Learn how to implement retries in your code to handle errors and improve application reliability.
Introduction
Alchemy is a powerful platform that provides developers with advanced blockchain tools, such as APIs, monitoring, and analytics, to build their blockchain applications faster and more efficiently. Alchemy’s Elastic Throughput system guarantees a given throughput limit measured in compute units per second, but you may still hit your throughput capacity in some cases. In this tutorial, we will explore how to implement retries to handle Alchemy 429 errors.
Option 1: Alchemy SDK
The Alchemy SDK is the easiest way to connect your dApp to the blockchain. It automatically handles retry logic for you. To use the Alchemy SDK, follow these steps:
- Create a new node.js project and Install the Alchemy SDK using npm or yarn:
- Import and configure the Alchemy SDK with your API key and choice of network.
- Start making requests to the blockchain:
- Here’s the complete code:
The Alchemy SDK automatically handles retries for you, so you don’t need to worry about implementing retry logic.
Option 2: Exponential Backoff
Exponential backoff is a standard error-handling strategy for network applications. It is a similar solution to retries, however, instead of waiting random intervals, an exponential backoff algorithm retries requests exponentially, increasing the waiting time between retries up to a maximum backoff time.
Here is an example of an exponential backoff algorithm:
- Make a request.
- If the request fails, wait
1 + random_number_milliseconds
seconds and retry the request. - If the request fails, wait
2 + random_number_milliseconds
seconds and retry the request. - If the request fails, wait
4 + random_number_milliseconds
seconds and retry the request. - And so on, up to a maximum_backoff time…
- Continue waiting and retrying up to some maximum number of retries, but do not increase the wait period between retries.
Where:
- The wait time is
min(((2^n)+random_number_milliseconds), maximum_backoff)
, withn
incremented by 1 for each iteration (request). random_number_milliseconds
is a random number of milliseconds less than or equal to 1000. This helps to avoid cases in which many clients are synchronized by some situation and all retry at once, sending requests in synchronized waves. The value ofrandom_number_milliseconds
is recalculated after each retry request.maximum_backoff
is typically 32 or 64 seconds. The appropriate value depends on the use case.- The client can continue retrying after it has reached the
maximum_backoff
time. Retries after this point do not need to continue increasing backoff time. For example, suppose a client uses amaximum_backoff
time of 64 seconds. After reaching this value, the client can retry every 64 seconds. At some point, clients should be prevented from retrying indefinitely.
To implement exponential backoff in your Alchemy application, you can use a library such as retry
or async-retry
for handling retries in a more structured and scalable way.
Here’s an example implementation of exponential backoff using the async-retry
library in a Node.js application where we call the eth_blockNumber
API using Alchemy:
In this example, we define a new function called fetchWithRetries
that uses the async-retry
library to retry the fetch request with exponential backoff. The retry function takes two arguments:
- An async function that performs the fetch request and returns a response object or throws an error.
- An options object that specifies the retry behavior. We set the number of retries to 5, the exponential factor to 2, and the minimum and maximum wait times to 1 second and 60 seconds, respectively.
Finally, we call the fetchWithRetries
function and log the result or the error to the console.
Option 3: Simple Retries
If exponential backoff poses a challenge to you, a simple retry solution is to wait a random interval between 1000 and 1250 milliseconds after receiving a 429 response and sending the request again, up to some maximum number of attempts you are willing to wait.
Here’s an example implementation of simple retries in a node.js application where we call the eth_blocknumber
API using Alchemy:
-
In this example, we define a
maxRetries
constant to limit the number of retries we’re willing to wait. We also define aretries
variable to keep track of how many times we’ve retried so far. -
We then define the
makeRequest()
function, which is responsible for making the API request. We use thefetch
function to send the request with the specifiedurl
and options. -
We then check the response status: if it’s a
429 (Too Many Requests)
response and we haven’t reached themaxRetries
limit, we wait a random interval between 1000 and 1250 milliseconds before callingmakeRequest()
again. Otherwise, if the response isOK
, we parse the JSON response usingres.json()
and log it to the console. If the response status is anything else, we throw an error. -
If an error is caught, we check if we’ve reached the
maxRetries
limit. If we haven’t, we log an error message and callmakeRequest()
again after waiting a random interval between 1000 and 1250 milliseconds. If we have reached themaxRetries
limit, we log an error message and exit the function.
Finally, we call makeRequest() to start the process.
Option 4: Retry-After
If you’re using HTTP instead of WebSockets, you might come across a ‘Retry-After’ header in the HTTP response. This header serves as the duration you should wait before initiating a subsequent request. Despite the utility of the ‘Retry-After’ header, we continue to advise the use of exponential backoff. This is because the ‘Retry-After’ header only provides a fixed delay duration, while exponential backoff offers a more adaptable delay scheme. By adjusting the delay durations, exponential backoff can effectively prevent a server from being swamped with a high volume of requests in a short time frame.
Here’s an example implementation of “Retry-After” in a node.js application where we call the eth_blocknumber
API using Alchemy:
- The code starts by defining the API endpoint URL and the request options. It then sets up a function
makeRequest()
that usesfetch()
to make a POST request to the API. - If the response status code is
429 (Too Many Requests)
, the code checks for aRetry-After
header in the response. - If the header is present, the code retries the request after the number of seconds specified in the header.
- If the header is not present, the code generates a random retry time between 1 and 250ms and retries the request after that time.
- If the response status code is not
429
and is notOK
, the code throws an error. - If the response is
OK
, the code returns the response JSON. If there is an error, the code catches the error and retries the request if the number of retries is less than the maximum number of retries. - If the number of retries is equal to the maximum number of retries, the code logs an error message and exits.
Conclusion
In conclusion, retries are an important error-handling strategy for network applications that can help improve application reliability and handle errors. In this tutorial, we discussed 4 ways in which we can implement retries namely: Exponential-Backoff, Retry-After, Simple Retries and Alchemy SDK.
By implementing retries in your Alchemy application, you can help ensure that your application can handle errors and continue to function reliably even in the face of unexpected errors and network disruptions.