Cache

The cache module provides optional integration with dogpile.cache for caching SQLAlchemy model instances. It supports multiple backends and provides automatic cache invalidation when models are modified.

Installation

The cache module requires the optional dogpile.cache dependency:

pip install advanced-alchemy[dogpile]

Without this dependency, the cache manager will use a NullRegion that provides the same interface but doesn’t actually cache anything.

Configuration

class advanced_alchemy.cache.CacheConfig[source]

Bases: object

Configuration for a dogpile.cache region.

This dataclass holds configuration options for setting up a cache region using dogpile.cache. It supports multiple backends (Redis, Memcached, file, memory) and provides sensible defaults for getting started quickly.

Example

Basic memory cache configuration:

config = CacheConfig(
    backend="dogpile.cache.memory",
    expiration_time=300,
)

Redis cache configuration:

config = CacheConfig(
    backend="dogpile.cache.redis",
    expiration_time=3600,
    arguments={
        "host": "localhost",
        "port": 6379,
        "db": 0,
    },
)
backend: str = 'dogpile.cache.null'

Cache backend identifier.

Common backends: - dogpile.cache.null: No-op cache (default, for development) - dogpile.cache.memory: In-process memory cache - dogpile.cache.redis: Redis backend - dogpile.cache.memcached: Memcached backend - dogpile.cache.dbm: DBM file-based cache

expiration_time: int = 3600

Default TTL (time-to-live) in seconds for cached items.

Set to -1 for no expiration. Default is 3600 (1 hour).

arguments: dict[str, Any]

Backend-specific configuration arguments.

These are passed directly to the dogpile.cache backend. See dogpile.cache documentation for backend-specific options.

Example for Redis:

{"host": "localhost", "port": 6379, "db": 0}

Example for Memcached:

{"url": ["127.0.0.1:11211"]}
key_prefix: str = 'aa:'

Prefix for all cache keys.

This helps avoid key collisions when sharing a cache backend with other applications. Default is aa: (advanced-alchemy).

__init__(backend='dogpile.cache.null', expiration_time=3600, arguments=<factory>, key_prefix='aa:', enabled=True, serializer=None, deserializer=None, region_factory=None)
Parameters:
Return type:

None

enabled: bool = True

Enable or disable caching globally.

When False, all cache operations are bypassed and data is always fetched from the database. Useful for debugging or testing.

serializer: Callable[[Any], bytes] | None = None

Custom serializer function.

If None, uses the default JSON serializer which handles SQLAlchemy models, datetime objects, and UUIDs.

The function should accept any value and return bytes.

deserializer: Callable[[bytes, type[Any]], Any] | None = None

Custom deserializer function.

If None, uses the default JSON deserializer.

The function should accept bytes and a model class, returning an instance of that class.

region_factory: Callable[[CacheConfig], Any] | None = None

Optional hook to construct a cache region instance.

This exists to keep the repository/service integration stable even if Advanced Alchemy swaps the underlying cache backend in the future.

If provided, CacheManager will call this factory instead of using dogpile.cache.make_region().

The returned object must implement the subset of dogpile’s region API that AA relies on (e.g. get(), set(), delete(), invalidate(), and optionally get_or_create()).

Cache Manager

class advanced_alchemy.cache.CacheManager[source]

Bases: object

Manages dogpile.cache regions with model-aware invalidation.

This class provides a high-level interface for caching SQLAlchemy model instances using dogpile.cache. It handles serialization, cache key generation, and model-version-based invalidation for list queries.

All cache operations are available in both sync and async variants: - Sync methods end with _sync (e.g., get_sync, set_sync) - Async methods end with _async (e.g., get_async, set_async)

Async methods use asyncio.to_thread() with capacity limiting to prevent blocking the event loop when using network-based backends like Redis or Memcached.

Features: - Lazy initialization of cache regions - Model-aware cache key generation - Version-based invalidation for list queries - Graceful degradation when dogpile.cache is not installed - Support for custom serializers - Both sync and async operation support

Example

Sync usage:

from advanced_alchemy.cache import CacheConfig, CacheManager

config = CacheConfig(
    backend="dogpile.cache.memory",
    expiration_time=300,
)
manager = CacheManager(config)

# Cache a value (sync)
result = manager.get_or_create_sync(
    "users:1",
    lambda: fetch_user_from_db(1),
)

