1 # -*- encoding: binary -*-
5 # Rack middleware to limit application-level concurrency independently
6 # of network conncurrency in \Rainbows! Since the +worker_connections+
7 # option in \Rainbows! is only intended to limit the number of
8 # simultaneous clients, this middleware may be used to limit the
9 # number of concurrent application dispatches independently of
12 # Instead of using M:N concurrency in \Rainbows!, this middleware
13 # allows M:N:P concurrency where +P+ is the AppPool +:size+ while
14 # +M+ remains the number of +worker_processes+ and +N+ remains the
15 # number of +worker_connections+.
18 # \_ rainbows worker[0]
19 # | \_ client[0,0]------\ ___app[0]
20 # | \_ client[0,1]-------\ /___app[1]
21 # | \_ client[0,2]-------->--< ...
22 # | ... __/ `---app[P]
23 # | \_ client[0,N]----/
24 # \_ rainbows worker[1]
25 # | \_ client[1,0]------\ ___app[0]
26 # | \_ client[1,1]-------\ /___app[1]
27 # | \_ client[1,2]-------->--< ...
28 # | ... __/ `---app[P]
29 # | \_ client[1,N]----/
30 # \_ rainbows worker[M]
31 # \_ client[M,0]------\ ___app[0]
32 # \_ client[M,1]-------\ /___app[1]
33 # \_ client[M,2]-------->--< ...
37 # AppPool should be used if you want to enforce a lower value of +P+
40 # AppPool has no effect on the Rev or EventMachine concurrency models
41 # as those are single-threaded/single-instance as far as application
42 # concurrency goes. In other words, +P+ is always +one+ when using
43 # Rev or EventMachine. As of \Rainbows! 0.7.0, it is safe to use with
44 # Revactor and the new FiberSpawn and FiberPool concurrency models.
46 # Since this is Rack middleware, you may load this in your Rack
47 # config.ru file and even use it in threaded servers other than
50 # use Rainbows::AppPool, :size => 30
52 # run Rack::Lobster.new
55 # You may to load this earlier or later in your middleware chain
56 # depending on the concurrency/copy-friendliness of your middleware(s).
57 class Rainbows::AppPool < Struct.new(:pool, :re)
59 # +opt+ is a hash, +:size+ is the size of the pool (default: 6)
60 # meaning you can have up to 6 concurrent instances of +app+
61 # within one \Rainbows! worker process. We support various
62 # methods of the +:copy+ option: +dup+, +clone+, +deep+ and +none+.
63 # Depending on your +app+, one of these options should be set.
64 # The default +:copy+ is +:dup+ as is commonly seen in existing
66 def initialize(app, opt = {})
68 (1...(opt[:size] || 6)).each do
69 pool << case (opt[:copy] || :dup)
71 when :dup then app.dup
72 when :clone then app.clone
73 when :deep then Marshal.load(Marshal.dump(app)) # unlikely...
75 raise ArgumentError, "unsupported copy method: #{opt[:copy].inspect}"
78 pool << app # the original
81 # Rack application endpoint, +env+ is the Rack environment
82 def call(env) # :nodoc:
84 # we have to do this check at call time (and not initialize)
85 # because of preload_app=true and models being changeable with SIGHUP
86 # fortunately this is safe for all the reentrant (but not multithreaded)
87 # classes that depend on it and a safe no-op for multithreaded
90 case env["rainbows.model"]
91 when :FiberSpawn, :FiberPool, :Revactor, :NeverBlock,
92 :RevFiberSpawn, :CoolioFiberSpawn
93 self.pool = Rainbows::Fiber::Queue.new(pool)