Fallback
Overview
The Fallback pattern provides an alternative execution path for an operation when it fails. Instead of allowing a failure to cascade through the system, a fallback can return a default value, data from an alternative source, or trigger a different logic path. This allows the application to degrade gracefully rather than failing completely.
Athomic provides the @fallback_handler decorator and the underlying FallbackLogicHandler for protecting generic functions.
Note on Caching: KVStore-specific fallback topologies (e.g., handling cache misses, write-through strategies) have been decoupled from the generic resilience module. Please refer to the KVStore Documentation for information on database fallback orchestration.
Generic Fallback with @fallback_handler
This decorator allows you to wrap any asynchronous or synchronous function with one or more fallback functions. If the primary function raises an exception, the fallback functions are executed in order until one succeeds.
How It Works
The FallbackLogicHandler executes your primary function.
If an exception occurs, it catches it and begins iterating through the list of fallback functions you provided, calling them with the same arguments as the original function.
The result of the first successful fallback is returned. If all fallbacks also fail, a single FallbackError containing all the underlying exceptions is raised.
Usage Example
from nala.athomic.resilience.fallback import fallback_handler
async def get_default_user_settings(*args, **kwargs) -> dict:
"""
Returns a default static configuration.
"""
return {"theme": "dark", "notifications": "enabled"}
async def get_user_settings_from_legacy_system(user_id: str) -> dict:
"""
Attempts to retrieve configuration from a secondary system.
"""
pass
@fallback_handler(fallbacks=[get_user_settings_from_legacy_system, get_default_user_settings])
async def get_user_settings_from_primary(user_id: str) -> dict:
"""
Attempts to fetch user config from the primary data source.
If it fails, the fallback handler automatically routes execution
to the legacy system, and finally to the default static configuration.
"""
return await api_client.get(f"/config/{user_id}")
Configuration
The generic fallback decorator does not require global TOML configuration. Execution paths are explicitly defined within the code via the decorator arguments.
However, metrics regarding fallback executions (successes and failures) are automatically exported to Prometheus.
API Reference
nala.athomic.resilience.fallback.decorator.fallback_handler(fallbacks)
Decorator that applies the Fallback resilience pattern to a function.
It wraps the decorated primary function with an execution chain: if the primary function fails for any reason, the configured list of fallback functions is executed sequentially until one succeeds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fallbacks
|
Union[Callable, List[Callable]]
|
A single callable or an ordered list of callables (sync or async) to be tried as alternatives if the decorated function raises an exception. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Callable |
Callable[[Callable[..., T]], Callable[..., T]]
|
The decorator function. |
nala.athomic.resilience.fallback.handler.FallbackLogicHandler
Encapsulates the logic for executing a primary function with a resilient chain of fallback functions, providing short-circuit protection and optimized async evaluation.
__init__(fallbacks, exclude_exceptions=())
Executes the primary function and, in case of failure, triggers the fallback chain. Handles both synchronous and asynchronous functions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fallbacks
|
Union[Callable[..., T], List[Callable[..., T]]]
|
A single fallback function or a list of fallback functions to execute in order if the primary function fails. |
required |
exclude_exceptions
|
Tuple[Type[Exception], ...]
|
A tuple of exception types that should not trigger the fallback chain and instead be raised immediately. Defaults to an empty tuple. |
()
|
Returns:
| Name | Type | Description |
|---|---|---|
T |
None
|
The successful result from either the primary function or a fallback function. |
Raises:
| Type | Description |
|---|---|
FallbackError
|
If the primary function and all fallback functions fail. |
execute(func, *args, **kwargs)
async
Executes the primary function and, in case of failure, triggers the fallback chain. Handles both synchronous and asynchronous functions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable
|
The primary function to attempt first. |
required |
*args
|
Any
|
Positional arguments for the function. |
()
|
**kwargs
|
Any
|
Keyword arguments for the function. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
T |
T
|
The successful result from either the primary function or a fallback function. |
Raises:
| Type | Description |
|---|---|
FallbackError
|
If the primary function and all fallback functions fail. |
nala.athomic.resilience.fallback.exceptions.FallbackError
Bases: Exception
Raised when all fallback attempts fail.