diff --git a/app/models/miq_schedule_worker/jobs.rb b/app/models/miq_schedule_worker/jobs.rb index a62e1f4122a..00536e20f81 100644 --- a/app/models/miq_schedule_worker/jobs.rb +++ b/app/models/miq_schedule_worker/jobs.rb @@ -115,6 +115,10 @@ def binary_blob_purge_timer queue_work(:class_name => "BinaryBlob", :method_name => "purge_timer", :zone => nil) end + def vim_performance_states_purge_timer + queue_work(:class_name => "VimPerformanceState", :method_name => "purge_timer", :zone => nil) + end + def miq_schedule_queue_scheduled_work(schedule_id, rufus_job) MiqSchedule.queue_scheduled_work(schedule_id, rufus_job.job_id, rufus_job.next_time.to_i, rufus_job.opts) end diff --git a/app/models/miq_schedule_worker/runner.rb b/app/models/miq_schedule_worker/runner.rb index bd7f2e1b4d0..2ee4fc5db6c 100644 --- a/app/models/miq_schedule_worker/runner.rb +++ b/app/models/miq_schedule_worker/runner.rb @@ -204,6 +204,11 @@ def schedules_for_scheduler_role enqueue(:binary_blob_purge_timer) end + every = worker_settings[:vim_performance_states_purge_interval] + scheduler.schedule_every(every, :first_in => every) do + enqueue(:vim_performance_states_purge_timer) + end + # Schedule every 24 hours at = worker_settings[:storage_file_collection_time_utc] if Time.now.strftime("%Y-%m-%d #{at}").to_time(:utc) < Time.now.utc diff --git a/app/models/mixins/purging_mixin.rb b/app/models/mixins/purging_mixin.rb index ba52e479b8a..e82de7ab290 100644 --- a/app/models/mixins/purging_mixin.rb +++ b/app/models/mixins/purging_mixin.rb @@ -98,8 +98,37 @@ def purge_by_scope(older_than = nil, window = nil, &block) _log.info("Purging #{table_name.humanize}...Complete - Deleted #{total} records") end + def purge_by_orphaned(fk_name, window = purge_window_size) + _log.info("Purging orphans in #{table_name.humanize}...") + total = purge_orphans(fk_name, window) + _log.info("Purging orphans in #{table_name.humanize}...Complete - Deleted #{total} records") + total + end + private + def purge_orphans(fk_name, window) + # For now, this only supports polymorphic references + # We don't currently have a situation where a table with a regular reference needs to be purged + polymorphic_type_column = "#{fk_name}_type" + polymorphic_id_column = connection.quote_column_name("#{fk_name}_id") + total = 0 + + polymorphic_classes(polymorphic_type_column).each do |klass| + resource_table = connection.quote_table_name(klass.table_name) + + scope = joins("LEFT OUTER JOIN #{resource_table} ON #{table_name}.#{polymorphic_id_column} = #{resource_table}.id") + .where(resource_table => {:id => nil}) + .where("#{table_name}.#{connection.quote_column_name(polymorphic_type_column)} = #{connection.quote(klass.name)}") + total += purge_in_batches(scope, window) + end + total + end + + def polymorphic_classes(polymorphic_type_column) + distinct(polymorphic_type_column).pluck(polymorphic_type_column).map(&:constantize) + end + # Private: The ids to purge if we want to keep a fixed number of records # for each resource. Newer records (with a higher id/ lower rank) are kept. # diff --git a/app/models/vim_performance_state.rb b/app/models/vim_performance_state.rb index 582d70df199..cbe463120a7 100644 --- a/app/models/vim_performance_state.rb +++ b/app/models/vim_performance_state.rb @@ -1,4 +1,6 @@ class VimPerformanceState < ApplicationRecord + include_concern 'Purging' + serialize :state_data belongs_to :resource, :polymorphic => true diff --git a/app/models/vim_performance_state/purging.rb b/app/models/vim_performance_state/purging.rb new file mode 100644 index 00000000000..fac1f7e668e --- /dev/null +++ b/app/models/vim_performance_state/purging.rb @@ -0,0 +1,16 @@ +class VimPerformanceState < ApplicationRecord + module Purging + extend ActiveSupport::Concern + include PurgingMixin + + module ClassMethods + def purge_mode_and_value + %w(orphaned resource) + end + + def purge_window_size + ::Settings.vim_performance_states.history.purge_window_size + end + end + end +end diff --git a/config/settings.yml b/config/settings.yml index 81aae4e91b5..724c486d339 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1048,6 +1048,9 @@ :active_task_timeout: 6.hours :ui: :mark_translated_strings: false +:vim_performance_states: + :history: + :purge_window_size: 1000 :webservices: :consume_protocol: https :contactwith: ipaddress @@ -1206,6 +1209,7 @@ :storage_file_collection_interval: 1.days :storage_file_collection_time_utc: 21600 :task_timeout_check_frequency: 1.hour + :vim_performance_states_purge_interval: 1.day :vm_retired_interval: 10.minutes :yum_update_check: 12.hours :ui_worker: diff --git a/spec/models/vim_performance_state/purging_spec.rb b/spec/models/vim_performance_state/purging_spec.rb new file mode 100644 index 00000000000..1a0e8d88860 --- /dev/null +++ b/spec/models/vim_performance_state/purging_spec.rb @@ -0,0 +1,37 @@ +describe VimPerformanceState do + context "::Purging" do + describe ".purge_by_orphaned" do + it "purges all the orphaned rows for all referenced classes" do + # Create an orphaned row referencing VmOrTemplate + vm1 = FactoryGirl.create(:vm_or_template) + vm_perf1 = FactoryGirl.create(:vim_performance_state, :resource => vm1) + vm1.delete + + # Create a non-orphaned row referencing VmOrTemplate + vm_perf2 = FactoryGirl.create(:vim_performance_state, :resource => FactoryGirl.create(:vm_or_template)) + + # Create an orphaned row referencing ExtManagementSystem + ems1 = FactoryGirl.create(:ext_management_system) + ems_perf1 = FactoryGirl.create(:vim_performance_state, :resource => ems1) + ems1.delete + + # Create a non-orphaned row referencing ExtManagementSystem + ems_perf2 = FactoryGirl.create(:vim_performance_state, :resource => FactoryGirl.create(:ext_management_system)) + + expect(described_class.all).to match_array([vm_perf1, vm_perf2, ems_perf1, ems_perf2]) + count = described_class.purge_by_orphaned("resource") + expect(described_class.all).to match_array([vm_perf2, ems_perf2]) + expect(count).to eq(2) + end + end + + describe ".purge_timer" do + it "queues the correct purge method" do + EvmSpecHelper.local_miq_server + described_class.purge_timer + q = MiqQueue.first + expect(q).to have_attributes(:class_name => described_class.name, :method_name => "purge_by_orphaned", :args => ["resource"]) + end + end + end +end