Skip to content

Cache API

Historical cache identity

Historical context usage is introduced in the Historical Context API reference; this section records the cache-identity compatibility contract.

Cache identity includes the effective historical instant. Current reads keep their existing cache identity, while an active as_of(...) context receives a separate namespace from current data and from every other historical instant. Managers explicitly bound to a historical search_date also carry that date in their serialized identity, including when passed to a cached function outside an active context.

Historical instants are canonicalized to UTC for cache keys. For example, 2022-01-01T01:00:00+01:00 and 2022-01-01T00:00:00Z share one identity. GeneralManager does not round historical timestamps: two genuinely different instants receive different keys, even when they are close together. This can produce more entries for highly granular timestamps, but prevents an incorrect snapshot from being reused.

The namespace applies consistently across cache scopes:

  • cache="run" isolates memoized values, ORM bucket results, bucket indexes, prefetched hits, and pending publications inside a calculation run;
  • cache="timeout" uses distinct backend keys per historical instant; and
  • cache="dependency" uses distinct backend keys and dependency metadata per historical instant.

GraphQL warm-up recipes use recipe format version 2 and retain the effective search_date. Background warm-up re-enters that snapshot before reconstructing the manager and refreshes the original historical cache key. Older or otherwise version-incompatible recipes are ignored. Deploying this key and recipe format can therefore cause a one-time cold cache, especially for historical entries; allow normal traffic or the configured warm-up jobs to repopulate it.

general_manager.cache.cache_decorator.cached

cached(func: FuncT) -> FuncT
cached(
    func: None = None,
    timeout: int | None = None,
    cache_backend: CacheBackend = django_cache,
    record_fn: RecordFn = record_dependencies,
    *,
    cache: CacheScope = "run"
) -> Callable[[FuncT], FuncT]
cached(
    func=None,
    timeout=None,
    cache_backend=django_cache,
    record_fn=record_dependencies,
    *,
    cache="run"
)

Decorate a callable with one of GeneralManager's cache strategies.

By default, cached values are scoped to the active :class:~general_manager.cache.run_context.CalculationRunContext and are discarded when that run ends. Use cache="dependency" to persist values in cache_backend and use record_fn to persist dependency metadata for invalidation. Use cache="timeout" with timeout set for cache-backend storage with time-based expiry; dependency recording is ignored for timeout-cached values.

The decorator supports both @cached and @cached(...) forms and preserves the wrapped function's type signature for static type checkers. Cache keys are built from the wrapped callable plus positional and keyword arguments through :func:general_manager.utils.make_cache_key.make_cache_key.

Parameters:

Name Type Description Default
func FuncT | None

Function being decorated when used as @cached. Leave unset when using @cached(...).

None
timeout int | None

Expiration in seconds for timeout-cached values. Required when cache is "timeout" and invalid with any other cache mode. The decorator validates only presence/absence; accepted value ranges are delegated to the configured backend.

None
cache_backend CacheBackend

Backend used to read and write dependency or timeout cached results. cache="run" and cache="none" do not use it.

cache
record_fn RecordFn

Callback invoked with (cache_key, dependencies) when cache is "dependency" and the default dependency publisher is not batching the write in a run context.

record_dependencies
cache CacheScope

Cache storage strategy. "run" memoizes for the active run, "dependency" stores in cache_backend with dependency tracking, "timeout" stores in cache_backend with time-based expiry, and "none" disables caching.

'run'

Returns:

Type Description
FuncT | Callable[[FuncT], FuncT]

The decorated callable when func is supplied, otherwise a decorator

FuncT | Callable[[FuncT], FuncT]

that wraps the target function with the selected caching behaviour.

Raises:

Type Description
UnsupportedCacheScopeError

If cache is not one of "dependency", "run", "timeout", or "none" at runtime.

CacheTimeoutConfigurationError

If cache="timeout" has no timeout or a timeout is supplied for another cache mode.

Cache backend errors

Propagated from cache_backend.get or cache_backend.set for dependency and timeout scopes.

Exception

Exceptions raised by the wrapped callable, dependency tracking, dependency publication, compute lease acquisition/waiting, or custom record_fn callbacks propagate unless the dependency publisher reports CachePublishAborted. In that case, the fresh function result is returned without publishing a dependency cache entry.

DependencyLockTimeoutError

Propagated from record_fn (i.e. :func:~general_manager.cache.dependency_index.record_dependencies) when the dependency-index lock cannot be acquired within the configured timeout. The cached value has already been stored at that point; only the dependency metadata is lost.

cached(func=None, timeout=None, cache_backend=django_cache, record_fn=record_dependencies, *, cache="run") wraps a callable with one of four cache strategies. It supports both @cached and @cached(...) forms and uses make_cache_key(function, args, kwargs) for every cache key.

Use the default cache="run" for per-request or per-calculation memoization. The decorator opens a temporary CalculationRunContext when none is active, so separate calls outside a shared context do not reuse values:

from general_manager.cache.cache_decorator import cached
from general_manager.cache.run_context import CalculationRunContext

@cached
def expensive_total(project_id: int) -> int:
    return Project(id=project_id).derivative_list.count()

with CalculationRunContext():
    first = expensive_total(10)
    second = expensive_total(10)  # Same run-local value.

Use cache="dependency" when the result should persist across runs and be invalidated by tracked manager dependencies. On a miss, the wrapped callable runs inside a DependencyTracker; model arguments are also collected after the call. The value and dependency metadata are published together. If a data-change generation moves during computation or publication is blocked by an active data change, the fresh result is returned but no dependency cache entry is published. Concurrent workers for the same key coordinate with a compute lease; waiters reuse a published hit when available.

@cached(cache="dependency")
def project_forecast(project_id: int) -> dict[str, int]:
    project = Project(id=project_id)
    return {"rows": project.derivative_list.count()}

Use cache="timeout" for backend-managed expiry without dependency tracking. timeout is required for this scope and rejected for every other scope. The decorator validates only whether timeout is present; exact timeout ranges and special values are delegated to the configured backend. Use cache="none" to preserve the decorator shape while disabling cache reads and writes.

Invalid cache names raise UnsupportedCacheScopeError. Missing or unexpected timeouts raise CacheTimeoutConfigurationError. Backend get/set errors, wrapped-callable errors, dependency tracking errors, compute-lease errors, and custom record_fn errors propagate. CachePublishAborted is handled by returning the freshly computed result without storing a dependency cache entry.

general_manager.cache.cache_decorator.CacheBackend

Bases: Protocol

Minimal cache backend protocol used by cached.

Implementations must behave like Django cache backends for single-key get() and set() operations. Stored values are intentionally typed as object because decorated functions may return any Python value accepted by the configured backend serializer.

get

get(key, default=None)

Return the cached value for key, or default when absent.

set

set(key, value, timeout=None)

Store value under key with an optional backend timeout.

CacheBackend is the minimal protocol accepted by cached: get(key, default) must return a cached object or the exact default value when absent, and set(key, value, timeout=None) stores any backend-serializable Python object. Return values from set() are ignored. Dependency and timeout scopes use the backend; run and none scopes do not.

general_manager.cache.dependency_cache.DependencyCacheEntry dataclass

Persisted dependency-cache payload stored at the main cache key.

Attributes:

Name Type Description
version int

Payload schema version. Unknown versions are treated as cache misses by the readers.

value object

Cached function result.

dependencies frozenset[Dependency]

Dependency set that must be replayed on cache hits and used by invalidation metadata.

__reduce__

__reduce__()

Pickle entries through constructor args instead of slot state.

general_manager.cache.dependency_cache.DependencyCacheHit dataclass

In-memory representation of a dependency-cache hit.

value is the cached function result. dependencies are replayed into any active DependencyTracker scope before the caller returns value.

general_manager.cache.dependency_cache.make_dependency_cache_entry

make_dependency_cache_entry(
    value, dependencies, *, trusted_dependencies=None
)

Build the current persisted dependency-cache payload.

Parameters:

Name Type Description Default
value object

Cached function result to persist.

required
dependencies Iterable[Dependency]

Dependencies captured while computing the value.

required
trusted_dependencies bool | None

Whether dependencies are known to come from DependencyTracker. When omitted, tracker-captured sets are detected automatically.

None

Returns:

Type Description
DependencyCacheEntry

A versioned DependencyCacheEntry with dependencies frozen.

general_manager.cache.dependency_cache.read_dependency_cache_hit

read_dependency_cache_hit(
    cache_backend, cache_key, *, sentinel=_MISSING
)

Read one dependency-cache entry, including legacy split entries.

Combined entries store a DependencyCacheEntry at cache_key. Legacy split entries store the cached value at cache_key and dependencies at {cache_key}:deps. A present legacy value with a missing dependency key is a hit with an empty dependency set. Legacy dependency payloads must be iterable dependency tuples; malformed payloads are treated as misses. Unknown future combined-entry versions are treated as misses and return sentinel.

