1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
|