Async usage:

# Cache a value (async)
result = await manager.get_or_create_async(
    "users:1",
    lambda: fetch_user_from_db(1),
)
__init__(config)[source]

Initialize the cache manager.

Parameters:
Return type:

None

Note

Model version tokens are stored in-cache for cross-process consistency. A random token is used per commit to avoid lost updates without requiring backend-specific atomic increment support.

Per-process singleflight registries (async and sync) are best-effort; they reduce stampedes within a single process but do not provide cross-process locking.

property region: SyncCacheRegionProtocol

Get the cache region, creating it if necessary.

The region is lazily initialized on first access. If dogpile.cache is not installed or initialization fails, returns a NullRegion that provides the same interface but doesn’t actually cache anything.

Returns:

The configured cache region or a NullRegion fallback.

get_sync(key)[source]

Get a value from the cache (sync).

Parameters:
  • key (str) – The cache key (without prefix).

  • key (str)

Return type:

object

Returns:

The cached value or NO_VALUE if not found.

set_sync(key, value)[source]

Set a value in the cache (sync).

Parameters:
  • key (str) – The cache key (without prefix).

  • value (Any) – The value to cache.

  • key (str)

  • value (Any)

Return type:

None

delete_sync(key)[source]

Delete a value from the cache (sync).

Parameters:
  • key (str) – The cache key (without prefix).

  • key (str)

Return type:

None

get_or_create_sync(key, creator, expiration_time=None)[source]

Get a value from cache or create it using the creator function (sync).

This method uses dogpile.cache’s get_or_create which provides mutex-based protection against the “thundering herd” problem when multiple requests try to create the same value simultaneously.

Note

The creator function must be synchronous. For async creators, you must await them before passing to this method.

Parameters:
  • key (str) – The cache key (without prefix).

  • creator (Callable[[], TypeVar(T)]) – A synchronous callable that returns the value to cache on miss.

  • expiration_time (Optional[int]) – Optional override for the default TTL.

  • key (str)

  • creator (Callable[[], T])

  • expiration_time (int | None)

Return type:

TypeVar(T)

Returns:

The cached or newly created value.

get_entity_sync(model_name, entity_id, model_class, bind_group=None)[source]

Get a cached entity by model name and ID (sync).

Parameters:
  • model_name (str) – The model/table name.

  • entity_id (Any) – The entity’s primary key value.

  • model_class (type[TypeVar(T)]) – The SQLAlchemy model class for deserialization.

  • bind_group (Optional[str]) – Optional routing group for multi-master configurations. When provided, entity caches are namespaced by bind_group to prevent data leaks between database shards/replicas.

  • model_name (str)

  • entity_id (Any)

  • model_class (type[T])

  • bind_group (str | None)

Return type:

Optional[TypeVar(T)]

Returns:

The cached model instance or None if not found.

set_entity_sync(model_name, entity_id, entity, bind_group=None)[source]

Cache an entity (sync).

Parameters:
  • model_name (str) – The model/table name.

  • entity_id (Any) – The entity’s primary key value.

  • entity (Any) – The SQLAlchemy model instance to cache.

  • bind_group (Optional[str]) – Optional routing group for multi-master configurations. When provided, entity caches are namespaced by bind_group to prevent data leaks between database shards/replicas.

  • model_name (str)

  • entity_id (Any)

  • entity (Any)

  • bind_group (str | None)

Return type:

None

invalidate_entity_sync(model_name, entity_id, bind_group=None)[source]

Invalidate the cache for a specific entity (sync).

This should be called after an entity is created, updated, or deleted to ensure the cache doesn’t serve stale data.

Parameters:
  • model_name (str) – The model/table name.

  • entity_id (Any) – The entity’s primary key value.

  • bind_group (Optional[str]) – Optional routing group for multi-master configurations. When provided, only the cache entry for that bind_group is invalidated.

  • model_name (str)

  • entity_id (Any)

  • bind_group (str | None)

Return type:

None

bump_model_version_sync(model_name)[source]

Bump the version token for a model (sync).

This is used for version-based invalidation of list queries. When a model is created, updated, or deleted, the version is bumped to a new random token, which effectively invalidates all list query caches that include that token in their cache key.

Parameters:
  • model_name (str) – The model/table name.

  • model_name (str)

Return type:

str

Returns:

The new version token.

