Add checks for ValidatePushRulesConstraints
As a result of the gitlab-com/support/readiness/emergencies#4124 customer-retro let's introduce a check for the known item in 16.4.0 that the customer was impacted by.
✅ ℹ️
-
issue_url:https://gitlab.com/gitlab-org/gitlab/-/issues/423445 -
workaround_url:https://docs.gitlab.com/update/versions/gitlab_16_changes/#1640-
💡 This would benefit from a KB article.
-
7️⃣ The Seven
-
author_email_regex!97 (merged) -
branch_name_regex!105 -
commit_message_negative_regex -
commit_message_regex -
delete_branch_regex -
file_name_regex -
force_push_regex
Once this one is merged, the other six should be fairly straightforward.
-
😅 We did bump into a check file name collision. The dates are just meant to provide uniqueness so Duncan worked around this by adjust the check file names.- We discussed the possibility of using
YYYYMMDDHHMMSSor similar in the future to reduce the likelihood of collisions. - Making sure folks understand that the date in the check file name does not need to be tied to something specific is the path forward right now. (The dates don't matter for these in the way that they do with database migrations.)
- We discussed the possibility of using
Prototyping
SELECT id FROM push_rules WHERE LENGTH(author_email_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(branch_name_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(commit_message_negative_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(commit_message_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(delete_branch_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(file_name_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(force_push_regex) > 511;
SQL to cause this check not to pass
First DROP each CONSTRAINT:
gitlabhq_production=# ALTER TABLE push_rules DROP CONSTRAINT author_email_regex_size_constraint;
ALTER TABLE
gitlabhq_production=# ALTER TABLE push_rules DROP CONSTRAINT branch_name_regex_size_constraint ;
ALTER TABLE
gitlabhq_production=# ALTER TABLE push_rules DROP CONSTRAINT commit_message_negative_regex_size_constraint;
ALTER TABLE
gitlabhq_production=# ALTER TABLE push_rules DROP CONSTRAINT commit_message_regex_size_constraint;
ALTER TABLE
gitlabhq_production=# ALTER TABLE push_rules DROP CONSTRAINT delete_branch_regex_size_constraint;
ALTER TABLE
gitlabhq_production=# ALTER TABLE push_rules DROP CONSTRAINT file_name_regex_size_constraint;
ALTER TABLE
gitlabhq_production=# ALTER TABLE push_rules DROP CONSTRAINT force_push_regex_size_constraint ;
-- author_email_regex over 511 characters
INSERT INTO push_rules (id, author_email_regex, created_at, updated_at) VALUES (1, 'author_email ([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|', NOW(), NOW());
-- branch_name_regex over 511 characters
INSERT INTO push_rules (id, branch_name_regex, created_at, updated_at) VALUES (2, 'branch_name ([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|', NOW(), NOW());
-- commit_message_negative_regex over 511 characters
INSERT INTO push_rules (id, commit_message_negative_regex, created_at, updated_at) VALUES (3, 'commit_message_negative ([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|', NOW(), NOW());
-- commit_message_regex over 511 characters
INSERT INTO push_rules (id, commit_message_regex, created_at, updated_at) VALUES (4, 'commit_message ([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|', NOW(), NOW());
-- delete_branch_regex over 511 characters
INSERT INTO push_rules (id, delete_branch_regex, created_at, updated_at) VALUES (5, 'delete_branch ([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|', NOW(), NOW());
-- file_name_regex over 511 characters
INSERT INTO push_rules (id, file_name_regex, created_at, updated_at) VALUES (6, 'file_name ([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|', NOW(), NOW());
-- force_push_regex over 511 characters
INSERT INTO push_rules (id, force_push_regex, created_at, updated_at) VALUES (7, 'force_push ([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|([a-zA-Z0-9_\-\.]+)|', NOW(), NOW());
📚 From the docs...
Docs
-
You might encounter the following error while upgrading to GitLab 16.4 or later:
main: == 20230830084959 ValidatePushRulesConstraints: migrating ===================== main: -- execute("SET statement_timeout TO 0") main: -> 0.0002s main: -- execute("ALTER TABLE push_rules VALIDATE CONSTRAINT force_push_regex_size_constraint;") main: -> 0.0004s main: -- execute("RESET statement_timeout") main: -> 0.0003s main: -- execute("ALTER TABLE push_rules VALIDATE CONSTRAINT delete_branch_regex_size_constraint;") rails aborted! StandardError: An error has occurred, all later migrations canceled: PG::CheckViolation: ERROR: check constraint "delete_branch_regex_size_constraint" of relation "push_rules" is violated by some rowThese constraints might return an error:
author_email_regex_size_constraintbranch_name_regex_size_constraintcommit_message_negative_regex_size_constraintcommit_message_regex_size_constraintdelete_branch_regex_size_constraintfile_name_regex_size_constraintforce_push_regex_size_constraint
To fix the error, find the records in the
push_rulestable that exceed the 511 character limit.;; replace `delete_branch_regex` with a name of the field used in constraint SELECT id FROM push_rules WHERE LENGTH(delete_branch_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(author_email_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(branch_name_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(commit_message_negative_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(commit_message_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(delete_branch_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(file_name_regex) > 511;
SELECT id FROM push_rules WHERE LENGTH(force_push_regex) > 511;
To find out if a push rule belongs to a project, group, or instance, run this script in the Rails console:
# replace `delete_branch_regex` with a name of the field used in constraint
long_rules = PushRule.where("length(delete_branch_regex) > 511")
array = long_rules.map do |lr|
if lr.project
"Push rule with ID #{lr.id} is configured in a project #{lr.project.full_name}"
elsif lr.group
"Push rule with ID #{lr.id} is configured in a group #{lr.group.full_name}"
else
"Push rule with ID #{lr.id} is configured on the instance level"
end
end
puts "Total long rules: #{array.count}"
puts array.join("\n")
Reduce the value length of the regex field for affected push rules records, then retry the migration.
If you have too many affected push rules, and you can’t update them through the GitLab UI, contact GitLab support.