loom.core.cache¶
- class loom.core.cache.CacheGateway(*, alias='default')[source]¶
Bases:
objectFacade over aiocache with msgpack serialization for entity data.
Two distinct usage modes:
Data gateway — configured with
MsgspecSerializer. Used byCachedRepositoryfor entity and list storage. All values are msgpack-encoded on write and decoded on read.Counter gateway — configured without a serializer (raw backend). Used by
GenerationalDependencyResolverfor generation counter storage. Values are stored as plain Python integers, enabling atomic native increment on both Redis andSimpleMemoryCache.The gateway auto-detects which mode it is in at construction time and routes
incr()accordingly — no manual configuration needed beyond choosing the right aiocache alias.- Parameters:
alias (str) – Registered aiocache alias to retrieve the backend from.
Example:
data_gateway = CacheGateway(alias=config.aiocache_alias) counter_gateway = CacheGateway(alias=config.effective_counter_alias) resolver = GenerationalDependencyResolver(counter_gateway)
- static configure(raw_config)[source]¶
Apply a configuration mapping to the global aiocache registry.
- classmethod apply_config(config)[source]¶
Configure aiocache from a
CacheConfig.Equivalent to
configure()but also injectsconfig.max_sizeinto everyaiocache.SimpleMemoryCachebackend entry that does not already declare its ownmax_size. Entries for other backend types (Redis, Memcached, …) are forwarded unchanged.- Parameters:
config (CacheConfig) – Resolved cache configuration.
- Return type:
None
Example:
cache_cfg = CacheConfig( aiocache_alias="cache", counter_alias="counters", max_size=1000, aiocache_config={ "cache": {"cache": "aiocache.SimpleMemoryCache", ...}, "counters": {"cache": "aiocache.SimpleMemoryCache"}, }, ) CacheGateway.apply_config(cache_cfg)
- async get_value(key: str, *, type: type[T]) T | None[source]¶
- async get_value(key: str, *, type: None = None) Any
Retrieve a cached value, optionally converting it to the given type.
- async multi_get_values(keys, *, type=None)[source]¶
Retrieve multiple values in a single round-trip.
- async multi_set_values(pairs, ttl=None)[source]¶
Store multiple key-value pairs in a single round-trip.
- async incr(key, delta=1)[source]¶
Increment a numeric value at the given key.
Routes to the optimal strategy based on the backend configuration detected at construction time:
Raw backend (no serializer): delegates to the backend’s native
incrementmethod when available. On Redis this is an atomicINCR/INCRBYcommand; onSimpleMemoryCacheit is asyncio-safe. Falls back to GET+SET for backends that do not exposeincrement.Serialized backend (
MsgspecSerializer): uses a non-atomic GET+SET. Correct for single-process deployments; the asyncio event loop does not preempt between the two awaits within a single coroutine execution.
- class loom.core.cache.CacheBackend(*args, **kwargs)[source]¶
Bases:
ProtocolAbstract cache backend defining the contract for key-value storage operations.
- async get_value(key, *, type=None)[source]¶
Retrieve a value by key, optionally converting it to the given type.
- async set_value(key, value, ttl=None)[source]¶
Store a value under the given key with an optional TTL in seconds.
- async multi_get_values(keys, *, type=None)[source]¶
Retrieve multiple values by their keys in a single round-trip.
- async multi_set_values(pairs, ttl=None)[source]¶
Store multiple key-value pairs in a single round-trip.
- class loom.core.cache.CacheConfig(*, enabled=True, aiocache_alias='default', counter_alias=None, aiocache_config=<factory>, default_ttl=200, default_list_ttl=120, ttl=<factory>, max_size=None)[source]¶
Bases:
StructConfiguration for cache behaviour including TTLs and aiocache backend settings.
- Parameters:
- aiocache_alias¶
Named alias for the aiocache data backend. The data backend must be configured with
MsgspecSerializer.- Type:
- counter_alias¶
Named alias for the counter backend used by
GenerationalDependencyResolver. This backend must have no serializer so that native atomic increment operations (RedisINCR,SimpleMemoryCache.increment) work correctly. Defaults toNone, meaning the same alias asaiocache_aliasis used (safe for single-process deployments; uses a non-atomic GET+SET fallback automatically).- Type:
str | None
- aiocache_config¶
Raw configuration mapping forwarded to
aiocache. Keyed by alias name. Useapply_configto havemax_sizeinjected automatically for memory backends.
- ttl¶
Per-entity TTL overrides keyed by entity name. Append
_listfor list overrides (e.g.{"user": 300, "user_list": 150}).
- max_size¶
Maximum number of entries for
aiocache.SimpleMemoryCachebackends. Injected automatically when callingapply_config(). Has no effect on Redis or other non-memory backends.- Type:
int | None
Example YAML (Redis + separate counter backend):
cache: aiocache_alias: cache counter_alias: counters default_ttl: 300 default_list_ttl: 120 max_size: 1000 ttl: user: 600 user_list: 300 aiocache: cache: cache: aiocache.RedisCache endpoint: ${oc.env:REDIS_HOST,redis} port: 6379 namespace: myapp serializer: class: loom.core.cache.serializer.MsgspecSerializer counters: cache: aiocache.RedisCache endpoint: ${oc.env:REDIS_HOST,redis} port: 6379 namespace: myapp_counters # no serializer — raw integer storage, atomic Redis INCRExample YAML (in-memory for development):
cache: aiocache_alias: cache counter_alias: counters max_size: 500 aiocache: cache: cache: aiocache.SimpleMemoryCache serializer: class: loom.core.cache.serializer.MsgspecSerializer counters: cache: aiocache.SimpleMemoryCache # no serializer
- property effective_counter_alias: str¶
Return the resolved counter alias, falling back to
aiocache_alias.
- classmethod from_mapping(data)[source]¶
Create a
CacheConfigfrom a raw dictionary (e.g. parsed TOML/YAML).
- class loom.core.cache.CachedRepository(repository, *, config, cache, dependency_resolver)[source]¶
Bases:
Repository[OutputT,CreateT,UpdateT,IdT],Generic[OutputT,CreateT,UpdateT,IdT]Cache-aside wrapper with generational invalidation.
- Parameters:
repository (Repository[OutputT, CreateT, UpdateT, IdT])
config (CacheConfig)
cache (CacheBackend)
dependency_resolver (DependencyResolver)
- async get_by_id(obj_id, profile='default')[source]¶
Fetch a single entity by its primary key.
- Parameters:
obj_id (IdT) – Primary key of the entity.
profile (str) – Loading profile name for eager-load options.
- Returns:
The entity output struct, or
Noneif not found.- Return type:
OutputT | None
- async get_by(field, value, profile='default')[source]¶
Fetch one entity by arbitrary field.
This path intentionally delegates to the wrapped repository without cache-aside behavior for now. Field-based lookups can target mutable columns and the cache invalidation surface is broader than id-based access; keeping it uncached preserves correctness while the lookup cache policy is designed explicitly.
- async exists_by(field, value)[source]¶
Check existence by arbitrary field.
Existence checks are delegated directly to the wrapped repository to avoid stale negative/positive cache entries on mutable fields.
- async list_paginated(page_params, filter_params=None, profile='default')[source]¶
Fetch a paginated list of entities.
- Parameters:
page_params (PageParams) – Pagination parameters (page and limit).
filter_params (FilterParams | None) – Optional filter criteria.
profile (str) – Loading profile name for eager-load options.
- Returns:
A
PageResultwith the matching items and pagination metadata.- Return type:
PageResult[OutputT]
- async list_with_query(query, profile='default')[source]¶
Fetch entities using a structured
QuerySpec.Supports both offset and cursor pagination, structured filters, and explicit sort directives. The concrete return type depends on
query.pagination:PaginationMode.OFFSET→PageResultPaginationMode.CURSOR→CursorResult
- Parameters:
- Returns:
A
PageResultfor offset queries or aCursorResultfor cursor queries.- Return type:
PageResult[OutputT] | CursorResult[OutputT]
- async create(data)[source]¶
Persist a new entity.
- Parameters:
data (CreateT) – Creation payload struct.
- Returns:
The newly created entity output struct.
- Return type:
OutputT
- class loom.core.cache.DependencyResolver(*args, **kwargs)[source]¶
Bases:
ProtocolProtocol for cache dependency tracking and invalidation.
- async fingerprint(tags)[source]¶
Compute a composite fingerprint from the current generation of each tag.
- async bump_from_events(events)[source]¶
Increment generation counters for all tags affected by mutation events.
- Parameters:
events (tuple[MutationEvent, ...]) – Mutation events produced within a transaction.
- Return type:
None
- class loom.core.cache.GenerationalDependencyResolver(cache)[source]¶
Bases:
DependencyResolverGenerational tags with monotonic counters in cache backend.
- Parameters:
cache (CacheBackend)
- async fingerprint(tags)[source]¶
Compute a composite fingerprint from generation counters of all tags.
- async bump_from_events(events)[source]¶
Increment generation counters for all tags affected by mutation events.
- Parameters:
events (tuple[MutationEvent, ...]) – Mutation events to process.
- Return type:
None
- class loom.core.cache.MsgspecSerializer[source]¶
Bases:
objectaiocache serializer backed by msgspec msgpack.