Parameters:

Name Type Description Default
cache_backend DependencyCacheBackend

Backend used for value and legacy dependency reads.

required
cache_key str

Main cache key to read.

required
sentinel object

Object returned when no compatible cache hit exists.

_MISSING

Returns:

Type Description
DependencyCacheHit | object

A DependencyCacheHit for combined or legacy entries, or sentinel

DependencyCacheHit | object

when the main key is absent or holds an unsupported future entry

DependencyCacheHit | object

version.

Raises:

Type Description
Exception

Backend get() errors propagate unchanged.

general_manager.cache.dependency_cache.read_many_dependency_cache_hits

read_many_dependency_cache_hits(cache_backend, cache_keys)

Bulk-read dependency-cache hits for known keys.

Duplicate cache keys are collapsed while preserving first-seen order. Backends with get_many() use one bulk read for main payloads and, when legacy split entries are present, one additional bulk read for dependency payloads. A present legacy value whose dependency key is absent from that second bulk read is returned as a hit with an empty dependency set. Backends without get_many() fall back to single-key reads. Missing main keys and unknown future combined-entry versions are omitted. Legacy entries with malformed dependency payloads are omitted.

Parameters:

Name Type Description Default
cache_backend DependencyCacheBackend

Backend used for cache reads.

required
cache_keys Iterable[str]

Cache keys to inspect.

required

Returns:

Type Description
dict[str, DependencyCacheHit]

Mapping of cache keys that had compatible hits to their

dict[str, DependencyCacheHit]

DependencyCacheHit values.

Raises:

Type Description
Exception

Backend get()/get_many() errors propagate unchanged.

general_manager.cache.dependency_cache.replay_dependency_cache_hit

replay_dependency_cache_hit(hit)

Replay cached dependencies into active dependency tracking scopes.

Parameters:

Name Type Description Default
hit DependencyCacheHit

Cache hit whose dependency tuples should be tracked.

required

Raises:

Type Description
TypeError

Propagated from DependencyTracker.track() if a dependency tuple has malformed value types.

ValueError

Propagated from DependencyTracker.track() if a dependency tuple uses an unsupported operation.

The dependency-cache helpers are low-level support for dependency-scoped cached values and GraphQL dependency-cache prefetching. Most application code should use @cached(cache="dependency") or @graph_ql_property(cache="dependency") instead of calling these helpers directly.

DependencyCacheEntry is the versioned payload stored at the main cache key for new dependency-cache entries. DependencyCacheHit is the in-memory hit shape returned by readers; its value is the cached function result and dependencies are replayed into active DependencyTracker scopes before the caller returns that value. make_dependency_cache_entry(value, dependencies) freezes the dependency iterable into the current persisted payload format.

read_dependency_cache_hit(cache_backend, cache_key, sentinel=...) first reads the main key. A current-version DependencyCacheEntry returns a DependencyCacheHit. A future-version entry is treated as a miss and returns the supplied sentinel. Plain values are treated as legacy split entries: the value remains the cached result and {cache_key}:deps is read for dependency metadata, defaulting to an empty dependency set when missing. Falsey cached values such as None, False, 0, [], and {} are valid hits as long as the backend distinguishes absence by returning the exact sentinel/default object. Legacy dependency payloads must be iterable dependency tuples; a missing dependency key and other falsey dependency payloads become an empty dependency set, while truthy non-iterable dependency payloads raise through normal frozenset(...) conversion.

read_many_dependency_cache_hits(cache_backend, cache_keys) collapses duplicate keys while preserving first-seen order. Backends with get_many() use one bulk read for main payloads and one additional bulk read for legacy dependency keys when legacy entries are present. If a legacy main value is present but its dependency key is absent from the second bulk read, the hit is returned with an empty dependency set. Backends without get_many() fall back to single-key reads. Missing main keys and future-version entries are omitted from the returned mapping. Backend read errors and malformed legacy dependency payloads propagate.

replay_dependency_cache_hit(hit) forwards each dependency tuple to DependencyTracker.track(). Malformed dependency tuple values raise TypeError, and unsupported dependency operations raise ValueError, matching the tracker contract.

general_manager.cache.dependency_publish.CachePublishAborted

Bases: RuntimeError

Raised when dependency-cache publishing is no longer safe.

Single-entry publication aborts when a data-change barrier is active or when the dependency generation differs from the generation observed before the cached value was computed. Batch publication aborts on an active barrier or a generation change during the batch publish, but stale entries already in the batch are skipped rather than raising by themselves.

general_manager.cache.dependency_publish.CacheComputeLease dataclass

Token proving ownership of a dependency-cache computation.

Attributes:

Name Type Description
key str

Coordination-cache key that stores the lease token.

token str

Random ownership token written to the coordination cache.

general_manager.cache.dependency_publish.PendingDependencyCachePublication dataclass

A dependency-cache miss waiting for guarded publication.

Buffered run-context misses use this value to publish dependency metadata and versioned cache payloads after the computation is known to be current. The lease is carried so the owner can release it after publish or discard; the publish helpers do not validate or release lease ownership.

general_manager.cache.dependency_publish.acquire_compute_lease

acquire_compute_lease(
    cache_key, *, timeout=COMPUTE_LOCK_TIMEOUT
)

Acquire a per-cache-key compute lease if no worker currently owns it.

Parameters:

Name Type Description Default
cache_key str

Dependency-cache key whose computation should be guarded.

required
timeout int

Coordination-cache TTL for the lease token.

COMPUTE_LOCK_TIMEOUT

Returns:

Type Description
CacheComputeLease | None

A lease token when this worker acquired ownership, otherwise None.

Raises:

Type Description
Exception

Errors from the coordination cache add() call propagate.

general_manager.cache.dependency_publish.release_compute_lease

release_compute_lease(lease)

Release a compute lease without risking deletion of a newer owner.

Django's cache API does not provide an atomic compare-and-delete operation. Checking the owner token first keeps normal recomputes fast while avoiding deletion when the lease has already been replaced by another worker.

general_manager.cache.dependency_publish.wait_for_cached_dependency_hit

wait_for_cached_dependency_hit(
    cache_backend,
    cache_key,
    timeout_seconds=LOCK_TIMEOUT,
    sentinel=_WAIT_MISS,
)

Poll for a dependency-cache hit until it appears or the wait expires.

Parameters:

Name Type Description Default
cache_backend DependencyCacheBackend

Backend containing dependency-cache entries.

required
cache_key str

Key to poll.

required
timeout_seconds float

Maximum wall-clock seconds to wait.

LOCK_TIMEOUT
sentinel object

Object returned when no hit appears before timeout.

_WAIT_MISS

Returns:

Type Description
DependencyCacheHit | object

A DependencyCacheHit when another worker publishes a compatible value,

DependencyCacheHit | object

otherwise sentinel. Falsey cached values are returned as hits through

DependencyCacheHit | object

DependencyCacheHit.

Raises:

Type Description
Exception

Backend read errors, legacy dependency conversion errors, and monotonic/sleep errors propagate.

general_manager.cache.dependency_publish.publish_dependency_cache_entries

publish_dependency_cache_entries(entries)

Publish dependency metadata and values for current entries in one batch.

The active data-change barrier is checked before stale entries are filtered; if it is active, the whole batch aborts. Otherwise entries whose started_generation no longer matches the current dependency generation are skipped. If the generation changes or a barrier begins after dependency metadata is recorded but before values are stored, CachePublishAborted is raised and cache payloads are not written. Dependency metadata for non-empty dependency sets is recorded before any cache value becomes visible. Backends with set_many() are written in groups by backend instance and timeout; failed keys returned by set_many() are retried with individual set() calls. Entry leases are not inspected or released by this helper; the caller that owns the lease remains responsible for releasing it.

Raises:

Type Description
CachePublishAborted

If publishing is unsafe because a data change is active.

Exception

Lock, dependency-index, and cache backend errors propagate.

general_manager.cache.dependency_publish.publish_dependency_cache_entry

publish_dependency_cache_entry(
    *,
    cache_key,
    result,
    dependencies,
    cache_backend,
    timeout,
    started_generation,
    record_many_fn=None,
    prefetch_manifest_key=None
)

Publish dependency metadata and value only if the computation is current.

When supplied, record_many_fn is called while the dependency-index lock acquired by acquire_lock_with_retry is still held. Custom callbacks must not acquire that lock again or call helpers that do so. The cache decorator passes None for the default record_dependencies implementation so this function can use the non-reentrant locked helper directly.

Parameters:

Name Type Description Default
cache_key str

Dependency-cache key to publish.

required
result object

Cached function result to persist.

required
dependencies Iterable[Dependency]

Dependencies captured during computation.

required
cache_backend DependencyCacheBackend

Backend that stores the versioned dependency-cache entry.

required
timeout int | None

Backend timeout for the cached value.

required
started_generation int

