rainbows 0.92.0 → 0.93.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Documentation/comparison.haml +45 -6
- data/FAQ +10 -4
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +3 -3
- data/README +2 -0
- data/Rakefile +16 -2
- data/config/isolate.rb +2 -2
- data/lib/rainbows.rb +3 -0
- data/lib/rainbows/base.rb +2 -0
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/queue_pool.rb +32 -0
- data/lib/rainbows/thread_spawn.rb +3 -0
- data/lib/rainbows/writer_thread_pool.rb +90 -0
- data/lib/rainbows/writer_thread_spawn.rb +113 -0
- data/local.mk.sample +1 -1
- data/rainbows.gemspec +1 -1
- data/t/.gitignore +1 -0
- data/t/GNUmakefile +2 -0
- data/t/simple-http_WriterThreadPool.ru +9 -0
- data/t/simple-http_WriterThreadSpawn.ru +9 -0
- data/t/t0004-heartbeat-timeout.sh +16 -5
- data/t/t0005-large-file-response.sh +8 -4
- data/t/t0007-worker-follows-master-to-death.sh +6 -1
- data/t/t0102-rack-input-short.sh +1 -1
- data/t/t0103-rack-input-limit.sh +1 -0
- data/t/t0104-rack-input-limit-tiny.sh +1 -0
- data/t/t0105-rack-input-limit-bigger.sh +2 -0
- data/t/t0200-async-response.sh +2 -0
- data/t/test-lib.sh +29 -1
- metadata +11 -3
@@ -99,6 +99,20 @@
|
|
99
99
|
%td.r19 Yes
|
100
100
|
%td.rbx No
|
101
101
|
%td.slow Yes
|
102
|
+
%tr.comp_row
|
103
|
+
%td.mod WriterThreadPool
|
104
|
+
%td.tee Yes
|
105
|
+
%td.r18 Yes
|
106
|
+
%td.r19 Yes
|
107
|
+
%td.rbx Yes
|
108
|
+
%td.slow no
|
109
|
+
%tr.comp_row
|
110
|
+
%td.mod WriterThreadSpawn
|
111
|
+
%td.tee Yes
|
112
|
+
%td.r18 Yes
|
113
|
+
%td.r19 Yes
|
114
|
+
%td.rbx Yes
|
115
|
+
%td.slow no
|
102
116
|
%ul
|
103
117
|
%li
|
104
118
|
RevThread* + 1.8 requires Rev >= 0.3.2 for reasonable performance
|
@@ -203,7 +217,16 @@
|
|
203
217
|
%a(href="Rainbows/Fiber/IO.html") Rainbows::Fiber::IO
|
204
218
|
%td.thr No
|
205
219
|
%td.reent Yes
|
206
|
-
|
220
|
+
%tr.comp_base
|
221
|
+
%td.mod WriterThreadPool
|
222
|
+
%td.slowio avoid
|
223
|
+
%td.thr Maybe
|
224
|
+
%td.reent Maybe
|
225
|
+
%tr.comp_base
|
226
|
+
%td.mod WriterThreadSpawn
|
227
|
+
%td.slowio avoid
|
228
|
+
%td.thr Maybe
|
229
|
+
%td.reent Maybe
|
207
230
|
%ul
|
208
231
|
%li
|
209
232
|
Requirements for single thread reentrancy are loose in that there is
|
@@ -219,6 +242,9 @@
|
|
219
242
|
on UNIX domain sockets and named pipes. Nearly all other operations
|
220
243
|
on POSIX filesystems can be considered "fast", or at least
|
221
244
|
uninterruptible.
|
245
|
+
%li
|
246
|
+
WriterThread{Pool,Spawn} will require thread safety if your response
|
247
|
+
body is dynamically generated during the body#each call.
|
222
248
|
|
223
249
|
%h2 middlewares and frameworks
|
224
250
|
%br
|
@@ -250,7 +276,7 @@
|
|
250
276
|
%td.ws Sunshowers
|
251
277
|
%tr.comp_row
|
252
278
|
%td.mod ThreadPool
|
253
|
-
%td.devfd
|
279
|
+
%td.devfd Yes
|
254
280
|
%td.app_pool Yes
|
255
281
|
%td.lock Yes
|
256
282
|
%td.async standard Ruby
|
@@ -264,7 +290,7 @@
|
|
264
290
|
%td.ws no
|
265
291
|
%tr.comp_row
|
266
292
|
%td.mod ThreadSpawn
|
267
|
-
%td.devfd
|
293
|
+
%td.devfd Yes
|
268
294
|
%td.app_pool Yes
|
269
295
|
%td.lock Yes
|
270
296
|
%td.async standard Ruby
|
@@ -316,15 +342,28 @@
|
|
316
342
|
%td.devfd Yes
|
317
343
|
%td.app_pool Yes
|
318
344
|
%td.lock Dumb
|
319
|
-
%td.async standard Ruby
|
345
|
+
%td.async Rev, standard Ruby
|
320
346
|
%td.ws no
|
321
347
|
%tr.comp_row
|
322
348
|
%td.mod RevFiberSpawn
|
323
349
|
%td.devfd Yes
|
324
350
|
%td.app_pool Yes
|
325
351
|
%td.lock No!
|
326
|
-
%td.async Rainbows::Fiber::IO, Rainbows.sleep
|
327
|
-
|
352
|
+
%td.async Rev, Rainbows::Fiber::IO, Rainbows.sleep
|
353
|
+
%tr.comp_row
|
354
|
+
%td.mod WriterThreadPool
|
355
|
+
%td.devfd Yes
|
356
|
+
%td.app_pool no-op
|
357
|
+
%td.lock no-op
|
358
|
+
%td.async Standard Ruby in response body only
|
359
|
+
%td.ws response body only
|
360
|
+
%tr.comp_row
|
361
|
+
%td.mod WriterThreadSpawn
|
362
|
+
%td.devfd Yes
|
363
|
+
%td.app_pool no-op
|
364
|
+
%td.lock no-op
|
365
|
+
%td.async Standard Ruby in response body only
|
366
|
+
%td.ws response body only
|
328
367
|
%ul
|
329
368
|
%li
|
330
369
|
"No!" means it's fundamentally incompatible, use an
|
data/FAQ
CHANGED
@@ -62,13 +62,19 @@ to ensure redirects go to "https://" URLs.
|
|
62
62
|
|
63
63
|
=== Is there a "rainbows_rails" command like there is "unicorn_rails"?
|
64
64
|
|
65
|
-
|
65
|
+
No.
|
66
66
|
|
67
67
|
"unicorn_rails" was written primarily to support older versions of
|
68
|
-
Rails. Since \Rainbows! is designed for newer
|
69
|
-
a "config.ru" file like other Rack frameworks and
|
68
|
+
Rails. Since \Rainbows! is designed for newer applications based on
|
69
|
+
Rack, it can just use a "config.ru" file like other Rack frameworks and
|
70
|
+
applications.
|
70
71
|
|
71
|
-
For Rails
|
72
|
+
For Rails 3.x, you should already have a config.ru file and
|
73
|
+
"rainbows(1)" will work out-of-the-box like "rackup(1)". Rails 3
|
74
|
+
will support RACK_ENV as set by "rainbows(1)", so you won't need
|
75
|
+
to set RAILS_ENV.
|
76
|
+
|
77
|
+
For Rails 2.3.x, the following config.ru will work for you:
|
72
78
|
|
73
79
|
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"]
|
74
80
|
require "config/environment"
|
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -17,8 +17,8 @@ ifeq ($(RUBY_VERSION),)
|
|
17
17
|
endif
|
18
18
|
|
19
19
|
# rake takes forever to start
|
20
|
-
isolate: tmp/
|
21
|
-
tmp/
|
20
|
+
isolate: tmp/isolate/ruby-$(RUBY_VERSION)/.isolate
|
21
|
+
tmp/isolate/ruby-$(RUBY_VERSION)/.isolate: $(ISOLATE_CONFIG)
|
22
22
|
ISOLATE_CONFIG=$(ISOLATE_CONFIG) $(RAKE) isolate
|
23
23
|
> $@
|
24
24
|
|
@@ -66,7 +66,7 @@ NEWS: GIT-VERSION-FILE
|
|
66
66
|
$(RAKE) -s news_rdoc > $@+
|
67
67
|
mv $@+ $@
|
68
68
|
|
69
|
-
SINCE = 0.
|
69
|
+
SINCE = 0.92.0
|
70
70
|
ChangeLog: LOG_VERSION = \
|
71
71
|
$(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
|
72
72
|
echo $(GIT_VERSION) || git describe)
|
data/README
CHANGED
@@ -24,6 +24,8 @@ For network concurrency, models we currently support are:
|
|
24
24
|
* {FiberPool}[link:Rainbows/FiberPool.html]
|
25
25
|
* {NeverBlock}[link:Rainbows/NeverBlock.html]
|
26
26
|
* {RevThreadPool}[link:Rainbows/RevThreadPool.html]
|
27
|
+
* {WriterThreadPool}[link:Rainbows/WriterThreadPool.html]
|
28
|
+
* {WriterThreadSpawn}[link:Rainbows/WriterThreadSpawn.html]
|
27
29
|
|
28
30
|
We have {many more on the way}[link:TODO.html] for handling network
|
29
31
|
concurrency. Additionally, we also use multiple processes (managed by
|
data/Rakefile
CHANGED
@@ -187,6 +187,20 @@ end
|
|
187
187
|
desc 'isolate gems for development'
|
188
188
|
task :isolate do
|
189
189
|
require 'isolate'
|
190
|
-
|
191
|
-
|
190
|
+
require 'rbconfig'
|
191
|
+
|
192
|
+
Isolate.now! :file => ENV['ISOLATE_CONFIG'], :system => false
|
193
|
+
|
194
|
+
# for Ruby 1.8 isolate uses "1.8" instead of "1.8.7" for paths,
|
195
|
+
# but we'll still try to support 1.8.6 for now even though isolate
|
196
|
+
# does not.
|
197
|
+
if Gem.ruby_engine == "ruby" &&
|
198
|
+
RbConfig::CONFIG["ruby_version"] != RUBY_VERSION
|
199
|
+
require 'fileutils'
|
200
|
+
Dir.chdir('tmp/isolate') do
|
201
|
+
FileUtils.rm_rf("ruby-#{RUBY_VERSION}")
|
202
|
+
File.symlink "ruby-#{RbConfig::CONFIG["ruby_version"]}",
|
203
|
+
"ruby-#{RUBY_VERSION}"
|
204
|
+
end
|
205
|
+
end
|
192
206
|
end
|
data/config/isolate.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# `rake isolate' or (faster in the unmodified case, `make isolate')
|
5
5
|
|
6
6
|
gem 'rack', '1.1.0'
|
7
|
-
gem 'unicorn', '0.
|
7
|
+
gem 'unicorn', '0.99.0'
|
8
8
|
|
9
9
|
gem 'iobuffer', '0.1.3'
|
10
10
|
gem 'rev', '0.3.2'
|
@@ -22,4 +22,4 @@ if defined?(::Fiber)
|
|
22
22
|
gem 'rack-fiber_pool', '0.9.0'
|
23
23
|
end
|
24
24
|
|
25
|
-
gem 'cramp', '0.
|
25
|
+
gem 'cramp', '0.11'
|
data/lib/rainbows.rb
CHANGED
@@ -35,6 +35,7 @@ module Rainbows
|
|
35
35
|
autoload :AppPool, 'rainbows/app_pool'
|
36
36
|
autoload :DevFdResponse, 'rainbows/dev_fd_response'
|
37
37
|
autoload :MaxBody, 'rainbows/max_body'
|
38
|
+
autoload :QueuePool, 'rainbows/queue_pool'
|
38
39
|
|
39
40
|
class << self
|
40
41
|
|
@@ -133,6 +134,8 @@ module Rainbows
|
|
133
134
|
# highly recommended
|
134
135
|
MODEL_WORKER_CONNECTIONS = {
|
135
136
|
:Base => 1, # this one can't change
|
137
|
+
:WriterThreadPool => 20,
|
138
|
+
:WriterThreadSpawn => 20,
|
136
139
|
:Revactor => 50,
|
137
140
|
:ThreadSpawn => 30,
|
138
141
|
:ThreadPool => 20,
|
data/lib/rainbows/base.rb
CHANGED
data/lib/rainbows/const.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Rainbows
|
5
|
+
|
6
|
+
# Thread pool class based on pulling off a single Ruby Queue.
|
7
|
+
# This is NOT used for the ThreadPool class, since that class does not
|
8
|
+
# need a userspace Queue.
|
9
|
+
class QueuePool < Struct.new(:queue, :threads)
|
10
|
+
G = Rainbows::G
|
11
|
+
|
12
|
+
def initialize(size = 20, &block)
|
13
|
+
q = Queue.new
|
14
|
+
self.threads = (1..size).map do
|
15
|
+
Thread.new do
|
16
|
+
while job = q.shift
|
17
|
+
block.call(job)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
self.queue = q
|
22
|
+
end
|
23
|
+
|
24
|
+
def quit!
|
25
|
+
threads.each { |_| queue << nil }
|
26
|
+
threads.delete_if do |t|
|
27
|
+
G.tick
|
28
|
+
t.alive? ? t.join(0.01) : true
|
29
|
+
end until threads.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -33,6 +33,9 @@ module Rainbows
|
|
33
33
|
# synchronization primitives for _every_ case, not just this
|
34
34
|
# unlikely one. Since this case is (or should be) uncommon,
|
35
35
|
# just busy wait when we have to.
|
36
|
+
# We don't use Thread.pass because it needlessly spins the
|
37
|
+
# CPU during I/O wait, CPU cycles that can be better used
|
38
|
+
# by other worker _processes_.
|
36
39
|
sleep(0.01)
|
37
40
|
elsif c = Rainbows.sync_accept(l)
|
38
41
|
klass.new(c) do |c|
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
module Rainbows
|
4
|
+
|
5
|
+
# This concurrency model implements a single-threaded app dispatch
|
6
|
+
# with a separate thread pool for writing responses.
|
7
|
+
#
|
8
|
+
# Unlike most \Rainbows! concurrency models, WriterThreadPool is
|
9
|
+
# designed to run behind nginx just like Unicorn is. This concurrency
|
10
|
+
# model may be useful for existing Unicorn users looking for more
|
11
|
+
# output concurrency than socket buffers can provide while still
|
12
|
+
# maintaining a single-threaded application dispatch (though if the
|
13
|
+
# response body is dynamically generated, it must be thread safe).
|
14
|
+
#
|
15
|
+
# For serving large or streaming responses, using more threads (via
|
16
|
+
# the +worker_connections+ setting) and setting "proxy_buffering off"
|
17
|
+
# in nginx is recommended. If your application does not handle
|
18
|
+
# uploads, then using any HTTP-aware proxy like haproxy is fine.
|
19
|
+
# Using a non-HTTP-aware proxy will leave you vulnerable to
|
20
|
+
# slow client denial-of-service attacks.
|
21
|
+
|
22
|
+
module WriterThreadPool
|
23
|
+
include Base
|
24
|
+
|
25
|
+
# used to wrap a BasicSocket to use with +q+ for all writes
|
26
|
+
# this is compatible with IO.select
|
27
|
+
class QueueSocket < Struct.new(:to_io, :q)
|
28
|
+
def readpartial(size, buf = "")
|
29
|
+
to_io.readpartial(size, buf)
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_nonblock(buf)
|
33
|
+
to_io.write_nonblock(buf)
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(buf)
|
37
|
+
q << [ to_io, buf ]
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
q << [ to_io, :close ]
|
42
|
+
end
|
43
|
+
|
44
|
+
def closed?
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
alias base_write_body write_body
|
50
|
+
if IO.respond_to?(:copy_stream)
|
51
|
+
undef_method :write_body
|
52
|
+
|
53
|
+
def write_body(qclient, body)
|
54
|
+
qclient.q << [ qclient.to_io, :body, body ]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
@@nr = 0
|
59
|
+
@@q = nil
|
60
|
+
|
61
|
+
def process_client(client)
|
62
|
+
@@nr += 1
|
63
|
+
super(QueueSocket[client, @@q[@@nr %= @@q.size]])
|
64
|
+
end
|
65
|
+
|
66
|
+
def worker_loop(worker)
|
67
|
+
# we have multiple, single-thread queues since we don't want to
|
68
|
+
# interleave writes from the same client
|
69
|
+
qp = (1..worker_connections).map do |n|
|
70
|
+
QueuePool.new(1) do |response|
|
71
|
+
begin
|
72
|
+
io, arg1, arg2 = response
|
73
|
+
case arg1
|
74
|
+
when :body then base_write_body(io, arg2)
|
75
|
+
when :close then io.close unless io.closed?
|
76
|
+
else
|
77
|
+
io.write(arg1)
|
78
|
+
end
|
79
|
+
rescue => err
|
80
|
+
Error.app(err)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@@q = qp.map { |q| q.queue }
|
86
|
+
super(worker) # accept loop from Unicorn
|
87
|
+
qp.map { |q| q.quit! }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'thread'
|
3
|
+
module Rainbows
|
4
|
+
|
5
|
+
# This concurrency model implements a single-threaded app dispatch and
|
6
|
+
# spawns a new thread for writing responses. This concurrency model
|
7
|
+
# should be ideal for apps that serve large responses or stream
|
8
|
+
# responses slowly.
|
9
|
+
#
|
10
|
+
# Unlike most \Rainbows! concurrency models, WriterThreadSpawn is
|
11
|
+
# designed to run behind nginx just like Unicorn is. This concurrency
|
12
|
+
# model may be useful for existing Unicorn users looking for more
|
13
|
+
# output concurrency than socket buffers can provide while still
|
14
|
+
# maintaining a single-threaded application dispatch (though if the
|
15
|
+
# response body is generated on-the-fly, it must be thread safe).
|
16
|
+
#
|
17
|
+
# For serving large or streaming responses, setting
|
18
|
+
# "proxy_buffering off" in nginx is recommended. If your application
|
19
|
+
# does not handle uploads, then using any HTTP-aware proxy like
|
20
|
+
# haproxy is fine. Using a non-HTTP-aware proxy will leave you
|
21
|
+
# vulnerable to slow client denial-of-service attacks.
|
22
|
+
|
23
|
+
module WriterThreadSpawn
|
24
|
+
include Base
|
25
|
+
|
26
|
+
CUR = {}
|
27
|
+
|
28
|
+
# used to wrap a BasicSocket to use with +q+ for all writes
|
29
|
+
# this is compatible with IO.select
|
30
|
+
class MySocket < Struct.new(:to_io, :q, :thr)
|
31
|
+
def readpartial(size, buf = "")
|
32
|
+
to_io.readpartial(size, buf)
|
33
|
+
end
|
34
|
+
|
35
|
+
def write_nonblock(buf)
|
36
|
+
to_io.write_nonblock(buf)
|
37
|
+
end
|
38
|
+
|
39
|
+
def queue_writer
|
40
|
+
# not using Thread.pass here because that spins the CPU during
|
41
|
+
# I/O wait and will eat cycles from other worker processes.
|
42
|
+
until CUR.size < MAX
|
43
|
+
CUR.delete_if { |t,_|
|
44
|
+
t.alive? ? t.join(0) : true
|
45
|
+
}.size >= MAX and sleep(0.01)
|
46
|
+
end
|
47
|
+
|
48
|
+
q = Queue.new
|
49
|
+
self.thr = Thread.new(to_io, q) do |io, q|
|
50
|
+
while response = q.shift
|
51
|
+
begin
|
52
|
+
arg1, arg2 = response
|
53
|
+
case arg1
|
54
|
+
when :body then Base.write_body(io, arg2)
|
55
|
+
when :close
|
56
|
+
io.close unless io.closed?
|
57
|
+
break
|
58
|
+
else
|
59
|
+
io.write(arg1)
|
60
|
+
end
|
61
|
+
rescue => e
|
62
|
+
Error.app(e)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
CUR.delete(Thread.current)
|
66
|
+
end
|
67
|
+
CUR[thr] = q
|
68
|
+
end
|
69
|
+
|
70
|
+
def write(buf)
|
71
|
+
(self.q ||= queue_writer) << buf
|
72
|
+
end
|
73
|
+
|
74
|
+
def write_body(body)
|
75
|
+
(self.q ||= queue_writer) << [ :body, body ]
|
76
|
+
end
|
77
|
+
|
78
|
+
def close
|
79
|
+
if q
|
80
|
+
q << :close
|
81
|
+
else
|
82
|
+
to_io.close
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def closed?
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
if IO.respond_to?(:copy_stream)
|
92
|
+
undef_method :write_body
|
93
|
+
|
94
|
+
def write_body(my_sock, body)
|
95
|
+
my_sock.write_body(body)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def process_client(client)
|
100
|
+
super(MySocket[client])
|
101
|
+
end
|
102
|
+
|
103
|
+
def worker_loop(worker)
|
104
|
+
MySocket.const_set(:MAX, worker_connections)
|
105
|
+
super(worker) # accept loop from Unicorn
|
106
|
+
CUR.delete_if do |t,q|
|
107
|
+
q << nil
|
108
|
+
G.tick
|
109
|
+
t.alive? ? t.join(0.01) : true
|
110
|
+
end until CUR.empty?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/local.mk.sample
CHANGED
@@ -27,7 +27,7 @@ endif
|
|
27
27
|
RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
|
28
28
|
|
29
29
|
updir := $(shell git rev-parse --show-cdup)
|
30
|
-
gem_paths := $(wildcard $(updir)tmp/
|
30
|
+
gem_paths := $(wildcard $(updir)tmp/isolate/ruby-$(RUBY_VERSION)/gems/*-*)
|
31
31
|
|
32
32
|
ifdef gem_paths
|
33
33
|
sp :=
|
data/rainbows.gemspec
CHANGED
@@ -43,7 +43,7 @@ Gem::Specification.new do |s|
|
|
43
43
|
# we need Unicorn for the HTTP parser and process management
|
44
44
|
# The HTTP parser in Unicorn <= 0.97.0 was vulnerable to a remote DoS
|
45
45
|
# when exposed directly to untrusted clients.
|
46
|
-
s.add_dependency(%q<unicorn>, [">= 0.97.1", "<
|
46
|
+
s.add_dependency(%q<unicorn>, [">= 0.97.1", "< 2.0.0"])
|
47
47
|
|
48
48
|
# Unicorn already depends on Rack
|
49
49
|
# s.add_dependency(%q<rack>)
|
data/t/.gitignore
CHANGED
data/t/GNUmakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/bin/sh
|
2
2
|
. ./test-lib.sh
|
3
3
|
|
4
|
-
t_plan
|
4
|
+
t_plan 12 "heartbeat/timeout test for $model"
|
5
5
|
|
6
6
|
t_begin "setup and startup" && {
|
7
7
|
rainbows_setup $model
|
@@ -22,8 +22,9 @@ t_begin "sleep for a bit, ensure worker PID does not change" && {
|
|
22
22
|
}
|
23
23
|
|
24
24
|
t_begin "block the worker process to force it to die" && {
|
25
|
+
rm $ok
|
25
26
|
t0=$(date +%s)
|
26
|
-
err="$(curl -sSf http://$listen/block-forever 2>&1 ||
|
27
|
+
err="$(curl -sSf http://$listen/block-forever 2>&1 || > $ok)"
|
27
28
|
t1=$(date +%s)
|
28
29
|
elapsed=$(($t1 - $t0))
|
29
30
|
t_info "elapsed=$elapsed err=$err"
|
@@ -31,9 +32,13 @@ t_begin "block the worker process to force it to die" && {
|
|
31
32
|
test x"$err" != x"$worker_pid"
|
32
33
|
}
|
33
34
|
|
34
|
-
t_begin "ensure
|
35
|
+
t_begin "ensure worker was killed" && {
|
36
|
+
test -e $ok
|
37
|
+
test 1 -eq $(grep timeout $r_err | grep killing | wc -l)
|
38
|
+
}
|
39
|
+
|
40
|
+
t_begin "ensure timeout took at least 3 seconds" && {
|
35
41
|
test $elapsed -ge 3
|
36
|
-
test $elapsed -le 6 # give it some slack in case box is bogged down
|
37
42
|
}
|
38
43
|
|
39
44
|
t_begin "wait for new worker to start up" && {
|
@@ -45,6 +50,10 @@ t_begin "we get a fresh new worker process" && {
|
|
45
50
|
test $new_worker_pid -ne $worker_pid
|
46
51
|
}
|
47
52
|
|
53
|
+
t_begin "truncate the server error log" && {
|
54
|
+
> $r_err
|
55
|
+
}
|
56
|
+
|
48
57
|
t_begin "SIGSTOP and SIGCONT on rainbows master does not kill worker" && {
|
49
58
|
kill -STOP $rainbows_pid
|
50
59
|
sleep 4
|
@@ -54,9 +63,11 @@ t_begin "SIGSTOP and SIGCONT on rainbows master does not kill worker" && {
|
|
54
63
|
}
|
55
64
|
|
56
65
|
t_begin "stop server" && {
|
57
|
-
kill $rainbows_pid
|
66
|
+
kill -QUIT $rainbows_pid
|
58
67
|
}
|
59
68
|
|
69
|
+
t_begin "check stderr" && check_stderr
|
70
|
+
|
60
71
|
dbgcat r_err
|
61
72
|
|
62
73
|
t_done
|
@@ -12,7 +12,7 @@ t_plan 10 "large file response slurp avoidance for $model"
|
|
12
12
|
|
13
13
|
t_begin "setup and startup" && {
|
14
14
|
rtmpfiles curl_out
|
15
|
-
rainbows_setup $model
|
15
|
+
rainbows_setup $model 1
|
16
16
|
# can't load Rack::Lint here since it'll cause Rev to slurp
|
17
17
|
rainbows -E none -D large-file-response.ru -c $unicorn_config
|
18
18
|
rainbows_wait_start
|
@@ -50,11 +50,11 @@ t_begin "HTTP/1.0 test" && {
|
|
50
50
|
t_begin "HTTP/0.9 test" && {
|
51
51
|
(
|
52
52
|
printf 'GET /random_blob\r\n'
|
53
|
-
|
53
|
+
rsha1 < $fifo > $tmp &
|
54
54
|
wait
|
55
55
|
echo ok > $ok
|
56
56
|
) | socat - TCP:$listen > $fifo
|
57
|
-
|
57
|
+
test $(cat $tmp) = $(rsha1 < random_blob)
|
58
58
|
test xok = x$(cat $ok)
|
59
59
|
}
|
60
60
|
|
@@ -72,8 +72,12 @@ t_begin "shutdown server" && {
|
|
72
72
|
|
73
73
|
t_begin "compare RSS before and after" && {
|
74
74
|
diff=$(( $rss_after - $rss_before ))
|
75
|
+
|
76
|
+
# default GC malloc limit in MRI:
|
77
|
+
fudge=$(( 8 * 1024 * 1024 ))
|
78
|
+
|
75
79
|
t_info "test diff=$diff < orig=$random_blob_size"
|
76
|
-
test $diff -le $random_blob_size
|
80
|
+
test $diff -le $(( $random_blob_size + $fudge ))
|
77
81
|
}
|
78
82
|
|
79
83
|
dbgcat r_err
|
@@ -32,7 +32,12 @@ t_begin "nuke the master once we're connected" && {
|
|
32
32
|
}
|
33
33
|
|
34
34
|
t_begin "worker is no longer running" && {
|
35
|
-
|
35
|
+
nr=30
|
36
|
+
while kill -0 $worker_pid 2>/dev/null && test $nr -gt 0
|
37
|
+
do
|
38
|
+
nr=$(( $nr - 1))
|
39
|
+
sleep 1
|
40
|
+
done
|
36
41
|
kill -0 $worker_pid 2> $tmp && false
|
37
42
|
test -s $tmp
|
38
43
|
}
|
data/t/t0102-rack-input-short.sh
CHANGED
@@ -8,7 +8,7 @@ t_begin "setup and startup" && {
|
|
8
8
|
rtmpfiles curl_out curl_err
|
9
9
|
rainbows_setup $model
|
10
10
|
rainbows -D sha1-random-size.ru -c $unicorn_config
|
11
|
-
blob_sha1=$(rsha1 random_blob)
|
11
|
+
blob_sha1=$(rsha1 < random_blob)
|
12
12
|
t_info "blob_sha1=$blob_sha1"
|
13
13
|
rainbows_wait_start
|
14
14
|
}
|
data/t/t0103-rack-input-limit.sh
CHANGED
data/t/t0200-async-response.sh
CHANGED
data/t/test-lib.sh
CHANGED
@@ -42,6 +42,19 @@ require_check () {
|
|
42
42
|
fi
|
43
43
|
}
|
44
44
|
|
45
|
+
skip_models () {
|
46
|
+
for i in "$@"
|
47
|
+
do
|
48
|
+
if test x"$model" != x"$i"
|
49
|
+
then
|
50
|
+
continue
|
51
|
+
fi
|
52
|
+
t_info "skipping $T since it is not compatible with $model"
|
53
|
+
exit 0
|
54
|
+
done
|
55
|
+
}
|
56
|
+
|
57
|
+
|
45
58
|
# given a list of variable names, create temporary files and assign
|
46
59
|
# the pathnames to those variables
|
47
60
|
rtmpfiles () {
|
@@ -146,7 +159,22 @@ rsha1 () {
|
|
146
159
|
|
147
160
|
# last resort, see comments in sha1sum.rb for reasoning
|
148
161
|
test -n "$_cmd" || _cmd=sha1sum.rb
|
149
|
-
expr "$($_cmd
|
162
|
+
expr "$($_cmd)" : '\([a-f0-9]\{40\}\)'
|
163
|
+
}
|
164
|
+
|
165
|
+
req_curl_chunked_upload_err_check () {
|
166
|
+
set +e
|
167
|
+
curl --version 2>/dev/null | awk '$1 == "curl" {
|
168
|
+
split($2, v, /\./)
|
169
|
+
if ((v[1] < 7) || (v[1] == 7 && v[2] < 18))
|
170
|
+
code = 1
|
171
|
+
}
|
172
|
+
END { exit(code) }'
|
173
|
+
if test $? -ne 0
|
174
|
+
then
|
175
|
+
t_info "curl >= 7.18.0 required for $T"
|
176
|
+
exit 0
|
177
|
+
fi
|
150
178
|
}
|
151
179
|
|
152
180
|
case $model in
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rainbows
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.93.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rainbows! hackers
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-05-
|
12
|
+
date: 2010-05-29 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -23,7 +23,7 @@ dependencies:
|
|
23
23
|
version: 0.97.1
|
24
24
|
- - <
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 2.0.0
|
27
27
|
version:
|
28
28
|
description: |-
|
29
29
|
\Rainbows! is an HTTP server for sleepy Rack applications. It is based on
|
@@ -61,6 +61,7 @@ extra_rdoc_files:
|
|
61
61
|
- lib/rainbows/max_body.rb
|
62
62
|
- lib/rainbows/never_block.rb
|
63
63
|
- lib/rainbows/never_block/event_machine.rb
|
64
|
+
- lib/rainbows/queue_pool.rb
|
64
65
|
- lib/rainbows/rev.rb
|
65
66
|
- lib/rainbows/rev/client.rb
|
66
67
|
- lib/rainbows/rev/core.rb
|
@@ -75,6 +76,8 @@ extra_rdoc_files:
|
|
75
76
|
- lib/rainbows/tee_input.rb
|
76
77
|
- lib/rainbows/thread_pool.rb
|
77
78
|
- lib/rainbows/thread_spawn.rb
|
79
|
+
- lib/rainbows/writer_thread_pool.rb
|
80
|
+
- lib/rainbows/writer_thread_spawn.rb
|
78
81
|
- LICENSE
|
79
82
|
- NEWS
|
80
83
|
- README
|
@@ -131,6 +134,7 @@ files:
|
|
131
134
|
- lib/rainbows/max_body.rb
|
132
135
|
- lib/rainbows/never_block.rb
|
133
136
|
- lib/rainbows/never_block/event_machine.rb
|
137
|
+
- lib/rainbows/queue_pool.rb
|
134
138
|
- lib/rainbows/rev.rb
|
135
139
|
- lib/rainbows/rev/client.rb
|
136
140
|
- lib/rainbows/rev/core.rb
|
@@ -145,6 +149,8 @@ files:
|
|
145
149
|
- lib/rainbows/tee_input.rb
|
146
150
|
- lib/rainbows/thread_pool.rb
|
147
151
|
- lib/rainbows/thread_spawn.rb
|
152
|
+
- lib/rainbows/writer_thread_pool.rb
|
153
|
+
- lib/rainbows/writer_thread_spawn.rb
|
148
154
|
- local.mk.sample
|
149
155
|
- man/man1/rainbows.1
|
150
156
|
- rainbows.gemspec
|
@@ -187,6 +193,8 @@ files:
|
|
187
193
|
- t/simple-http_Revactor.ru
|
188
194
|
- t/simple-http_ThreadPool.ru
|
189
195
|
- t/simple-http_ThreadSpawn.ru
|
196
|
+
- t/simple-http_WriterThreadPool.ru
|
197
|
+
- t/simple-http_WriterThreadSpawn.ru
|
190
198
|
- t/sleep.ru
|
191
199
|
- t/t0000-simple-http.sh
|
192
200
|
- t/t0000.ru
|