Skip to content

Circuit Breaker

Primary interface for the Circuit Breaker utility.

FUNCTION DESCRIPTION
circuit_breaker

Protect a function that calls an unhealthy-prone downstream with a circuit breaker.

circuit_breaker

circuit_breaker(name: str, persistence_store: CircuitBreakerPersistenceLayer, on_circuit_open: Callable | None = None, on_transition: Callable | None = None, config: CircuitBreakerConfig | None = None) -> Callable

Protect a function that calls an unhealthy-prone downstream with a circuit breaker.

Wrap the function that makes the downstream call, not the whole Lambda handler, so a tripped circuit reflects one dependency rather than unrelated handler logic.

When the circuit is open the protected function is not called. Instead, if an on_circuit_open callback is registered it runs and its return value becomes the call's result; otherwise :class:CircuitBreakerOpenError is raised.

PARAMETER DESCRIPTION
name

Unique circuit name. Each name is an independent circuit; a function calling several backends should use one circuit per backend.

TYPE: str

persistence_store

Shared state store (for example CircuitBreakerDynamoDBPersistence).

TYPE: CircuitBreakerPersistenceLayer

on_circuit_open

Called when the circuit is open, with the protected function's own arguments (positional stay positional, keyword stay keyword) plus a trailing circuit keyword argument carrying a CircuitInfo. Its return value becomes the call's result. If None, an open circuit raises CircuitBreakerOpenError.

TYPE: Callable | None DEFAULT: None

on_transition

Called with a single CircuitTransition argument whenever the circuit changes state (open, probe, close, reopen). Fires only on transitions, never on the per-invocation hot path, so it is a safe place to emit a CloudWatch metric. Any exception it raises is swallowed and logged so observability never breaks the protected call.

TYPE: Callable | None DEFAULT: None

config

Tunables. Defaults to CircuitBreakerConfig() when omitted.

TYPE: CircuitBreakerConfig | None DEFAULT: None

RETURNS DESCRIPTION
Callable

The decorated function.

Example

Protect a payment backend, buffering rejected requests

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from aws_lambda_powertools.utilities.circuit_breaker_alpha import circuit_breaker, CircuitInfo
from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import (
    CircuitBreakerDynamoDBPersistence,
)

persistence = CircuitBreakerDynamoDBPersistence(table_name="CircuitBreakerState")

def buffer(order: dict, circuit: CircuitInfo):
    sqs.send_message(QueueUrl=url, MessageBody=json.dumps(order))

@circuit_breaker(name="payment-backend", persistence_store=persistence, on_circuit_open=buffer)
def charge(order: dict) -> dict:
    return payment_api.charge(order)
Source code in aws_lambda_powertools/utilities/circuit_breaker_alpha/circuit_breaker.py
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def circuit_breaker(
    name: str,
    persistence_store: CircuitBreakerPersistenceLayer,
    on_circuit_open: Callable | None = None,
    on_transition: Callable | None = None,
    config: CircuitBreakerConfig | None = None,
) -> Callable:
    """
    Protect a function that calls an unhealthy-prone downstream with a circuit breaker.

    Wrap the function that makes the downstream call, not the whole Lambda handler, so a
    tripped circuit reflects one dependency rather than unrelated handler logic.

    When the circuit is open the protected function is not called. Instead, if an
    ``on_circuit_open`` callback is registered it runs and its return value becomes the
    call's result; otherwise :class:`CircuitBreakerOpenError` is raised.

    Parameters
    ----------
    name : str
        Unique circuit name. Each name is an independent circuit; a function calling
        several backends should use one circuit per backend.
    persistence_store : CircuitBreakerPersistenceLayer
        Shared state store (for example ``CircuitBreakerDynamoDBPersistence``).
    on_circuit_open : Callable | None
        Called when the circuit is open, with the protected function's own arguments
        (positional stay positional, keyword stay keyword) plus a trailing ``circuit``
        keyword argument carrying a ``CircuitInfo``. Its return value becomes the call's
        result. If ``None``, an open circuit raises ``CircuitBreakerOpenError``.
    on_transition : Callable | None
        Called with a single ``CircuitTransition`` argument whenever the circuit changes
        state (open, probe, close, reopen). Fires only on transitions, never on the
        per-invocation hot path, so it is a safe place to emit a CloudWatch metric. Any
        exception it raises is swallowed and logged so observability never breaks the
        protected call.
    config : CircuitBreakerConfig | None
        Tunables. Defaults to ``CircuitBreakerConfig()`` when omitted.

    Returns
    -------
    Callable
        The decorated function.

    Example
    -------
    **Protect a payment backend, buffering rejected requests**

        from aws_lambda_powertools.utilities.circuit_breaker_alpha import circuit_breaker, CircuitInfo
        from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import (
            CircuitBreakerDynamoDBPersistence,
        )

        persistence = CircuitBreakerDynamoDBPersistence(table_name="CircuitBreakerState")

        def buffer(order: dict, circuit: CircuitInfo):
            sqs.send_message(QueueUrl=url, MessageBody=json.dumps(order))

        @circuit_breaker(name="payment-backend", persistence_store=persistence, on_circuit_open=buffer)
        def charge(order: dict) -> dict:
            return payment_api.charge(order)
    """
    config = config or CircuitBreakerConfig()

    def decorator(function: Callable) -> Callable:
        @functools.wraps(function)
        def wrapper(*args, **kwargs) -> Any:
            # Skip the circuit entirely when disabled (development only).
            if strtobool(os.getenv(constants.CIRCUIT_BREAKER_DISABLED_ENV, "false")):
                warnings.warn(
                    message="Disabling the circuit breaker is intended for development environments only "
                    "and should not be used in production.",
                    category=PowertoolsUserWarning,
                    stacklevel=2,
                )
                return function(*args, **kwargs)

            handler = CircuitBreakerHandler(
                function=function,
                name=name,
                config=config,
                persistence_store=persistence_store,
                on_circuit_open=on_circuit_open,
                on_transition=on_transition,
                function_args=args,
                function_kwargs=kwargs,
            )
            return handler.handle()

        return wrapper

    return decorator