require
spaghetti
type
columns stored the ancestors chain (something like VmOrTemplate>Vm>Providers::Infra::Vm>Providers::Infra::Ovirt::Vm
), queries like Vm.all
could be based on prefix (LIKE 'VmOrTemplate>Vm>%'
), without having to know all descendants. descendant loader could go away!
# Without descendant loader
irb(main):001:0> VmOrTemplate.descendants.collect(&:name)
=> []
irb(main):002:0> Vm
=> Vm(id: integer, ....
irb(main):003:0> VmOrTemplate.descendants.collect(&:name)
=> ["Vm"]
# With descendant loader
irb(main):001:0> VmOrTemplate.descendants.collect(&:name)
=> ["Vm", "MiqTemplate", "VmServer", "ManageIQ::Providers::InfraManager::Vm", "ManageIQ::Providers::CloudManager::Vm", "VmXen", "ManageIQ::Providers::Redhat::InfraManager::Vm", "ManageIQ::Providers::Microsoft::InfraManager::Vm", "ManageIQ::Providers::Vmware::InfraManager::Vm", "ManageIQ::Providers::Amazon::CloudManager::Vm", "ManageIQ::Providers::Azure::CloudManager::Vm", "ManageIQ::Providers::Google::CloudManager::Vm", "ManageIQ::Providers::Openstack::CloudManager::Vm", "ManageIQ::Providers::Vmware::CloudManager::Vm", "ManageIQ::Providers::InfraManager::Template", "ManageIQ::Providers::CloudManager::Template", "ManageIQ::Providers::Redhat::InfraManager::Template", "ManageIQ::Providers::Microsoft::InfraManager::Template", "ManageIQ::Providers::Vmware::InfraManager::Template", "TemplateXen", "ManageIQ::Providers::Openstack::InfraManager::Template", "ManageIQ::Providers::Amazon::CloudManager::Template", "ManageIQ::Providers::Azure::CloudManager::Template", "ManageIQ::Providers::Google::CloudManager::Template", "ManageIQ::Providers::Openstack::CloudManager::Template", "ManageIQ::Providers::Vmware::CloudManager::Template", "ManageIQ::Providers::Openstack::CloudManager::VolumeSnapshotTemplate", "ManageIQ::Providers::Openstack::CloudManager::VolumeTemplate"]
ManageIQ::Providers::InfraManager::Vm
won't load ManageIQ::Providers::CloudManager::Vm
VmOrTemplate.all
had no filter, and Vm.all
only searched for template = false
, then it will not need to know the subclasses. All other searches will be from the class it is searching - so it should work just fine.ManageIQ::Providers::CloudManager::Vm
. Not sure how valid this is.
VmOrTemplate
, but.. There are several classes (may or may not be AR based) that can be instantiated based on UI selections. In some cases, there is a drop-down in the UI to choose the thing based on something like Thing.descendants.collect(&:display_name)
. All descendants of Thing
are valid choices. There may not yet be any Thing
s in the database, or may not be all types of Thing
s in the database yet. In these cases I believe our only choice is DescendantLoader or eager-load everything.
DescendantLoader
hack in the first place? Since we usually air on the argument of "let's do this the Rails way" in general?
eager_load
")
if I'm firing up a Rails console why would I want to eager load the world
making this point kinda moot
/bin/rails c; Vm.first
with descendant loader, but turns out we have actually busted eager loading in our application
Hawkular::MiddlewareManager::Inventory
, for what it is worth
config/environments/development.rb
and switched the eager_load
and cache_classes
vals to true
inventory.rb
stub file
module ManageIQ::Providers
class Hawkular::Inventory::Collector::MiddlewareManager
...
end
end
what is Inventory
? is that a module?
MiqServer#monitor
you still leak a bit of memory
Settings
code
monitor_poll
MiqServer#monitor
isn't the looping code by the way
Settings
GC.start
in the server process roughly every 30-60 seconds. From your logging @NickLaMuro, sometimes it's 4-5 minutes between full GCs(major_gc_count gets incremented)
/Users/jfrey/.gem/ruby/2.4.2/bundler/gems/manageiq-providers-nuage-a1f64a1cc408/app/models/manageiq/providers/nuage/network_manager/event_catcher/messaging_handler.rb:1:in `<top (required)>': uninitialized constant Qpid (NameError)
require "qpid_proton"
bundle exec irb
which makes sense, but :/
manageiq-providers-vmware/app/models/manageiq/providers/vmware/infra_manager/metrics_fetcher_worker.rb:1:in
'<top (required)>': uninitialized constant ManageIQ::Providers::BaseManager::MetricsFetcherWorker (NameError)
Benchmark.realtime_block
# loop_monitor.rb
require 'sys-proctable'
loop do
puts Sys::ProcTable.ps(ARGV[0].to_i).rss
sleep 5
end
puts "PID: #{Process.pid}"
# loop_test_1.rb
require 'config/environment.rb'
puts "starting loop..."
loop do
::Settings.server.monitor_poll.to_i_with_method
sleep 0.5
end
# loop_test_2.rb
puts "PID: #{Process.pid}"
require 'config/environment.rb'
puts "starting loop..."
def monitor
# intentional no-op
end
loop do
_dummy, timings = Benchmark.realtime_block(:total_time) { monitor }
sleep 0.5
end
Vm.all.to_sql
but .to_sql is about the same numbers...
DescendantLoader uncached:
bundle exec rails r 'Vm.all.to_sql' 48.99s user 2.29s system 99% cpu 51.430 total
DescendantLoader cached:
bundle exec rails r 'Vm.all.to_sql' 8.07s user 2.03s system 98% cpu 10.207 total
DescendantLoader disabled and eager_load enabled (with fixes):
bundle exec rails r 'Vm.all.to_sql' 11.28s user 2.75s system 99% cpu 14.138 total
loop_test_2.rb
needs a loop?
loop do
_dummy, timings = Benchmark.realtime_block(:total_time) { monitor }
sleep 0.5
end
?
loop
does?
loop
? Or Benchmark
?
Benchmark
[5] pry(main)> loop do
[5] pry(main)* _dummy, timings = Benchmark.realtime_block(:total_time) { monitor }
[5] pry(main)* sleep 1
[5] pry(main)* GC.start
[5] pry(main)* puts MiqProcess.processInfo[:memory_usage]
[5] pry(main)* end
174018560
174018560
174018560
174018560...forever
forever
:smile:
$ ruby loop_monitor.rb 22621
36418
36418 X 19
36482
36482 X 19
36548
# note, I ctrl-c'd right here to get some stuff
36548 X 32
36614
36614 X 31
36680
...
50X + on 36680
...https://www.youtube.com/watch?v=w-I6XTVZXww
)
delete_current_realtime
MALLOC_ARENA_MAX
should minimize that, but ¯\_(ツ)_/¯
jemalloc
may fix it...malloc
"
GC.start
does clear up the leak...
GC.start
gets called
x = 500 * [500 * "x"]
the variable would go out of scope - but keep it around at lest until after the benchmark block
GC.start
in place
monitor
, then it will be hard to find a leak
I'm not convinced doing a GC.start is fair in a script we're trying to recreate a leak for
We don't GC.start in the server. We do it when there are enough allocations to warrant one.
Partially agree. Doing a GC forces you to not accidentally claim that a graph that looks like :chart_with_upwards_trend: is automatically a leak. It's very highly probable that the monitor loop ends up GC'ing at some point and putting in a manual GC just "speeds that up". That being said, if you think the issue is more of a small Ruby object with a large memory footprint, then yeah, you wouldn't do that, but I don't think we have any evidence pointing to that kind of situation here (unlike how we most obviously had that in vmware land with the giant XMLs)
heapfrag
to take the dumps I have and if the heap gets fragmented over time
GC.stats
to determine that