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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe LooseForeignKeys::CleanupWorker do
include MigrationsHelpers
def create_table_structure
migration = ActiveRecord::Migration.new.extend(Gitlab::Database::MigrationHelpers::LooseForeignKeyHelpers)
migration.create_table :_test_loose_fk_parent_table_1
migration.create_table :_test_loose_fk_parent_table_2
migration.create_table :_test_loose_fk_child_table_1_1 do |t|
t.bigint :parent_id
end
migration.create_table :_test_loose_fk_child_table_1_2 do |t|
t.bigint :parent_id_with_different_column
end
migration.create_table :_test_loose_fk_child_table_2_1 do |t|
t.bigint :parent_id
end
migration.track_record_deletions(:_test_loose_fk_parent_table_1)
migration.track_record_deletions(:_test_loose_fk_parent_table_2)
end
let(:all_loose_foreign_key_definitions) do
{
'_test_loose_fk_parent_table_1' => [
ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(
'_test_loose_fk_child_table_1_1',
'_test_loose_fk_parent_table_1',
{
column: 'parent_id',
on_delete: :async_delete,
gitlab_schema: :gitlab_main
}
),
ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(
'_test_loose_fk_child_table_1_2',
'_test_loose_fk_parent_table_1',
{
column: 'parent_id_with_different_column',
on_delete: :async_nullify,
gitlab_schema: :gitlab_main
}
)
],
'_test_loose_fk_parent_table_2' => [
ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(
'_test_loose_fk_child_table_2_1',
'_test_loose_fk_parent_table_2',
{
column: 'parent_id',
on_delete: :async_delete,
gitlab_schema: :gitlab_main
}
)
]
}
end
let(:loose_fk_parent_table_1) { table(:_test_loose_fk_parent_table_1) }
let(:loose_fk_parent_table_2) { table(:_test_loose_fk_parent_table_2) }
let(:loose_fk_child_table_1_1) { table(:_test_loose_fk_child_table_1_1) }
let(:loose_fk_child_table_1_2) { table(:_test_loose_fk_child_table_1_2) }
let(:loose_fk_child_table_2_1) { table(:_test_loose_fk_child_table_2_1) }
before(:all) do
create_table_structure
end
after(:all) do
migration = ActiveRecord::Migration.new
migration.drop_table :_test_loose_fk_parent_table_1
migration.drop_table :_test_loose_fk_parent_table_2
migration.drop_table :_test_loose_fk_child_table_1_1
migration.drop_table :_test_loose_fk_child_table_1_2
migration.drop_table :_test_loose_fk_child_table_2_1
end
before do
allow(Gitlab::Database::LooseForeignKeys).to receive(:definitions_by_table).and_return(all_loose_foreign_key_definitions)
parent_record_1 = loose_fk_parent_table_1.create!
loose_fk_child_table_1_1.create!(parent_id: parent_record_1.id)
loose_fk_child_table_1_2.create!(parent_id_with_different_column: parent_record_1.id)
parent_record_2 = loose_fk_parent_table_1.create!
2.times { loose_fk_child_table_1_1.create!(parent_id: parent_record_2.id) }
3.times { loose_fk_child_table_1_2.create!(parent_id_with_different_column: parent_record_2.id) }
parent_record_3 = loose_fk_parent_table_2.create!
5.times { loose_fk_child_table_2_1.create!(parent_id: parent_record_3.id) }
loose_fk_parent_table_1.delete_all
loose_fk_parent_table_2.delete_all
end
it 'cleans up all rows' do
described_class.new.perform
expect(loose_fk_child_table_1_1.count).to eq(0)
expect(loose_fk_child_table_1_2.where(parent_id_with_different_column: nil).count).to eq(4)
expect(loose_fk_child_table_2_1.count).to eq(0)
end
context 'when deleting in batches' do
before do
stub_const('LooseForeignKeys::CleanupWorker::BATCH_SIZE', 2)
end
it 'cleans up all rows' do
expect(LooseForeignKeys::BatchCleanerService).to receive(:new).exactly(:twice).and_call_original
described_class.new.perform
expect(loose_fk_child_table_1_1.count).to eq(0)
expect(loose_fk_child_table_1_2.where(parent_id_with_different_column: nil).count).to eq(4)
expect(loose_fk_child_table_2_1.count).to eq(0)
end
end
context 'when the deleted rows count limit have been reached' do
def count_deletable_rows
loose_fk_child_table_1_1.count + loose_fk_child_table_2_1.count
end
before do
stub_const('LooseForeignKeys::ModificationTracker::MAX_DELETES', 2)
stub_const('LooseForeignKeys::CleanerService::DELETE_LIMIT', 1)
end
it 'cleans up 2 rows' do
expect { described_class.new.perform }.to change { count_deletable_rows }.by(-2)
end
end
context 'when the loose_foreign_key_cleanup feature flag is off' do
before do
stub_feature_flags(loose_foreign_key_cleanup: false)
end
it 'does nothing' do
expect { described_class.new.perform }.not_to change { LooseForeignKeys::DeletedRecord.status_processed.count }
end
end
end
|