Sitemap
JavaScript Expert

JavaScript advance code snippets

Integrating Circuit Breaker Pattern into API Call Retry Mechanism

--

Photo by Nenad Grujic on Unsplash

In software architecture, the circuit breaker pattern is designed to prevent an application from continuously trying to execute an operation that is likely to fail. It acts as a safeguard, allowing your application to fail fast and provide alternative paths instead of overwhelming an already struggling service. Integrating the circuit breaker pattern into your retry mechanism for API calls enhances resilience and helps in avoiding cascading failures.

How the Circuit Breaker Pattern Works

The circuit breaker has three states:

  1. Closed: The circuit is “closed” when everything is functioning normally. API calls are allowed to proceed.
  2. Open: If the number of failed attempts exceeds a certain threshold, the circuit “opens,” preventing further attempts to call the API. The application will throw an error immediately, allowing time for the service to recover.
  3. Half-Open: After a predetermined time, the circuit transitions to a “half-open” state, allowing a limited number of attempts to see if the service has recovered. If these calls succeed, the circuit closes again; if they fail, it opens once more.

Implementation

We will implement a function fetchWithCircuitBreaker that combines both the retry mechanism and the circuit breaker pattern.

Step 1: Implement the Circuit Breaker

Here’s how you can implement this pattern:

// circuitBreaker.js
class CircuitBreaker {
constructor(failureThreshold = 3, recoveryTimeout = 10000) {
this.failureThreshold = failureThreshold;
this.recoveryTimeout = recoveryTimeout;
this.failureCount = 0;
this.state = 'CLOSED'; // can be 'CLOSED', 'OPEN', or 'HALF_OPEN'
this.lastFailureTime = null;
}
isOpen() {
return this.state === 'OPEN';
}
recordSuccess() {
this.failureCount = 0; // reset on success
this.state = 'CLOSED';
}
recordFailure() {
this.failureCount++;
if (this.failureCount >= this.failureThreshold) {
this.state = 'OPEN';
this.lastFailureTime = Date.now();
}
}
shouldWait() {
if (this.state === 'OPEN' && (Date.now() - this.lastFailureTime) >= this.recoveryTimeout) {
this.state = 'HALF_OPEN';
return false; // attempt allowed
}
return true; // still waiting
}
}
module.exports = CircuitBreaker;

Step 2: Integrate Circuit Breaker with Retry Mechanism

We will now integrate the circuit breaker into our fetch function:

// fetchWithCircuitBreaker.js
const fetch = require('node-fetch'); // Make sure to install node-fetch for Node.js
const CircuitBreaker = require('./circuitBreaker');
const circuitBreaker = new CircuitBreaker();
async function fetchWithCircuitBreaker(url, options = {}, retries = 3) {
// Check if the circuit is open
if (circuitBreaker.isOpen()) {
if (circuitBreaker.shouldWait()) {
throw new Error('Service temporarily unavailable. Please try again later.');
}
}
try {
const response = await fetch(url, options);

// Check response and throw an error if not ok
if (!response.ok) {
circuitBreaker.recordFailure(); // count this as a failure
throw new Error(`HTTP error! Status: ${response.status}`);
}
circuitBreaker.recordSuccess(); // record success
return await response.json();
} catch (error) {
console.error(`Fetch attempt failed: ${error.message}. Retries left: ${retries}`);
// Retry logic
if (retries > 0) {
return fetchWithCircuitBreaker(url, options, retries - 1);
}
throw error; // final failure after retries
}
}
// Example usage
fetchWithCircuitBreaker('https://jsonplaceholder.typicode.com/posts')
.then(data => console.log('Fetched Data:', data))
.catch(err => console.error('Error after retries:', err.message));

Explanation

Circuit Breaker Class:

  • Manages the states (CLOSED, OPEN, and HALF_OPEN), tracks failure counts, and determines when to allow calls based on previous failures.

Fetch Function:

  • Before attempting an API call, it checks if the circuit is open. If open, it checks if enough time has passed to transition to the half-open state.
  • Each successful fetch call resets the failure count, while each failed call increments it.
  • If the fetch fails, it calls the retry mechanism up to a specified limit.

Exception Management:

  • If the maximum retries are exhausted, the error will be thrown, and the circuit state will manage subsequent API calls.

Key Takeaways

  • Failure Management: Integrating the circuit breaker pattern allows your application to manage failures effectively, avoiding unnecessary calls to a failing service.
  • Improved Resilience: This approach enhances the overall resilience of your applications, allowing them to respond gracefully to visible issues in external dependencies.
  • User Experience: By providing immediate feedback when a service is down, users can better understand why the application is unresponsive rather than experiencing prolonged (and unsuccessful) attempts.

Implementing a circuit breaker alongside a retry mechanism ensures that your API calls are resilient and maintain a balance between reliability and efficiency. Happy coding!

--

--

JavaScript Expert
JavaScript Expert
Neeraj Dana
Neeraj Dana

Written by Neeraj Dana

Top Writer in Javascript React Angular Node js NLP Typescript Machine Learning Data science Maths

No responses yet