Decision Cache
The authorization cache, its TTL, and invalidation triggers.
is_allowed can be called for every row of a large scan, so its result is
cached. morbac.auth_cache stores one row per
(user_id, org_id, activity, view) with the boolean decision and an expiry
computed from the cache_ttl_seconds config key (default 300 seconds).
SELECT morbac.get_config('cache_ttl_seconds'); -- 300
SELECT morbac.set_config('cache_ttl_seconds', '60');morbac.is_allowed(...) consults the cache first and falls back to
morbac.is_allowed_nocache(...), which always computes from scratch. Use the
nocache variant when debugging a surprising decision.
Invalidation
You never invalidate manually in normal operation. Triggers on every table
that can change a decision (user_roles, rules, user_rules, global_rules,
cross_org_rules, delegations, negative assignments, role hierarchy, system
principals, contexts, ...) call morbac.invalidate_cache(user_id, org_id)
with the narrowest scope they can prove:
- a role assignment change invalidates that user in that org;
- a rule change invalidates the whole org;
- hierarchy changes refresh the materialized closures and flush everything.
Manual controls exist for completeness:
SELECT morbac.invalidate_cache(:user_id, :org_id); -- narrow flush
SELECT morbac.invalidate_cache(); -- full flush (both args NULL)
SELECT morbac.cleanup_auth_cache(); -- purge expired rowsWhat the TTL really bounds
Trigger-based invalidation covers everything inside the database. What it
cannot see is the outside world changing under a
context evaluator: if business-hours flips at
19:00, cached true decisions may survive up to TTL seconds past the flip.
Size cache_ttl_seconds to the staleness your most dynamic context can
tolerate; keep contexts coarse-grained and the default TTL is fine.
cleanup_auth_cache() is housekeeping, not correctness: expired rows are
ignored by reads either way. Schedule it if the table grows large.