discourse/lib/middleware/unicorn_oobgc.rb

96 lines
2.4 KiB
Ruby
Raw Normal View History

2013-11-14 20:15:37 -05:00
# Hook into unicorn, unicorn middleware, not rack middleware
#
# Since we need no knowledge about the request we can simply
# hook unicorn
module Middleware::UnicornOobgc
2013-11-18 22:13:42 -05:00
MIN_REQUESTS_PER_OOBGC = 3
# TUNE ME, for Discourse this number is good
2013-11-18 23:06:34 -05:00
MIN_FREE_SLOTS = 50_000
2013-11-18 22:13:42 -05:00
def verbose(msg=nil)
@verbose ||= ENV["OOBGC_VERBOSE"] == "1" ? :true : :false
if @verbose == :true
if(msg)
puts msg
end
true
end
end
2013-11-14 20:15:37 -05:00
def self.init
# hook up HttpServer intercept
ObjectSpace.each_object(Unicorn::HttpServer) do |s|
s.extend(self)
end
rescue
puts "Attempted to patch Unicorn but it is not loaded"
2013-11-14 20:15:37 -05:00
end
2013-11-18 22:13:42 -05:00
# 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
2013-11-14 20:15:37 -05:00
def process_client(client)
stat = GC.stat
@num_requests ||= 0
@num_requests += 1
gc_count = stat[:count]
live_num = stat[:heap_live_num]
2013-11-18 22:13:42 -05:00
@expect_gc_at ||= estimate_live_num_at_gc(stat)
2013-11-14 20:15:37 -05:00
super(client) # Unicorn::HttpServer#process_client
# at this point client is serviced
stat = GC.stat
new_gc_count = stat[:count]
new_live_num = stat[:heap_live_num]
# no GC happened during the request
if new_gc_count == gc_count
2013-11-18 22:13:42 -05:00
delta = new_live_num - live_num
@max_delta ||= delta
if delta > @max_delta
new_delta = (delta * 1.5).to_i
@max_delta = [new_delta, delta].min
else
# this may seem like a very tiny decay rate, but some apps using caching
# can really mess stuff up, if our delta is too low the algorithm fails
new_delta = (delta * 0.995).to_i
2013-11-18 22:13:42 -05:00
@max_delta = [new_delta, delta].max
end
if @max_delta < MIN_FREE_SLOTS
@max_delta = MIN_FREE_SLOTS
end
2013-11-18 22:13:42 -05:00
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
2013-11-14 20:15:37 -05:00
end
else
2013-11-18 22:13:42 -05:00
2013-11-18 22:27:18 -05:00
verbose "OobGC miss pid: #{Process.pid} reqs: #{@num_requests} max delta: #{@max_delta}"
@num_requests = 0
2013-11-18 22:13:42 -05:00
@expect_gc_at = estimate_live_num_at_gc(stat)
2013-11-14 20:15:37 -05:00
end
end
end