From 95b75a5043b34f39ece4f52befb4b3f884dfdd20 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 14 May 2010 18:27:35 +0000 Subject: add Unicorn::OobGC middleware This middleware allows configurable out-of-band garbage collection outside of the normal request/response cycle. It offers configurable paths (to only GC on expensive actions) and intervals to limit GC frequency. It is only expected to work well with Unicorn, as it would hurt performance on single-threaded servers if they have keepalive enabled. Obviously this does not work well for multi-threaded or evented servers that serve multiple clients at once. --- lib/unicorn/oob_gc.rb | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 lib/unicorn/oob_gc.rb (limited to 'lib/unicorn/oob_gc.rb') diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb new file mode 100644 index 0000000..8dc4dcf --- /dev/null +++ b/lib/unicorn/oob_gc.rb @@ -0,0 +1,58 @@ +# -*- encoding: binary -*- +module Unicorn + + # Run GC after every request, after closing the client socket and + # before attempting to accept more connections. + # + # This shouldn't hurt overall performance as long as the server cluster + # is at <50% CPU capacity, and improves the performance of most memory + # intensive requests. This serves to improve _client-visible_ + # performance (possibly at the cost of overall performance). + # + # We'll call GC after each request is been written out to the socket, so + # the client never sees the extra GC hit it. + # + # This middleware is _only_ effective for applications that use a lot + # of memory, and will hurt simpler apps/endpoints that can process + # multiple requests before incurring GC. + # + # This middleware is only designed to work with Unicorn, as it harms + # keepalive performance. + # + # Example (in config.ru): + # + # require 'unicorn/oob_gc' + # + # # GC ever two requests that hit /expensive/foo or /more_expensive/foo + # # in your app. By default, this will GC once every 5 requests + # # for all endpoints in your app + # use Unicorn::OobGC, 2, %r{\A/(?:expensive/foo|more_expensive/foo)} + class OobGC < Struct.new(:app, :interval, :path, :nr, :env, :body) + + def initialize(app, interval = 5, path = %r{\A/}) + super(app, interval, path, interval) + end + + def call(env) + status, headers, self.body = app.call(self.env = env) + [ status, headers, self ] + end + + def each(&block) + body.each(&block) + end + + # in Unicorn, this is closed _after_ the client socket + def close + body.close if body.respond_to?(:close) + + if path =~ env['PATH_INFO'] && ((self.nr -= 1) <= 0) + self.nr = interval + self.body = nil + env.clear + GC.start + end + end + + end +end -- cgit v1.2.3-24-ge0c7