From 2449d2f2fabd4f5c633dc9d3694ae4985ed7a454 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Sun, 9 Aug 2026 21:42:24 +0100 Subject: [PATCH] feat: promote circuit breaker to GA --- .../utilities/circuit_breaker/__init__.py | 29 +++++++++++++++ .../base.py | 10 +++--- .../circuit_breaker.py | 10 +++--- .../config.py | 2 +- .../exceptions.py | 2 +- .../circuit_breaker/persistence/__init__.py | 15 ++++++++ .../persistence/base.py | 6 ++-- .../persistence/dynamodb.py | 10 +++--- .../persistence/record.py | 2 +- .../states.py | 0 .../circuit_breaker_alpha/__init__.py | 35 ------------------- .../persistence/__init__.py | 15 -------- .../circuit_breaker/circuit_breaker.md | 2 ++ docs/api_doc/circuit_breaker/config.md | 2 ++ docs/api_doc/circuit_breaker/exceptions.md | 2 ++ docs/api_doc/circuit_breaker/persistence.md | 2 ++ docs/api_doc/circuit_breaker/states.md | 2 ++ .../circuit_breaker_alpha/circuit_breaker.md | 2 -- docs/api_doc/circuit_breaker_alpha/config.md | 2 -- .../circuit_breaker_alpha/exceptions.md | 2 -- .../circuit_breaker_alpha/persistence.md | 2 -- docs/api_doc/circuit_breaker_alpha/states.md | 2 -- docs/utilities/circuit_breaker.md | 21 ++++------- .../getting_started_with_circuit_breaker.py | 4 +-- .../src/working_with_callback.py | 4 +-- .../src/working_with_composite_primary_key.py | 4 +-- .../src/working_with_metrics.py | 4 +-- .../src/working_without_callback.py | 4 +-- .../templates/sam.yaml | 0 .../__init__.py | 0 .../conftest.py | 10 +++--- .../test_circuit_breaker.py | 8 ++--- .../test_dynamodb_persistence.py | 12 +++---- .../_fastjsonschema/test_validator.py | 3 +- .../__init__.py | 0 .../test_config_and_states.py | 8 ++--- 36 files changed, 113 insertions(+), 125 deletions(-) create mode 100644 aws_lambda_powertools/utilities/circuit_breaker/__init__.py rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/base.py (96%) rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/circuit_breaker.py (90%) rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/config.py (98%) rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/exceptions.py (96%) create mode 100644 aws_lambda_powertools/utilities/circuit_breaker/persistence/__init__.py rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/persistence/base.py (97%) rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/persistence/dynamodb.py (96%) rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/persistence/record.py (95%) rename aws_lambda_powertools/utilities/{circuit_breaker_alpha => circuit_breaker}/states.py (100%) delete mode 100644 aws_lambda_powertools/utilities/circuit_breaker_alpha/__init__.py delete mode 100644 aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/__init__.py create mode 100644 docs/api_doc/circuit_breaker/circuit_breaker.md create mode 100644 docs/api_doc/circuit_breaker/config.md create mode 100644 docs/api_doc/circuit_breaker/exceptions.md create mode 100644 docs/api_doc/circuit_breaker/persistence.md create mode 100644 docs/api_doc/circuit_breaker/states.md delete mode 100644 docs/api_doc/circuit_breaker_alpha/circuit_breaker.md delete mode 100644 docs/api_doc/circuit_breaker_alpha/config.md delete mode 100644 docs/api_doc/circuit_breaker_alpha/exceptions.md delete mode 100644 docs/api_doc/circuit_breaker_alpha/persistence.md delete mode 100644 docs/api_doc/circuit_breaker_alpha/states.md rename examples/{circuit_breaker_alpha => circuit_breaker}/src/getting_started_with_circuit_breaker.py (83%) rename examples/{circuit_breaker_alpha => circuit_breaker}/src/working_with_callback.py (87%) rename examples/{circuit_breaker_alpha => circuit_breaker}/src/working_with_composite_primary_key.py (80%) rename examples/{circuit_breaker_alpha => circuit_breaker}/src/working_with_metrics.py (88%) rename examples/{circuit_breaker_alpha => circuit_breaker}/src/working_without_callback.py (90%) rename examples/{circuit_breaker_alpha => circuit_breaker}/templates/sam.yaml (100%) rename tests/functional/{circuit_breaker_alpha => circuit_breaker}/__init__.py (100%) rename tests/functional/{circuit_breaker_alpha => circuit_breaker}/conftest.py (90%) rename tests/functional/{circuit_breaker_alpha => circuit_breaker}/test_circuit_breaker.py (98%) rename tests/functional/{circuit_breaker_alpha => circuit_breaker}/test_dynamodb_persistence.py (96%) rename tests/unit/{circuit_breaker_alpha => circuit_breaker}/__init__.py (100%) rename tests/unit/{circuit_breaker_alpha => circuit_breaker}/test_config_and_states.py (93%) diff --git a/aws_lambda_powertools/utilities/circuit_breaker/__init__.py b/aws_lambda_powertools/utilities/circuit_breaker/__init__.py new file mode 100644 index 00000000000..ffe5ce3cb7a --- /dev/null +++ b/aws_lambda_powertools/utilities/circuit_breaker/__init__.py @@ -0,0 +1,29 @@ +""" +Circuit Breaker utility for protecting unhealthy downstream dependencies. +""" + +from aws_lambda_powertools.utilities.circuit_breaker.circuit_breaker import circuit_breaker +from aws_lambda_powertools.utilities.circuit_breaker.config import CircuitBreakerConfig +from aws_lambda_powertools.utilities.circuit_breaker.exceptions import ( + CircuitBreakerConfigError, + CircuitBreakerError, + CircuitBreakerOpenError, + CircuitBreakerPersistenceError, +) +from aws_lambda_powertools.utilities.circuit_breaker.states import ( + CircuitInfo, + CircuitState, + CircuitTransition, +) + +__all__ = ( + "circuit_breaker", + "CircuitBreakerConfig", + "CircuitInfo", + "CircuitState", + "CircuitTransition", + "CircuitBreakerError", + "CircuitBreakerOpenError", + "CircuitBreakerConfigError", + "CircuitBreakerPersistenceError", +) diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/base.py b/aws_lambda_powertools/utilities/circuit_breaker/base.py similarity index 96% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/base.py rename to aws_lambda_powertools/utilities/circuit_breaker/base.py index 88fcedf61fe..f0375885d18 100644 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/base.py +++ b/aws_lambda_powertools/utilities/circuit_breaker/base.py @@ -15,17 +15,17 @@ import uuid from typing import TYPE_CHECKING, Any -from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerOpenError -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitState, CircuitTransition +from aws_lambda_powertools.utilities.circuit_breaker.exceptions import CircuitBreakerOpenError +from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitState, CircuitTransition if TYPE_CHECKING: from collections.abc import Callable - from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig - from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import ( + from aws_lambda_powertools.utilities.circuit_breaker.config import CircuitBreakerConfig + from aws_lambda_powertools.utilities.circuit_breaker.persistence.base import ( CircuitBreakerPersistenceLayer, ) - from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitInfo + from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitInfo logger = logging.getLogger(__name__) diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/circuit_breaker.py b/aws_lambda_powertools/utilities/circuit_breaker/circuit_breaker.py similarity index 90% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/circuit_breaker.py rename to aws_lambda_powertools/utilities/circuit_breaker/circuit_breaker.py index de4e66b0680..f0d65542607 100644 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/circuit_breaker.py +++ b/aws_lambda_powertools/utilities/circuit_breaker/circuit_breaker.py @@ -12,14 +12,14 @@ from aws_lambda_powertools.shared import constants from aws_lambda_powertools.shared.functions import strtobool -from aws_lambda_powertools.utilities.circuit_breaker_alpha.base import CircuitBreakerHandler -from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig +from aws_lambda_powertools.utilities.circuit_breaker.base import CircuitBreakerHandler +from aws_lambda_powertools.utilities.circuit_breaker.config import CircuitBreakerConfig from aws_lambda_powertools.warnings import PowertoolsUserWarning if TYPE_CHECKING: from collections.abc import Callable - from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import ( + from aws_lambda_powertools.utilities.circuit_breaker.persistence.base import ( CircuitBreakerPersistenceLayer, ) @@ -73,8 +73,8 @@ def circuit_breaker( ------- **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 ( + from aws_lambda_powertools.utilities.circuit_breaker import circuit_breaker, CircuitInfo + from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/config.py b/aws_lambda_powertools/utilities/circuit_breaker/config.py similarity index 98% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/config.py rename to aws_lambda_powertools/utilities/circuit_breaker/config.py index 398ff33d6e2..7ee1259a697 100644 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/config.py +++ b/aws_lambda_powertools/utilities/circuit_breaker/config.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING -from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerConfigError +from aws_lambda_powertools.utilities.circuit_breaker.exceptions import CircuitBreakerConfigError if TYPE_CHECKING: from collections.abc import Iterable diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/exceptions.py b/aws_lambda_powertools/utilities/circuit_breaker/exceptions.py similarity index 96% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/exceptions.py rename to aws_lambda_powertools/utilities/circuit_breaker/exceptions.py index cf3c350fc83..063cf5f9c4a 100644 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/exceptions.py +++ b/aws_lambda_powertools/utilities/circuit_breaker/exceptions.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitInfo + from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitInfo class CircuitBreakerError(Exception): diff --git a/aws_lambda_powertools/utilities/circuit_breaker/persistence/__init__.py b/aws_lambda_powertools/utilities/circuit_breaker/persistence/__init__.py new file mode 100644 index 00000000000..3669d207b26 --- /dev/null +++ b/aws_lambda_powertools/utilities/circuit_breaker/persistence/__init__.py @@ -0,0 +1,15 @@ +""" +Persistence layers for the Circuit Breaker utility. +""" + +from aws_lambda_powertools.utilities.circuit_breaker.persistence.base import CircuitBreakerPersistenceLayer +from aws_lambda_powertools.utilities.circuit_breaker.persistence.dynamodb import ( + CircuitBreakerDynamoDBPersistence, +) +from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord + +__all__ = ( + "CircuitBreakerPersistenceLayer", + "CircuitBreakerDynamoDBPersistence", + "CircuitStateRecord", +) diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/base.py b/aws_lambda_powertools/utilities/circuit_breaker/persistence/base.py similarity index 97% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/base.py rename to aws_lambda_powertools/utilities/circuit_breaker/persistence/base.py index a4ca7e1985b..c7dc05c637f 100644 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/base.py +++ b/aws_lambda_powertools/utilities/circuit_breaker/persistence/base.py @@ -15,11 +15,11 @@ from typing import TYPE_CHECKING, NamedTuple from aws_lambda_powertools.shared.cache_dict import LRUDict -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitState +from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord +from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitState if TYPE_CHECKING: - from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig + from aws_lambda_powertools.utilities.circuit_breaker.config import CircuitBreakerConfig logger = logging.getLogger(__name__) diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/dynamodb.py b/aws_lambda_powertools/utilities/circuit_breaker/persistence/dynamodb.py similarity index 96% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/dynamodb.py rename to aws_lambda_powertools/utilities/circuit_breaker/persistence/dynamodb.py index edcbf502ab5..3b1f36aa9f3 100644 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/dynamodb.py +++ b/aws_lambda_powertools/utilities/circuit_breaker/persistence/dynamodb.py @@ -15,13 +15,13 @@ from botocore.exceptions import ClientError from aws_lambda_powertools.shared import constants, user_agent -from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerConfigError -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import ( +from aws_lambda_powertools.utilities.circuit_breaker.exceptions import CircuitBreakerConfigError +from aws_lambda_powertools.utilities.circuit_breaker.persistence.base import ( CircuitBreakerExistingLockError, CircuitBreakerPersistenceLayer, ) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitState +from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord +from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitState from aws_lambda_powertools.warnings import PowertoolsUserWarning if TYPE_CHECKING: @@ -75,7 +75,7 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer): ------- **Create a DynamoDB-backed circuit breaker store** - from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import ( + from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/record.py b/aws_lambda_powertools/utilities/circuit_breaker/persistence/record.py similarity index 95% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/record.py rename to aws_lambda_powertools/utilities/circuit_breaker/persistence/record.py index ec48737810f..55b486099cd 100644 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/record.py +++ b/aws_lambda_powertools/utilities/circuit_breaker/persistence/record.py @@ -6,7 +6,7 @@ from dataclasses import dataclass -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitInfo, CircuitState +from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitInfo, CircuitState @dataclass diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/states.py b/aws_lambda_powertools/utilities/circuit_breaker/states.py similarity index 100% rename from aws_lambda_powertools/utilities/circuit_breaker_alpha/states.py rename to aws_lambda_powertools/utilities/circuit_breaker/states.py diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/__init__.py b/aws_lambda_powertools/utilities/circuit_breaker_alpha/__init__.py deleted file mode 100644 index e931245f9d0..00000000000 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Circuit Breaker utility for protecting unhealthy downstream dependencies. - -!!! warning "Alpha / experimental" - This utility is published under the `_alpha` namespace while we collect - feedback. The public API may change in a backwards-incompatible way before it - is promoted to GA. Pin your version and follow the tracking discussion before - relying on it in production. -""" - -from aws_lambda_powertools.utilities.circuit_breaker_alpha.circuit_breaker import circuit_breaker -from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig -from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import ( - CircuitBreakerConfigError, - CircuitBreakerError, - CircuitBreakerOpenError, - CircuitBreakerPersistenceError, -) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import ( - CircuitInfo, - CircuitState, - CircuitTransition, -) - -__all__ = ( - "circuit_breaker", - "CircuitBreakerConfig", - "CircuitInfo", - "CircuitState", - "CircuitTransition", - "CircuitBreakerError", - "CircuitBreakerOpenError", - "CircuitBreakerConfigError", - "CircuitBreakerPersistenceError", -) diff --git a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/__init__.py b/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/__init__.py deleted file mode 100644 index 18704bff793..00000000000 --- a/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Persistence layers for the Circuit Breaker utility. -""" - -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import CircuitBreakerPersistenceLayer -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.dynamodb import ( - CircuitBreakerDynamoDBPersistence, -) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord - -__all__ = ( - "CircuitBreakerPersistenceLayer", - "CircuitBreakerDynamoDBPersistence", - "CircuitStateRecord", -) diff --git a/docs/api_doc/circuit_breaker/circuit_breaker.md b/docs/api_doc/circuit_breaker/circuit_breaker.md new file mode 100644 index 00000000000..2cb6f9b712e --- /dev/null +++ b/docs/api_doc/circuit_breaker/circuit_breaker.md @@ -0,0 +1,2 @@ + +::: aws_lambda_powertools.utilities.circuit_breaker.circuit_breaker diff --git a/docs/api_doc/circuit_breaker/config.md b/docs/api_doc/circuit_breaker/config.md new file mode 100644 index 00000000000..28c7f208022 --- /dev/null +++ b/docs/api_doc/circuit_breaker/config.md @@ -0,0 +1,2 @@ + +::: aws_lambda_powertools.utilities.circuit_breaker.config diff --git a/docs/api_doc/circuit_breaker/exceptions.md b/docs/api_doc/circuit_breaker/exceptions.md new file mode 100644 index 00000000000..72ff6a993b2 --- /dev/null +++ b/docs/api_doc/circuit_breaker/exceptions.md @@ -0,0 +1,2 @@ + +::: aws_lambda_powertools.utilities.circuit_breaker.exceptions diff --git a/docs/api_doc/circuit_breaker/persistence.md b/docs/api_doc/circuit_breaker/persistence.md new file mode 100644 index 00000000000..aeabefdfa0c --- /dev/null +++ b/docs/api_doc/circuit_breaker/persistence.md @@ -0,0 +1,2 @@ + +::: aws_lambda_powertools.utilities.circuit_breaker.persistence diff --git a/docs/api_doc/circuit_breaker/states.md b/docs/api_doc/circuit_breaker/states.md new file mode 100644 index 00000000000..215886daa74 --- /dev/null +++ b/docs/api_doc/circuit_breaker/states.md @@ -0,0 +1,2 @@ + +::: aws_lambda_powertools.utilities.circuit_breaker.states diff --git a/docs/api_doc/circuit_breaker_alpha/circuit_breaker.md b/docs/api_doc/circuit_breaker_alpha/circuit_breaker.md deleted file mode 100644 index 824b40225bc..00000000000 --- a/docs/api_doc/circuit_breaker_alpha/circuit_breaker.md +++ /dev/null @@ -1,2 +0,0 @@ - -::: aws_lambda_powertools.utilities.circuit_breaker_alpha.circuit_breaker diff --git a/docs/api_doc/circuit_breaker_alpha/config.md b/docs/api_doc/circuit_breaker_alpha/config.md deleted file mode 100644 index 20b548082c1..00000000000 --- a/docs/api_doc/circuit_breaker_alpha/config.md +++ /dev/null @@ -1,2 +0,0 @@ - -::: aws_lambda_powertools.utilities.circuit_breaker_alpha.config diff --git a/docs/api_doc/circuit_breaker_alpha/exceptions.md b/docs/api_doc/circuit_breaker_alpha/exceptions.md deleted file mode 100644 index 283374e42e5..00000000000 --- a/docs/api_doc/circuit_breaker_alpha/exceptions.md +++ /dev/null @@ -1,2 +0,0 @@ - -::: aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions diff --git a/docs/api_doc/circuit_breaker_alpha/persistence.md b/docs/api_doc/circuit_breaker_alpha/persistence.md deleted file mode 100644 index f865dfd10f0..00000000000 --- a/docs/api_doc/circuit_breaker_alpha/persistence.md +++ /dev/null @@ -1,2 +0,0 @@ - -::: aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence diff --git a/docs/api_doc/circuit_breaker_alpha/states.md b/docs/api_doc/circuit_breaker_alpha/states.md deleted file mode 100644 index c6f232a0e28..00000000000 --- a/docs/api_doc/circuit_breaker_alpha/states.md +++ /dev/null @@ -1,2 +0,0 @@ - -::: aws_lambda_powertools.utilities.circuit_breaker_alpha.states diff --git a/docs/utilities/circuit_breaker.md b/docs/utilities/circuit_breaker.md index b2117f7316b..ab219c942c3 100644 --- a/docs/utilities/circuit_breaker.md +++ b/docs/utilities/circuit_breaker.md @@ -5,13 +5,6 @@ description: Utility -!!! warning "Alpha / experimental" - This utility ships under the **`circuit_breaker_alpha`** namespace while we collect - feedback. The public API may change in a backwards-incompatible way before it is - promoted to GA, at which point the import path becomes `circuit_breaker`. Pin your - Powertools version and follow the tracking discussion before relying on it in - production. - The circuit breaker utility stops sending traffic to an unhealthy downstream dependency, giving it room to recover while you decide what happens to the rejected requests. ## Key features @@ -99,7 +92,7 @@ You **can** use a single DynamoDB table for all your circuits. Already have a si === "AWS Serverless Application Model (SAM) example" ```yaml hl_lines="3-15 24-29 32-33" - --8<-- "examples/circuit_breaker_alpha/templates/sam.yaml" + --8<-- "examples/circuit_breaker/templates/sam.yaml" ``` ### Circuit breaker in action @@ -109,7 +102,7 @@ The common case is the `@circuit_breaker` decorator wrapping the function that m === "getting_started_with_circuit_breaker.py" ```python hl_lines="3-6 9 19 27" - --8<-- "examples/circuit_breaker_alpha/src/getting_started_with_circuit_breaker.py" + --8<-- "examples/circuit_breaker/src/getting_started_with_circuit_breaker.py" ``` !!! note "Wrap the downstream call, not the whole handler" @@ -137,7 +130,7 @@ Register an `on_circuit_open` callback to decide what happens to a rejected requ === "working_with_callback.py" ```python hl_lines="6 24-28 31-35" - --8<-- "examples/circuit_breaker_alpha/src/working_with_callback.py" + --8<-- "examples/circuit_breaker/src/working_with_callback.py" ``` !!! info "Why a callback instead of built-in S3/SQS sinks?" @@ -152,7 +145,7 @@ If you don't register a callback, an open circuit raises `CircuitBreakerOpenErro === "working_without_callback.py" ```python hl_lines="5 16-22 38-43" - --8<-- "examples/circuit_breaker_alpha/src/working_without_callback.py" + --8<-- "examples/circuit_breaker/src/working_without_callback.py" ``` ## Configuration @@ -211,7 +204,7 @@ Register an `on_transition` hook to be notified whenever the circuit changes sta === "working_with_metrics.py" ```python hl_lines="3 12-21 26" - --8<-- "examples/circuit_breaker_alpha/src/working_with_metrics.py" + --8<-- "examples/circuit_breaker/src/working_with_metrics.py" ``` Any exception raised inside the hook is swallowed and logged, so a misbehaving metric call can never break the protected request. @@ -234,14 +227,14 @@ Set **`POWERTOOLS_CIRCUIT_BREAKER_DISABLED`**{: .copyMe} to a truthy value to by #### Using a composite primary key -Use the `sort_key_attr` parameter when your table is configured with a composite primary key _(hash+range key)_, as in a single-table design. +Use the `sort_key_attr` parameter when your table is configured with a composite primary key *(hash+range key)*, as in a single-table design. When enabled, the circuit name is saved in the sort key instead, and the partition key defaults to `circuit_breaker#{LAMBDA_FUNCTION_NAME}` — this **namespaces circuits per function** so two functions can use the same circuit name without sharing state. (Without `sort_key_attr`, the default remains partition-key-only with the circuit name as the partition key.) Set `static_pk_value` to a fixed value (as below) when you instead want **multiple functions to share the same circuit** — for example, every function guarding the same downstream dependency should trip together. ```python hl_lines="12-14" ---8<-- "examples/circuit_breaker_alpha/src/working_with_composite_primary_key.py" +--8<-- "examples/circuit_breaker/src/working_with_composite_primary_key.py" ``` ??? note "Click to expand and learn how table items would look like" diff --git a/examples/circuit_breaker_alpha/src/getting_started_with_circuit_breaker.py b/examples/circuit_breaker/src/getting_started_with_circuit_breaker.py similarity index 83% rename from examples/circuit_breaker_alpha/src/getting_started_with_circuit_breaker.py rename to examples/circuit_breaker/src/getting_started_with_circuit_breaker.py index ad9a4de1066..d97ff2bf235 100644 --- a/examples/circuit_breaker_alpha/src/getting_started_with_circuit_breaker.py +++ b/examples/circuit_breaker/src/getting_started_with_circuit_breaker.py @@ -1,7 +1,7 @@ import os -from aws_lambda_powertools.utilities.circuit_breaker_alpha import circuit_breaker -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import ( +from aws_lambda_powertools.utilities.circuit_breaker import circuit_breaker +from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) from aws_lambda_powertools.utilities.typing import LambdaContext diff --git a/examples/circuit_breaker_alpha/src/working_with_callback.py b/examples/circuit_breaker/src/working_with_callback.py similarity index 87% rename from examples/circuit_breaker_alpha/src/working_with_callback.py rename to examples/circuit_breaker/src/working_with_callback.py index d2b97ad6ac5..5725ac32a9b 100644 --- a/examples/circuit_breaker_alpha/src/working_with_callback.py +++ b/examples/circuit_breaker/src/working_with_callback.py @@ -3,8 +3,8 @@ import boto3 -from aws_lambda_powertools.utilities.circuit_breaker_alpha import CircuitInfo, circuit_breaker -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import ( +from aws_lambda_powertools.utilities.circuit_breaker import CircuitInfo, circuit_breaker +from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) from aws_lambda_powertools.utilities.typing import LambdaContext diff --git a/examples/circuit_breaker_alpha/src/working_with_composite_primary_key.py b/examples/circuit_breaker/src/working_with_composite_primary_key.py similarity index 80% rename from examples/circuit_breaker_alpha/src/working_with_composite_primary_key.py rename to examples/circuit_breaker/src/working_with_composite_primary_key.py index d2ef0ae9a17..0bceacdd9fa 100644 --- a/examples/circuit_breaker_alpha/src/working_with_composite_primary_key.py +++ b/examples/circuit_breaker/src/working_with_composite_primary_key.py @@ -1,7 +1,7 @@ import os -from aws_lambda_powertools.utilities.circuit_breaker_alpha import circuit_breaker -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import ( +from aws_lambda_powertools.utilities.circuit_breaker import circuit_breaker +from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) from aws_lambda_powertools.utilities.typing import LambdaContext diff --git a/examples/circuit_breaker_alpha/src/working_with_metrics.py b/examples/circuit_breaker/src/working_with_metrics.py similarity index 88% rename from examples/circuit_breaker_alpha/src/working_with_metrics.py rename to examples/circuit_breaker/src/working_with_metrics.py index f37f1b68351..8129e91f36e 100644 --- a/examples/circuit_breaker_alpha/src/working_with_metrics.py +++ b/examples/circuit_breaker/src/working_with_metrics.py @@ -1,11 +1,11 @@ import os from aws_lambda_powertools.metrics import MetricUnit, single_metric -from aws_lambda_powertools.utilities.circuit_breaker_alpha import ( +from aws_lambda_powertools.utilities.circuit_breaker import ( CircuitTransition, circuit_breaker, ) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import ( +from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) from aws_lambda_powertools.utilities.typing import LambdaContext diff --git a/examples/circuit_breaker_alpha/src/working_without_callback.py b/examples/circuit_breaker/src/working_without_callback.py similarity index 90% rename from examples/circuit_breaker_alpha/src/working_without_callback.py rename to examples/circuit_breaker/src/working_without_callback.py index a5f166438c8..2ac205333ad 100644 --- a/examples/circuit_breaker_alpha/src/working_without_callback.py +++ b/examples/circuit_breaker/src/working_without_callback.py @@ -1,11 +1,11 @@ import os -from aws_lambda_powertools.utilities.circuit_breaker_alpha import ( +from aws_lambda_powertools.utilities.circuit_breaker import ( CircuitBreakerConfig, CircuitBreakerOpenError, circuit_breaker, ) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import ( +from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) from aws_lambda_powertools.utilities.typing import LambdaContext diff --git a/examples/circuit_breaker_alpha/templates/sam.yaml b/examples/circuit_breaker/templates/sam.yaml similarity index 100% rename from examples/circuit_breaker_alpha/templates/sam.yaml rename to examples/circuit_breaker/templates/sam.yaml diff --git a/tests/functional/circuit_breaker_alpha/__init__.py b/tests/functional/circuit_breaker/__init__.py similarity index 100% rename from tests/functional/circuit_breaker_alpha/__init__.py rename to tests/functional/circuit_breaker/__init__.py diff --git a/tests/functional/circuit_breaker_alpha/conftest.py b/tests/functional/circuit_breaker/conftest.py similarity index 90% rename from tests/functional/circuit_breaker_alpha/conftest.py rename to tests/functional/circuit_breaker/conftest.py index 297c088750b..30925dfc5e8 100644 --- a/tests/functional/circuit_breaker_alpha/conftest.py +++ b/tests/functional/circuit_breaker/conftest.py @@ -4,14 +4,14 @@ import pytest -import aws_lambda_powertools.utilities.circuit_breaker_alpha.base as base_module -from aws_lambda_powertools.utilities.circuit_breaker_alpha.base import CircuitBreakerHandler -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import ( +import aws_lambda_powertools.utilities.circuit_breaker.base as base_module +from aws_lambda_powertools.utilities.circuit_breaker.base import CircuitBreakerHandler +from aws_lambda_powertools.utilities.circuit_breaker.persistence.base import ( CircuitBreakerExistingLockError, CircuitBreakerPersistenceLayer, ) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitState +from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord +from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitState class FakePersistence(CircuitBreakerPersistenceLayer): diff --git a/tests/functional/circuit_breaker_alpha/test_circuit_breaker.py b/tests/functional/circuit_breaker/test_circuit_breaker.py similarity index 98% rename from tests/functional/circuit_breaker_alpha/test_circuit_breaker.py rename to tests/functional/circuit_breaker/test_circuit_breaker.py index 8a6daf7c17f..1db372ee900 100644 --- a/tests/functional/circuit_breaker_alpha/test_circuit_breaker.py +++ b/tests/functional/circuit_breaker/test_circuit_breaker.py @@ -6,16 +6,16 @@ import pytest -import aws_lambda_powertools.utilities.circuit_breaker_alpha.base as base_module -from aws_lambda_powertools.utilities.circuit_breaker_alpha import ( +import aws_lambda_powertools.utilities.circuit_breaker.base as base_module +from aws_lambda_powertools.utilities.circuit_breaker import ( CircuitBreakerConfig, CircuitBreakerOpenError, CircuitState, CircuitTransition, circuit_breaker, ) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerError -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord +from aws_lambda_powertools.utilities.circuit_breaker.exceptions import CircuitBreakerError +from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord # All tests disable the local read cache (max_age=0) so each call re-reads the fake store. diff --git a/tests/functional/circuit_breaker_alpha/test_dynamodb_persistence.py b/tests/functional/circuit_breaker/test_dynamodb_persistence.py similarity index 96% rename from tests/functional/circuit_breaker_alpha/test_dynamodb_persistence.py rename to tests/functional/circuit_breaker/test_dynamodb_persistence.py index c395d663cad..ac67bfd0897 100644 --- a/tests/functional/circuit_breaker_alpha/test_dynamodb_persistence.py +++ b/tests/functional/circuit_breaker/test_dynamodb_persistence.py @@ -5,12 +5,12 @@ from botocore.config import Config from botocore.stub import Stubber -from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig -from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerConfigError -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import ( +from aws_lambda_powertools.utilities.circuit_breaker.config import CircuitBreakerConfig +from aws_lambda_powertools.utilities.circuit_breaker.exceptions import CircuitBreakerConfigError +from aws_lambda_powertools.utilities.circuit_breaker.persistence import ( CircuitBreakerDynamoDBPersistence, ) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitState +from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitState from aws_lambda_powertools.warnings import PowertoolsUserWarning TABLE_NAME = "CircuitBreakerState" @@ -196,7 +196,7 @@ def test_item_to_record_minimal_item(persistence): def test_record_to_item_full_record(persistence): """Cover _record_to_item with all fields set (lines 124-139).""" - from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord + from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord record = CircuitStateRecord( name="payment", @@ -219,7 +219,7 @@ def test_record_to_item_full_record(persistence): def test_record_to_item_minimal_record(persistence): """Cover _record_to_item with optional fields as None (branch misses on lines 131-137).""" - from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord + from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord record = CircuitStateRecord(name="payment", state=CircuitState.CLOSED) item = persistence._record_to_item(record) diff --git a/tests/functional/validator/_fastjsonschema/test_validator.py b/tests/functional/validator/_fastjsonschema/test_validator.py index d29efd09cae..e14bc5e01c1 100644 --- a/tests/functional/validator/_fastjsonschema/test_validator.py +++ b/tests/functional/validator/_fastjsonschema/test_validator.py @@ -77,12 +77,13 @@ def test_validate_accept_schema_custom_format( ) -@pytest.mark.parametrize("invalid_format", [None, False, {}, [], object]) +@pytest.mark.parametrize("invalid_format", [object, 123, 1.5, True]) def test_validate_invalid_custom_format( eventbridge_schema_registry_cloudtrail_v2_s3, eventbridge_cloudtrail_s3_head_object_event, invalid_format, ): + # formats must be a mapping; anything we can't look a format name up in fails to compile with pytest.raises(exceptions.InvalidSchemaFormatError): validate( event=eventbridge_cloudtrail_s3_head_object_event, diff --git a/tests/unit/circuit_breaker_alpha/__init__.py b/tests/unit/circuit_breaker/__init__.py similarity index 100% rename from tests/unit/circuit_breaker_alpha/__init__.py rename to tests/unit/circuit_breaker/__init__.py diff --git a/tests/unit/circuit_breaker_alpha/test_config_and_states.py b/tests/unit/circuit_breaker/test_config_and_states.py similarity index 93% rename from tests/unit/circuit_breaker_alpha/test_config_and_states.py rename to tests/unit/circuit_breaker/test_config_and_states.py index 602f7f4c9d7..03700dc91d3 100644 --- a/tests/unit/circuit_breaker_alpha/test_config_and_states.py +++ b/tests/unit/circuit_breaker/test_config_and_states.py @@ -4,13 +4,13 @@ import pytest -from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig -from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import ( +from aws_lambda_powertools.utilities.circuit_breaker.config import CircuitBreakerConfig +from aws_lambda_powertools.utilities.circuit_breaker.exceptions import ( CircuitBreakerConfigError, CircuitBreakerOpenError, ) -from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord -from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitInfo, CircuitState +from aws_lambda_powertools.utilities.circuit_breaker.persistence.record import CircuitStateRecord +from aws_lambda_powertools.utilities.circuit_breaker.states import CircuitInfo, CircuitState def test_circuit_state_serializes_to_plain_string():