Switch write call sites from push_rules to project_push_rules
What does this MR do and why?
As part of the Cells 1.0 initiative, the push_rules table is being restructured so that each entity type (organization, group, project) has its own dedicated table. The organization and group migrations are already complete. The project push rules table switch is split into multiple parts. Implementation Plan:
- Convert reads - Switch read call sites from
push_rulestoproject_push_rules. - Convert writes - Switch write call sites from
push_rulestoproject_push_rules.👈 (this work) - Roll out FFs - Gradual rollout for reads, 100% at once for writes.
- Clean up FFs - Remove feature flag conditionals.
- Remove triggers - Remove DB sync triggers between the two tables.
Where project push rules writes happen:
-
1.
ee/app/controllers/ee/projects/settings/repository_controller.rb
When it's used: When a user visits the repository settings page (creates empty push rule if none exists) -
2.
ee/app/services/push_rules/create_or_update_service.rb
When it's used: When push rules are updated via UI form or API
(/api/v4/projects/:id/push_rule) -
3.
ee/app/services/push_rules/create_predefined_rule_service.rb
When it's used: When a project is created and inherits push rules fromorganization/group -
4.
lib/gitlab/github_import/importer/protected_branch_importer.rb
When it's used: When importing a project from GitHub -
5.
ee/lib/api/project_push_rule.rbWhen it's used: When deleting push rule via API
Dual-Write Approach
There was a discussion(stemmed from here) on how to switch the write operations, and we decided to perform dual writes to both tables (push_rules(old) and project_push_rules(new)).
1. How does the write switch work?
When FF write_project_push_rules is enabled:
- Write to project_push_rules (new table) first
- Log errors if something goes wrong
- Then write to push_rules (old table)
2. Why still write to the old table when FF is enabled?
To prevent potential data loss. If the new table write fails, the old table write ensures data is still persisted.
3. But there's a trigger that syncs push_rules → project_push_rules, right?
Yes. So even if the new table write fails, the trigger will sync from the old table write - the failure is masked silently. That's why we log errors before writing to the old table. This gives us visibility into new write path issues while ensuring no data is lost.
4. If all goes well, doesn't this cause duplicate writes to the new table?
Yes - the new table write succeeds, then the trigger fires and writes again. However, the trigger handles conflicts on project_id by updating the existing record. This is some resource overhead, but compared to the risk of data loss, it's a reasonable tradeoff. Push rule updates are also infrequent - users don't update them daily.
References
Issue: #588805 (closed)
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.