moderator: page-access grant cache is on the system Redis client; it is a pure cache and belongs on the cache client
apps/moderator/src/lib/server/page-access.ts reads and writes its grant map through getSysRedis(). Under our cache-vs-durable split — a cache belongs on the cache client, durable or cross-app state belongs on the system client — it is on the wrong one.
It is unambiguously a cache:
readThrough()falls back togetPageAccessGrants(dbRead, APP), so Postgres is the source of truth- the entry is written with
EX 300, and there is a 30 s in-process LRU (memo) in front of it - both failure paths are already written as cache failures — a read that throws falls through to Postgres, and
publish()failing is logged as "loaded grants, but caching them failed" rather than raised
Losing the entry costs a Postgres read. Nothing is unrecoverable.
It is also the only system-client consumer in this app that is a cache of its own data. The others are on it for good reasons and should stay:
| consumer | why it belongs on the system client |
|---|---|
sessions.ts |
cross-app session revocation registry — the comment is explicit that other apps must see a mute or force-logout immediately |
user-restriction.service.ts |
lPush/lTrim on SUSPICIOUS_AUDIT_MATCHES, no TTL — the only store for those records |
image-moderation-effects.ts, image-deletion.ts |
write CACHES.IMAGE_EXISTS for the main app to read; they must target whatever the main app reads, so this is not the moderator's choice |
So the change is one import and two call sites in page-access.ts — getSysRedis() → getRedis() — plus moving the key onto the cache key namespace.
Worth checking while in there
The cache key is currently built from REDIS_SYS_KEYS.APP.PAGE_ACCESS. Moving clients means moving key namespaces too, so the two must change together or the read and the write will disagree. There is also a one-time effect on deploy: the first request after the move misses and reads Postgres, which the read-through already handles.
Out of scope
This is separate from #4436, which is a deployment-side configuration problem, not app code. Do not fold them together — that issue's fix is infra-only and this one is code-only.
Closing condition
A PR moving page-access.ts from getSysRedis() to getRedis() (key namespace included) is merged to main, and after it deploys a page-access grant change made in the moderator UI takes effect within the 300 s TTL for a second moderator session — i.e. the cache is still doing its job on the new client.
Checked by: @ZacxDev, by making that grant change after the deploy and observing it apply.
Source: civitai/civitai