Skip to content

Cache Isolation — Tenant-Scoped Keys

FusionCache is registered as a singleton with a global key prefix ("dd:"). When multiple tenants share a Redis instance, cache keys from different tenants can collide — a settings key written by Tenant A is readable by Tenant B.

Some modules manually include TenantId in their cache keys (Permission, Features), but this is opt-in, error-prone, and easy to forget. A single missed key creates a cross-tenant cache leak.

Granit wraps the singleton IFusionCache with a scoped decorator that automatically prefixes every cache key and tag with the current tenant identifier. No code change needed in modules — the decorator is transparent.

ContextKey formatExample
Tenant activet:{tenantId:N}:{key}t:3fa85f6456954b5ab7d9c4f11f0b7f5e:settings
Host (no tenant)t:host:{key}t:host:global-config

Tags are prefixed identically, ensuring RemoveByTag("products") only invalidates the current tenant’s entries.

OperationPrefixed
GetOrSetAsync, GetOrDefaultAsync, TryGetAsyncKey
SetAsyncKey + tags
RemoveAsync, ExpireAsyncKey
RemoveByTagAsyncTags
ClearNo (clears everything)
Infrastructure (SetupDistributedCache, etc.)No (passthrough)
DisposeNo (singleton not disposed)
sequenceDiagram
    participant S as Service
    participant D as TenantAwareFusionCache (scoped)
    participant I as ICurrentTenant
    participant F as FusionCache (singleton)

    S->>D: SetAsync("settings", value)
    D->>I: Id → 3fa8...
    D->>D: PrefixKey → "t:3fa8...:settings"
    D->>F: SetAsync("t:3fa8...:settings", value)

The raw FusionCache singleton is moved to a keyed service. The decorator becomes the default IFusionCache:

IFusionCache (default) → TenantAwareFusionCache (scoped)
└── IFusionCache ("__granit_raw_cache__") → FusionCache (singleton)

All existing module code that injects IFusionCache gets the decorator automatically — zero changes needed.

Cache isolation is enabled automatically by AddGranitCaching(). No configuration needed.

If ICurrentTenant is not registered (standalone usage without multi-tenancy), a NullCurrentTenant fallback is used — all keys are prefixed with t:host:.

Modules that already include TenantId in their cache keys (Permission, Features, Settings) will temporarily have a double prefix:

t:3fa8...:perm:3fa8...:Admin:Users.Read

This is harmless — it causes a cold cache miss (new key format), not a data leak. The module keys can be cleaned up in a follow-up to remove the manual TenantId segment, resulting in cleaner keys:

t:3fa8...:perm:Admin:Users.Read

The Redis pub/sub backplane uses cache keys for invalidation notifications. Since the decorator prefixes keys before they reach the inner cache, the backplane sees the prefixed keys and invalidates correctly. No backplane configuration change needed.