acl: version-checked cache for grant reads instead of re-reading the whole table (#499)
Every ACL question (grantsFor, handlesFor, grantsOfKind, list) loaded the entire acl_grants table and filtered in process, on every call. At a few calls per second this full-table read becomes the single largest consumer of database time even though grant writes are rare. The grant store now keeps an in-process cache invalidated by a version counter that every write path bumps (via a trigger), so reads hit the table only when a write has actually happened. The cache is a pure read-through in front of Postgres, so multi-instance deployments stay correct: a stale instance re-reads on the next version mismatch.
**Deployment notes**
The createPgPool multi-migration refactor it depends on must be in the same PR (or already
upstream)
Release note: boot now creates a plpgsql function + trigger on acl_grants; DB role needs
those privileges
New migration acl/grants/0002 runs at boot via the createPgPool migration list (CREATE TABLE IF
NOT EXISTS acl_grants_version + INSERT ... ON CONFLICT DO NOTHING + CREATE OR REPLACE
FUNCTION + DROP/CREATE TRIGGER) — idempotent, no manual step.
Restructures createPgPool's signature from (conn, id, statements) to (conn, [{id, statements}]) —
upstream must carry the matching createPgPool change and note says 0001 is
checksummed/immutable; verify upstream's migration ledger accepts the new list form for a DB
that already ran 0001.
Blue-green is the designed-for case: the statement-level trigger bumps the version for writes
from OLD code too, so a new instance's warm cache invalidates on any writer. Safe both
directions.
Rollback caveat: rolling back leaves the trigger + version table in place; old code never reads
them and every write still fires the trigger — harmless but permanent unless dropped by hand.
Requires plpgsql available and DDL privileges (CREATE FUNCTION/TRIGGER) for the app's DB
role at boot — a locked-down deployment role without TRIGGER privilege fails boot; worth a
release-note line.
<!-- codesmith:footer -->
---
<a href="https://app.blacksmith.sh/yc-software/codesmith/qm/pr/467"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1789248209&installation_model_id=19911&pr_number=467&repository=yc-software%2Fqm&return_to=https%3A%2F%2Fgithub.com%2Fyc-software%2Fqm%2Fpull%2F467&signature=4b1a4294398fff4973b6a2d77637aa190ff55e6994b35e63ea6283054f7694bf"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need. Autofix is disabled.</sup>
<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer --> R
Regan Bell committed
48fc1e57935db18de7c6f0850d871cb0ef0327a5
Parent: f665948
Committed by GitHub <noreply@github.com>
on 8/13/2026, 10:22:26 PM