Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-16 21:18:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-16 21:18:33 +0300
commitf64a639bcfa1fc2bc89ca7db268f594306edfd7c (patch)
treea2c3c2ebcc3b45e596949db485d6ed18ffaacfa1 /spec/lib/gitlab/database
parentbfbc3e0d6583ea1a91f627528bedc3d65ba4b10f (diff)
Add latest changes from gitlab-org/gitlab@13-10-stable-eev13.10.0-rc40
Diffstat (limited to 'spec/lib/gitlab/database')
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_job_spec.rb50
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb160
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb70
-rw-r--r--spec/lib/gitlab/database/background_migration/scheduler_spec.rb182
-rw-r--r--spec/lib/gitlab/database/bulk_update_spec.rb36
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb28
-rw-r--r--spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb116
-rw-r--r--spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb68
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb31
-rw-r--r--spec/lib/gitlab/database/similarity_score_spec.rb2
10 files changed, 708 insertions, 35 deletions
diff --git a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
new file mode 100644
index 00000000000..1020aafcf08
--- /dev/null
+++ b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model do
+ it_behaves_like 'having unique enum values'
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:batched_migration).with_foreign_key(:batched_background_migration_id) }
+ end
+
+ describe 'delegated batched_migration attributes' do
+ let(:batched_job) { build(:batched_background_migration_job) }
+ let(:batched_migration) { batched_job.batched_migration }
+
+ describe '#migration_aborted?' do
+ before do
+ batched_migration.status = :aborted
+ end
+
+ it 'returns the migration aborted?' do
+ expect(batched_job.migration_aborted?).to eq(batched_migration.aborted?)
+ end
+ end
+
+ describe '#migration_job_class' do
+ it 'returns the migration job_class' do
+ expect(batched_job.migration_job_class).to eq(batched_migration.job_class)
+ end
+ end
+
+ describe '#migration_table_name' do
+ it 'returns the migration table_name' do
+ expect(batched_job.migration_table_name).to eq(batched_migration.table_name)
+ end
+ end
+
+ describe '#migration_column_name' do
+ it 'returns the migration column_name' do
+ expect(batched_job.migration_column_name).to eq(batched_migration.column_name)
+ end
+ end
+
+ describe '#migration_job_arguments' do
+ it 'returns the migration job_arguments' do
+ expect(batched_job.migration_job_arguments).to eq(batched_migration.job_arguments)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
new file mode 100644
index 00000000000..f4a939e7c1f
--- /dev/null
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -0,0 +1,160 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :model do
+ it_behaves_like 'having unique enum values'
+
+ describe 'associations' do
+ it { is_expected.to have_many(:batched_jobs).with_foreign_key(:batched_background_migration_id) }
+
+ describe '#last_job' do
+ let!(:batched_migration) { create(:batched_background_migration) }
+ let!(:batched_job1) { create(:batched_background_migration_job, batched_migration: batched_migration) }
+ let!(:batched_job2) { create(:batched_background_migration_job, batched_migration: batched_migration) }
+
+ it 'returns the most recent (in order of id) batched job' do
+ expect(batched_migration.last_job).to eq(batched_job2)
+ end
+ end
+ end
+
+ describe '.queue_order' do
+ let!(:migration1) { create(:batched_background_migration) }
+ let!(:migration2) { create(:batched_background_migration) }
+ let!(:migration3) { create(:batched_background_migration) }
+
+ it 'returns batched migrations ordered by their id' do
+ expect(described_class.queue_order.all).to eq([migration1, migration2, migration3])
+ end
+ end
+
+ describe '#interval_elapsed?' do
+ context 'when the migration has no last_job' do
+ let(:batched_migration) { build(:batched_background_migration) }
+
+ it 'returns true' do
+ expect(batched_migration.interval_elapsed?).to eq(true)
+ end
+ end
+
+ context 'when the migration has a last_job' do
+ let(:interval) { 2.minutes }
+ let(:batched_migration) { create(:batched_background_migration, interval: interval) }
+
+ context 'when the last_job is less than an interval old' do
+ it 'returns false' do
+ freeze_time do
+ create(:batched_background_migration_job,
+ batched_migration: batched_migration,
+ created_at: Time.current - 1.minute)
+
+ expect(batched_migration.interval_elapsed?).to eq(false)
+ end
+ end
+ end
+
+ context 'when the last_job is exactly an interval old' do
+ it 'returns true' do
+ freeze_time do
+ create(:batched_background_migration_job,
+ batched_migration: batched_migration,
+ created_at: Time.current - 2.minutes)
+
+ expect(batched_migration.interval_elapsed?).to eq(true)
+ end
+ end
+ end
+
+ context 'when the last_job is more than an interval old' do
+ it 'returns true' do
+ freeze_time do
+ create(:batched_background_migration_job,
+ batched_migration: batched_migration,
+ created_at: Time.current - 3.minutes)
+
+ expect(batched_migration.interval_elapsed?).to eq(true)
+ end
+ end
+ end
+ end
+ end
+
+ describe '#create_batched_job!' do
+ let(:batched_migration) { create(:batched_background_migration) }
+
+ it 'creates a batched_job with the correct batch configuration' do
+ batched_job = batched_migration.create_batched_job!(1, 5)
+
+ expect(batched_job).to have_attributes(
+ min_value: 1,
+ max_value: 5,
+ batch_size: batched_migration.batch_size,
+ sub_batch_size: batched_migration.sub_batch_size)
+ end
+ end
+
+ describe '#next_min_value' do
+ let!(:batched_migration) { create(:batched_background_migration) }
+
+ context 'when a previous job exists' do
+ let!(:batched_job) { create(:batched_background_migration_job, batched_migration: batched_migration) }
+
+ it 'returns the next value after the previous maximum' do
+ expect(batched_migration.next_min_value).to eq(batched_job.max_value + 1)
+ end
+ end
+
+ context 'when a previous job does not exist' do
+ it 'returns the migration minimum value' do
+ expect(batched_migration.next_min_value).to eq(batched_migration.min_value)
+ end
+ end
+ end
+
+ describe '#job_class' do
+ let(:job_class) { Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob }
+ let(:batched_migration) { build(:batched_background_migration) }
+
+ it 'returns the class of the job for the migration' do
+ expect(batched_migration.job_class).to eq(job_class)
+ end
+ end
+
+ describe '#batch_class' do
+ let(:batch_class) { Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy}
+ let(:batched_migration) { build(:batched_background_migration) }
+
+ it 'returns the class of the batch strategy for the migration' do
+ expect(batched_migration.batch_class).to eq(batch_class)
+ end
+ end
+
+ shared_examples_for 'an attr_writer that demodulizes assigned class names' do |attribute_name|
+ let(:batched_migration) { build(:batched_background_migration) }
+
+ context 'when a module name exists' do
+ it 'removes the module name' do
+ batched_migration.public_send(:"#{attribute_name}=", '::Foo::Bar')
+
+ expect(batched_migration[attribute_name]).to eq('Bar')
+ end
+ end
+
+ context 'when a module name does not exist' do
+ it 'does not change the given class name' do
+ batched_migration.public_send(:"#{attribute_name}=", 'Bar')
+
+ expect(batched_migration[attribute_name]).to eq('Bar')
+ end
+ end
+ end
+
+ describe '#job_class_name=' do
+ it_behaves_like 'an attr_writer that demodulizes assigned class names', :job_class_name
+ end
+
+ describe '#batch_class_name=' do
+ it_behaves_like 'an attr_writer that demodulizes assigned class names', :batch_class_name
+ end
+end
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
new file mode 100644
index 00000000000..17cceb35ff7
--- /dev/null
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '#perform' do
+ let(:migration_wrapper) { described_class.new }
+ let(:job_class) { Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob }
+
+ let_it_be(:active_migration) { create(:batched_background_migration, :active, job_arguments: [:id, :other_id]) }
+
+ let!(:job_record) { create(:batched_background_migration_job, batched_migration: active_migration) }
+
+ it 'runs the migration job' do
+ expect_next_instance_of(job_class) do |job_instance|
+ expect(job_instance).to receive(:perform).with(1, 10, 'events', 'id', 1, 'id', 'other_id')
+ end
+
+ migration_wrapper.perform(job_record)
+ end
+
+ it 'updates the the tracking record in the database' do
+ expect(job_record).to receive(:update!).with(hash_including(attempts: 1, status: :running)).and_call_original
+
+ freeze_time do
+ migration_wrapper.perform(job_record)
+
+ reloaded_job_record = job_record.reload
+
+ expect(reloaded_job_record).not_to be_pending
+ expect(reloaded_job_record.attempts).to eq(1)
+ expect(reloaded_job_record.started_at).to eq(Time.current)
+ end
+ end
+
+ context 'when the migration job does not raise an error' do
+ it 'marks the tracking record as succeeded' do
+ expect_next_instance_of(job_class) do |job_instance|
+ expect(job_instance).to receive(:perform).with(1, 10, 'events', 'id', 1, 'id', 'other_id')
+ end
+
+ freeze_time do
+ migration_wrapper.perform(job_record)
+
+ reloaded_job_record = job_record.reload
+
+ expect(reloaded_job_record).to be_succeeded
+ expect(reloaded_job_record.finished_at).to eq(Time.current)
+ end
+ end
+ end
+
+ context 'when the migration job raises an error' do
+ it 'marks the tracking record as failed before raising the error' do
+ expect_next_instance_of(job_class) do |job_instance|
+ expect(job_instance).to receive(:perform)
+ .with(1, 10, 'events', 'id', 1, 'id', 'other_id')
+ .and_raise(RuntimeError, 'Something broke!')
+ end
+
+ freeze_time do
+ expect { migration_wrapper.perform(job_record) }.to raise_error(RuntimeError, 'Something broke!')
+
+ reloaded_job_record = job_record.reload
+
+ expect(reloaded_job_record).to be_failed
+ expect(reloaded_job_record.finished_at).to eq(Time.current)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/background_migration/scheduler_spec.rb b/spec/lib/gitlab/database/background_migration/scheduler_spec.rb
new file mode 100644
index 00000000000..ba745acdf8a
--- /dev/null
+++ b/spec/lib/gitlab/database/background_migration/scheduler_spec.rb
@@ -0,0 +1,182 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::BackgroundMigration::Scheduler, '#perform' do
+ let(:scheduler) { described_class.new }
+
+ shared_examples_for 'it has no jobs to run' do
+ it 'does not create and run a migration job' do
+ test_wrapper = double('test wrapper')
+
+ expect(test_wrapper).not_to receive(:perform)
+
+ expect do
+ scheduler.perform(migration_wrapper: test_wrapper)
+ end.not_to change { Gitlab::Database::BackgroundMigration::BatchedJob.count }
+ end
+ end
+
+ context 'when there are no active migrations' do
+ let!(:migration) { create(:batched_background_migration, :finished) }
+
+ it_behaves_like 'it has no jobs to run'
+ end
+
+ shared_examples_for 'it has completed the migration' do
+ it 'marks the migration as finished' do
+ relation = Gitlab::Database::BackgroundMigration::BatchedMigration.finished.where(id: first_migration.id)
+
+ expect { scheduler.perform }.to change { relation.count }.by(1)
+ end
+ end
+
+ context 'when there are active migrations' do
+ let!(:first_migration) { create(:batched_background_migration, :active, batch_size: 2) }
+ let!(:last_migration) { create(:batched_background_migration, :active) }
+
+ let(:job_relation) do
+ Gitlab::Database::BackgroundMigration::BatchedJob.where(batched_background_migration_id: first_migration.id)
+ end
+
+ context 'when the migration interval has not elapsed' do
+ before do
+ expect_next_found_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigration) do |migration|
+ expect(migration).to receive(:interval_elapsed?).and_return(false)
+ end
+ end
+
+ it_behaves_like 'it has no jobs to run'
+ end
+
+ context 'when the interval has elapsed' do
+ before do
+ expect_next_found_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigration) do |migration|
+ expect(migration).to receive(:interval_elapsed?).and_return(true)
+ end
+ end
+
+ context 'when the first migration has no previous jobs' do
+ context 'when the migration has batches to process' do
+ let!(:event1) { create(:event) }
+ let!(:event2) { create(:event) }
+ let!(:event3) { create(:event) }
+
+ it 'runs the job for the first batch' do
+ first_migration.update!(min_value: event1.id, max_value: event3.id)
+
+ expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper) do |wrapper|
+ expect(wrapper).to receive(:perform).and_wrap_original do |_, job_record|
+ expect(job_record).to eq(job_relation.first)
+ end
+ end
+
+ expect { scheduler.perform }.to change { job_relation.count }.by(1)
+
+ expect(job_relation.first).to have_attributes(
+ min_value: event1.id,
+ max_value: event2.id,
+ batch_size: first_migration.batch_size,
+ sub_batch_size: first_migration.sub_batch_size)
+ end
+ end
+
+ context 'when the migration has no batches to process' do
+ it_behaves_like 'it has no jobs to run'
+ it_behaves_like 'it has completed the migration'
+ end
+ end
+
+ context 'when the first migration has previous jobs' do
+ let!(:event1) { create(:event) }
+ let!(:event2) { create(:event) }
+ let!(:event3) { create(:event) }
+
+ let!(:previous_job) do
+ create(:batched_background_migration_job,
+ batched_migration: first_migration,
+ min_value: event1.id,
+ max_value: event2.id,
+ batch_size: 2,
+ sub_batch_size: 1)
+ end
+
+ context 'when the migration is ready to process another job' do
+ it 'runs the migration job for the next batch' do
+ first_migration.update!(min_value: event1.id, max_value: event3.id)
+
+ expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper) do |wrapper|
+ expect(wrapper).to receive(:perform).and_wrap_original do |_, job_record|
+ expect(job_record).to eq(job_relation.last)
+ end
+ end
+
+ expect { scheduler.perform }.to change { job_relation.count }.by(1)
+
+ expect(job_relation.last).to have_attributes(
+ min_value: event3.id,
+ max_value: event3.id,
+ batch_size: first_migration.batch_size,
+ sub_batch_size: first_migration.sub_batch_size)
+ end
+ end
+
+ context 'when the migration has no batches remaining' do
+ let!(:final_job) do
+ create(:batched_background_migration_job,
+ batched_migration: first_migration,
+ min_value: event3.id,
+ max_value: event3.id,
+ batch_size: 2,
+ sub_batch_size: 1)
+ end
+
+ it_behaves_like 'it has no jobs to run'
+ it_behaves_like 'it has completed the migration'
+ end
+ end
+
+ context 'when the bounds of the next batch exceed the migration maximum value' do
+ let!(:events) { create_list(:event, 3) }
+ let(:event1) { events[0] }
+ let(:event2) { events[1] }
+
+ context 'when the batch maximum exceeds the migration maximum' do
+ it 'clamps the batch maximum to the migration maximum' do
+ first_migration.update!(batch_size: 5, min_value: event1.id, max_value: event2.id)
+
+ expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper) do |wrapper|
+ expect(wrapper).to receive(:perform)
+ end
+
+ expect { scheduler.perform }.to change { job_relation.count }.by(1)
+
+ expect(job_relation.first).to have_attributes(
+ min_value: event1.id,
+ max_value: event2.id,
+ batch_size: first_migration.batch_size,
+ sub_batch_size: first_migration.sub_batch_size)
+ end
+ end
+
+ context 'when the batch minimum exceeds the migration maximum' do
+ let!(:previous_job) do
+ create(:batched_background_migration_job,
+ batched_migration: first_migration,
+ min_value: event1.id,
+ max_value: event2.id,
+ batch_size: 5,
+ sub_batch_size: 1)
+ end
+
+ before do
+ first_migration.update!(batch_size: 5, min_value: 1, max_value: event2.id)
+ end
+
+ it_behaves_like 'it has no jobs to run'
+ it_behaves_like 'it has completed the migration'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/bulk_update_spec.rb b/spec/lib/gitlab/database/bulk_update_spec.rb
index f2a7d6e69d8..dbafada26ca 100644
--- a/spec/lib/gitlab/database/bulk_update_spec.rb
+++ b/spec/lib/gitlab/database/bulk_update_spec.rb
@@ -13,8 +13,8 @@ RSpec.describe Gitlab::Database::BulkUpdate do
i_a, i_b = create_list(:issue, 2)
{
- i_a => { title: 'Issue a' },
- i_b => { title: 'Issue b' }
+ i_a => { title: 'Issue a' },
+ i_b => { title: 'Issue b' }
}
end
@@ -51,7 +51,7 @@ RSpec.describe Gitlab::Database::BulkUpdate do
it 'is possible to update all objects in a single query' do
users = create_list(:user, 3)
- mapping = users.zip(%w(foo bar baz)).to_h do |u, name|
+ mapping = users.zip(%w[foo bar baz]).to_h do |u, name|
[u, { username: name, admin: true }]
end
@@ -61,13 +61,13 @@ RSpec.describe Gitlab::Database::BulkUpdate do
# We have optimistically updated the values
expect(users).to all(be_admin)
- expect(users.map(&:username)).to eq(%w(foo bar baz))
+ expect(users.map(&:username)).to eq(%w[foo bar baz])
users.each(&:reset)
# The values are correct on reset
expect(users).to all(be_admin)
- expect(users.map(&:username)).to eq(%w(foo bar baz))
+ expect(users.map(&:username)).to eq(%w[foo bar baz])
end
it 'is possible to update heterogeneous sets' do
@@ -79,8 +79,8 @@ RSpec.describe Gitlab::Database::BulkUpdate do
mapping = {
mr_a => { title: 'MR a' },
- i_a => { title: 'Issue a' },
- i_b => { title: 'Issue b' }
+ i_a => { title: 'Issue a' },
+ i_b => { title: 'Issue b' }
}
expect do
@@ -99,8 +99,8 @@ RSpec.describe Gitlab::Database::BulkUpdate do
i_a, i_b = create_list(:issue, 2)
mapping = {
- i_a => { title: 'Issue a' },
- i_b => { title: 'Issue b' }
+ i_a => { title: 'Issue a' },
+ i_b => { title: 'Issue b' }
}
described_class.execute(%i[title], mapping)
@@ -113,23 +113,19 @@ RSpec.describe Gitlab::Database::BulkUpdate do
include_examples 'basic functionality'
context 'when prepared statements are configured differently to the normal test environment' do
- # rubocop: disable RSpec/LeakyConstantDeclaration
- # This cop is disabled because you cannot call establish_connection on
- # an anonymous class.
- class ActiveRecordBasePreparedStatementsInverted < ActiveRecord::Base
- def self.abstract_class?
- true # So it gets its own connection
+ before do
+ klass = Class.new(ActiveRecord::Base) do
+ def self.abstract_class?
+ true # So it gets its own connection
+ end
end
- end
- # rubocop: enable RSpec/LeakyConstantDeclaration
- before_all do
+ stub_const('ActiveRecordBasePreparedStatementsInverted', klass)
+
c = ActiveRecord::Base.connection.instance_variable_get(:@config)
inverted = c.merge(prepared_statements: !ActiveRecord::Base.connection.prepared_statements)
ActiveRecordBasePreparedStatementsInverted.establish_connection(inverted)
- end
- before do
allow(ActiveRecord::Base).to receive(:connection_specification_name)
.and_return(ActiveRecordBasePreparedStatementsInverted.connection_specification_name)
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 6de7fc3a50e..9178707a3d0 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -180,6 +180,32 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
end
+ context 'when with_lock_retries re-runs the block' do
+ it 'only creates constraint for unique definitions' do
+ expected_sql = <<~SQL
+ ALTER TABLE "#{table_name}"\nADD CONSTRAINT "check_cda6f69506" CHECK (char_length("name") <= 255)
+ SQL
+
+ expect(model).to receive(:create_table).twice.and_call_original
+
+ expect(model).to receive(:execute).with(expected_sql).and_raise(ActiveRecord::LockWaitTimeout)
+ expect(model).to receive(:execute).with(expected_sql).and_call_original
+
+ model.create_table_with_constraints table_name do |t|
+ t.timestamps_with_timezone
+ t.integer :some_id, null: false
+ t.boolean :active, null: false, default: true
+ t.text :name
+
+ t.text_limit :name, 255
+ end
+
+ expect_table_columns_to_match(column_attributes, table_name)
+
+ expect_check_constraint(table_name, 'check_cda6f69506', 'char_length(name) <= 255')
+ end
+ end
+
context 'when constraints are given invalid names' do
let(:expected_max_length) { described_class::MAX_IDENTIFIER_NAME_LENGTH }
let(:expected_error_message) { "The maximum allowed constraint name is #{expected_max_length} characters" }
@@ -1720,7 +1746,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
.with(
2.minutes,
'CopyColumnUsingBackgroundMigrationJob',
- [event.id, event.id, :events, :id, :id, 'id_convert_to_bigint', 100]
+ [event.id, event.id, :events, :id, 100, :id, 'id_convert_to_bigint']
)
expect(Gitlab::BackgroundMigration)
diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
index 3e8563376ce..e25e4af2e86 100644
--- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
context 'with enough rows to bulk queue jobs more than once' do
before do
- stub_const('Gitlab::Database::Migrations::BackgroundMigrationHelpers::BACKGROUND_MIGRATION_JOB_BUFFER_SIZE', 1)
+ stub_const('Gitlab::Database::Migrations::BackgroundMigrationHelpers::JOB_BUFFER_SIZE', 1)
end
it 'queues jobs correctly' do
@@ -262,6 +262,120 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
end
+ describe '#queue_batched_background_migration' do
+ it 'creates the database record for the migration' do
+ expect do
+ model.queue_batched_background_migration(
+ 'MyJobClass',
+ :projects,
+ :id,
+ job_interval: 5.minutes,
+ batch_min_value: 5,
+ batch_max_value: 1000,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 100,
+ sub_batch_size: 10)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
+ job_class_name: 'MyJobClass',
+ table_name: 'projects',
+ column_name: 'id',
+ interval: 300,
+ min_value: 5,
+ max_value: 1000,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 100,
+ sub_batch_size: 10,
+ job_arguments: %w[],
+ status: 'active')
+ end
+
+ context 'when the job interval is lower than the minimum' do
+ let(:minimum_delay) { described_class::BATCH_MIN_DELAY }
+
+ it 'sets the job interval to the minimum value' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: minimum_delay - 1.minute)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
+
+ expect(created_migration.interval).to eq(minimum_delay)
+ end
+ end
+
+ context 'when additional arguments are passed to the method' do
+ it 'saves the arguments on the database record' do
+ expect do
+ model.queue_batched_background_migration(
+ 'MyJobClass',
+ :projects,
+ :id,
+ 'my',
+ 'arguments',
+ job_interval: 5.minutes,
+ batch_max_value: 1000)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
+ job_class_name: 'MyJobClass',
+ table_name: 'projects',
+ column_name: 'id',
+ interval: 300,
+ min_value: 1,
+ max_value: 1000,
+ job_arguments: %w[my arguments])
+ end
+ end
+
+ context 'when the max_value is not given' do
+ context 'when records exist in the database' do
+ let!(:event1) { create(:event) }
+ let!(:event2) { create(:event) }
+ let!(:event3) { create(:event) }
+
+ it 'creates the record with the current max value' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
+
+ expect(created_migration.max_value).to eq(event3.id)
+ end
+
+ it 'creates the record with an active status' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_active
+ end
+ end
+
+ context 'when the database is empty' do
+ it 'sets the max value to the min value' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
+
+ expect(created_migration.max_value).to eq(created_migration.min_value)
+ end
+
+ it 'creates the record with a finished status' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :projects, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_finished
+ end
+ end
+ end
+ end
+
describe '#migrate_async' do
it 'calls BackgroundMigrationWorker.perform_async' do
expect(BackgroundMigrationWorker).to receive(:perform_async).with("Class", "hello", "world")
diff --git a/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb b/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
new file mode 100644
index 00000000000..a3b03050b33
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Migrations::Observers::QueryStatistics do
+ subject { described_class.new }
+
+ let(:connection) { ActiveRecord::Base.connection }
+
+ def mock_pgss(enabled: true)
+ if enabled
+ allow(subject).to receive(:function_exists?).with(:pg_stat_statements_reset).and_return(true)
+ allow(connection).to receive(:view_exists?).with(:pg_stat_statements).and_return(true)
+ else
+ allow(subject).to receive(:function_exists?).with(:pg_stat_statements_reset).and_return(false)
+ allow(connection).to receive(:view_exists?).with(:pg_stat_statements).and_return(false)
+ end
+ end
+
+ describe '#before' do
+ context 'with pgss available' do
+ it 'resets pg_stat_statements' do
+ mock_pgss(enabled: true)
+ expect(connection).to receive(:execute).with('select pg_stat_statements_reset()').once
+
+ subject.before
+ end
+ end
+
+ context 'without pgss available' do
+ it 'executes nothing' do
+ mock_pgss(enabled: false)
+ expect(connection).not_to receive(:execute)
+
+ subject.before
+ end
+ end
+ end
+
+ describe '#record' do
+ let(:observation) { Gitlab::Database::Migrations::Observation.new }
+ let(:result) { double }
+ let(:pgss_query) do
+ <<~SQL
+ SELECT query, calls, total_time, max_time, mean_time, rows
+ FROM pg_stat_statements
+ ORDER BY total_time DESC
+ SQL
+ end
+
+ context 'with pgss available' do
+ it 'fetches data from pg_stat_statements and stores on the observation' do
+ mock_pgss(enabled: true)
+ expect(connection).to receive(:execute).with(pgss_query).once.and_return(result)
+
+ expect { subject.record(observation) }.to change { observation.query_statistics }.from(nil).to(result)
+ end
+ end
+
+ context 'without pgss available' do
+ it 'executes nothing' do
+ mock_pgss(enabled: false)
+ expect(connection).not_to receive(:execute)
+
+ expect { subject.record(observation) }.not_to change { observation.query_statistics }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
index 76b1be1e497..757da2d9092 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
@@ -81,7 +81,7 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :
end
describe '#rename_path_for_routable' do
- context 'for namespaces' do
+ context 'for personal namespaces' do
let(:namespace) { create(:namespace, path: 'the-path') }
it "renames namespaces called the-path" do
@@ -119,13 +119,16 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :
expect(project.route.reload.path).to eq('the-path-but-not-really/the-project')
end
+ end
- context "the-path namespace -> subgroup -> the-path0 project" do
+ context 'for groups' do
+ context "the-path group -> subgroup -> the-path0 project" do
it "updates the route of the project correctly" do
- subgroup = create(:group, path: "subgroup", parent: namespace)
+ group = create(:group, path: 'the-path')
+ subgroup = create(:group, path: "subgroup", parent: group)
project = create(:project, :repository, path: "the-path0", namespace: subgroup)
- subject.rename_path_for_routable(migration_namespace(namespace))
+ subject.rename_path_for_routable(migration_namespace(group))
expect(project.route.reload.path).to eq("the-path0/subgroup/the-path0")
end
@@ -158,23 +161,27 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :
end
describe '#perform_rename' do
- describe 'for namespaces' do
- let(:namespace) { create(:namespace, path: 'the-path') }
-
+ context 'for personal namespaces' do
it 'renames the path' do
+ namespace = create(:namespace, path: 'the-path')
+
subject.perform_rename(migration_namespace(namespace), 'the-path', 'renamed')
expect(namespace.reload.path).to eq('renamed')
+ expect(namespace.reload.route.path).to eq('renamed')
end
+ end
- it 'renames all the routes for the namespace' do
- child = create(:group, path: 'child', parent: namespace)
+ context 'for groups' do
+ it 'renames all the routes for the group' do
+ group = create(:group, path: 'the-path')
+ child = create(:group, path: 'child', parent: group)
project = create(:project, :repository, namespace: child, path: 'the-project')
- other_one = create(:namespace, path: 'the-path-is-similar')
+ other_one = create(:group, path: 'the-path-is-similar')
- subject.perform_rename(migration_namespace(namespace), 'the-path', 'renamed')
+ subject.perform_rename(migration_namespace(group), 'the-path', 'renamed')
- expect(namespace.reload.route.path).to eq('renamed')
+ expect(group.reload.route.path).to eq('renamed')
expect(child.reload.route.path).to eq('renamed/child')
expect(project.reload.route.path).to eq('renamed/child/the-project')
expect(other_one.reload.route.path).to eq('the-path-is-similar')
diff --git a/spec/lib/gitlab/database/similarity_score_spec.rb b/spec/lib/gitlab/database/similarity_score_spec.rb
index cf75e5a72d9..b7b66494390 100644
--- a/spec/lib/gitlab/database/similarity_score_spec.rb
+++ b/spec/lib/gitlab/database/similarity_score_spec.rb
@@ -71,7 +71,7 @@ RSpec.describe Gitlab::Database::SimilarityScore do
let(:search) { 'xyz' }
it 'results have 0 similarity score' do
- expect(query_result.map { |row| row['similarity'] }).to all(eq(0))
+ expect(query_result.map { |row| row['similarity'].to_f }).to all(eq(0))
end
end
end