Skip to content

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.