Dependency generation observed before computation.

required
record_many_fn RecordManyDependenciesFn | None

Optional dependency-recording callback. When omitted, the built-in shard recorder is used.

None

Raises:

Type Description
CachePublishAborted

If publishing is unsafe before metadata recording, after a custom/built-in dependency record, or before the value write.

Exception

Lock, dependency-index, custom recorder, and cache backend errors propagate.

The dependency-publish helpers coordinate dependency-cache misses between workers and protect cache writes from overlapping data changes. They are framework support functions; application code should normally use @cached(cache="dependency").

acquire_compute_lease(cache_key, timeout=...) writes a random token to the coordination cache and returns CacheComputeLease when this worker owns the computation. It returns None if another worker already owns the key. release_compute_lease(lease) deletes the lease only when the coordination cache still contains the same token, so an expired-and-reacquired lease is not removed accidentally.

wait_for_cached_dependency_hit(cache_backend, cache_key, timeout_seconds=..., sentinel=...) polls with exponential backoff until read_dependency_cache_hit returns a compatible hit or the timeout expires. It returns DependencyCacheHit for published values, including falsey cached results, and returns the exact sentinel object on timeout. Backend read errors and sleep/clock errors propagate.

PendingDependencyCachePublication is the buffered miss shape used by CalculationRunContext: it carries the cache key, computed result, frozen dependencies, backend, timeout, generation observed before computation, and compute lease. The lease is carried for the caller that owns the computation; the publish helpers do not validate lease ownership and do not release leases. publish_dependency_cache_entries(entries) publishes a batch of current pending entries. It checks for an active data-change barrier before filtering stale entries; an active barrier aborts the whole batch. When no barrier is active, entries from stale generations are skipped. Non-empty dependency sets are recorded before any cache value becomes visible. If a generation change or barrier begins after dependency metadata is recorded but before values are written, CachePublishAborted is raised and values are not stored. Backends with set_many() are grouped by backend instance and timeout; failed keys returned by set_many() are retried with individual set() calls.

publish_dependency_cache_entry(...) performs the same guarded publish for one value. It checks the generation/barrier before dependency recording and again before the cache write. A custom record_many_fn runs while the dependency-index lock is held and must not acquire that lock recursively. Empty dependency sets skip dependency-index recording but still publish the value when the generation is current. Unlike batch publishing, a stale generation for this one value raises CachePublishAborted. Lock, dependency-index, custom recorder, and cache backend errors propagate.

general_manager.cache.dependency_index.Dependency

Dependency = Tuple[general_manager_name, filter_type, str]

Dependency is the public tuple shape captured by DependencyTracker: tuple[str, Literal["filter", "exclude", "identification", "request_query", "all"], str]. The tuple slots are the GeneralManager class name that owns the tracked read, the dependency action, and the serialized identifier payload for that action.

general_manager.cache.dependency_index.serialize_dependency_identifier

serialize_dependency_identifier(value)

Serialize dependency payloads into the canonical dependency identifier format.

Parameters:

Name Type Description Default
value object

Dependency payload captured from manager identification, filter parameters, exclusion parameters, or request-query metadata.

required

Returns:

Type Description
str

A deterministic JSON string. Normalization checks value categories in

str

this order: mappings, lists/tuples, sets, datetimes, dates, JSON scalar

str

values, mapping-shaped __getstate__(), then repr(...) fallback.

str

Mapping keys are coerced with Python's exact str(key) result and

str

ordered by that string form; sets are sorted by each member's string

str

form; lists and tuples keep their order; dates and datetimes serialize

str

with isoformat() including any timezone offset; scalars and None

str

keep their JSON meaning; mapping-shaped __getstate__() payloads are

str

stored under {"__state__": ...}; unsupported objects fall back to

str

{"__repr__": repr(value)}. The parser returns these normalized

str

JSON-compatible structures and does not rehydrate dates, datetimes, or

str

unsupported objects. The current string format is the direct

str

json.dumps(..., sort_keys=True) output of that normalized structure.

str

Dependency payloads should avoid mapping keys or set members that

str

collide after str(...). If mapping keys normalize to the same string,

str

normalization keeps the last item after sorting by str(key); equal sort

str

keys keep the input mapping's iteration order. Set members with

str

identical string forms have intentionally unspecified ordering, so their

str

byte-for-byte serialized output is not a public guarantee. Non-finite

str

floats follow Python's default json.dumps() spelling (NaN,

str

Infinity, or -Infinity), which is accepted by Python's parser but is

str

not portable strict JSON; dependency identifiers containing these values

str

are Python-cache metadata rather than strict JSON interchange values. If

str

__getstate__() exists but returns a non-mapping, the value uses the

str

same repr(...) fallback as other unsupported objects.

general_manager.cache.dependency_index.parse_dependency_identifier

parse_dependency_identifier(identifier)

Parse a serialized dependency identifier back into JSON-compatible data.

Parameters:

Name Type Description Default
identifier str

JSON string previously produced by serialize_dependency_identifier().

required

Returns:

Type Description
object | None

The decoded JSON-compatible value, such as a mapping, sequence, scalar,

object | None

or None when identifier is malformed JSON. A valid serialized JSON

object | None

null payload also decodes to None.

Use serialize_dependency_identifier() when custom integrations need to create the canonical string stored in a Dependency identifier slot. The paired parser returns JSON-compatible decoded data or None for malformed JSON; a serialized JSON null payload also decodes to None, so callers that need to distinguish those cases should avoid using null as a dependency identifier payload. The parser is intentionally lossy: dates, datetimes, __getstate__() payloads, and unsupported-object representation markers come back as normalized JSON-compatible structures rather than the original Python objects. The serialized string is the current json.dumps(..., sort_keys=True) output of that normalized structure. Normalization checks value categories in this order: mappings, lists/tuples, sets, datetimes, dates, JSON scalar values, mapping-shaped __getstate__(), then repr(...) fallback. Mapping keys are coerced with Python's exact str(key) result. Dependency payloads should avoid mapping keys or set members that collide after str(...). If mapping keys normalize to the same string, normalization keeps the last item after sorting by str(key); equal sort keys keep the input mapping's iteration order. Set members with identical string forms have intentionally unspecified ordering, so their byte-for-byte serialized output is not a public guarantee. Non-finite floats use Python's default JSON spellings (NaN, Infinity, or -Infinity), which are accepted by Python's parser but are not portable strict JSON; dependency identifiers containing these values are Python-cache metadata rather than strict JSON interchange values. If __getstate__() exists but returns a non-mapping, the value uses the same repr(...) fallback as other unsupported objects.

record_dependencies() deduplicates dependency tuples and is a no-op for an empty dependency iterable; an empty call does not clear existing metadata for that cache key. Re-recording non-empty dependencies for an existing cache key removes the key's previous sharded and reverse metadata before writing the new set. A non-empty iterable whose dependencies all normalize to no shards still replaces prior metadata with an empty reverse entry. Malformed or non-mapping filter / exclude identifiers are ignored rather than stored. For "all" dependencies, invalidation is keyed by manager name and the identifier is retained only as opaque reverse metadata; all "all" dependencies for the changed manager invalidate together regardless of identifier. Multiple "all" tuples with different identifiers for the same cache key are retained as separate reverse metadata entries until a later non-empty record_dependencies() call replaces that key's metadata. The retained identifiers preserve the original dependency tuple and have no invalidation meaning.

"request_query" dependencies invalidate on any change for the same manager name. "identification" dependencies invalidate when the changed manager's current identification serializes exactly to the stored identifier. "all" dependencies invalidate on any change for the same manager name. invalidate_cache_key() deletes only the cached value, while remove_cache_key_from_index() removes only dependency-index metadata. Use invalidate_and_remove_cache_keys() internally when both steps must happen together.

Filter and exclude invalidation compare serialized expected values with the changed manager's before/after attribute values. Equality and membership dependencies coerce JSON scalars, ISO dates/datetimes, booleans, {"__state__": ...} mappings, and {"__repr__": ...} markers back toward the runtime value being compared. Range operators use the runtime value's ordering after coercion. String lookup operators (contains, startswith, endswith, and regex) compare against str(runtime_value). Supported lookup suffixes are gt, gte, lt, lte, in, contains, startswith, endswith, and regex; any other suffix is treated as part of the nested attribute path and uses equality matching. Missing attributes resolve to None.

