Skip to content

ScheduleBackfillPushRulesIdInProjects fails if there are multiple rows in application_settings

On my local instance that installed the latest nightly build, I just tried running gitlab-rake db:migrate, and it failed due to the migration introduced in !28286 (merged):

# sudo gitlab-rake db:migrate
== 20200204113225 ScheduleRecalculateProjectAuthorizationsThirdRun: migrating =
-- Scheduling RecalculateProjectAuthorizationsWithMinMaxUserId jobs
== 20200204113225 ScheduleRecalculateProjectAuthorizationsThirdRun: migrated (0.2954s) 

== 20200325104755 AddPushRulesIdToProjectSettings: migrating ==================
-- add_column(:project_settings, :push_rule_id, :bigint)
   -> 0.0355s
== 20200325104755 AddPushRulesIdToProjectSettings: migrated (0.0864s) =========

== 20200325104756 AddPushRulesForeignKeyToProjectSettings: migrating ==========
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:project_settings, :push_rule_id, {:unique=>true, :algorithm=>:concurrently})
   -> 0.0069s
-- execute("SET statement_timeout TO 0")
   -> 0.0007s
-- add_index(:project_settings, :push_rule_id, {:unique=>true, :algorithm=>:concurrently})
   -> 0.1125s
-- execute("RESET ALL")
   -> 0.0010s
-- transaction_open?()
   -> 0.0000s
-- foreign_keys(:project_settings)
   -> 0.0456s
-- execute("ALTER TABLE project_settings\nADD CONSTRAINT fk_413a953e20\nFOREIGN KEY (push_rule_id)\nREFERENCES push_rules (id)\nON DELETE CASCADE\nNOT VALID;\n")
   -> 0.1017s
-- execute("SET statement_timeout TO 0")
   -> 0.0007s
-- execute("ALTER TABLE project_settings VALIDATE CONSTRAINT fk_413a953e20;")
   -> 0.0703s
-- execute("RESET ALL")
   -> 0.0009s
== 20200325104756 AddPushRulesForeignKeyToProjectSettings: migrated (0.3489s) =

== 20200325104833 AddPushRulesIdToApplicationSettings: migrating ==============
-- add_column(:application_settings, :push_rule_id, :bigint)
   -> 0.0017s
== 20200325104833 AddPushRulesIdToApplicationSettings: migrated (0.0042s) =====

== 20200325104834 AddPushRulesForeignKeyToApplicationSettings: migrating ======
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:application_settings, :push_rule_id, {:unique=>true, :algorithm=>:concurrently})
   -> 0.0065s
-- execute("SET statement_timeout TO 0")
   -> 0.0005s
-- add_index(:application_settings, :push_rule_id, {:unique=>true, :algorithm=>:concurrently})
   -> 0.0130s
-- execute("RESET ALL")
   -> 0.0005s
-- transaction_open?()
   -> 0.0000s
-- foreign_keys(:application_settings)
   -> 0.0060s
-- execute("ALTER TABLE application_settings\nADD CONSTRAINT fk_693b8795e4\nFOREIGN KEY (push_rule_id)\nREFERENCES push_rules (id)\nON DELETE SET NULL\nNOT VALID;\n")
   -> 0.0107s
-- execute("SET statement_timeout TO 0")
   -> 0.0007s
-- execute("ALTER TABLE application_settings VALIDATE CONSTRAINT fk_693b8795e4;")
   -> 0.0035s
-- execute("RESET ALL")
   -> 0.0006s
== 20200325104834 AddPushRulesForeignKeyToApplicationSettings: migrated (0.0474s) 

== 20200325111432 AddIssuesCreateLimitToApplicationSettings: migrating ========
-- add_column(:application_settings, :issues_create_limit, :integer, {:default=>300, :null=>false})
   -> 0.0091s
== 20200325111432 AddIssuesCreateLimitToApplicationSettings: migrated (0.0093s) 

== 20200325162730 ScheduleBackfillPushRulesIdInProjects: migrating ============
-- execute("UPDATE application_settings SET push_rule_id = 9")
rake aborted!
StandardError: An error has occurred, all later migrations canceled:

PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_application_settings_on_push_rule_id"
DETAIL:  Key (push_rule_id)=(9) already exists.
/opt/gitlab/embedded/service/gitlab-rails/db/post_migrate/20200325162730_schedule_backfill_push_rules_id_in_projects.rb:21:in `up'
/opt/gitlab/embedded/bin/bundle:23:in `load'
/opt/gitlab/embedded/bin/bundle:23:in `<main>'

Caused by:
ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_application_settings_on_push_rule_id"
DETAIL:  Key (push_rule_id)=(9) already exists.
/opt/gitlab/embedded/service/gitlab-rails/db/post_migrate/20200325162730_schedule_backfill_push_rules_id_in_projects.rb:21:in `up'
/opt/gitlab/embedded/bin/bundle:23:in `load'
/opt/gitlab/embedded/bin/bundle:23:in `<main>'

Caused by:
PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_application_settings_on_push_rule_id"
DETAIL:  Key (push_rule_id)=(9) already exists.
/opt/gitlab/embedded/service/gitlab-rails/db/post_migrate/20200325162730_schedule_backfill_push_rules_id_in_projects.rb:21:in `up'
/opt/gitlab/embedded/bin/bundle:23:in `load'
/opt/gitlab/embedded/bin/bundle:23:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

This looks like it happens if the instance happens to have duplicate application_settings. This happened long ago when we had a bug where multiple application_settings were inserted.

gitlabhq_production=> select id, push_rule_id, created_at, updated_at from application_settings order by id;
 id | push_rule_id |         created_at         |         updated_at         
----+--------------+----------------------------+----------------------------
  1 |              | 2017-02-06 20:49:09.216214 | 2019-12-09 21:37:29.287837
  2 |              | 2017-02-06 20:49:09.254403 | 2019-12-09 21:37:29.291537
  3 |              | 2017-02-06 20:49:09.372803 | 2019-12-09 21:37:29.297227
  4 |              | 2017-02-06 20:49:09.4092   | 2019-12-09 21:37:29.294251
  5 |              | 2017-02-06 20:49:09.519043 | 2020-03-26 17:25:50.787984
(5 rows)

Before, it wasn't a big deal since we always took the latest id, but now that we are actually depending on this table to have one row, the unique index is failing.

dev.gitlab.org also has this problem:

gitlabhq_production=> select id, created_at, updated_at from application_settings order by id;
 id |         created_at         |         updated_at
----+----------------------------+----------------------------
  1 | 2015-01-09 03:00:45.55412  | 2019-12-04 03:22:32.425003
  2 | 2017-07-07 09:42:24.30994  | 2019-12-04 03:22:32.401102
  3 | 2017-07-07 09:42:24.598087 | 2019-12-04 03:22:32.40349
  4 | 2017-07-07 09:42:24.800332 | 2019-12-04 03:22:32.405854
  5 | 2017-07-07 09:42:25.2129   | 2019-12-04 03:22:32.389287
  6 | 2017-07-07 09:42:25.223187 | 2019-12-04 03:22:32.413069
  7 | 2017-07-07 09:44:22.13386  | 2019-12-04 03:22:32.415555
  8 | 2017-07-07 09:44:22.150257 | 2019-12-04 03:22:32.3674
  9 | 2017-07-07 09:44:22.280643 | 2019-12-04 03:22:32.417921
 10 | 2017-07-07 09:44:22.399299 | 2019-12-04 03:22:32.420296
 11 | 2017-07-07 09:44:23.028125 | 2019-12-04 03:22:32.391625
 12 | 2017-07-07 09:44:23.098763 | 2019-12-04 03:22:32.408206
 13 | 2017-07-08 09:48:36.270305 | 2019-12-04 03:22:32.369904
 14 | 2017-07-08 09:48:36.370462 | 2019-12-04 03:22:32.410581
 15 | 2017-07-08 09:48:36.88248  | 2019-12-04 03:22:32.379547
 16 | 2017-07-08 09:48:36.970432 | 2019-12-04 03:22:32.37225
 17 | 2017-07-08 12:24:20.00659  | 2019-12-04 03:22:32.364074
 18 | 2017-07-08 12:24:20.226801 | 2019-12-04 03:22:32.393988
 19 | 2017-07-08 12:24:20.227876 | 2019-12-04 03:22:32.374616
 20 | 2017-07-08 12:24:20.542347 | 2019-12-04 03:22:32.382088
 21 | 2017-07-08 12:24:20.859801 | 2019-12-04 03:22:32.377013
 22 | 2017-07-08 13:03:35.820201 | 2019-12-04 03:22:32.384526
 23 | 2017-07-08 13:03:35.948099 | 2019-12-04 03:22:32.396392
 24 | 2017-07-08 13:03:36.053212 | 2019-12-04 03:22:32.42266
 25 | 2017-07-08 13:03:36.13958  | 2019-12-04 03:22:32.386914
 26 | 2017-07-08 13:03:36.187089 | 2019-12-04 03:22:32.398749
 27 | 2017-07-08 13:03:36.460217 | 2019-12-04 03:22:32.427465
(27 rows)

I think to fix this problem we might want to drop all other rows except the max(id).

/cc: @mksionek, @jprovaznik

Edited by Stan Hu