diff --git a/lib/middleware/unicorn_oobgc.rb b/lib/middleware/unicorn_oobgc.rb index 14b76a90dd4..7d78a9f304f 100644 --- a/lib/middleware/unicorn_oobgc.rb +++ b/lib/middleware/unicorn_oobgc.rb @@ -5,8 +5,18 @@ # hook unicorn module Middleware::UnicornOobgc - MIN_REQUESTS_PER_OOBGC = 5 - MAX_DELTAS = 25 + MIN_REQUESTS_PER_OOBGC = 3 + + def verbose(msg=nil) + @verbose ||= ENV["OOBGC_VERBOSE"] == "1" ? :true : :false + if @verbose == :true + if(msg) + puts msg + end + + true + end + end def self.init # hook up HttpServer intercept @@ -17,21 +27,22 @@ module Middleware::UnicornOobgc puts "Attempted to patch Unicorn but it is not loaded" end + # the closer this is to the GC run the more accurate it is + def estimate_live_num_at_gc(stat) + stat[:heap_live_num] + stat[:heap_free_num] + end + def process_client(client) stat = GC.stat - @previous_deltas ||= [] @num_requests ||= 0 @num_requests += 1 - # only track N deltas - if @previous_deltas.length > MAX_DELTAS - @previous_deltas.delete_at(0) - end - gc_count = stat[:count] live_num = stat[:heap_live_num] + @expect_gc_at ||= estimate_live_num_at_gc(stat) + super(client) # Unicorn::HttpServer#process_client # at this point client is serviced @@ -41,25 +52,32 @@ module Middleware::UnicornOobgc # no GC happened during the request if new_gc_count == gc_count - @previous_deltas << (new_live_num - live_num) + delta = new_live_num - live_num - if @gc_live_num && @num_requests > MIN_REQUESTS_PER_OOBGC - largest = @previous_deltas.max - if (largest * 3) + new_live_num > @gc_live_num - # While tuning consider printing this out, each time this happens you saved - # a user from running a GC inline + @max_delta ||= delta - # t = Time.now - GC.start - # puts "OobGC invoked req count: #{@num_requests} largest delta: #{largest} #{((Time.now - t) * 1000).to_i}ms saved" - @num_requests = 0 - end + if delta > @max_delta + new_delta = (delta * 1.5).to_i + @max_delta = [new_delta, delta].min + else + new_delta = (delta * 0.99).to_i + @max_delta = [new_delta, delta].max + end + + if @num_requests > MIN_REQUESTS_PER_OOBGC && @max_delta * 2 + new_live_num > @expect_gc_at + t = Time.now + GC.start + stat = GC.stat + @expect_gc_at = estimate_live_num_at_gc(stat) + verbose "OobGC hit pid: #{Process.pid} req: #{@num_requests} max delta: #{@max_delta} expect at: #{@expect_gc_at} #{((Time.now - t) * 1000).to_i}ms saved" + @num_requests = 0 end else - puts "OobGC: GC live num adjusted: old live_num #{@gc_live_num} new: #{live_num} - reqs since GC: #{@num_requests} largest delta: #{@previous_deltas.max}" + + verbose "OobGC miss pid: #{Process.pid} reqs: #{@num_requests}" @num_requests = 0 - @gc_live_num = live_num + @expect_gc_at = estimate_live_num_at_gc(stat) end