Filter dependencies invalidate when either the old or new value matches because the changed object may enter or leave the cached result. Exclude dependencies invalidate when match status changes because that changes whether the object is excluded from the cached result. ISO strings are not self-describing: an ISO-looking stored string compares as a date/datetime when the runtime value is a date/datetime, and as a string when the runtime value is a string. Date runtime values only accept strings parsed by date.fromisoformat(); datetime-shaped strings do not match date runtime values. Date values do not match datetime runtime values. Datetime strings are parsed with datetime.fromisoformat() after replacing a trailing Z with +00:00 and replacing the first space separator with T; timezone-aware parsed values have timezone information removed when the runtime value is naive, and naive parsed values receive the runtime value's timezone when it is aware. Boolean runtime values accept booleans, any integer via Python truthiness, and the strings true, 1, yes, y, t, false, 0, no, n, and f case-insensitively after trimming whitespace. Other runtime values attempt type(runtime_value)(stored_value) coercion, so numeric strings and non-finite floats follow that runtime type's constructor behavior. Range operators use runtime ordering after coercion; failed coercion is a non-match, while ordering exceptions from the runtime value propagate. {"__state__": ...} mappings are compared by constructing the runtime value's type from keyword state, with a positional (magnitude, unit) fallback; constructor failures do not match. {"__repr__": ...} markers compare only with repr(runtime_value).

Lookup paths are __-separated attribute names resolved with getattr(); there is no escaping syntax, dict/list traversal, or callable invocation beyond normal property access. A final path segment matching a supported suffix is always parsed as that lookup operator; for example, field__in is membership lookup, not equality on field.in. contains means stored_pattern in str(runtime_value), startswith means str(runtime_value).startswith(stored_pattern), endswith means str(runtime_value).endswith(stored_pattern), in means the runtime value matches at least one item in the stored JSON list, and regex uses re.search(stored_pattern, str(runtime_value)) without flags. Invalid regex patterns do not match. For string operators, non-string expected values are coerced with str(expected_value) after JSON parsing. For in, a stored expected value that is not a JSON list is a non-match. Only parse, constructor TypeError/ValueError, and regex compilation failures are converted to non-matches; exceptions raised by attribute properties, str(runtime_value), repr(runtime_value), or comparison operators propagate to the invalidation caller.

An empty filter or exclude mapping ({}) is not evaluated as a normal composite predicate; it records an all-records dependency for that manager and invalidates on any change for that manager. Multi-lookup identifiers are composite dependencies within their single action: a "filter" identifier matches only when every lookup in that identifier matches, and an "exclude" identifier also computes match status by requiring every lookup in that identifier to match. The action then controls invalidation: filters invalidate when old or new composite status is true, while excludes invalidate when old and new composite status differ. Malformed expected values inside an otherwise valid mapping are treated as non-matches for that lookup, not as stored dependency errors. Constructor coercion calls the runtime value's type; constructors with side effects should not be used for dependency values.

Sharded Dependency Helpers

general_manager.cache.dependency_shards exposes the low-level shard store used by the dependency index. Most applications should call record_dependencies(), invalidate_cache_key(), or remove_cache_key_from_index() instead, but integrations that need to inspect or migrate dependency metadata can use these helpers directly.

Dependency tuples have the shape (manager_name, action, identifier), where action is "filter", "exclude", "identification", "request_query", or "all". Composite dependencies are filter/exclude dependencies whose identifier serializes multiple lookup paths. Simple dependencies are all other dependency tuples retained in reverse metadata. ReverseDependencyMembership instances are considered valid reverse payloads by type; dependency tuple members inside an otherwise valid instance are returned unchanged rather than sanitized.

general_manager.cache.dependency_shards.ReverseDependencyMembership dataclass

Reverse metadata describing where one cache key was registered.

Attributes:

Name Type Description
cache_key str

Application cache key being tracked.

shard_keys frozenset[str]

Forward shard cache keys containing cache_key.

composite_dependencies frozenset[CompositeDependency]

Filter/exclude dependency tuples that must be re-evaluated after a candidate lookup changes.

simple_dependencies frozenset[SimpleDependency]

Non-composite dependency tuples retained for runtime invalidation checks. The default empty set is part of the public construction contract.

Notes

Read helpers treat only actual ReverseDependencyMembership instances as valid reverse payloads. They do not validate or sanitize dependency tuple members stored inside an otherwise valid instance.

general_manager.cache.dependency_shards.reverse_membership_key

reverse_membership_key(cache_key)

Return the reverse metadata cache key for an application cache key.

The raw cache_key is SHA-256 hashed, so application key characters are not embedded in the returned string. The format is general_manager:dependency:v1:reverse:{hash}.

general_manager.cache.dependency_shards.exact_lookup_shard_key

exact_lookup_shard_key(
    manager_name, action, lookup, operator, value
)

Return the shard key for an exact lookup/value dependency.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class that owns the dependency.

required
action str

Dependency action namespace, normally "filter" or "exclude".

required
lookup str

Normalized lookup attribute path.

required
operator str

Lookup operator namespace. Exact dependencies use "eq".

required
value object

Dependency value to hash with GeneralManager's stable serializer.

required

Returns:

Type Description
str

A deterministic cache key for cache entries depending on that exact

str

lookup value. The format is

str

general_manager:dependency:v1:lookup:{manager}:{action}:{lookup}:

str

{operator}:{stable_value_hash(value)}. Text inputs are interpolated as

str

provided; only value is normalized and hashed. Serialization rules and

str

errors are those of stable_value_hash(): mappings, sequences, sets,

str

dates, datetimes, JSON scalar values, mapping-shaped __getstate__(),

str

and repr(...) fallback are normalized deterministically before the

str

SHA-256 hash is computed.

general_manager.cache.dependency_shards.scan_lookup_shard_key

scan_lookup_shard_key(
    manager_name, action, lookup, operator
)

Return the shard key for a lookup that must be predicate-scanned.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class that owns the dependency.

required
action str

Dependency action namespace, normally "filter" or "exclude".

required
lookup str

Lookup string stored for the scan operator.

required
operator str

Non-exact lookup operator, such as "gte" or "contains".

required

Returns:

Type Description
str

A deterministic cache key for cache entries that need runtime predicate

str

evaluation when this lookup changes. The format is

str

general_manager:dependency:v1:scan:{manager}:{action}:{lookup}:

str

{operator}. Text inputs are interpolated as provided.

general_manager.cache.dependency_shards.composite_lookup_shard_key

composite_lookup_shard_key(manager_name, action, lookup)

Return the shard key containing composite candidates for one lookup.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class that owns the dependency.

required
action str

Composite dependency action, either "filter" or "exclude".

required
lookup str

Normalized lookup attribute path that can affect the composite.

required

Returns:

Type Description
str

A deterministic cache key for cache entries whose multi-lookup

str

dependency must be re-evaluated when the lookup changes. The format is

str

general_manager:dependency:v1:composite_lookup:{manager}:{action}:

str

{lookup}. Text inputs are interpolated as provided.

general_manager.cache.dependency_shards.all_records_shard_key

all_records_shard_key(manager_name)

Return the shard key for cache entries affected by any row change.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class that owns the dependency.

required

Returns:

Type Description
str

A deterministic cache key for cache entries invalidated by every change

str

for that manager. The format is

str

general_manager:dependency:v1:all:{manager}. The manager name is

str

interpolated as provided.

general_manager.cache.dependency_shards.request_query_shard_key

request_query_shard_key(manager_name)

Return the shard key for request-query cache entries for a manager.

Parameters:

Name Type Description Default
manager_name str

Name of the remote/request manager class.

required

Returns:

Type Description
str

A deterministic cache key for request-query dependency candidates. The

str

format is general_manager:dependency:v1:request_query:{manager}. The

str

manager name is interpolated as provided.

general_manager.cache.dependency_shards.lookup_registry_key

lookup_registry_key(manager_name, action)

Return the cache key that stores lookup names tracked for a manager/action.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class.

required
action str

Lookup action namespace, normally "filter" or "exclude".

required

Returns:

Type Description
str

The cache key for the set of lookup names tracked for that pair. The

str

format is general_manager:dependency:v1:lookups:{manager}:{action}.

str

Text inputs are interpolated as provided.

general_manager.cache.dependency_shards.cache_set_members

cache_set_members(key)

Read string members from a cache-backed set-like value.

Parameters:

Name Type Description Default
key str

Cache key to read.

required

Returns:

Type Description
set[str]

String members from a cached set, frozenset, list, or tuple. Missing

set[str]

values, None, and other payload types return an empty set. Non-string

set[str]

members inside an accepted collection are dropped member-by-member.

general_manager.cache.dependency_shards.clear_legacy_dependency_index

clear_legacy_dependency_index()

Delete legacy full-index metadata and cache values referenced by it.

Returns:

Type Description
set[str]

Cache keys found in the legacy index and submitted to cache.delete()

set[str]

as stale cached values. The supported legacy schema has top-level

set[str]

"all" manager sections, "request_query" manager/query sections, and

set[str]

"filter" / "exclude" manager sections containing lookup maps plus

set[str]

optional "__cache_dependencies__" cache-key maps. Malformed legacy

set[str]

structures are non-dict section payloads, non-set-like member

set[str]

collections, or non-string cache keys; malformed branches are skipped

set[str]

member-by-member where possible and may produce an empty set after

set[str]

deleting the index key. Delete failures or backend errors propagate from

