Skip to content
Snippets Groups Projects
Verified Commit 79696f5b authored by Yorick Peterse's avatar Yorick Peterse
Browse files

Hash concurrent foreign key names similar to Rails

This was initially not implemented simply because I forgot about the
size limit of constraint names in PostgreSQL (63 bytes). Using the old
technique we can't add foreign keys for certain tables. For example,
adding a foreign key on
protected_branch_merge_access_levels.protected_branch_id would lead to
the following key name:

    fk_protected_branch_merge_access_levels_protected_branches_protected_branch_id

This key is 78 bytes long, thus violating the PostgreSQL size
requirements.

The hashing strategy is copied from Rails' foreign_key_name() method,
which unfortunately is private and subject to change without notice.
parent 459a97d4
No related branches found
No related tags found
Loading
Pipeline #
Loading
Loading
@@ -54,7 +54,7 @@ module Gitlab
 
disable_statement_timeout
 
key_name = "fk_#{source}_#{target}_#{column}"
key_name = concurrent_foreign_key_name(source, column)
 
# Using NOT VALID allows us to create a key without immediately
# validating it. This means we keep the ALTER TABLE lock only for a
Loading
Loading
@@ -74,6 +74,15 @@ module Gitlab
execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};")
end
 
# Returns the name for a concurrent foreign key.
#
# PostgreSQL constraint names have a limit of 63 bytes. The logic used
# here is based on Rails' foreign_key_name() method, which unfortunately
# is private so we can't rely on it directly.
def concurrent_foreign_key_name(table, column)
"fk_#{Digest::SHA256.hexdigest("#{table}_#{column}_fk").first(10)}"
end
# Long-running migrations may take more than the timeout allowed by
# the database. Disable the session's statement timeout to ensure
# migrations don't get killed prematurely. (PostgreSQL only)
Loading
Loading
Loading
Loading
@@ -101,6 +101,16 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
end
 
describe '#concurrent_foreign_key_name' do
it 'returns the name for a foreign key' do
name = model.concurrent_foreign_key_name(:this_is_a_very_long_table_name,
:with_a_very_long_column_name)
expect(name).to be_an_instance_of(String)
expect(name.length).to eq(13)
end
end
describe '#disable_statement_timeout' do
context 'using PostgreSQL' do
it 'disables statement timeouts' do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment