about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-10-15 00:37:59 -0700
committerEric Wong <normalperson@yhbt.net>2009-10-15 00:46:19 -0700
commitcbaa604582a9f80eba3fc0c2423234f497f21726 (patch)
tree4a8436099453a90eb57e045a7308a4882dc87cab /lib
parent537dffa88eeb7b573aa8176270f0635085de4706 (diff)
downloadrainbows-cbaa604582a9f80eba3fc0c2423234f497f21726.tar.gz
Diffstat (limited to 'lib')
-rw-r--r--lib/rainbows.rb1
-rw-r--r--lib/rainbows/app_pool.rb90
2 files changed, 91 insertions, 0 deletions
diff --git a/lib/rainbows.rb b/lib/rainbows.rb
index c2813a5..7978288 100644
--- a/lib/rainbows.rb
+++ b/lib/rainbows.rb
@@ -7,6 +7,7 @@ module Rainbows
   require 'rainbows/http_server'
   require 'rainbows/http_response'
   require 'rainbows/base'
+  autoload :AppPool, 'rainbows/app_pool'
 
   class << self
 
diff --git a/lib/rainbows/app_pool.rb b/lib/rainbows/app_pool.rb
new file mode 100644
index 0000000..c0e1df3
--- /dev/null
+++ b/lib/rainbows/app_pool.rb
@@ -0,0 +1,90 @@
+# -*- encoding: binary -*-
+
+require 'thread'
+
+module Rainbows
+
+  # Rack middleware to limit application-level concurrency independently
+  # of network conncurrency in \Rainbows!   Since the +worker_connections+
+  # option in \Rainbows! is only intended to limit the number of
+  # simultaneous clients, this middleware may be used to limit the
+  # number of concurrent application dispatches independently of
+  # concurrent clients.
+  #
+  # Instead of using M:N concurrency in \Rainbows!, this middleware
+  # allows M:N:P concurrency where +P+ is the AppPool +:size+ while
+  # +M+ remains the number of +worker_processes+ and +N+ remains the
+  # number of +worker_connections+.
+  #
+  #   rainbows master
+  #    \_ rainbows worker[0]
+  #    |  \_ client[0,0]------\      ___app[0]
+  #    |  \_ client[0,1]-------\    /___app[1]
+  #    |  \_ client[0,2]-------->--<       ...
+  #    |  ...                __/    `---app[P]
+  #    |  \_ client[0,N]----/
+  #    \_ rainbows worker[1]
+  #    |  \_ client[1,0]------\      ___app[0]
+  #    |  \_ client[1,1]-------\    /___app[1]
+  #    |  \_ client[1,2]-------->--<       ...
+  #    |  ...                __/    `---app[P]
+  #    |  \_ client[1,N]----/
+  #    \_ rainbows worker[M]
+  #       \_ client[M,0]------\      ___app[0]
+  #       \_ client[M,1]-------\    /___app[1]
+  #       \_ client[M,2]-------->--<       ...
+  #       ...                __/    `---app[P]
+  #       \_ client[M,N]----/
+  #
+  # AppPool should be used if you want to enforce a lower value of +P+
+  # than +N+.
+  #
+  # AppPool has no effect on the Rainbows::Rev concurrency model as that is
+  # single-threaded/single-instance as far as application concurrency goes.
+  # In other words, +P+ is always +one+ when using \Rev (but not
+  # \Revactor) regardless of (or even if) this middleware is loaded.
+  #
+  # Since this is Rack middleware, you may load this in your Rack
+  # config.ru file and even use it in servers other than \Rainbows!
+  #
+  #   use Rainbows::AppPool, :size => 30
+  #   map "/lobster" do
+  #     run Rack::Lobster.new
+  #   end
+  #
+  # You may to load this earlier or later in your middleware chain
+  # depending on the concurrency/copy-friendliness of your middleware(s).
+
+  class AppPool < Struct.new(:pool)
+
+    # +opt+ is a hash, +:size+ is the size of the pool (default: 6)
+    # meaning you can have up to 6 concurrent instances of +app+
+    # within one \Rainbows! worker process.  We support various
+    # methods of the +:copy+ option: +dup+, +clone+, +deep+ and +none+.
+    # Depending on your +app+, one of these options should be set.
+    # The default +:copy+ is +:dup+ as is commonly seen in existing
+    # Rack middleware.
+    def initialize(app, opt = {})
+      self.pool = Queue.new
+      (1...(opt[:size] || 6)).each do
+        pool << case (opt[:copy] || :dup)
+        when :none then app
+        when :dup then app.dup
+        when :clone then app.clone
+        when :deep then Marshal.load(Marshal.dump(app)) # unlikely...
+        else
+          raise ArgumentError, "unsupported copy method: #{opt[:copy].inspect}"
+        end
+      end
+      pool << app # the original
+    end
+
+    # Rack application endpoint, +env+ is the Rack environment
+    def call(env)
+      app = pool.shift
+      app.call(env)
+      ensure
+        pool << app
+    end
+  end
+end