set[str]

Django's cache backend.

general_manager.cache.dependency_shards.record_cache_dependencies

record_cache_dependencies(cache_key, dependencies)

Record dependency metadata for one cache key in deterministic shards.

Parameters:

Name Type Description Default
cache_key str

Cache entry whose dependency metadata should be replaced.

required
dependencies Iterable[Dependency]

Dependency tuples in (manager_name, action, identifier) form. Empty iterables are a no-op. A non-empty iterable has the same replacement semantics as record_many_cache_dependencies(), including writing empty reverse metadata when all supplied dependencies normalize to no shards.

required

general_manager.cache.dependency_shards.record_many_cache_dependencies

record_many_cache_dependencies(entries)

Record dependency metadata for many cache keys.

Parameters:

Name Type Description Default
entries Iterable[tuple[str, Iterable[Dependency]]]

(cache_key, dependencies) pairs. Duplicate cache keys and duplicate dependencies are merged before the cache backend is touched.

required
Behavior

A non-empty dependency set replaces prior reverse membership for that cache key, clears legacy full-index metadata once per batch, and writes all shard/reverse updates with batched cache operations where possible. Empty dependency iterables are ignored per cache key; repeated entries for the same cache key are unioned as a Python set before writing, so an empty repeated entry does not cancel a non-empty repeated entry. The public type contract requires well-formed 3-tuples with string manager names, typed actions, and string identifiers; callers that violate that contract may receive normal Python unpacking/type errors. Within that contract, unsupported actions, malformed filter/exclude JSON, and non-mapping filter/exclude identifiers map to no shard. If every supplied dependency for a cache key maps to no shard, the previous metadata is still replaced with an empty reverse membership. Cache writes are not atomic across shards; the order is legacy cleanup, previous-shard removal, new shard/lookup-registry writes, then reverse metadata writes. Backend errors and partial-write behavior propagate from Django's cache backend.

general_manager.cache.dependency_shards.remove_cache_key_from_shards

remove_cache_key_from_shards(cache_key)

Remove a cache key from all shards using its reverse membership.

Parameters:

Name Type Description Default
cache_key str

Cache entry whose shard membership should be removed.

required
Behavior

Missing reverse metadata or a payload that is not a ReverseDependencyMembership is treated as already removed; the reverse metadata key is deleted and removed from the reverse registry. Backend errors and partial-write behavior propagate from Django's cache backend.

general_manager.cache.dependency_shards.candidate_cache_keys_for_lookup

candidate_cache_keys_for_lookup(
    manager_name,
    action,
    lookup,
    *,
    old_value=VALUE_NOT_PROVIDED,
    new_value=VALUE_NOT_PROVIDED
)

Return cache keys stored in shards that may be affected by a lookup change.

Parameters:

Name Type Description Default
manager_name str

Name of the changed manager class.

required
action Literal['filter', 'exclude']

Dependency action to inspect, either "filter" or "exclude". Runtime callers are expected to honor this typed contract; the helper does not perform additional action validation.

required
lookup str

Changed lookup attribute path.

required
old_value object

Deprecated compatibility parameter; exact-value shards are no longer queried.

VALUE_NOT_PROVIDED
new_value object

Deprecated compatibility parameter; exact-value shards are no longer queried.

VALUE_NOT_PROVIDED

Returns:

Type Description
set[str]

Cache keys from scan, composite, and all-records shards that may

set[str]

need runtime dependency evaluation. Scan shards are queried for every

set[str]

operator in SCAN_OPERATORS using both {lookup}__{operator} and the

set[str]

normalized base lookup name returned by lookup_spec_from_key(), which

set[str]

treats a supported operator suffix such as status__gte as lookup path

set[str]

status plus operator gte.

general_manager.cache.dependency_shards.request_query_cache_keys

request_query_cache_keys(manager_name)

Return request-query cache keys for a manager.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class.

required

Returns:

Type Description
set[str]

Cache keys registered as request-query dependencies for that manager.

set[str]

Missing or malformed shard payloads follow cache_set_members()

set[str]

behavior.

general_manager.cache.dependency_shards.all_records_cache_keys

all_records_cache_keys(manager_name)

Return all-records cache keys for a manager.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class.

required

Returns:

Type Description
set[str]

Cache keys registered as all-records dependencies for that manager.

set[str]

Missing or malformed shard payloads follow cache_set_members()

set[str]

behavior.

general_manager.cache.dependency_shards.tracked_lookup_names

tracked_lookup_names(manager_name)

Return lookup attribute paths tracked for a manager across filter/exclude.

Parameters:

Name Type Description Default
manager_name str

Name of the manager class.

required

Returns:

Type Description
set[str]

Lookup attribute paths that should be read from changed manager

set[str]

instances before sharded invalidation runs. Filter and exclude registries

set[str]

are read independently; missing or malformed payloads on either side

set[str]

contribute an empty set through cache_set_members(). Registered names

set[str]

are normalized lookup attribute paths from lookup_spec_from_key();

set[str]

operator suffixes are stripped before registration.

general_manager.cache.dependency_shards.reverse_memberships

reverse_memberships()

Return all reverse memberships known to the shard store.

Returns:

Type Description
ReverseDependencyMembership

Valid reverse-membership payloads currently listed in the reverse

...

membership registry. Missing or malformed registry members are skipped.

tuple[ReverseDependencyMembership, ...]

A malformed registry payload contributes no reverse keys through

tuple[ReverseDependencyMembership, ...]

cache_set_members(). Dependency tuple members inside a valid

tuple[ReverseDependencyMembership, ...]

ReverseDependencyMembership are returned unchanged.

The shard helpers store cache keys in exact, scan, composite, request-query, and all-records sets. record_many_cache_dependencies() deduplicates cache keys and dependencies in memory, clears the legacy full-index cache key once per batch, removes any previous reverse membership for each cache key, and then writes batched shard and reverse metadata. Empty dependency iterables are no-ops. Repeated entries for the same cache key are merged as a Python set, so duplicate dependency tuples collapse without preserving input order; an empty repeated entry does not cancel a non-empty repeated entry for the same cache key. The public type contract requires dependency 3-tuples with a string manager name, one of the typed actions, and a string identifier. Callers that violate that tuple shape or type contract can receive normal Python unpacking or type errors. Within the contract, unsupported actions, malformed filter / exclude JSON, non-mapping filter/exclude identifiers, and otherwise unshardable dependencies write empty reverse metadata when the cache key has at least one supplied dependency, preserving the fact that newer metadata replaced older metadata. record_cache_dependencies() is the one-cache-key wrapper around that same behavior. The write order is legacy-index cleanup, previous-shard removal, new shard and lookup-registry writes, then reverse metadata writes.

candidate_cache_keys_for_lookup() is intentionally conservative. It returns cache keys from exact old/new value shards, scan-operator shards, composite lookup shards, and all-records shards; callers still need to evaluate reverse metadata before deleting cached values. Passing VALUE_NOT_PROVIDED for an old or new value suppresses that exact-value lookup; passing None is a real dependency value and is hashed. Runtime callers are expected to pass "filter" or "exclude" for action; the helper relies on the type contract and does not add runtime validation. Exact old/new candidate lookups use only equality ("eq") shards for the base lookup name. Scan candidates are read for every operator in SCAN_OPERATORS using both {lookup}__{operator} and the base lookup name returned by lookup_spec_from_key(), which strips supported operator suffixes such as status__gte to the status attribute path.

cache_set_members() accepts only cached set, frozenset, list, or tuple payloads. Missing values, None, and other payload types produce an empty set; non-string members inside accepted collections are dropped member-by-member. Helpers that read shard sets inherit this behavior, so malformed filter and exclude lookup registries are ignored independently. reverse_memberships() first reads the reverse registry through cache_set_members(), then returns only cached values that are actual ReverseDependencyMembership instances.

Shard-key builders interpolate string inputs exactly as provided; they do not escape empty strings, colons, or other unusual characters. reverse_membership_key() hashes the application cache key before embedding it. exact_lookup_shard_key() hashes only the dependency value through stable_value_hash(), whose normalization rules are the same as serialize_dependency_identifier(): mappings, sequences, sets, dates, datetimes, JSON scalar values, mapping-shaped __getstate__(), and repr(...) fallback are normalized deterministically before hashing. Lookup names stored in the filter/exclude registries are normalized attribute paths with supported operator suffixes stripped.

clear_legacy_dependency_index() understands the old top-level "all", "request_query", "filter", and "exclude" sections. "all" stores manager sections of cache-key collections; "request_query" stores manager/query sections of cache-key collections; filter/exclude sections store manager lookup maps and may include "__cache_dependencies__" cache-key maps for composite dependencies. Malformed branches, non-set-like member collections, and non-string referenced cache keys are skipped member-by-member where possible. The helper deletes the legacy index key and returns the legacy cache keys it submitted to cache.delete(). The helpers are not atomic across multiple cache keys or shards. They do not raise deliberate package-specific exceptions; errors and partial-write behavior from the configured Django cache backend or value serialization propagate to the caller.

