Course contentsShow
← C#
Lesson 1541 of 1,90814. Background Work and MessagingFree lesson

Circuit Breaking in Message Consumers

Prevent cascading failure by automatically stopping message consumption when downstream dependencies are unavailable.

In this lesson, you will learn how to implement the Circuit Breaker pattern to protect your messaging infrastructure from cascading failures when downstream dependencies go offline.

The Problem: The "Death Spiral"

When a background worker consumes messages from a queue, it often relies on external services (like a database or an API). If that dependency crashes, your worker might encounter constant exceptions. If you blindly retry, you risk flooding the already-struggling dependency, or worse, consuming and losing messages that you cannot process. This creates a "death spiral" where your worker keeps pulling messages, failing, and wasting resources.

The Circuit Breaker Pattern

The Circuit Breaker acts as an electrical safety switch for your code. It operates in three states:

  1. Closed: Everything is fine. Messages are processed normally.
  2. Open: The dependency is failing. The "circuit" is broken, and the consumer stops pulling messages entirely, allowing the dependency time to recover.
  3. Half-Open: The consumer periodically tests the connection with a single message to see if the dependency is back online.

Implementation Concept

Instead of a simple try-catch block inside your ExecuteAsync loop, you wrap your processing logic in a state-aware wrapper. Using a library like Polly is standard, but the logic remains the same: monitor failure counts. If the threshold is hit, you "trip the circuit."

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        // CircuitBreaker logic: Check if we are allowed to process
        if (_circuitBreaker.IsAllowedToProcess())
        {
            var message = await _queue.ReceiveAsync();
            try {
                await ProcessMessage(message);
                _circuitBreaker.ReportSuccess();
            }
            catch (Exception) {
                _circuitBreaker.ReportFailure();
            }
        }
        else
        {
            // Circuit is open, pause consumption to avoid cascading failure
            await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
        }
    }
}

By stopping the consumption loop when the circuit is Open, you provide "backpressure" to the messaging system, preventing it from overwhelming your infrastructure during an outage. This turns an uncontrolled failure into a managed pause.

Key Takeaway: A Circuit Breaker protects your application by proactively stopping message consumption when downstream dependencies fail, preventing resource exhaustion and giving services time to recover.