unindent most files
[rainbows.git] / lib / rainbows / app_pool.rb
blobb406b320ae717c9e6c00b8335f28fd8e2f31935c
1 # -*- encoding: binary -*-
3 require 'thread'
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
10 # concurrent clients.
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+.
17 #   rainbows master
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]-------->--<       ...
34 #       ...                __/    `---app[P]
35 #       \_ client[M,N]----/
37 # AppPool should be used if you want to enforce a lower value of +P+
38 # than +N+.
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
48 # \Rainbows!
50 #   use Rainbows::AppPool, :size => 30
51 #   map "/lobster" do
52 #     run Rack::Lobster.new
53 #   end
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
65   # Rack middleware.
66   def initialize(app, opt = {})
67     self.pool = Queue.new
68     (1...(opt[:size] || 6)).each do
69       pool << case (opt[:copy] || :dup)
70       when :none then app
71       when :dup then app.dup
72       when :clone then app.clone
73       when :deep then Marshal.load(Marshal.dump(app)) # unlikely...
74       else
75         raise ArgumentError, "unsupported copy method: #{opt[:copy].inspect}"
76       end
77     end
78     pool << app # the original
79   end
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
88     # concurrency models
89     self.re ||= begin
90       case env["rainbows.model"]
91       when :FiberSpawn, :FiberPool, :Revactor, :NeverBlock, :RevFiberSpawn
92         self.pool = Rainbows::Fiber::Queue.new(pool)
93       end
94       true
95     end
97     app = pool.shift
98     app.call(env)
99     ensure
100       pool << app
101   end