GetDifferences
GetDifferences
GetDifferences
GetDifferences
Get Differences between Retry Pattern and Circuit Breaker Pattern
Get Differences between Retry Pattern and Circuit Breaker Pattern

Get Differences between Retry Pattern and Circuit Breaker Pattern

In a distributed environment, resilience and fault tolerance are important. Two essential techniques, the Retry Pattern and the Circuit Breaker Pattern, stand out as pillars in the search for robustness.

Both patterns appear to address identical issues like improving system reliability by gracefully accepting temporary errors. But when you dive in deeper, you'll figure out there are some small but important differences in their techniques, trade-offs, and ideal use cases.

In this post, we will compare the Retry Pattern with the Circuit Breaker Pattern to discover their features, advantages and disadvantages.

Let’s jump into the differences.

Purpose

The Retry Pattern enables an application to handle transient failures when it tries to connect to a service or network resource by transparently retrying a failed operation.

The Retry Pattern
The Retry Pattern

The Circuit Breaker Pattern is designed to stop making requests to a service or component that's not working properly. It does this by temporarily 'breaking' the connection and sending requests to an alternate path or giving a backup response instead. The Retry Pattern enables an application to handle transient failures when it tries to connect to a service or network resource by transparently retrying a failed operation.

The Retry Pattern
The Retry Pattern
Handling Failures

In the Retry Pattern, retries are attempted immediately or with a slight delay after each failure, without considering the nature or cause of the failure.

The Circuit Breaker keeps track of the number of successive failures and opens the circuit when a predefined threshold is reached. It stays open for a predetermined amount of time, during which requests are not passed along, giving the failed component time to recover.

Fault Tolerance

The Retry pattern provides a basic way to handle fault tolerance by making multiple attempts to overcome transient failures. However, if the failure persists or gets worse then making continuous attempts might make things worse.

The Circuit Breaker provides a more advanced way to handle fault tolerance by actively controlling the flow of requests. It prevents cascading failures by quickly isolating problematic components and failing fast, thus minimizing the impact on the overall system.

Resource Consumption

The retry pattern may increase resource consumption, particularly when they happen quickly or without backup plans. Each retry attempt consumes additional resources.

The Circuit Breaker Pattern conserves resources by cutting down unnecessary calls to failing services or components. Once the circuit is open, requests are diverted, relieving the burden on the failing resource.

Recovery and Adaptation

The Retry Pattern mostly depends on repetitive attempts for recovery without having adaptive behavior. It might not fit effectively to long-lasting or systemic failures.

The Circuit Breaker Pattern enables the system to respond dynamically based on changing circumstances, which encourage adaptive behavior. It can gradually close the circuit as the failing component recovers or divert traffic to alternative resources.

Implementation Complexity

The Retry Pattern is easier to implement and it requires less configuration and code modifications. It works well for scenarios where transient failures are common and immediate retries are acceptable.

The circuit breaker Pattern uses more complex logic to achieve state management to track the state of circuits and handle transitions between states. It's preferred in scenarios when fault tolerance and system stability are crucial.

Strategy

The Retry Pattern handles the failure using below strategies:

  • Cancel.

    • The application should cancel the operation and report an exception in case the failure isn't transient or is unlikely to be successful if repeated.
  • Retry.

    • The application can retry the failing request again because the same failure is unlikely to be repeated and the request will probably be successful in case of transient failure like timeout error.
  • Retry after delay.

    • The application should wait for a suitable time before retrying the request.

The Circuit Breaker Pattern handles the failure using below strategies:

  • Closed.

    • The request is routed to the operation.
  • Open.

    • The request fails immediately and an exception is returned to the application.
  • Half-Open.

    • A limited number of requests are allowed to pass through and invoke the operation.
Code Snippet

Here is the basic implementation for both patterns so you can get a better understanding.

Retry Pattern
const retryCount = 3;
const delayInMilliseconds = 2000

export class RetryPattern {

    async fetchWithRetry(url: string) {
        let currentRetry = 0;

        while (currentRetry < retryCount) { 
            try {
                const response = await fetch(url);
                // Check if response is successful, if yes, return the data
                if (response.ok) {
                    const data = await response.json();
                    return data;
                } else {
                    // If response is not successful, throw an error
                    throw new Error(`Failed to fetch data: ${response.status} - ${response.statusText}`);
                }
            } catch (error: any) {
                // Log the error or handle it as needed
                console.error(`Error fetching data: ${error.message}`);
                
                // Increment retries
                currentRetry++;
                
                // Wait for a short period before retrying
                await new Promise(resolve => setTimeout(resolve, delayInMilliseconds));
            }
        }
    } 

}
Circuit Breaker
const FAILURE_THRESHOLD: number = 3;
const DELAY_IN_MILLISECONDS: number = 5000;

enum CircuitState {
    Closed,
    Open,
    HalfOpen
}

class CircuitBreakerPattern {
    private failures: number;
    private circuitState: CircuitState;

    constructor() {
        this.failures = 0;
        this.circuitState = CircuitState.Closed;
    }

    async performOperation(operation: () => Promise<any>): Promise<any> {
        if (this.circuitState === CircuitState.Open) {
            throw new Error('Circuit is open');
        }

        try {
            const result = await operation();
            this.reset();

            return result;
        } catch (error) {
            this.failures++;

            if (this.failures >= FAILURE_THRESHOLD) {
                this.openCircuit();
            }

            throw error;
        }
    }

    private openCircuit(): void {
        this.circuitState = CircuitState.Open;

        setTimeout(() => {
            this.circuitState = CircuitState.HalfOpen;
        }, DELAY_IN_MILLISECONDS);
    }

    private reset(): void {
        this.failures = 0;
        this.circuitState = CircuitState.Closed;
    }
}

Thanks for reading!

If you like the content, please do not forget to subscribe the GetDifferences channel.

Design patterns
Microservice architecture
Retry pattern
Circuit breaker pattern
Architectural pattern
Design patterns
Microservice architecture
Retry pattern
Circuit breaker pattern
Architectural pattern

Harshad Kathiriya
Harshad KathiriyaA seasoned technical enthusiast and blogger with a passion for exploring cutting-edge technologies, having 12+ years of experience in complete Software Development Life Cycle (SDLC) covering Requirements Gathering, Data Analysis, Data Modeling, Database Design, Development, Testing and Deployment of business applications. He enjoys sharing practical insights, tutorials, and best practices to help fellow developers and tech enthusiasts stay ahead in this fast-paced digital world.