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:
objectConfiguration 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
-1for 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,
CacheManagerwill call this factory instead of usingdogpile.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 optionallyget_or_create()).
Cache Manager¶
- class advanced_alchemy.cache.CacheManager[source]¶
Bases:
objectManages 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:
config¶ (
CacheConfig) – Configuration for the cache region.config (CacheConfig)
- 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_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:
- 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_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:
- 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).
- 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.
- 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.
- 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.
- get_list_sync(key, model_class)[source]¶
Get a cached list of entities (sync).
The list is stored as base64-encoded serialized entity payloads.
- 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.
- 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:
- 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:
- 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_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:
- 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).
- 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.
- 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.
- 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.
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:
- Return type:
- 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:
- 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:
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.