general_manager.cache.cache_tracker.DependencyTracker

Capture dependencies touched inside a read or cache-computation scope.

Tracking is thread-local, not async task-local; same-thread async tasks share tracker state if their execution is interleaved inside one active context.

__enter__

__enter__()

Enter a dependency tracking context and return the current collector.

Returns:

Type Description
set[Dependency]

Mutable set capturing dependencies discovered inside this context.

set[Dependency]

A Dependency is `tuple[str, Literal["filter", "exclude",

set[Dependency]

"identification", "request_query", "all"], str]`.

set[Dependency]

Nested contexts receive their own set, and track(...) records each

set[Dependency]

dependency in every active enclosing context. The returned set

set[Dependency]

remains a usable snapshot after the context exits; clearing

set[Dependency]

thread-local storage does not mutate sets that were already returned.

__exit__

__exit__(exc_type, exc_val, exc_tb)

Leave the dependency tracking context and clean up this thread.

Parameters:

Name Type Description Default
exc_type type[BaseException] | None

Exception type raised within the context, if any.

required
exc_val BaseException | None

Exception instance raised within the context, if any.

required
exc_tb TracebackType | None

Traceback generated by the exception, if any.

required

The tracker does not suppress exceptions. Exiting the outermost context clears all thread-local tracking state; exiting a nested context removes only that nested collector. Calling __exit__ after reset_thread_local_storage() or otherwise without an active context is a no-op.

track staticmethod

track(class_name, operation, identifier)

Record a dependency tuple in all active tracking scopes.

Parameters:

Name Type Description Default
class_name general_manager_name

Name of the GeneralManager subclass.

required
operation filter_type

Operation being tracked, such as filter, exclude, identification, request_query, or all.

required
identifier str

String representation of the lookup parameters.

required

Calling this method without an active DependencyTracker context is a no-op after validating the supplied values. Duplicate tuples collapse naturally because each collector is a set.

Raises:

Type Description
TypeError

If class_name, operation, or identifier is not a string.

ValueError

If operation is not one of filter, exclude, identification, request_query, or all.

Concrete exception subclasses and messages are internal implementation details; callers should rely on the public base TypeError and ValueError contract.

is_active staticmethod

is_active()

Return whether dependency tracking is active for this execution context.

reset_thread_local_storage staticmethod

reset_thread_local_storage()

Clear all dependency tracking data for the current thread.

It is safe to call with no active context or inside an active context. Already returned collector sets keep their current contents, later track(...) calls are ignored until a new context is entered, and the eventual __exit__ for the reset context is a no-op.

general_manager.cache.run_context.CalculationRunContext

Cache calculation work for one request, graph, bulk operation, or task.

Entering the context makes it available through current_calculation_run_context(). Clean exits flush buffered dependency-cache publications; exceptional exits discard them. Run-local values are namespaced by an active historical snapshot, allowing one run to execute sequential snapshots safely. In all cases the active context token is reset and run-local values, dependency hits, and pending publications are cleared before exit returns. Active context lookup uses Python contextvars, so visibility follows normal context-variable propagation for async tasks and does not automatically cross unrelated threads. Nested CalculationRunContext instances replace the active context for their nested block and restore the previous context on exit.

__init__

__init__(
    *,
    dependency_cache_publish_batch_size=DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE
)

Initialize empty run-local storage.

Parameters:

Name Type Description Default
dependency_cache_publish_batch_size int

Number of pending dependency cache publications that triggers an automatic flush. Values less than or equal to zero are accepted and make every buffered publication flush immediately.

DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE

__enter__

__enter__()

Activate this context and return it for the with block.

__exit__

__exit__(exc_type, exc_val, exc_tb)

Flush or discard buffered dependency-cache state and deactivate the context.

Re-entering the same context instance is supported: inner exits restore the previous active context without flushing or clearing state, and only the outermost exit performs publication cleanup and storage clearing. Calling __exit__ without a matching __enter__ is a no-op.

Parameters:

Name Type Description Default
exc_type type[BaseException] | None

Exception type from the wrapped block, or None on a clean exit.

required
exc_val BaseException | None

Exception instance from the wrapped block, or None.

required
exc_tb TracebackType | None

Traceback from the wrapped block, or None.

required

Raises:

Type Description
Exception

Errors from flushing/discarding dependency-cache publications, lease release, or context-variable reset propagate unchanged.

get_or_set

get_or_set(key, loader)

Return a cached value for key, loading it at most once per context.

The first call for a missing key invokes loader() and stores its return value. Later calls with the same key return the stored object, even if they pass a different loader. Loader exceptions propagate and do not store a value. The key may be any hashable value. loader is a synchronous callable; coroutine objects are stored as ordinary return values if a loader returns one.

get

get(key, default=None)

Return the stored value for key, or default when key is absent.

set

set(key, value)

Store a value for the active run.

set_dependency_cache_hits

set_dependency_cache_hits(hits)

Merge prefetched dependency-cache hits into the active run.

Provided keys replace existing hits for the same cache key, while omitted existing keys remain available.

get_dependency_cache_hit

get_dependency_cache_hit(key, default=None)

Return a prefetched dependency-cache hit, or default when absent.

buffer_dependency_cache_publication

buffer_dependency_cache_publication(entry)

Buffer a dependency-cache miss for guarded batch publication.

The buffered result is immediately visible through get_dependency_cache_hit() in the same run. A later entry for the same cache key replaces the previous one and releases the previous compute lease when the lease token differs. The buffered hit also replaces any prefetched hit for the same key. Reaching the configured batch size, or using a batch size less than or equal to zero, flushes pending publications immediately. entry is a PendingDependencyCachePublication created by the dependency-cache compute path; this context reads its cache_key, result, dependencies, and lease fields but does not derive those values.

Raises:

Type Description
Exception

Errors from release_compute_lease(), publish_dependency_cache_entries(), or cache backend lease operations propagate unchanged except CachePublishAborted, which is handled by flush_dependency_cache_publications().

flush_dependency_cache_publications

flush_dependency_cache_publications()

Publish buffered dependency-cache misses and release their leases.

CachePublishAborted is logged and swallowed because guarded publish aborts are expected when backend generations changed. Leases are released in a finally block for every entry submitted to the publish attempt.

Raises:

Type Description
Exception

Unexpected publish errors or lease-release errors propagate unchanged.

discard_dependency_cache_publications

discard_dependency_cache_publications()

Drop buffered dependency-cache misses and release their leases.

Raises:

Type Description
Exception

Lease-release errors propagate unchanged.

discard_dependency_cache_state

discard_dependency_cache_state()

Drop prefetched hits and buffered publications for this run.

discard_prefix

discard_prefix(prefix)

Discard cached tuple keys whose leading items equal prefix.

get_orm_bucket_result

get_orm_bucket_result(key)

Return a cached ORM bucket result for key, or None when absent.

set_orm_bucket_result

set_orm_bucket_result(key, value)

Store or overwrite an ORM bucket result for the active run.

get_orm_bucket_rows

get_orm_bucket_rows(key)

Return cached ORM bucket rows for key, or None when absent.

set_orm_bucket_rows

set_orm_bucket_rows(key, value)

Store or overwrite ORM bucket rows for the active run.

get_orm_model_row

get_orm_model_row(
    model, primary_key, database_alias, default=None
)

Return an indexed ORM row by model/database/primary key, if present.

get_orm_model_row_items

get_orm_model_row_items(model)

Return indexed ORM row items for model in insertion order.

get_orm_model_relation_prefetched_keys

get_orm_model_relation_prefetched_keys(
    model, database_alias, accessor_name
)

Return row keys already prefetched for one model relation.

add_orm_model_relation_prefetched_keys

add_orm_model_relation_prefetched_keys(
    model, database_alias, accessor_name, row_keys
)

Record row keys whose relation has been prefetched in this run.

get_orm_relation_manager

get_orm_relation_manager(key)

Return a cached relation manager for key, or None when absent.

set_orm_relation_manager

set_orm_relation_manager(key, value)

Store a manager created by a generated ORM relation accessor.

get_orm_bucket_managers

get_orm_bucket_managers(key)

Return cached ORM bucket managers for key, or None when absent.

get_orm_bucket_manager_dependencies

get_orm_bucket_manager_dependencies(key)

Return cached ORM manager dependencies for key, when available.

set_orm_bucket_managers

set_orm_bucket_managers(key, value, dependencies=None)

Store or overwrite ORM bucket managers for the active run.

get_orm_bucket_first_row

get_orm_bucket_first_row(key, default=None)

Return a cached ORM first-row result, or default when absent.

set_orm_bucket_first_row

set_orm_bucket_first_row(key, value)

Store or overwrite an ORM first-row result for the active run.

