Commit bfcd9d9f authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'cablett-concurrent-validate-false' into 'master'

Optional skip validation for concurrent foreign key

See merge request gitlab-org/gitlab!22282
parents 1e61436a 69055aa6
......@@ -158,7 +158,7 @@ module Gitlab
# name - The name of the foreign key.
#
# rubocop:disable Gitlab/RailsLogger
def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, name: nil)
def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, name: nil, validate: true)
# Transactions would result in ALTER TABLE locks being held for the
# duration of the transaction, defeating the purpose of this method.
if transaction_open?
......@@ -197,10 +197,16 @@ module Gitlab
# Validate the existing constraint. This can potentially take a very
# long time to complete, but fortunately does not lock the source table
# while running.
# Disable this check by passing `validate: false` to the method call
# The check will be enforced for new data (inserts) coming in,
# but validating existing data is delayed.
#
# Note this is a no-op in case the constraint is VALID already
disable_statement_timeout do
execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{options[:name]};")
if validate
disable_statement_timeout do
execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{options[:name]};")
end
end
end
# rubocop:enable Gitlab/RailsLogger
......
......@@ -325,6 +325,25 @@ describe Gitlab::Database::MigrationHelpers do
end
end
end
describe 'validate option' do
let(:args) { [:projects, :users] }
let(:options) { { column: :user_id, on_delete: nil } }
context 'when validate is supplied with a falsey value' do
it_behaves_like 'skips validation', validate: false
it_behaves_like 'skips validation', validate: nil
end
context 'when validate is supplied with a truthy value' do
it_behaves_like 'performs validation', validate: true
it_behaves_like 'performs validation', validate: :whatever
end
context 'when validate is not supplied' do
it_behaves_like 'performs validation', {}
end
end
end
end
......
# frozen_string_literal: true
shared_examples 'skips validation' do |validation_option|
it 'skips validation' do
expect(model).not_to receive(:disable_statement_timeout)
expect(model).to receive(:execute).with(/ADD CONSTRAINT/)
expect(model).not_to receive(:execute).with(/VALIDATE CONSTRAINT/)
model.add_concurrent_foreign_key(*args, **options.merge(validation_option))
end
end
shared_examples 'performs validation' do |validation_option|
it 'performs validation' do
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).with(/RESET ALL/)
model.add_concurrent_foreign_key(*args, **options.merge(validation_option))
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment