Skip to content

Commit 3551ae6

Browse files
committed
Add purge mode support: counts or purge
Default to just report counts with no provided mode. User would need to specify --mode purge to actually modify the database. Change polymorphic_orphan and non_polymorphic_orphan methods to return scopes instead of building the scope and executing purge_in_batches with that scope. Instead, we can let the caller do the purge or counts on the resulting scope. Add test for counts for vim_performance_states purge by orphans.
1 parent e775b40 commit 3551ae6

File tree

3 files changed

+48
-29
lines changed

3 files changed

+48
-29
lines changed

app/models/mixins/purging_mixin.rb

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ def purge_by_scope(older_than = nil, window = nil, &block)
116116
total
117117
end
118118

119-
def purge_by_orphaned(fk_name, window = purge_window_size)
119+
def purge_by_orphaned(fk_name, window = purge_window_size, purge_mode = :purge)
120120
_log.info("Purging orphans in #{table_name}...")
121-
total = purge_orphans(fk_name, window)
122-
_log.info("Purging orphans in #{table_name}...Complete - Deleted #{total} records")
121+
total = purge_orphans(fk_name, window, purge_mode)
122+
_log.info("Purging orphans in #{table_name}...Complete - #{purge_mode.to_sym == :count ? 'Would delete' : 'Deleted'} #{total} records")
123123
total
124124
end
125125

@@ -133,40 +133,38 @@ def purge_by_date_and_orphaned(older_than, fk_name, window = purge_window_size)
133133

134134
private
135135

136-
def purge_orphans(fk_name, window)
136+
def purge_orphans(fk_name, window, purge_mode = :purge)
137137
reflection = reflect_on_association(fk_name)
138-
if reflection.polymorphic?
139-
purge_polymorphic_orphans(fk_name, window)
138+
scopes = reflection.polymorphic? ? polymorphic_orphan_scopes(fk_name) : non_polymorphic_orphan_scopes(fk_name, reflection.klass)
139+
140+
if purge_mode.to_sym == :count
141+
scopes.sum(&:count)
140142
else
141-
purge_non_polymorphic_orphans(fk_name, window, reflection.klass)
143+
scopes.sum { |s| purge_in_batches(s, window) }
142144
end
143145
end
144146

145-
def purge_polymorphic_orphans(fk_name, window)
147+
def polymorphic_orphan_scopes(fk_name)
146148
polymorphic_type_column = "#{fk_name}_type"
147149
polymorphic_id_column = connection.quote_column_name("#{fk_name}_id")
148-
total = 0
149150

150-
polymorphic_classes(polymorphic_type_column).each do |klass|
151+
polymorphic_classes(polymorphic_type_column).collect do |klass|
151152
resource_table = connection.quote_table_name(klass.table_name)
152153
q_table_name = connection.quote_table_name(table_name)
153154

154-
scope = joins("LEFT OUTER JOIN #{resource_table} ON #{q_table_name}.#{polymorphic_id_column} = #{resource_table}.id")
155-
.where(resource_table => {:id => nil})
156-
.where("#{q_table_name}.#{connection.quote_column_name(polymorphic_type_column)} = #{connection.quote(klass.name)}")
157-
total += purge_in_batches(scope, window)
155+
joins("LEFT OUTER JOIN #{resource_table} ON #{q_table_name}.#{polymorphic_id_column} = #{resource_table}.id")
156+
.where(resource_table => {:id => nil})
157+
.where("#{q_table_name}.#{connection.quote_column_name(polymorphic_type_column)} = #{connection.quote(klass.name)}")
158158
end
159-
total
160159
end
161160

162-
def purge_non_polymorphic_orphans(fk_name, window, reflection_klass)
161+
def non_polymorphic_orphan_scopes(fk_name, reflection_klass)
163162
resource_table = connection.quote_table_name(reflection_klass.table_name)
164163
assoc_id = connection.quote_column_name("#{fk_name}_id")
165164
q_table_name = connection.quote_table_name(table_name)
166165

167-
scope = joins("LEFT OUTER JOIN #{resource_table} ON #{q_table_name}.#{assoc_id} = #{resource_table}.id")
168-
.where(resource_table => {:id => nil})
169-
purge_in_batches(scope, window)
166+
[joins("LEFT OUTER JOIN #{resource_table} ON #{q_table_name}.#{assoc_id} = #{resource_table}.id")
167+
.where(resource_table => {:id => nil})]
170168
end
171169

