about summary refs log tree commit homepage
path: root/lib/unicorn/oob_gc.rb
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-05-14 18:27:35 +0000
committerEric Wong <normalperson@yhbt.net>2010-05-14 18:40:17 +0000
commit95b75a5043b34f39ece4f52befb4b3f884dfdd20 (patch)
tree502016acafb35277ca8305b9e0255cad6ba7d9f6 /lib/unicorn/oob_gc.rb
parentbbefb358174e73d7edbf8a1014817fe1557e39af (diff)
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.
Diffstat (limited to 'lib/unicorn/oob_gc.rb')
-rw-r--r--lib/unicorn/oob_gc.rb58
1 files changed, 58 insertions, 0 deletions
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