Table of Contents

Class QueryCache

Namespace
CL.MySQL2.Services
Assembly
CL.MySQL2.dll

Facade over ICacheStore providing query-result caching with two key properties:

  • Time-quantized keys — DateTime parameters derived from UtcNow are 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

int

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

enabled bool
maxEntries int
timeQuantizeSeconds int

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

cacheKey string
tableName string
factory Func<Task<T>>
ttl TimeSpan
connectionId string

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

QueryCacheStats

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

tableName string

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

cacheKey string
value object
ttl TimeSpan
tableName string
ct CancellationToken

Returns

Task

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

coordinator ICacheCoordinator

UseStore(ICacheStore)

Replace the underlying store (e.g. with a Redis adapter).

public static void UseStore(ICacheStore store)

Parameters

store ICacheStore