get_model_version_sync(model_name)[source]

Get the current version token for a model (sync).

This is used to include the version in list query cache keys, ensuring that list caches are invalidated when models change.

Parameters:
  • model_name (str) – The model/table name.

  • model_name (str)

Return type:

str

Returns:

The current version token (“0” if not set).

get_list_sync(key, model_class)[source]

Get a cached list of entities (sync).

The list is stored as base64-encoded serialized entity payloads.

Parameters:
  • key (str) – Cache key (without prefix).

  • model_class (type[TypeVar(T)]) – Model class for deserialization.

  • key (str)

  • model_class (type[T])

Return type:

Optional[list[TypeVar(T)]]

Returns:

A list of detached model instances or None if not found.

set_list_sync(key, items)[source]

Cache a list of entities (sync).

Parameters:
  • key (str) – Cache key (without prefix).

  • items (list[Any]) – List of entities to cache.

  • key (str)

  • items (list[Any])

Return type:

None

get_list_and_count_sync(key, model_class)[source]

Get a cached list+count payload (sync).

Return type:

Optional[tuple[list[TypeVar(T)], int]]

Parameters:
set_list_and_count_sync(key, items, count)[source]

Cache a list+count payload (sync).

Return type:

None

Parameters:
singleflight_sync(key, creator)[source]

Coalesce concurrent sync cache misses per-process.

This reduces stampedes in thread-based sync apps. It does not provide cross-process locking.

Return type:

TypeVar(T)

Parameters:
invalidate_all_sync()[source]

Invalidate all cached values (sync).

Warning

This invalidates the entire region, not just keys with the configured prefix. Use with caution in shared cache environments.

Return type:

None

async get_async(key)[source]

Get a value from the cache (async).

Parameters:
  • key (str) – The cache key (without prefix).

  • key (str)

Return type:

object

Returns:

The cached value or NO_VALUE if not found.

async set_async(key, value)[source]

Set a value in the cache (async).

Parameters:
  • key (str) – The cache key (without prefix).

  • value (Any) – The value to cache.

  • key (str)

  • value (Any)

Return type:

None

async delete_async(key)[source]

Delete a value from the cache (async).

Parameters:
  • key (str) – The cache key (without prefix).

  • key (str)

Return type:

None

async get_or_create_async(key, creator, expiration_time=None)[source]

Get a value from cache or create it using the creator function (async).

This method uses dogpile.cache’s get_or_create which provides mutex-based protection against the “thundering herd” problem when multiple requests try to create the same value simultaneously.

Note

The creator function must be synchronous since dogpile.cache runs in a thread pool. For async creators, you must await them and wrap the result before passing to this method.

Parameters:
  • key (str) – The cache key (without prefix).

  • creator (Callable[[], TypeVar(T)]) – A synchronous callable that returns the value to cache on miss.

  • expiration_time (Optional[int]) – Optional override for the default TTL.

  • key (str)

  • creator (Callable[[], T])

  • expiration_time (int | None)

Return type:

TypeVar(T)

Returns:

The cached or newly created value.

async get_entity_async(model_name, entity_id, model_class, bind_group=None)[source]

Get a cached entity by model name and ID (async).

Parameters:
  • model_name (str) – The model/table name.

  • entity_id (Any) – The entity’s primary key value.

  • model_class (type[TypeVar(T)]) – The SQLAlchemy model class for deserialization.

  • bind_group (Optional[str]) – Optional routing group for multi-master configurations. When provided, entity caches are namespaced by bind_group to prevent data leaks between database shards/replicas.

  • model_name (str)

  • entity_id (Any)

  • model_class (type[T])

  • bind_group (str | None)

Return type:

Optional[TypeVar(T)]

Returns:

The cached model instance or None if not found.

async set_entity_async(model_name, entity_id, entity, bind_group=None)[source]

Cache an entity (async).

Parameters:
  • model_name (str) – The model/table name.

  • entity_id (Any) – The entity’s primary key value.

  • entity (Any) – The SQLAlchemy model instance to cache.

  • bind_group (Optional[str]) – Optional routing group for multi-master configurations. When provided, entity caches are namespaced by bind_group to prevent data leaks between database shards/replicas.

  • model_name (str)

  • entity_id (Any)

  • entity (Any)

  • bind_group (str | None)

Return type:

None

