Fix compliance framework policy sync causing stale approval rules
What does this MR do and why?
Fixes race conditions in compliance framework policy sync that cause stale or missing ApprovalMergeRequestRule records to persist on open merge requests after rapid compliance framework changes.
Race conditions
- Deduplication drops critical events: The policy sync worker deduplicates on
(project, policy), discarding event data. When rapid framework changes produce competing events, the "removed" or "re-link" event is silently lost — producing orphaned or missing rules. Fix: ignore event data entirely and query current DB state to decide link vs. unlink. - Default branch changes can re-create deleted rules: Default branch changes trigger
MergeRequests::Refresh::ApprovalWorkerwhich iterates all project-level approval rules and upserts MR rules, running outside the policy sync flow. It can re-create rules that were just deleted by an unlink. - Concurrent INSERT during delete window:
MergeRequests::Refresh::ApprovalWorkercan load rules into memory before an unlink starts, then insert MR rules after the delete completes — the new rows are invisible to the deletion operation. - Default branch change blindly syncs inapplicable policies: Default branch change events sync all linked policies without checking scope applicability, perpetuating orphaned rules when the compliance framework handler was deduplicated away.
Database queries
#synchronize_approval_rules_from_target_project
-- https://console.postgres.ai/gitlab/gitlab-production-main/sessions/50814/commands/150678
SELECT
"approval_project_rules".*
FROM
"approval_project_rules"
WHERE
"approval_project_rules"."project_id" = 80934902
AND "approval_project_rules"."rule_type" = 2
AND ("approval_project_rules"."approval_policy_rule_id" IN (
SELECT
"approval_policy_rule_project_links"."approval_policy_rule_id"
FROM
"approval_policy_rule_project_links"
WHERE
"approval_policy_rule_project_links"."project_id" = 80934902)
OR "approval_project_rules"."approval_policy_rule_id" IS NULL);References
Code references
Root cause (before this MR):
sync_rules_for_compliance_framework_changed_eventfans out to ALL policies in the config viaconfig.security_policies.undeletedSyncProjectPolicyWorkerdedup key ignores event payloadsync_policy_for_compliance_frameworkdispatches onevent_typerather than current statesynchronize_approval_rules_from_target_projectsyncs ALL report_approver rules without checking link statussync_all_ruleshas no scope-applicability guard
How to set up and validate locally
Prerequisites
- Create a new top-level group and a contained project
- On the project-level, create the
developbranch and protect it - On the group-level, navigate to
Secure > Compliance center > Frameworksand create theSOC2andHIPAAframeworks - On the group-level, navigate to
Secure > Policiesand create the following merge request approval policies, substituting<FRAMEWORK_ID>respectively:
approval_policy:
- name: SOC2
enabled: true
policy_scope:
compliance_frameworks:
- id: <FRAMEWORK_ID>
actions:
- type: require_approval
approvals_required: 1
role_approvers:
- owner
rules:
- type: any_merge_request
branch_type: protected
commits: anyapproval_policy:
- name: HIPAA
enabled: true
policy_scope:
compliance_frameworks:
- id: <FRAMEWORK_ID>
actions:
- type: require_approval
approvals_required: 1
role_approvers:
- owner
rules:
- type: any_merge_request
branch_type: protected
commits: any- Open a new merge request on the project
- On the group-level or project-level, navigate to
Secure > Compliance center > Frameworksand assign one of the two frameworks to the project - Verify the merge request has only the HIPAA approval rule
- Swap frameworks via the compliance dashboard
- Change the project's default branch to increase Sidekiq contention
- Swap back both frameworks
- Reload the MR: it may show both rules (stale orphan) or zero rules (missing re-link).
Note: Since this is non-deterministic, you can use this reproduction script to attempt to trigger the race condition.
Verifying the fix
-
Enable the feature flag:
Feature.enable(:scoped_compliance_framework_policy_sync) -
Repeat the steps above and verify that stale rules no longer persist.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Edited by Dominic Bauer