Class QueryCache
Facade over ICacheStore providing query-result caching with two key properties:
- Time-quantized keys — DateTime parameters derived from
UtcNoware rounded to a configurable window before hashing, so.Where(x => x.At >= UtcNow.AddDays(-30))no longer produces a unique key per call. - Table-version invalidation — mutations bump a per-table version counter that participates in the cache key; prior entries simply become un-hittable and are swept on eviction. No need to track-and-evict individual keys.
Keeps a static facade so existing callers (QueryBuilder, Repository)
compile unchanged.
public static class QueryCache
- Inheritance
-
QueryCache
- Inherited Members
Properties
Count
Total cached entries (best-effort).
public static int Count { get; }
Property Value
Methods
Clear()
Clear the entire cache (admin / tests).
public static void Clear()
Configure(bool, int, int)
Apply runtime configuration from CacheConfiguration.
public static void Configure(bool enabled, int maxEntries, int timeQuantizeSeconds)
Parameters
GetOrSetAsync<T>(string, string, Func<Task<T>>, TimeSpan, string?)
Cache-aside helper. Returns the cached value if fresh; otherwise runs factory,
stores the result, and returns it.
Failure CodeLogic.Core.Results.Result<T> values are NEVER cached and never served as cache hits — a transient DB failure during a cold-warmup cannot poison the cache. If a previously-cached value is detected as a failure (legacy entries from older versions), it's evicted on read so the call falls through to a fresh execution.
public static Task<T> GetOrSetAsync<T>(string cacheKey, string tableName, Func<Task<T>> factory, TimeSpan ttl, string? connectionId = null)
Parameters
Returns
- Task<T>
Type Parameters
T
GetStats()
Diagnostic snapshot: total entries + per-table counts + table-version map. Returned types are immutable copies; safe to log or render on an admin page without holding cache locks.
public static QueryCacheStats GetStats()
Returns
Invalidate(string)
Invalidate all cached entries for the given table.
Two-step: bump the per-table version (so future reads compute a different cache key and miss any in-flight refresh writes), then evict the now-orphaned entries from the underlying store so they don't accumulate. Without the second step, every mutation leaves behind the previous version's entries until TTL/LRU clears them, which on a busy app produces unbounded memory growth.
When a table has active SmartCachePool entries, the
pool's background refresh is the source of truth for freshness — it
re-executes queries every RefreshEvery and writes to the
current version's cache key. Flushing the cache on every mutation
(e.g. a stats ingest INSERT) would defeat the pool: every request
between the flush and the next refresh tick hits the DB cold. For
pool-managed tables we skip the eviction and let the pool refresh
deliver naturally-stale-within-interval reads instead.
public static void Invalidate(string tableName)
Parameters
tableNamestring
Invalidate<T>()
Invalidate for the table behind entity type T.
public static void Invalidate<T>() where T : class
Type Parameters
T
SetDirectAsync(string, object, TimeSpan, string, CancellationToken)
Direct write into the cache, bypassing the cache-aside flow. Used by SmartCachePool to overwrite an entry after a background refresh produces a fresh value. No observability event is emitted — the refresh isn't a cache hit or miss from a caller's perspective.
public static Task SetDirectAsync(string cacheKey, object value, TimeSpan ttl, string tableName, CancellationToken ct = default)
Parameters
cacheKeystringvalueobjectttlTimeSpantableNamestringctCancellationToken
Returns
UseCoordinator(ICacheCoordinator)
Installs a multi-node ICacheCoordinator (e.g. a Redis pub/sub adapter) so mutations fan out to peers and smart-cache pools refresh single-flight. Wires the coordinator's peer-invalidation callback to the local (non-broadcasting) invalidation path. Pair with a shared ICacheStore via UseStore(ICacheStore).
public static void UseCoordinator(ICacheCoordinator coordinator)
Parameters
coordinatorICacheCoordinator
UseStore(ICacheStore)
Replace the underlying store (e.g. with a Redis adapter).
public static void UseStore(ICacheStore store)
Parameters
storeICacheStore