172170
def polymorphic_classes(polymorphic_type_column)

spec/models/vim_performance_state/purging_spec.rb

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
11
RSpec.describe VimPerformanceState do
22
context "::Purging" do
33
describe ".purge_by_orphaned" do
4-
it "purges all the orphaned rows for all referenced classes" do
4+
before do
55
# Create an orphaned row referencing VmOrTemplate
66
vm1 = FactoryBot.create(:vm_or_template)
7-
vm_perf1 = FactoryBot.create(:vim_performance_state, :resource => vm1)
7+
@vm_perf1 = FactoryBot.create(:vim_performance_state, :resource => vm1)
88
vm1.delete
99

1010
# Create a non-orphaned row referencing VmOrTemplate
11-
vm_perf2 = FactoryBot.create(:vim_performance_state, :resource => FactoryBot.create(:vm_or_template))
11+
@vm_perf2 = FactoryBot.create(:vim_performance_state, :resource => FactoryBot.create(:vm_or_template))
1212

1313
# Create an orphaned row referencing ExtManagementSystem
1414
ems1 = FactoryBot.create(:ext_management_system)
15-
ems_perf1 = FactoryBot.create(:vim_performance_state, :resource => ems1)
15+
@ems_perf1 = FactoryBot.create(:vim_performance_state, :resource => ems1)
1616
ems1.delete
1717

1818
# Create a non-orphaned row referencing ExtManagementSystem
19-
ems_perf2 = FactoryBot.create(:vim_performance_state, :resource => FactoryBot.create(:ext_management_system))
19+
@ems_perf2 = FactoryBot.create(:vim_performance_state, :resource => FactoryBot.create(:ext_management_system))
20+
expect(described_class.all).to match_array([@vm_perf1, @vm_perf2, @ems_perf1, @ems_perf2])
21+
end
2022

21-
expect(described_class.all).to match_array([vm_perf1, vm_perf2, ems_perf1, ems_perf2])
23+
it "purges all the orphaned rows for all referenced classes" do
2224
count = described_class.purge_by_orphaned("resource")
23-
expect(described_class.all).to match_array([vm_perf2, ems_perf2])
25+
expect(described_class.all).to match_array([@vm_perf2, @ems_perf2])
26+
expect(count).to eq(2)
27+
end
28+
29+
it "count purge_mode returns count of the orphaned rows for all referenced classes without actually purging" do
30+
count = described_class.purge_by_orphaned("resource", 1000, :count)
31+
expect(described_class.all).to match_array([@vm_perf1, @vm_perf2, @ems_perf1, @ems_perf2])
2432
expect(count).to eq(2)
2533
end
2634
end

tools/purge_orphans.rb

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
#!/usr/bin/env ruby
2+
require "optimist"
3+
4+
VALID_MODES = %w[count purge].freeze
5+
opts = Optimist.options do
6+
synopsis "Purge orphaned rows from several tables."
7+
usage "[options]"
8+
9+
opt :mode, "Mode", :default => "count", :permitted => VALID_MODES
10+
end
11+
12+
purge_mode = opts[:mode].to_sym
13+
14+
# now load rails
215
require File.expand_path('../config/environment', __dir__)
316

4-
def purge_by_orphaned(klass, fk, window)
17+
def purge_by_orphaned(klass, fk, window, purge_mode)
518
klass.include PurgingMixin
619
klass.define_method(:purge_method) { :destroy }
7-
klass.purge_by_orphaned(fk, window).tap { |total| puts "Purged: #{klass}: #{total}" }
20+
klass.purge_by_orphaned(fk, window, purge_mode).tap { |total| puts "#{purge_mode == :count ? 'Would purge' : 'Purged'}: #{klass}: #{total} rows" }
821
end
922

1023
CLASSES_TO_PURGE = [
@@ -22,7 +35,7 @@ def purge_by_orphaned(klass, fk, window)
2235
_result, bm = Benchmark.realtime_block("TotalTime") do
2336
CLASSES_TO_PURGE.each_slice(3) do |klass, fk, window|
2437
Benchmark.realtime_block(klass.name) do
25-
purge_by_orphaned(klass, fk, window)
38+
purge_by_orphaned(klass, fk, window, purge_mode)
2639
end
2740
end
2841
nil

0 commit comments

Comments
 (0)