diff --git a/config/initializers/99-unicorn.rb b/config/initializers/99-unicorn.rb index e5b6473df..273a70e76 100644 --- a/config/initializers/99-unicorn.rb +++ b/config/initializers/99-unicorn.rb @@ -1,4 +1,4 @@ -if (oobgc=ENV['UNICORN_OOBGC_REQS'].to_i) > 0 - require 'unicorn/oob_gc' - Rails.configuration.middleware.insert 0, Unicorn::OobGC, oobgc +if ENV['UNICORN_ENABLE_OOBGC'] + require 'middleware/unicorn_oobgc' + Middleware::UnicornOobgc.init end diff --git a/config/unicorn.conf.rb b/config/unicorn.conf.rb index 3c4b7bf21..7f9c4fb99 100644 --- a/config/unicorn.conf.rb +++ b/config/unicorn.conf.rb @@ -1,7 +1,7 @@ # See http://unicorn.bogomips.org/Unicorn/Configurator.html -# RUN out of band GC every 2 requests -# ENV['UNICORN_OOBGC_REQS'] = "2" unless ENV['UNICORN_OOBGC_REQS'] +# enable out of band gc out of the box, it is low risk and improves perf a lot +ENV['UNICORN_ENABLE_OOBGC'] = "1" discourse_path = File.expand_path(File.expand_path(File.dirname(__FILE__)) + "/../") diff --git a/lib/middleware/unicorn_oobgc.rb b/lib/middleware/unicorn_oobgc.rb new file mode 100644 index 000000000..579091756 --- /dev/null +++ b/lib/middleware/unicorn_oobgc.rb @@ -0,0 +1,58 @@ + +# Hook into unicorn, unicorn middleware, not rack middleware +# +# Since we need no knowledge about the request we can simply +# hook unicorn +module Middleware::UnicornOobgc + + MIN_REQUESTS_PER_OOBGC = 6 + MAX_DELTAS = 20 + + def self.init + # hook up HttpServer intercept + ObjectSpace.each_object(Unicorn::HttpServer) do |s| + s.extend(self) + end + 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] + + 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 + @previous_deltas << (new_live_num - live_num) + + if @gc_live_num && @num_requests > MIN_REQUESTS_PER_OOBGC + largest = @previous_deltas.max + if largest * (2 + Random.rand(2)) + new_live_num > @gc_live_num + GC.start + @num_requests = 0 + end + end + else + puts "OobGC, GC live num adjusted, GC was not avoided: #{live_num}" + @gc_live_num = live_num + end + + end + +end