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. AppPool currently only works with the
46 # ThreadSpawn and ThreadPool models. It does not yet work reliably
47 # with the Revactor model, but actors are far more lightweight and
48 # probably better suited for lightweight applications that would
49 # not benefit from AppPool.
51 # Since this is Rack middleware, you may load this in your Rack
52 # config.ru file and even use it in threaded servers other than
55 # use Rainbows::AppPool, :size => 30
57 # run Rack::Lobster.new
60 # You may to load this earlier or later in your middleware chain
61 # depending on the concurrency/copy-friendliness of your middleware(s).
63 class AppPool < Struct.new(:pool)
65 # +opt+ is a hash, +:size+ is the size of the pool (default: 6)
66 # meaning you can have up to 6 concurrent instances of +app+
67 # within one \Rainbows! worker process. We support various
68 # methods of the +:copy+ option: +dup+, +clone+, +deep+ and +none+.
69 # Depending on your +app+, one of these options should be set.
70 # The default +:copy+ is +:dup+ as is commonly seen in existing
72 def initialize(app, opt = {})
74 (1...(opt[:size] || 6)).each do
75 pool << case (opt[:copy] || :dup)
77 when :dup then app.dup
78 when :clone then app.clone
79 when :deep then Marshal.load(Marshal.dump(app)) # unlikely...
81 raise ArgumentError, "unsupported copy method: #{opt[:copy].inspect}"
84 pool << app # the original
87 # Rack application endpoint, +env+ is the Rack environment