Draft: Add lazy Redis cache for MR approval state checks
Summary
Introduces a lazy Redis-based caching layer for ApprovalState#approved?, gated behind the approval_state_redis_cache feature flag. This avoids repeated expensive DB queries for approval state on merge requests that haven't changed.
- Cache is purely lazy — no Redis writes happen until
approved?is actually called -
after_commitcallbacks on related models invalidate the cache when approval-relevant data changes - Merge strategies pass
use_cache: falseto ensure ground truth during actual merges
Relates to https://gitlab.com/gitlab-org/gitlab/-/issues/523857
Design
Redis Keys
-
approval_state:v1:mr:{id}— cached result ({ cached_at, approved }) -
approval_state:v1:relations:mr:{id}— SET of tracked relation keys -
approval_state:v1:relation:{type}:{id}— timestamp for each tracked relation
Cache Invalidation
| Trigger | Mechanism |
|---|---|
| Approval created/destroyed | Direct .delete via EE::Approval after_commit
|
| MR-level approval rule CRUD | Direct .delete via ApprovalMergeRequestRule after_commit
|
| Policy dismissal (upsert_all) | Direct .delete in DismissPolicyViolationsService
|
| Policy bypass event | Direct .delete in BypassMergeRequestService
|
| Project-level approval rule change | Relation timestamp update (staleness detected on next read) |
| HABTM users/groups on rules | Relation timestamp update via join table after_commit
|
| Group member change | Relation timestamp update via EE::Member after_commit
|
Protected branch code_owner_approval_required
|
Direct .delete for affected MRs in EE::ProtectedBranches::UpdateService
|
reset_approval_cache! |
Direct .delete in VisibleApprovable
|
use_cache: false Bypass
Threaded through mergeable? → params → CheckApprovedService → approved? → ApprovalState#approved?. Used by FromSourceBranch and FromTrainRef merge strategies to ensure ground truth at merge time.
Database
This MR introduces no new database queries. The Danger bot flagged .delete calls, but these are Redis DEL operations on Gitlab::Redis::Cache, not database operations. The only DB query is in EE::ProtectedBranches::UpdateService#invalidate_approval_state_caches which queries open MRs by target branch — this uses existing scopes and only runs when code_owner_approval_required changes on a protected branch (rare operation).
Testing
- 11 unit specs for
ApprovalStateCache - 4 integration specs for
ApprovalStatecaching behavior - Callback specs for all 10 invalidation paths
- 279 existing
ApprovalStatespecs pass with flag disabled by default - GDK end-to-end validation: approve/unapprove, rule CRUD, user/group changes, protected branch toggle, feature flag on/off, actual merge
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
- I have evaluated the MR acceptance checklist for this MR.