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