One of the easiest cloud design pattern that one can try out is the Retry Pattern. I wanted to show how to use an Retry Pattern in Node.js using Promise as a example. So what does the Retry Pattern achieves?
Problem Statement – What is the issue the pattern solves?
When building applications you always have some sort of outside/external service including another MicroService that you have to consume or call. Sometimes there could be momentary loss of network connectivity, or a temporary unavailability, or timeouts that occur when that service is busy. You may be calling a database or a restful service that may be busy and fail but if you try back again it will pass. These types of faults are usually self-correcting, and most of the time require some type of delay in calling it again, which will have a success response.
Retry Pattern
- Enable an application to handle transient failures
- When the applications tries to connect to a service or network resource
- By transparently retrying a failed operation
- Improves the stability of your application
Typical Application
Below is a typical application diagram, where you a service or web app.
TypicalApplication
But when the connection to the service fails we usually get an error on our application.
Typical-Application-Network-Failure
When to use Retry Pattern
- Use retry for only transient failure that is more than likely to resolve themselves quickly
- Match the retry policies with the application
- Otherwise use the circuit break pattern
When not to use Retry Pattern
- Don’t cause a chain reaction to all components
- For internal exceptions caused by business logic
- Log all retry attempts to the service
Sample Code
Below is a sample in node.js that shows the usage using Promise in Node.js. The code tries to call https://httpbin.org/status/200,408 with a POST which gives us a status of 200 or 408 randomly. First, lets create our code and add the package fetch into it.
|
$mkdir NodeRetryPattern $cd NodeRetryPattern $npm init $npm install node-fetch $touch index.js #launch vscode $code . |
Without Promise
We will write a sample application that will call the the web service without retry to get 408 errors.
I am just using a console logger but you should be using a proper logger when you do retry pattern.
|
const fetch = require('node-fetch'); fetch("https://httpbin.org/status/200,408").then(res => console.log(res.status)); |
After couple of runs you will see it response back with 408 RequestTimeout
|
> node .\index.js 408 > node .\index.js 408 > node .\index.js 408 > node .\index.js 200 |
Using Retry with Promise
Now we will introduce the retry pattern with using Promise into our code with an incremental delay of 1 second to 3 seconds and lastly 9 seconds.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
const fetch = require('node-fetch'); const fetchWithRetry = (url, numberOfRetry) => { return new Promise((resolve, reject) => { let attempts = 1; const fetch_retry = (url, n) => { return fetch(url).then(res => { const status = res.status; if(status === 200) { return resolve(res); } else if (n === 1) { throw reject("Error in getting http data"); } else { console.log("Retry again: Got back " + status); console.log("With delay " + attempts * 3000); setTimeout(() => { attempts++; fetch_retry(url, n - 1); }, attempts * 3000); } }).catch(function (error) { if (n === 1) { reject(error) } else { setTimeout(() => { attempts++ fetch_retry(url, n - 1); }, attempts * 3000); } }); } return fetch_retry(url, numberOfRetry); }); } fetchWithRetry("https://httpbin.org/status/200,408", 3).then(x => console.log("Finally " + x.status)).catch(e => { console.log(e); }); |
Output
Below you will see three runs of the application with sample output.
|
#First Run > node .\index.js Finally 200 #Second run > node .\index.js Retry again: Got back 408 With delay 3000 Finally 200 |
Summary
As you can see Retry Pattern is quite useful for transient and self correcting failure, not to mention it is quite simple to implement in NodeJS with the help of Promise.