diff options
Diffstat (limited to 'spec/models/batched_git_ref_updates/deletion_spec.rb')
-rw-r--r-- | spec/models/batched_git_ref_updates/deletion_spec.rb | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/spec/models/batched_git_ref_updates/deletion_spec.rb b/spec/models/batched_git_ref_updates/deletion_spec.rb new file mode 100644 index 00000000000..1679e8977b3 --- /dev/null +++ b/spec/models/batched_git_ref_updates/deletion_spec.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BatchedGitRefUpdates::Deletion, feature_category: :gitaly do + describe '.mark_records_processed' do + let_it_be(:deletion_1) { described_class.create!(project_id: 5, ref: 'refs/test/1') } + let_it_be(:deletion_2) { described_class.create!(project_id: 1, ref: 'refs/test/2') } + let_it_be(:deletion_3) { described_class.create!(project_id: 3, ref: 'refs/test/3') } + let_it_be(:deletion_4) { described_class.create!(project_id: 1, ref: 'refs/test/4') } + let_it_be(:deletion_5) { described_class.create!(project_id: 4, ref: 'refs/test/5', status: :processed) } + + it 'updates all records' do + expect(described_class.status_pending.count).to eq(4) + expect(described_class.status_processed.count).to eq(1) + + deletions = described_class.for_project(1).select_ref_and_identity + described_class.mark_records_processed(deletions) + + deletions.each do |deletion| + expect(deletion.reload.status).to eq("processed") + end + + expect(described_class.status_pending.count).to eq(2) + expect(described_class.status_processed.count).to eq(3) + end + end + + describe 'sliding_list partitioning' do + let(:partition_manager) { Gitlab::Database::Partitioning::PartitionManager.new(described_class) } + + describe 'next_partition_if callback' do + let(:active_partition) { described_class.partitioning_strategy.active_partition } + + subject(:value) { described_class.partitioning_strategy.next_partition_if.call(active_partition) } + + context 'when the partition is empty' do + it { is_expected.to eq(false) } + end + + context 'when the partition has records' do + before do + described_class.create!(project_id: 1, ref: 'refs/test/1', status: :processed) + described_class.create!(project_id: 2, ref: 'refs/test/2', status: :pending) + end + + it { is_expected.to eq(false) } + end + + context 'when the first record of the partition is older than PARTITION_DURATION' do + before do + described_class.create!( + project_id: 1, + ref: 'refs/test/1', + created_at: (described_class::PARTITION_DURATION + 1.day).ago) + + described_class.create!(project_id: 2, ref: 'refs/test/2') + end + + it { is_expected.to eq(true) } + end + end + + describe 'detach_partition_if callback' do + let(:active_partition) { described_class.partitioning_strategy.active_partition } + + subject(:value) { described_class.partitioning_strategy.detach_partition_if.call(active_partition) } + + context 'when the partition contains unprocessed records' do + before do + described_class.create!(project_id: 1, ref: 'refs/test/1') + described_class.create!(project_id: 2, ref: 'refs/test/2', status: :processed) + end + + it { is_expected.to eq(false) } + end + + context 'when the partition contains only processed records' do + before do + described_class.create!(project_id: 1, ref: 'refs/test/1', status: :processed) + described_class.create!(project_id: 2, ref: 'refs/test/2', status: :processed) + end + + it { is_expected.to eq(true) } + end + end + + describe 'the behavior of the strategy' do + it 'moves records to new partitions as time passes', :freeze_time do + # We start with partition 1 + expect(described_class.partitioning_strategy.current_partitions.map(&:value)).to eq([1]) + + # it's not a day old yet so no new partitions are created + partition_manager.sync_partitions + + expect(described_class.partitioning_strategy.current_partitions.map(&:value)).to eq([1]) + + # add one record so the next partition will be created + described_class.create!(project_id: 1, ref: 'refs/test/1') + + # after traveling forward a day + travel(described_class::PARTITION_DURATION + 1.second) + + # a new partition is created + partition_manager.sync_partitions + + expect(described_class.partitioning_strategy.current_partitions.map(&:value)).to contain_exactly(1, 2) + + # and we can insert to the new partition + described_class.create!(project_id: 2, ref: 'refs/test/2') + + # after processing old records + described_class.mark_records_processed(described_class.for_partition(1).select_ref_and_identity) + + partition_manager.sync_partitions + + # the old one is removed + expect(described_class.partitioning_strategy.current_partitions.map(&:value)).to eq([2]) + + # and we only have the newly created partition left. + expect(described_class.count).to eq(1) + end + end + end +end |