get_orm_query_bucket

get_orm_query_bucket(key)

Return a cached constructed ORM query bucket, or None when absent.

set_orm_query_bucket

set_orm_query_bucket(key, value)

Store a constructed ORM query bucket for the active run.

get_orm_bucket_exists

get_orm_bucket_exists(key)

Return cached ORM bucket existence for key, or None when absent.

set_orm_bucket_exists

set_orm_bucket_exists(key, value)

Store an ORM bucket existence result for the active run.

clear_orm_bucket_results

clear_orm_bucket_results()

Discard all run-scoped ORM bucket result entries.

get_bucket_index_result

get_bucket_index_result(
    source_signature, key_spec, many, max_rows
)

Return a cached bucket index and replay its source dependencies.

Missing entries and entries under the same key that are not BucketIndexRunCacheEntry return None. Cache hits replay the stored dependencies through DependencyTracker.track() before returning the cached value.

set_bucket_index_result

set_bucket_index_result(
    source_signature,
    key_spec,
    many,
    value,
    dependencies,
    max_rows,
)

Store a bucket index and freeze the dependencies touched while building it.

clear_bucket_indexes

clear_bucket_indexes()

Discard all run-scoped bucket index entries.

clear_trusted_orm_managers

clear_trusted_orm_managers()

Discard run-scoped manager wrappers built from trusted ORM rows.

has

has(key)

Return whether key has a value in the active run.

__contains__

__contains__(key)

Return whether key has a value in the active run.

index

index(*, key, loader, index_by)

Load a working set once and index it by the supplied key function.

The loader is evaluated only on the first call for ("index", key). Rows sharing an index key overwrite earlier rows, matching normal dictionary comprehension semantics in loader iteration order. Loader or index-key exceptions propagate and do not store a value. Both callables are synchronous; coroutine objects returned by them are treated as ordinary row or key values.

group_by

group_by(*, key, loader, group_by)

Load a working set once and group rows by the supplied key function.

The loader is evaluated only on the first call for ("group_by", key). Group and row order follows the loader's iteration order. Loader or grouping-key exceptions propagate and do not store a value. Both callables are synchronous; coroutine objects returned by them are treated as ordinary row or key values.

index_many

index_many(*, key, loader, index_by)

Alias group_by() for callers that think in multi-value indexes.

CalculationRunContext(dependency_cache_publish_batch_size=1000) is a context-manager-scoped in-memory cache for one request, graph, bulk operation, or background task. Values less than or equal to zero for dependency_cache_publish_batch_size are accepted and make every buffered dependency-cache publication flush immediately. __enter__() activates the context in the current context-variable scope and returns the context. On clean __exit__(), buffered dependency-cache publications are flushed; on exceptional exit, they are discarded. Both paths reset the active context token and clear run-local values, prefetched dependency hits, and pending publications, even when flushing or lease release raises. Calling __exit__() on an instance that was not entered is a no-op. Re-entering the same context instance is supported: inner exits restore the current active context without flushing or clearing state, and the outermost exit owns publication cleanup and storage clearing. Flush, discard, lease-release, cache backend, and context-variable reset errors propagate unchanged, except guarded CachePublishAborted batch-publish results are debug-logged and swallowed before leases are released. Active context lookup follows Python contextvars semantics: nested CalculationRunContext instances replace the active context for their nested block and restore the previous context on exit, async task propagation follows normal context-variable behavior, and unrelated threads do not automatically share the active context.

get_or_set(key, loader) stores the first successful loader result under the hashable key and returns that same object for later calls with the same key. Loader exceptions propagate and do not store a value. key, source_signature, key_spec, and direct storage keys are opaque hashable values owned by the caller. loader, index_by, and group_by callables are synchronous; coroutine objects returned by those callables are treated as normal values. get(key, default=None) returns the stored object or the exact default object when absent. set(key, value), discard_prefix(prefix), set_dependency_cache_hits(...), dependency-publication mutators, ORM bucket mutators, and clear methods return None. has(key) and key in context return booleans. discard_prefix(prefix) removes tuple keys whose leading items match the supplied tuple.

index(key=..., loader=..., index_by=...) stores a dictionary under ("index", key) and keeps the last row when multiple rows produce the same index key, determined by loader iteration order. group_by(...) stores lists under ("group_by", key) in loader iteration order, and index_many(...) is an alias for group_by(...). Loader and key-function exceptions propagate and do not store a partial index/group.

set_dependency_cache_hits(...) merges prefetched dependency-cache hits into the active run. Provided keys replace existing hits for the same cache key, and omitted existing keys remain available. buffer_dependency_cache_publication(entry) accepts a PendingDependencyCachePublication with the public dataclass fields cache_key, result, dependencies, cache_backend, timeout, started_generation, and lease. The lease is a CacheComputeLease with key and token; both values are created by dependency-publish helpers, not by the run context. Buffering makes a computed miss visible as a run hit immediately, replacing any prefetched hit for the same cache key. It also replaces prior buffered entries for the same cache key, releases a replaced lease when the lease token differs, and flushes when the configured batch size is reached. flush_dependency_cache_publications(), discard_dependency_cache_publications(), and discard_dependency_cache_state() own the pending-publication lifecycle. Flush and discard copy pending entries, clear the pending map before publication or release, and then release leases; a failed publish or release therefore propagates but does not leave those pending entries buffered for a later retry by the same run context. entry is a PendingDependencyCachePublication produced by the dependency-cache compute path; the context reads its cache_key, result, dependencies, and lease fields but does not derive cache keys or lease tokens. Dependency tuples use the public Dependency shape from dependency_index. Concrete exception classes from cache backends, dependency-index publishing, lease release, or custom backend implementations are not normalized by this module, so callers see the source exception type. The only run-context-specific publication exception handling is that CachePublishAborted from batch publishing is swallowed after a debug log; callers that need to catch backend-specific failures must catch the backend or dependency-publish exception types used by their configured backend.

ORM bucket helpers store result and row snapshots under internal tuple prefixes. get_orm_bucket_result(key) reads the value stored under ("orm_bucket_result", key) and returns None when absent. set_orm_bucket_result(key, value) stores or overwrites that value. get_orm_bucket_rows(key) and set_orm_bucket_rows(key, value) do the same under ("orm_bucket_row_result", key). key is an opaque hashable value chosen by the ORM bucket layer. clear_orm_bucket_results() clears both namespaces. Bucket-index helpers store BucketIndexRunCacheEntry values keyed by source signature, key spec, many, and max_rows. set_bucket_index_result(source_signature, key_spec, many, value, dependencies, max_rows) stores a BucketIndexRunCacheEntry under ("bucket_index", source_signature, key_spec, many, max_rows) after freezing the dependency iterable. get_bucket_index_result(source_signature, key_spec, many, max_rows) reads the same key and returns None for missing or non-entry values; cache hits replay stored source dependencies through DependencyTracker.track() before returning the cached value. clear_bucket_indexes() clears the bucket-index namespace.

general_manager.cache.run_context.current_calculation_run_context

current_calculation_run_context()

Return the context active in the current context-variable scope, if any.

current_calculation_run_context() returns the context active in the current context-variable scope, or None when no calculation run context is active.

general_manager.cache.run_context.ensure_calculation_run_context

Use the current run context or create one for the wrapped block.

If a CalculationRunContext is already active, the manager returns it and leaves lifecycle ownership to the outer context. Otherwise it creates, enters, and later exits a temporary context.

__enter__

__enter__()

Return the active context, creating one when needed.

__exit__

__exit__(exc_type, exc_val, exc_tb)

Exit the owned temporary context, if one was created.

ensure_calculation_run_context() is a context manager that returns the current active context when one exists. When none exists, it creates, enters, and later exits a temporary CalculationRunContext. Use it only in with statements: with ensure_calculation_run_context() as context:. It is a class-based context manager, not a decorator or generator context manager; __enter__() yields a CalculationRunContext and __exit__() returns None.

general_manager.cache.signals.pre_data_change module-attribute

pre_data_change = Signal()

general_manager.cache.signals.post_data_change module-attribute

post_data_change = Signal()

general_manager.cache.signals.data_change

data_change(func: Callable[P, R]) -> Callable[P, R]
data_change(
    func: classmethod[object, P, R],
) -> Callable[P, R]
data_change(func)

Wrap a data-modifying function with pre- and post-change signal dispatching.

The wrapper preserves the wrapped callable's metadata with functools.wraps. It opens a dependency-cache publish barrier before the mutation, clears run-scoped ORM bucket/index caches, emits pre_data_change, invokes the wrapped callable, then emits post_data_change with the changed instance, previous instance, action name, identification, and copied _old_values payload. Both signals receive a per-call change_context and the manager interface's database alias. ORM-backed signal dispatch and the mutation share one transaction. GraphQL warm-up requeue keys collected during signal handling are drained only after the outermost active data-change barrier has closed; failed mutations drain pending keys but do not enqueue rewarm work.

Parameters:

Name Type Description Default
func Callable[P, R] | classmethod[object, P, R]

Function that performs a data mutation. Methods named create are treated as class-level creates; every other name is treated as an instance mutation. Raw classmethod descriptor objects are accepted for compatibility; normal class methods should still prefer @classmethod outside @data_change.

required

Returns:

Type Description
Callable[P, R]

Wrapped function that returns the wrapped callable's result.

Raises:

Type Description
BaseException

Exceptions from the wrapped callable and signal receivers propagate. Cleanup errors propagate when the wrapped callable succeeded; if the wrapped callable already failed, cleanup errors are logged and the original exception is re-raised. GraphQL warm-up enqueue errors are logged and suppressed.

data_change(func) wraps GeneralManager create, update, and delete methods with dependency-cache barrier management and Django signal dispatch. The wrapper preserves the wrapped callable's metadata with functools.wraps, opens the dependency publish barrier before the mutation, clears run-scoped ORM bucket and bucket-index snapshots in the active CalculationRunContext, emits pre_data_change, calls the wrapped function, emits post_data_change, and then closes the barrier. Methods named create are treated as class-level creates and use the first positional argument as the Django signal sender; every other method name is treated as an instance mutation and uses instance.__class__ as sender. Raw classmethod descriptor objects are accepted for compatibility, but normal class methods should prefer @classmethod outside @data_change.

pre_data_change receives sender, instance, action, and the wrapped method keyword arguments. post_data_change receives sender, instance, previous_instance, identification, action, old_relevant_values, and the wrapped method keyword arguments. For deletes or other mutations returning None, post_data_change.instance is None and previous_instance carries the pre-mutation object. The identification value prefers the returned instance's current identification; if that is missing or None, the wrapper uses a deep copy of the pre-mutation identification. old_relevant_values comes from the pre-mutation instance's _old_values attribute, defaulting to {}, and the wrapper removes _old_values from that instance after a successful post-change signal.

Signal receiver exceptions propagate and skip later steps in the normal Python call path. The dependency barrier is still closed in finally. Cleanup errors propagate when the wrapped mutation succeeded; if the wrapped mutation already failed, cleanup errors are logged and the original mutation exception is re-raised. Invalidated GraphQL warm-up cache keys collected during signal handling are drained only after the outermost active data-change barrier has closed, so nested @data_change calls enqueue one final rewarm batch. Failed mutations still drain pending rewarm keys after the outermost barrier closes, but enqueueing runs only for completed mutations. GraphQL warm-up enqueue errors are logged and suppressed.

general_manager.cache.dependency_index.record_dependencies

record_dependencies(cache_key, dependencies)

Register dependency-index metadata for one cache key.

Each dependency tuple is (manager_name, action, identifier), where manager_name is the GeneralManager class name that owns the tracked read, action is one of "filter", "exclude", "identification", "request_query", or "all", and identifier is the serialized payload for that action. Repeated calls for the same cache key replace that key's previous sharded dependency metadata; duplicate tuples in one call collapse. An empty dependency iterable is a no-op and does not clear existing metadata for cache_key; call remove_cache_key_from_index() for explicit cleanup. A non-empty iterable whose dependencies all normalize to no shards still replaces prior metadata with an empty reverse entry.

Parameters:

Name Type Description Default
cache_key str

Cache key to associate with the declared dependencies.

required
dependencies Iterable[Dependency]

Dependency tuples to store. For "filter" and "exclude", identifier must be a serialized mapping of lookup names to expected values; malformed or non-mapping identifiers are ignored rather than stored. {} tracks all records for the manager. For "identification", identifier is the serialized manager identification payload. For "request_query" and "all", the identifier is stored as reverse metadata only; "all" invalidation is keyed by the manager name and does not inspect the identifier, so all "all" dependencies for the changed manager invalidate together. Multiple "all" tuples with different identifiers for the same cache key are retained as separate reverse metadata entries until a later non-empty record_dependencies() call replaces that key's metadata; the retained identifiers preserve the original dependency tuple and have no invalidation meaning.

required
Invalidation

"request_query" dependencies invalidate on any change for the same manager name. "identification" dependencies invalidate when the changed manager's current identification serializes exactly to the stored identifier. "all" dependencies invalidate on any change for the same manager name.

Notes

Filter and exclude invalidation compare the serialized expected value with the changed manager's before/after attribute values. Equality and membership dependencies coerce JSON scalars, ISO dates/datetimes, booleans, {"__state__": ...} mappings, and {"__repr__": ...} markers back toward the runtime value being compared. Range operators use the runtime value's ordering after coercion. String lookup operators (contains, startswith, endswith, and regex) compare against str(runtime_value). Supported lookup suffixes are gt, gte, lt, lte, in, contains, startswith, endswith, and regex; any other suffix is treated as part of the nested attribute path and uses equality matching. There is no escaping syntax for a final attribute segment literally named like a supported suffix; field__in is always the in lookup, not equality on field.in. Missing attributes resolve to None. Filter dependencies invalidate when either the old or new value matches because the changed object may enter or leave the cached result. Exclude dependencies invalidate when match status changes because that changes whether the object is excluded from the cached result. ISO strings are not self-describing: an ISO-looking stored string compares as a date/datetime when the runtime value is a date/datetime, and as a string when the runtime value is a string. Date runtime values only accept strings parsed by date.fromisoformat(); datetime-shaped strings do not match date runtime values. Date values do not match datetime runtime values. Datetime strings are parsed with datetime.fromisoformat() after replacing a trailing Z with +00:00 and replacing the first space separator with T; timezone-aware parsed values have timezone information removed when the runtime value is naive, and naive parsed values receive the runtime value's timezone when it is aware. Boolean runtime values accept booleans, any integer via Python truthiness, and the strings true, 1, yes, y, t, false, 0, no, n, and f case-insensitively after trimming whitespace. Other runtime values attempt type(runtime_value)(stored_value) coercion, so numeric strings and non-finite floats follow that runtime type's constructor behavior. {"__state__": ...} mappings are compared by constructing the runtime value's type from keyword state, with a positional (magnitude, unit) fallback; constructor failures do not match. {"__repr__": ...} markers compare only with repr(runtime_value). Lookup paths are __-separated attribute names resolved with getattr(); there is no escaping syntax, dict/list traversal, or callable invocation beyond normal property access. contains means stored_pattern in str(runtime_value), in means the runtime value matches at least one item in the stored list, and regex uses re.search(stored_pattern, str(runtime_value)) without flags; invalid regex patterns do not match. startswith and endswith call str(runtime_value).startswith(stored_pattern) and str(runtime_value).endswith(stored_pattern). For string operators, non-string expected values are coerced with str(expected_value) after JSON parsing. For in, a stored expected value that is not a JSON list is a non-match. For range operators, failed coercion is a non-match; ordering exceptions from the runtime value propagate. Only documented parse, constructor, and regex compilation failures are converted to non-matches. Exceptions raised by attribute properties, str(runtime_value), repr(runtime_value), or comparison operators propagate to the invalidation caller. An empty filter or exclude mapping ({}) is not evaluated as a normal composite predicate; it records an all-records dependency for that manager and invalidates on any change for that manager. Multi-lookup identifiers are composite dependencies within their single action: a "filter" identifier matches only when every lookup in that identifier matches, and an "exclude" identifier also computes match status by requiring every lookup in that identifier to match. The action then controls invalidation: filters invalidate when old or new composite status is true, while excludes invalidate when old and new composite status differ. Malformed expected values inside an otherwise valid mapping are treated as non-matches for that lookup, not as stored dependency errors. Constructor coercion calls the runtime value's type; constructors with side effects should not be used for dependency values.

Raises:

Type Description
DependencyLockTimeoutError

If a lock cannot be acquired within the configured timeout while updating the index.

general_manager.cache.dependency_index.invalidate_cache_key

invalidate_cache_key(cache_key)

Delete the cached value associated with the provided key.

This function only calls the configured cache backend's delete() for the value key. It does not remove dependency-index metadata; call remove_cache_key_from_index() separately, or use invalidate_and_remove_cache_keys(), when both steps are required.

Parameters:

Name Type Description Default
cache_key str

Key referencing the cached value.

required

Returns:

Type Description
None

None

general_manager.cache.dependency_index.remove_cache_key_from_index

remove_cache_key_from_index(cache_key)

Remove a cache key from dependency-index metadata without deleting the value.

Acquires the dependency lock to update and persist the index or sharded index metadata. This is index-only cleanup; use invalidate_cache_key() or invalidate_and_remove_cache_keys() when the cached value should also be deleted.

Parameters:

Name Type Description Default
cache_key str

Cache key to expunge from all recorded dependency mappings.

required

Raises:

Type Description
DependencyLockTimeoutError

If the dependency lock cannot be acquired within LOCK_TIMEOUT.