Deduplicate existing pipeline iids across partitions
What does this MR do and why?
Context
The unique index (project_id, iid, partition_id) on p_ci_pipelines only
enforces pipeline iid uniqueness within a partition, so a workflow race
could persist duplicate iids for the same project across partitions. Going
forward this is now prevented by the p_ci_pipeline_iids tracking table and its
uniqueness triggers, together with a backfill of existing iids. This MR is the
next step: a batched background migration that fixes the existing
duplicates.
This step was delayed for a few milestones because our CI BBMs were occupied with ci_builds_metadata but only one of those BBMs now remain, so this MR can proceed.
The affected data on GitLab.com is small and frozen since the triggers prevent any new duplicates:
- 20 projects affected
- 42,942 duplicated
(project_id, iid)pairs (85,884 rows) - at most 2 copies of any
(project_id, iid) - all duplicates are confined to partitions 100–107
Queries
Totals:
SELECT
COUNT(DISTINCT project_id) AS projects_count,
COUNT(*) AS distinct_dup_count,
SUM(dup_count) AS total_dup_count,
MAX(dup_count) AS max_dup_count_per_distinct_dup
FROM (
SELECT project_id, iid, COUNT(*) AS dup_count
FROM p_ci_pipelines
WHERE iid IS NOT NULL
GROUP BY project_id, iid
HAVING COUNT(*) > 1
) duplicates; projects_count | distinct_dup_count | total_dup_count | max_dup_count_per_distinct_dup
----------------+--------------------+-----------------+--------------------------------
20 | 42942 | 85884 | 2Distribution across partitions:
SELECT
partitions,
COUNT(*) AS dup_groups,
SUM(dup_count) AS dup_rows
FROM (
SELECT project_id, iid,
COUNT(*) AS dup_count,
array_agg(DISTINCT partition_id ORDER BY partition_id) AS partitions
FROM p_ci_pipelines
WHERE iid IS NOT NULL
GROUP BY project_id, iid
HAVING COUNT(*) > 1
) d
GROUP BY partitions
ORDER BY dup_groups DESC; partitions | dup_groups | dup_rows
------------+------------+----------
{101,102} | 12651 | 25302
{101,107} | 6527 | 13054
{101,104} | 5882 | 11764
{101,103} | 5464 | 10928
{100,102} | 4337 | 8674
{101,105} | 4147 | 8294
{101,106} | 3082 | 6164
{102,106} | 289 | 578
{102,104} | 200 | 400
{102,105} | 102 | 204
{102,107} | 79 | 158
{100,105} | 73 | 146
{100,106} | 38 | 76
{100,104} | 37 | 74
{100,103} | 27 | 54
{103,104} | 4 | 8
{102,103} | 3 | 6This MR
This implementation replaces the old draft from !224616 (closed). It implements iid generation in bulk instead of updating them one at a time.
Adds the DeduplicatePipelineIids batched background migration. The queueing
migration enqueues one BBM instance per p_ci_pipelines partition (skipping
empty partitions, and — on GitLab.com only — partitions newer than the
duplicates, partition_id > 107). Each instance:
- batches by
idwithin its single partition (so the heavy scan stays partition-local), - detects
(project_id, iid)values that also exist in a lower partition, so each duplicate group is handled once by its highest-partition copy, - reserves the iids it needs in bulk from
internal_ids(mirroringInternalId.generate, flushing and retrying if a stalelast_valuecollides), and - reassigns every copy of the duplicate via a partition-pruned bulk
UPDATE. All copies must be renumbered because the uniqueness trigger deletes the sharedp_ci_pipeline_iidsrow whenever any copy's iid changes.
References
- Resolves Prevent duplicate pipeline IIDs -- Next step: B... (#582339 - closed)
- Overall implementation steps: https://gitlab.com/gitlab-org/gitlab/-/issues/545167+
Database queries
See MR comments.
How to set up and validate locally
- Run
bundle exec rails db:migrate. - Confirm the BBMs were enqueued (one per non-empty partition):
SELECT table_name, job_arguments, min_value, max_value FROM batched_background_migrations WHERE job_class_name = 'DeduplicatePipelineIids'; - Migrate down and confirm they're removed:
VERSION=20260611192449 bundle exec rails db:migrate:down:ci VERSION=20260611192449 bundle exec rails db:migrate:down:main
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.