Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve metrics saving #15976

Merged
merged 12 commits into from
Sep 26, 2017
Next Next commit
Save metrics in batches
Save metrics in batches
  • Loading branch information
Ladas committed Sep 26, 2017
commit c3b649c34d0e3f7c2b1cd4b6cbf662fa8e6e6ddc
72 changes: 48 additions & 24 deletions lib/active_metrics/connection_adapters/miq_postgres_adapter.rb
Original file line number Diff line number Diff line change
@@ -11,18 +11,20 @@ def self.create_connection(_config)
end

def write_multiple(*metrics)
metrics.flatten!

flatten_metrics(metrics).each do |interval_name, by_resource|
by_resource.each do |resource, by_timestamp|
start_time = by_timestamp.keys.min
end_time = by_timestamp.keys.max
data = by_timestamp.values
write_rows(interval_name, resource, start_time, end_time, data)
Benchmark.realtime_block(:write_multiple) do
metrics.flatten!

flatten_metrics(metrics).each do |interval_name, by_resource|
by_resource.each do |resource, by_timestamp|
start_time = by_timestamp.keys.min
end_time = by_timestamp.keys.max
data = by_timestamp.values
write_rows(interval_name, resource, start_time, end_time, data)
end
end
end

metrics
metrics
end
end

private
@@ -40,47 +42,69 @@ def flatten_metrics(metrics)
end

def write_rows(interval_name, resource, start_time, end_time, data)
klass, meth = Metric::Helper.class_and_association_for_interval_name(interval_name)
limiting_arel = klass.where(:capture_interval_name => interval_name).where(:resource => resource).where(:timestamp => start_time..end_time)

samples_inventory_collection = ::ManagerRefresh::InventoryCollection.new(
:manager_ref => [:resource_type, :resource_id, :timestamp, :capture_interval_name],
:name => meth,
:saver_strategy => :batch,
:arel => limiting_arel,
:complete => false,
:model_class => klass,
)

log_header = "[#{interval_name}]"

# Read all the existing perfs for this time range to speed up lookups
# TODO(lsmola) we need to fetch everything we have in DB first, because we process aligned 20s intervals, this
# can go away once we remove the need for 20s intervals and processing based on that.
obj_perfs, = Benchmark.realtime_block(:db_find_prev_perfs) do
# TODO(lsmola) store just attributes, not whole AR objects here
Metric::Finders.hash_by_capture_interval_name_and_timestamp(resource, start_time, end_time, interval_name)
end

_klass, meth = Metric::Helper.class_and_association_for_interval_name(interval_name)

# Create or update the performance rows from the hashes
_log.info("#{log_header} Processing #{data.length} performance rows...")
a = u = 0
data.each do |v|
ts = v[:timestamp]
perf = nil

Benchmark.realtime_block(:process_perfs) do
perf = obj_perfs.fetch_path(interval_name, ts)
perf ||= obj_perfs.store_path(interval_name, ts, resource.send(meth).build(:resource_name => resource.name))
perf.new_record? ? a += 1 : u += 1
if (perf = obj_perfs.fetch_path(interval_name, ts))
v.reverse_merge!(perf.attributes.symbolize_keys)
v.delete(:id) # Remove protected attributes
end

v.reverse_merge!(perf.attributes.symbolize_keys)
v.delete("id") # Remove protected attributes
v[:resource_id] = resource.id
v[:resource_type] = resource.class.base_class.name
v.merge!(Metric::Processing.process_derived_columns(resource, v, interval_name == 'realtime' ? Metric::Helper.nearest_hourly_timestamp(ts) : nil))
samples_inventory_collection.build(v)
end
end
# Assign nil so GC can clean it up
obj_perfs = nil

# TODO: Should we change this into a single metrics.push like we do in ems_refresh?
Benchmark.realtime_block(:process_perfs_db) { perf.update_attributes(v) }
Benchmark.realtime_block(:process_perfs_db) do
ManagerRefresh::SaveInventory.save_inventory(ExtManagementSystem.first, [samples_inventory_collection])
end
created_count = samples_inventory_collection.created_records.count
updated_count = samples_inventory_collection.updated_records.count
# Assign nil so GC can clean it up
samples_inventory_collection = nil

if interval_name == 'hourly'
if interval_name == 'hourly'
# TODO(lsmola) pff, it needs AR objects, quite ineffective to batch here
limiting_arel.find_each do |perf|
Benchmark.realtime_block(:process_perfs_tag) { VimPerformanceTagValue.build_from_performance_record(perf) }
end
end

if interval_name == 'hourly'
_log.info("#{log_header} Adding missing timestamp intervals...")
Benchmark.realtime_block(:add_missing_intervals) { Metric::Processing.add_missing_intervals(resource, "hourly", start_time, end_time) }
_log.info("#{log_header} Adding missing timestamp intervals...Complete")
end

_log.info("#{log_header} Processing #{data.length} performance rows...Complete - Added #{a} / Updated #{u}")
_log.info("#{log_header} Processing #{data.length} performance rows...Complete - Added #{created_count} / Updated #{updated_count}")
end
end
end