18.8: table_sync_function causes PG::UniqueViolation on git-upload-pack, breaking all CI pipelines after first run per day
### Summary After upgrading to GitLab 18.8.x, all CI pipeline jobs fail at the git clone step. Root cause: a PostgreSQL trigger `table_sync_function_c237afdf68()` introduced by a 18.8 batched background migration causes `PG::UniqueViolation` on every `POST /git-upload-pack` request except the first one per day per project. ### Steps to reproduce 1. Upgrade GitLab self-managed (Omnibus) to 18.8.x 2. Run a CI pipeline that clones a repository — first pipeline of the day succeeds 3. Run any subsequent pipeline for the same project the same day — it fails at git clone ### Example Project N/A (self-managed only, reproducible on any project after the first clone per day) ### What is the current *bug* behavior? All CI pipelines after the first one per day fail at git clone: ``` error: expected flush after ref listing fatal: the remote end hung up unexpectedly ``` GitLab returns HTTP 500 on `POST /git-upload-pack`. ### What is the expected *correct* behavior? All CI pipelines clone the repository successfully regardless of how many times they run per day. ### Relevant logs and/or screenshots From `production_json.log` (captured via correlation ID from workhorse log): ```json { "exception.class": "ActiveRecord::RecordNotUnique", "exception.message": "PG::UniqueViolation: ERROR: duplicate key value violates unique constraint \"index_project_daily_statistics_on_project_id_and_date\"\nDETAIL: Key (project_id, date)=(15, 2026-03-06) already exists.\nCONTEXT: SQL statement \"INSERT INTO project_daily_statistics_archived (\\\"id\\\", \\\"project_id\\\", \\\"fetch_count\\\", \\\"date\\\") VALUES (NEW.\\\"id\\\", NEW.\\\"project_id\\\", NEW.\\\"fetch_count\\\", NEW.\\\"date\\\")\"\n PL/pgSQL function table_sync_function_c237afdf68() line 12 at SQL statement", "controller": "Repositories::GitHttpController", "action": "git_upload_pack" } ``` Call chain: ``` Repositories::GitHttpController#git_upload_pack → FetchStatisticsIncrementService#execute → ProjectDailyStatistic.upsert (ON CONFLICT DO NOTHING — safe) → trigger table_sync_function_c237afdf68() fires on INSERT → INSERT INTO project_daily_statistics_archived ← missing ON CONFLICT DO NOTHING → PG::UniqueViolation on second fetch same day → HTTP 500 → git receives invalid response → "expected flush after ref listing" ``` ### Output of checks #### Results of GitLab environment info <details> <summary>Expand for output related to GitLab environment info</summary> <pre> GitLab version: 18.8.5-ee Installation type: omnibus (Docker) PostgreSQL: bundled </pre> </details> ### Possible fixes The trigger function `table_sync_function_c237afdf68()` is missing `ON CONFLICT DO NOTHING` in its INSERT branch. Current body (line 12): ```sql INSERT INTO project_daily_statistics_archived ("id", "project_id", "fetch_count", "date") VALUES (NEW."id", NEW."project_id", NEW."fetch_count", NEW."date"); ``` Fix — add `ON CONFLICT DO NOTHING`: ```sql INSERT INTO project_daily_statistics_archived ("id", "project_id", "fetch_count", "date") VALUES (NEW."id", NEW."project_id", NEW."fetch_count", NEW."date") ON CONFLICT DO NOTHING; ``` **Workaround** (apply directly in PostgreSQL until fix is released): ```sql CREATE OR REPLACE FUNCTION public.table_sync_function_c237afdf68() RETURNS trigger LANGUAGE plpgsql AS $function$ BEGIN IF (TG_OP = 'DELETE') THEN DELETE FROM project_daily_statistics_archived where "id" = OLD."id"; ELSIF (TG_OP = 'UPDATE') THEN UPDATE project_daily_statistics_archived SET "project_id" = NEW."project_id", "fetch_count" = NEW."fetch_count", "date" = NEW."date" WHERE project_daily_statistics_archived."id" = NEW."id"; ELSIF (TG_OP = 'INSERT') THEN INSERT INTO project_daily_statistics_archived ("id", "project_id", "fetch_count", "date") VALUES (NEW."id", NEW."project_id", NEW."fetch_count", NEW."date") ON CONFLICT DO NOTHING; END IF; RETURN NULL; END $function$; ```
issue