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_commit callbacks on related models invalidate the cache when approval-relevant data changes
  • Merge strategies pass use_cache: false to 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 → CheckApprovedServiceapproved?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 ApprovalState caching behavior
  • Callback specs for all 10 invalidation paths
  • 279 existing ApprovalState specs 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.

Merge request reports

Loading