async invalidate_entity_async(model_name, entity_id, bind_group=None)[source]

Invalidate the cache for a specific entity (async).

This should be called after an entity is created, updated, or deleted to ensure the cache doesn’t serve stale data.

Parameters:
  • model_name (str) – The model/table name.

  • entity_id (Any) – The entity’s primary key value.

  • bind_group (Optional[str]) – Optional routing group for multi-master configurations. When provided, only the cache entry for that bind_group is invalidated.

  • model_name (str)

  • entity_id (Any)

  • bind_group (str | None)

Return type:

None

async bump_model_version_async(model_name)[source]

Bump the version token for a model (async).

This is used for version-based invalidation of list queries. When a model is created, updated, or deleted, the version is bumped, which effectively invalidates all list query caches that include that model’s version in their cache key.

Parameters:
  • model_name (str) – The model/table name.

  • model_name (str)

Return type:

str

Returns:

The new version token.

async get_model_version_async(model_name)[source]

Get the current version token for a model (async).

This is used to include the version in list query cache keys, ensuring that list caches are invalidated when models change.

Parameters:
  • model_name (str) – The model/table name.

  • model_name (str)

Return type:

str

Returns:

The current version token (“0” if not set).

async get_list_async(key, model_class)[source]

Get a cached list of entities (async).

Return type:

Optional[list[TypeVar(T)]]

Parameters:
async set_list_async(key, items)[source]

Cache a list of entities (async).

Return type:

None

Parameters:
async get_list_and_count_async(key, model_class)[source]

Get a cached list+count payload (async).

Return type:

Optional[tuple[list[TypeVar(T)], int]]

Parameters:
async set_list_and_count_async(key, items, count)[source]

Cache a list+count payload (async).

Return type:

None

Parameters:
async singleflight_async(key, creator)[source]

Coalesce concurrent async cache misses per-process.

The creator is invoked once per key at a time; concurrent callers await the same in-flight task. This does not provide cross-process locking.

Return type:

TypeVar(T)

Parameters:
async invalidate_all_async()[source]

Invalidate all cached values (async).

Warning

This invalidates the entire region, not just keys with the configured prefix. Use with caution in shared cache environments.

Return type:

None

Serialization

advanced_alchemy.cache.default_serializer(model)[source]

Serialize a SQLAlchemy model instance to JSON bytes.

This function extracts column values from a SQLAlchemy model and serializes them to JSON format. The serialized data includes metadata about the model class for validation during deserialization.

Note

Relationships are NOT serialized. Only column values are included. The deserialized object will be a detached instance without relationship data loaded.

Parameters:
  • model (Any) – The SQLAlchemy model instance to serialize.

  • model (Any)

Return type:

bytes

Returns:

JSON-encoded bytes representation of the model.

Example

Serializing a model:

user = User(id=1, name="John", email="john@example.com")
data = default_serializer(user)
# b'{"__aa_model__": "User", "__aa_table__": "users", "id": 1, ...}'
advanced_alchemy.cache.default_deserializer(data, model_class)[source]

Deserialize JSON bytes to a SQLAlchemy model instance.

Creates a new, detached instance of the model class populated with the serialized column values. The instance is NOT attached to any session and should be treated as a read-only snapshot.

Warning

The returned instance is detached and does not have relationships loaded. Accessing lazy-loaded relationships will raise DetachedInstanceError. Use session.merge() if you need to work with relationships.

Parameters:
  • data (bytes) – JSON bytes to deserialize.

  • model_class (type[TypeVar(T)]) – The SQLAlchemy model class to instantiate.

  • data (bytes)

  • model_class (type[T])

Return type:

TypeVar(T)

Returns:

A new, detached instance of the model class.

Raises:

ValueError – If the serialized data is for a different model class.

Example

Deserializing data:

data = b'{"__aa_model__": "User", "id": 1, "name": "John"}'
user = default_deserializer(data, User)
# user is a detached User instance

Setup

advanced_alchemy._listeners.setup_cache_listeners()[source]

Register cache invalidation event listeners globally.

This registers the unified listener on Session, which handles both sync and async contexts by detecting a running event loop at commit time. For more control, prefer using scoped listeners via SQLAlchemyConfig.

Return type:

None

Constants

advanced_alchemy.cache.DOGPILE_CACHE_INSTALLED

Returns True when the argument is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.