Approval-policy rules can silently fail to resync after an interrupted policy sync
Summary
Three related gaps in approval-policy resync (link_policy/unlink_policy/resync event handling) can leave a project without its MR approval-policy rules, with nothing in the application logs to explain why.
- Orphaned
scan_result_policy_readblocks all future resyncs. A row can be left behind by an interrupted resync (partial failure, retry, timeout) holding the same(config, project, policy_idx, rule_idx, action_idx)uniqueness slot as the row a later resync tries to create, but with a staleapproval_policy_rule_id. The "already exists?" guard inSecurity::ScanResultPolicies::ApprovalRules::CreateServicekeys offapproval_policy_rule_id, so it never finds the stale row, andcreate!raisesActiveRecord::RecordInvalidon the DB uniqueness validation instead. - Non-atomic unlink.
Security::Policy#unlink_project!deleted theApprovalPolicyRuleProjectLinkjoin row in its own transaction; the matchingApprovalProjectRulerow was deleted separately, later. Between the two, the rule row existed with no join row, andEE::MergeRequest#linked_report_approver_project_rulestreats that as "unlinked" and silently skips syncing it onto merge requests. - Silent discard.
Security::SyncProjectPolicyWorker'ssidekiq_retry_indiscards a job that raisedRecordInvalid, permanently for a resync event, with no retry, and never logged it. Combined with (1), a project can stop resyncing its approval rules indefinitely with zero trace in the application logs.
Impact
A merge request approval policy can go missing for an in-scope project with no error anywhere a support engineer or admin would normally look. Recovery requires an unrelated event to trigger a fresh resync, and even that isn't guaranteed to succeed if the retry hits the same orphaned row.
Fix
!252506 (closed) fixes all three: reclaim a stale scan_result_policy_read by its real uniqueness key instead of raising, delete the project's approval_project_rules in the same transaction as the link, and log before discarding a RecordInvalid job.
Related
- #557737 — adjacent, acknowledged report of the same failure class (approval rules silently not created/updated on policy sync)
Edited by 🤖 GitLab Bot 🤖