unicorn 0.2.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +1 -1
- data/.gitignore +1 -0
- data/CHANGELOG +1 -0
- data/DESIGN +4 -0
- data/GNUmakefile +30 -6
- data/Manifest +62 -3
- data/README +52 -42
- data/SIGNALS +17 -17
- data/TODO +27 -5
- data/bin/unicorn +15 -13
- data/bin/unicorn_rails +59 -22
- data/ext/unicorn/http11/http11.c +25 -104
- data/ext/unicorn/http11/http11_parser.c +24 -23
- data/ext/unicorn/http11/http11_parser.h +1 -3
- data/ext/unicorn/http11/http11_parser.rl +2 -1
- data/lib/unicorn.rb +58 -44
- data/lib/unicorn/app/old_rails.rb +23 -0
- data/lib/unicorn/app/old_rails/static.rb +58 -0
- data/lib/unicorn/cgi_wrapper.rb +151 -0
- data/lib/unicorn/configurator.rb +71 -31
- data/lib/unicorn/const.rb +9 -34
- data/lib/unicorn/http_request.rb +63 -66
- data/lib/unicorn/http_response.rb +6 -1
- data/lib/unicorn/socket.rb +15 -2
- data/test/benchmark/README +55 -0
- data/test/benchmark/big_request.rb +35 -0
- data/test/benchmark/dd.ru +18 -0
- data/test/benchmark/request.rb +47 -0
- data/test/benchmark/response.rb +29 -0
- data/test/exec/test_exec.rb +41 -157
- data/test/rails/app-1.2.3/.gitignore +2 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +4 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-1.2.3/config/boot.rb +9 -0
- data/test/rails/app-1.2.3/config/database.yml +12 -0
- data/test/rails/app-1.2.3/config/environment.rb +10 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +7 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +3 -0
- data/test/rails/app-1.2.3/config/routes.rb +4 -0
- data/test/rails/app-1.2.3/db/.gitignore +0 -0
- data/test/rails/app-1.2.3/public/404.html +1 -0
- data/test/rails/app-1.2.3/public/500.html +1 -0
- data/test/rails/app-2.0.2/.gitignore +2 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.0.2/config/boot.rb +9 -0
- data/test/rails/app-2.0.2/config/database.yml +12 -0
- data/test/rails/app-2.0.2/config/environment.rb +14 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +6 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +3 -0
- data/test/rails/app-2.0.2/config/routes.rb +4 -0
- data/test/rails/app-2.0.2/db/.gitignore +0 -0
- data/test/rails/app-2.0.2/public/404.html +1 -0
- data/test/rails/app-2.0.2/public/500.html +1 -0
- data/test/rails/app-2.2.2/.gitignore +2 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.2.2/config/boot.rb +109 -0
- data/test/rails/app-2.2.2/config/database.yml +12 -0
- data/test/rails/app-2.2.2/config/environment.rb +14 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +5 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +3 -0
- data/test/rails/app-2.2.2/config/routes.rb +4 -0
- data/test/rails/app-2.2.2/db/.gitignore +0 -0
- data/test/rails/app-2.2.2/public/404.html +1 -0
- data/test/rails/app-2.2.2/public/500.html +1 -0
- data/test/rails/app-2.3.2.1/.gitignore +2 -0
- data/test/rails/app-2.3.2.1/app/controllers/application_controller.rb +3 -0
- data/test/rails/app-2.3.2.1/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-2.3.2.1/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.3.2.1/config/boot.rb +107 -0
- data/test/rails/app-2.3.2.1/config/database.yml +12 -0
- data/test/rails/app-2.3.2.1/config/environment.rb +14 -0
- data/test/rails/app-2.3.2.1/config/environments/development.rb +5 -0
- data/test/rails/app-2.3.2.1/config/environments/production.rb +4 -0
- data/test/rails/app-2.3.2.1/config/routes.rb +4 -0
- data/test/rails/app-2.3.2.1/db/.gitignore +0 -0
- data/test/rails/app-2.3.2.1/public/404.html +1 -0
- data/test/rails/app-2.3.2.1/public/500.html +1 -0
- data/test/rails/test_rails.rb +243 -0
- data/test/test_helper.rb +149 -2
- data/test/unit/test_configurator.rb +46 -0
- data/test/unit/test_http_parser.rb +77 -36
- data/test/unit/test_request.rb +2 -0
- data/test/unit/test_response.rb +20 -4
- data/test/unit/test_server.rb +30 -1
- data/test/unit/test_socket_helper.rb +159 -0
- data/unicorn.gemspec +5 -5
- metadata +68 -5
- data/test/benchmark/previous.rb +0 -11
- data/test/benchmark/simple.rb +0 -11
- data/test/benchmark/utils.rb +0 -82
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
= Performance
|
|
2
|
+
|
|
3
|
+
Unicorn is pretty fast, and we want it to get faster. Unicorn strives
|
|
4
|
+
to get HTTP requests to your application and write HTTP responses back
|
|
5
|
+
as quickly as possible. Unicorn does not do any background processing
|
|
6
|
+
while your app runs, so your app will get all the CPU time provided to
|
|
7
|
+
it by your OS kernel.
|
|
8
|
+
|
|
9
|
+
A gentle reminder: Unicorn is NOT for serving clients over slow network
|
|
10
|
+
connections. Use nginx (or something similar) to complement Unicorn if
|
|
11
|
+
you have slow clients.
|
|
12
|
+
|
|
13
|
+
== dd.ru
|
|
14
|
+
|
|
15
|
+
This is a pure I/O benchmark. In the context of Unicorn, this is the
|
|
16
|
+
only one that matters. It is a standard rackup-compatible .ru file and
|
|
17
|
+
may be used with other Rack-compatible servers.
|
|
18
|
+
|
|
19
|
+
unicorn -E none dd.ru
|
|
20
|
+
|
|
21
|
+
You can change the size and number of chunks in the response with
|
|
22
|
+
the "bs" and "count" environment variables. The following command
|
|
23
|
+
will cause dd.ru to return 4 chunks of 16384 bytes each, leading to
|
|
24
|
+
65536 byte response:
|
|
25
|
+
|
|
26
|
+
bs=16384 count=4 unicorn -E none dd.ru
|
|
27
|
+
|
|
28
|
+
Or if you want to add logging (small performance impact):
|
|
29
|
+
|
|
30
|
+
unicorn -E deployment dd.ru
|
|
31
|
+
|
|
32
|
+
Eric runs then runs clients on a LAN it in several different ways:
|
|
33
|
+
|
|
34
|
+
client@host1 -> unicorn@host1(tcp)
|
|
35
|
+
client@host2 -> unicorn@host1(tcp)
|
|
36
|
+
client@host3 -> nginx@host1 -> unicorn@host1(tcp)
|
|
37
|
+
client@host3 -> nginx@host1 -> unicorn@host1(unix)
|
|
38
|
+
client@host3 -> nginx@host2 -> unicorn@host1(tcp)
|
|
39
|
+
|
|
40
|
+
The benchmark client is usually httperf.
|
|
41
|
+
|
|
42
|
+
Another gentle reminder: performance with slow networks/clients
|
|
43
|
+
is NOT our problem. That is the job of nginx (or similar).
|
|
44
|
+
|
|
45
|
+
== request.rb, response.rb, big_request.rb
|
|
46
|
+
|
|
47
|
+
These are micro-benchmarks designed to test internal components
|
|
48
|
+
of Unicorn. It assumes the internal Unicorn API is mostly stable.
|
|
49
|
+
|
|
50
|
+
== Contributors
|
|
51
|
+
|
|
52
|
+
This directory is maintained independently in the "benchmark" branch
|
|
53
|
+
based against v0.1.0. Only changes to this directory (test/benchmarks)
|
|
54
|
+
are committed to this branch although the master branch may merge this
|
|
55
|
+
branch occassionaly.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require 'tempfile'
|
|
3
|
+
require 'unicorn'
|
|
4
|
+
nr = ENV['nr'] ? ENV['nr'].to_i : 100
|
|
5
|
+
bs = ENV['bs'] ? ENV['bs'].to_i : (1024 * 1024)
|
|
6
|
+
count = ENV['count'] ? ENV['count'].to_i : 4
|
|
7
|
+
length = bs * count
|
|
8
|
+
slice = (' ' * bs).freeze
|
|
9
|
+
|
|
10
|
+
big = Tempfile.new('')
|
|
11
|
+
def big.unicorn_peeraddr; '127.0.0.1'; end
|
|
12
|
+
big.syswrite(
|
|
13
|
+
"PUT /hello/world/puturl?abcd=efg&hi#anchor HTTP/1.0\r\n" \
|
|
14
|
+
"Host: localhost\r\n" \
|
|
15
|
+
"Accept: */*\r\n" \
|
|
16
|
+
"Content-Length: #{length}\r\n" \
|
|
17
|
+
"User-Agent: test-user-agent 0.1.0 (Mozilla compatible) 5.0 asdfadfasda\r\n" \
|
|
18
|
+
"\r\n")
|
|
19
|
+
count.times { big.syswrite(slice) }
|
|
20
|
+
big.sysseek(0)
|
|
21
|
+
big.fsync
|
|
22
|
+
|
|
23
|
+
include Unicorn
|
|
24
|
+
request = HttpRequest.new(Logger.new($stderr))
|
|
25
|
+
|
|
26
|
+
Benchmark.bmbm do |x|
|
|
27
|
+
x.report("big") do
|
|
28
|
+
for i in 1..nr
|
|
29
|
+
request.read(big)
|
|
30
|
+
request.reset
|
|
31
|
+
big.sysseek(0)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# This benchmark is the simplest test of the I/O facilities in
|
|
2
|
+
# unicorn. It is meant to return a fixed-sized blob to test
|
|
3
|
+
# the performance of things in Unicorn, _NOT_ the app.
|
|
4
|
+
#
|
|
5
|
+
# Adjusting this benchmark is done via the "bs" (byte size) and "count"
|
|
6
|
+
# environment variables. "count" designates the count of elements of
|
|
7
|
+
# "bs" length in the Rack response body. The defaults are bs=4096, count=1
|
|
8
|
+
# to return one 4096-byte chunk.
|
|
9
|
+
bs = ENV['bs'] ? ENV['bs'].to_i : 4096
|
|
10
|
+
count = ENV['count'] ? ENV['count'].to_i : 1
|
|
11
|
+
slice = (' ' * bs).freeze
|
|
12
|
+
body = (1..count).map { slice }.freeze
|
|
13
|
+
hdr = {
|
|
14
|
+
'Content-Length' => (bs * count).to_s.freeze,
|
|
15
|
+
'Content-Type' => 'text/plain'.freeze
|
|
16
|
+
}.freeze
|
|
17
|
+
response = [ 200, hdr, body ].freeze
|
|
18
|
+
run(lambda { |env| response })
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require 'unicorn'
|
|
3
|
+
nr = ENV['nr'] ? ENV['nr'].to_i : 100000
|
|
4
|
+
|
|
5
|
+
class TestClient
|
|
6
|
+
def initialize(response)
|
|
7
|
+
@response = (response.join("\r\n") << "\r\n\r\n").freeze
|
|
8
|
+
end
|
|
9
|
+
def sysread(len, buf)
|
|
10
|
+
buf.replace(@response)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def unicorn_peeraddr
|
|
14
|
+
'127.0.0.1'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
small = TestClient.new([
|
|
19
|
+
'GET / HTTP/1.0',
|
|
20
|
+
'Host: localhost',
|
|
21
|
+
'Accept: */*',
|
|
22
|
+
'User-Agent: test-user-agent 0.1.0'
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
medium = TestClient.new([
|
|
26
|
+
'GET /hello/world/geturl?abcd=efg&hi#anchor HTTP/1.0',
|
|
27
|
+
'Host: localhost',
|
|
28
|
+
'Accept: */*',
|
|
29
|
+
'User-Agent: test-user-agent 0.1.0 (Mozilla compatible) 5.0 asdfadfasda'
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
include Unicorn
|
|
33
|
+
request = HttpRequest.new(Logger.new($stderr))
|
|
34
|
+
Benchmark.bmbm do |x|
|
|
35
|
+
x.report("small") do
|
|
36
|
+
for i in 1..nr
|
|
37
|
+
request.read(small)
|
|
38
|
+
request.reset
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
x.report("medium") do
|
|
42
|
+
for i in 1..nr
|
|
43
|
+
request.read(medium)
|
|
44
|
+
request.reset
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require 'unicorn'
|
|
3
|
+
|
|
4
|
+
class NullWriter
|
|
5
|
+
def syswrite(buf); buf.size; end
|
|
6
|
+
def close; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
include Unicorn
|
|
10
|
+
|
|
11
|
+
socket = NullWriter.new
|
|
12
|
+
bs = ENV['bs'] ? ENV['bs'].to_i : 4096
|
|
13
|
+
count = ENV['count'] ? ENV['count'].to_i : 1
|
|
14
|
+
slice = (' ' * bs).freeze
|
|
15
|
+
body = (1..count).map { slice }.freeze
|
|
16
|
+
hdr = {
|
|
17
|
+
'Content-Length' => (bs * count).to_s.freeze,
|
|
18
|
+
'Content-Type' => 'text/plain'.freeze
|
|
19
|
+
}.freeze
|
|
20
|
+
response = [ 200, hdr, body ].freeze
|
|
21
|
+
|
|
22
|
+
nr = ENV['nr'] ? ENV['nr'].to_i : 100000
|
|
23
|
+
Benchmark.bmbm do |x|
|
|
24
|
+
x.report do
|
|
25
|
+
for i in 1..nr
|
|
26
|
+
HttpResponse.write(socket.dup, response)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/test/exec/test_exec.rb
CHANGED
|
@@ -1,43 +1,34 @@
|
|
|
1
1
|
# Copyright (c) 2009 Eric Wong
|
|
2
|
-
STDIN.sync = STDOUT.sync = STDERR.sync = true
|
|
3
2
|
require 'test/test_helper'
|
|
4
|
-
require 'pathname'
|
|
5
|
-
require 'tempfile'
|
|
6
|
-
require 'fileutils'
|
|
7
3
|
|
|
8
4
|
do_test = true
|
|
9
|
-
DEFAULT_TRIES = 1000
|
|
10
|
-
DEFAULT_RES = 0.2
|
|
11
|
-
|
|
12
5
|
$unicorn_bin = ENV['UNICORN_TEST_BIN'] || "unicorn"
|
|
13
6
|
redirect_test_io do
|
|
14
7
|
do_test = system($unicorn_bin, '-v')
|
|
15
8
|
end
|
|
16
9
|
|
|
17
10
|
unless do_test
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
warn "#{$unicorn_bin} not found in PATH=#{ENV['PATH']}, " \
|
|
12
|
+
"skipping this test"
|
|
20
13
|
end
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
rescue LoadError
|
|
25
|
-
STDERR.puts "Unable to load Rack, skipping this test"
|
|
15
|
+
unless try_require('rack')
|
|
16
|
+
warn "Unable to load Rack, skipping this test"
|
|
26
17
|
do_test = false
|
|
27
18
|
end
|
|
28
19
|
|
|
29
20
|
class ExecTest < Test::Unit::TestCase
|
|
30
|
-
trap(
|
|
21
|
+
trap(:QUIT, 'IGNORE')
|
|
31
22
|
|
|
32
23
|
HI = <<-EOS
|
|
33
24
|
use Rack::ContentLength
|
|
34
|
-
run proc { |env| [ 200, { 'Content-Type' => 'text/plain' }, "HI\\n" ] }
|
|
25
|
+
run proc { |env| [ 200, { 'Content-Type' => 'text/plain' }, [ "HI\\n" ] ] }
|
|
35
26
|
EOS
|
|
36
27
|
|
|
37
28
|
HELLO = <<-EOS
|
|
38
29
|
class Hello
|
|
39
30
|
def call(env)
|
|
40
|
-
[ 200, { 'Content-Type' => 'text/plain' }, "HI\\n" ]
|
|
31
|
+
[ 200, { 'Content-Type' => 'text/plain' }, [ "HI\\n" ] ]
|
|
41
32
|
end
|
|
42
33
|
end
|
|
43
34
|
EOS
|
|
@@ -47,7 +38,6 @@ end
|
|
|
47
38
|
HEAVY_CFG = <<-EOS
|
|
48
39
|
worker_processes 4
|
|
49
40
|
timeout 30
|
|
50
|
-
backlog 128
|
|
51
41
|
logger Logger.new('#{COMMON_TMP.path}')
|
|
52
42
|
before_fork do |server, worker_nr|
|
|
53
43
|
server.logger.info "before_fork: worker=\#{worker_nr}"
|
|
@@ -113,7 +103,7 @@ end
|
|
|
113
103
|
pid_file = "#{@tmpdir}/test.pid"
|
|
114
104
|
old_file = "#{pid_file}.oldbin"
|
|
115
105
|
ucfg = Tempfile.new('unicorn_test_config')
|
|
116
|
-
ucfg.syswrite("
|
|
106
|
+
ucfg.syswrite("listen %(#@addr:#@port)\n")
|
|
117
107
|
ucfg.syswrite("pid %(#{pid_file})\n")
|
|
118
108
|
ucfg.syswrite("logger Logger.new(%(#{@tmpdir}/log))\n")
|
|
119
109
|
pid = xfork do
|
|
@@ -126,14 +116,16 @@ end
|
|
|
126
116
|
|
|
127
117
|
wait_for_file(pid_file)
|
|
128
118
|
Process.waitpid(pid)
|
|
129
|
-
Process.kill(
|
|
119
|
+
Process.kill(:USR2, File.read(pid_file).to_i)
|
|
130
120
|
wait_for_file(old_file)
|
|
131
121
|
wait_for_file(pid_file)
|
|
132
|
-
|
|
122
|
+
old_pid = File.read(old_file).to_i
|
|
123
|
+
Process.kill(:QUIT, old_pid)
|
|
124
|
+
wait_for_death(old_pid)
|
|
133
125
|
|
|
134
126
|
ucfg.syswrite("timeout %(#{pid_file})\n") # introduce a bug
|
|
135
127
|
current_pid = File.read(pid_file).to_i
|
|
136
|
-
Process.kill(
|
|
128
|
+
Process.kill(:USR2, current_pid)
|
|
137
129
|
|
|
138
130
|
# wait for pid_file to restore itself
|
|
139
131
|
tries = DEFAULT_TRIES
|
|
@@ -156,9 +148,11 @@ end
|
|
|
156
148
|
# fix the bug
|
|
157
149
|
ucfg.sysseek(0)
|
|
158
150
|
ucfg.truncate(0)
|
|
159
|
-
ucfg.syswrite("
|
|
151
|
+
ucfg.syswrite("listen %(#@addr:#@port)\n")
|
|
152
|
+
ucfg.syswrite("listen %(#@addr:#{port2})\n")
|
|
160
153
|
ucfg.syswrite("pid %(#{pid_file})\n")
|
|
161
|
-
Process.kill(
|
|
154
|
+
assert_nothing_raised { Process.kill(:USR2, current_pid) }
|
|
155
|
+
|
|
162
156
|
wait_for_file(old_file)
|
|
163
157
|
wait_for_file(pid_file)
|
|
164
158
|
new_pid = File.read(pid_file).to_i
|
|
@@ -170,8 +164,8 @@ end
|
|
|
170
164
|
assert_equal String, results[1].class
|
|
171
165
|
|
|
172
166
|
assert_nothing_raised do
|
|
173
|
-
Process.kill(
|
|
174
|
-
Process.kill(
|
|
167
|
+
Process.kill(:QUIT, current_pid)
|
|
168
|
+
Process.kill(:QUIT, new_pid)
|
|
175
169
|
end
|
|
176
170
|
end
|
|
177
171
|
|
|
@@ -192,14 +186,16 @@ end
|
|
|
192
186
|
|
|
193
187
|
wait_for_file(pid_file)
|
|
194
188
|
Process.waitpid(pid)
|
|
195
|
-
Process.kill(
|
|
189
|
+
Process.kill(:USR2, File.read(pid_file).to_i)
|
|
196
190
|
wait_for_file(old_file)
|
|
197
191
|
wait_for_file(pid_file)
|
|
198
|
-
|
|
192
|
+
old_pid = File.read(old_file).to_i
|
|
193
|
+
Process.kill(:QUIT, old_pid)
|
|
194
|
+
wait_for_death(old_pid)
|
|
199
195
|
|
|
200
196
|
File.unlink("config.ru") # break reloading
|
|
201
197
|
current_pid = File.read(pid_file).to_i
|
|
202
|
-
Process.kill(
|
|
198
|
+
Process.kill(:USR2, current_pid)
|
|
203
199
|
|
|
204
200
|
# wait for pid_file to restore itself
|
|
205
201
|
tries = DEFAULT_TRIES
|
|
@@ -210,17 +206,17 @@ end
|
|
|
210
206
|
rescue Errno::ENOENT
|
|
211
207
|
(sleep(DEFAULT_RES) and (tries -= 1) > 0) and retry
|
|
212
208
|
end
|
|
213
|
-
assert_equal current_pid, File.read(pid_file).to_i
|
|
214
209
|
|
|
215
210
|
tries = DEFAULT_TRIES
|
|
216
211
|
while File.exist?(old_file)
|
|
217
212
|
(sleep(DEFAULT_RES) and (tries -= 1) > 0) or break
|
|
218
213
|
end
|
|
219
214
|
assert ! File.exist?(old_file), "oldbin=#{old_file} gone"
|
|
215
|
+
assert_equal current_pid, File.read(pid_file).to_i
|
|
220
216
|
|
|
221
217
|
# fix the bug
|
|
222
218
|
File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
|
|
223
|
-
Process.kill(
|
|
219
|
+
assert_nothing_raised { Process.kill(:USR2, current_pid) }
|
|
224
220
|
wait_for_file(old_file)
|
|
225
221
|
wait_for_file(pid_file)
|
|
226
222
|
new_pid = File.read(pid_file).to_i
|
|
@@ -230,24 +226,21 @@ end
|
|
|
230
226
|
assert_equal String, results[0].class
|
|
231
227
|
|
|
232
228
|
assert_nothing_raised do
|
|
233
|
-
Process.kill(
|
|
234
|
-
Process.kill(
|
|
229
|
+
Process.kill(:QUIT, current_pid)
|
|
230
|
+
Process.kill(:QUIT, new_pid)
|
|
235
231
|
end
|
|
236
232
|
end
|
|
237
233
|
|
|
238
|
-
def
|
|
239
|
-
port2 = unused_port(@addr)
|
|
234
|
+
def test_unicorn_config_listen_with_options
|
|
240
235
|
File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
|
|
241
|
-
# listeners = [ ... ] => should _override_ command-line options
|
|
242
236
|
ucfg = Tempfile.new('unicorn_test_config')
|
|
243
|
-
ucfg.syswrite("
|
|
237
|
+
ucfg.syswrite("listen '#{@addr}:#{@port}', :backlog => 512,\n")
|
|
238
|
+
ucfg.syswrite(" :rcvbuf => 4096,\n")
|
|
239
|
+
ucfg.syswrite(" :sndbuf => 4096\n")
|
|
244
240
|
pid = xfork do
|
|
245
|
-
redirect_test_io
|
|
246
|
-
exec($unicorn_bin, "-c#{ucfg.path}", "-l#{@addr}:#{port2}")
|
|
247
|
-
end
|
|
241
|
+
redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") }
|
|
248
242
|
end
|
|
249
243
|
results = retry_hit(["http://#{@addr}:#{@port}/"])
|
|
250
|
-
assert_raises(Errno::ECONNREFUSED) { TCPSocket.new(@addr, port2) }
|
|
251
244
|
assert_equal String, results[0].class
|
|
252
245
|
assert_shutdown(pid)
|
|
253
246
|
end
|
|
@@ -289,29 +282,30 @@ end
|
|
|
289
282
|
rotate = Tempfile.new('unicorn_rotate')
|
|
290
283
|
assert_nothing_raised do
|
|
291
284
|
File.rename(COMMON_TMP.path, rotate.path)
|
|
292
|
-
Process.kill(
|
|
285
|
+
Process.kill(:USR1, pid)
|
|
293
286
|
end
|
|
294
287
|
wait_for_file(COMMON_TMP.path)
|
|
295
288
|
assert File.exist?(COMMON_TMP.path), "#{COMMON_TMP.path} exists"
|
|
296
289
|
# USR1 should've been passed to all workers
|
|
297
290
|
tries = DEFAULT_TRIES
|
|
298
291
|
log = File.readlines(rotate.path)
|
|
299
|
-
while (tries -= 1) > 0 &&
|
|
292
|
+
while (tries -= 1) > 0 &&
|
|
293
|
+
log.grep(/rotating logs\.\.\./).size < 5
|
|
300
294
|
sleep DEFAULT_RES
|
|
301
295
|
log = File.readlines(rotate.path)
|
|
302
296
|
end
|
|
303
|
-
assert_equal
|
|
297
|
+
assert_equal 5, log.grep(/rotating logs\.\.\./).size
|
|
304
298
|
assert_equal 0, log.grep(/done rotating logs/).size
|
|
305
299
|
|
|
306
300
|
tries = DEFAULT_TRIES
|
|
307
301
|
log = File.readlines(COMMON_TMP.path)
|
|
308
|
-
while (tries -= 1) > 0 && log.grep(/done rotating logs/).size <
|
|
302
|
+
while (tries -= 1) > 0 && log.grep(/done rotating logs/).size < 5
|
|
309
303
|
sleep DEFAULT_RES
|
|
310
304
|
log = File.readlines(COMMON_TMP.path)
|
|
311
305
|
end
|
|
312
|
-
assert_equal
|
|
306
|
+
assert_equal 5, log.grep(/done rotating logs/).size
|
|
313
307
|
assert_equal 0, log.grep(/rotating logs\.\.\./).size
|
|
314
|
-
assert_nothing_raised { Process.kill(
|
|
308
|
+
assert_nothing_raised { Process.kill(:QUIT, pid) }
|
|
315
309
|
status = nil
|
|
316
310
|
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
|
317
311
|
assert status.success?, "exited successfully"
|
|
@@ -430,7 +424,7 @@ end
|
|
|
430
424
|
ucfg.syswrite("pid \"#{pid_file}\"\n")
|
|
431
425
|
ucfg.syswrite("logger Logger.new('#{new_log.path}')\n")
|
|
432
426
|
ucfg.close
|
|
433
|
-
Process.kill(
|
|
427
|
+
Process.kill(:HUP, pid)
|
|
434
428
|
end
|
|
435
429
|
|
|
436
430
|
wait_for_file(new_sock_path)
|
|
@@ -473,114 +467,4 @@ end
|
|
|
473
467
|
reexec_usr2_quit_test(new_pid, pid_file)
|
|
474
468
|
end
|
|
475
469
|
|
|
476
|
-
private
|
|
477
|
-
|
|
478
|
-
# sometimes the server may not come up right away
|
|
479
|
-
def retry_hit(uris = [])
|
|
480
|
-
tries = DEFAULT_TRIES
|
|
481
|
-
begin
|
|
482
|
-
hit(uris)
|
|
483
|
-
rescue Errno::ECONNREFUSED => err
|
|
484
|
-
if (tries -= 1) > 0
|
|
485
|
-
sleep DEFAULT_RES
|
|
486
|
-
retry
|
|
487
|
-
end
|
|
488
|
-
raise err
|
|
489
|
-
end
|
|
490
|
-
end
|
|
491
|
-
|
|
492
|
-
def assert_shutdown(pid)
|
|
493
|
-
wait_master_ready("#{@tmpdir}/test_stderr.#{pid}.log")
|
|
494
|
-
assert_nothing_raised { Process.kill('QUIT', pid) }
|
|
495
|
-
status = nil
|
|
496
|
-
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
|
497
|
-
assert status.success?, "exited successfully"
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
def wait_workers_ready(path, nr_workers)
|
|
501
|
-
tries = DEFAULT_TRIES
|
|
502
|
-
lines = []
|
|
503
|
-
while (tries -= 1) > 0
|
|
504
|
-
begin
|
|
505
|
-
lines = File.readlines(path).grep(/worker=\d+ spawned/)
|
|
506
|
-
lines.size == nr_workers and return
|
|
507
|
-
rescue Errno::ENOENT
|
|
508
|
-
end
|
|
509
|
-
sleep DEFAULT_RES
|
|
510
|
-
end
|
|
511
|
-
raise "#{nr_workers} workers never became ready:" \
|
|
512
|
-
"\n\t#{lines.join("\n\t")}\n"
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
def wait_master_ready(master_log)
|
|
516
|
-
tries = DEFAULT_TRIES
|
|
517
|
-
while (tries -= 1) > 0
|
|
518
|
-
begin
|
|
519
|
-
File.readlines(master_log).grep(/master process ready/)[0] and return
|
|
520
|
-
rescue Errno::ENOENT
|
|
521
|
-
end
|
|
522
|
-
sleep DEFAULT_RES
|
|
523
|
-
end
|
|
524
|
-
raise "master process never became ready"
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
def reexec_usr2_quit_test(pid, pid_file)
|
|
528
|
-
assert File.exist?(pid_file), "pid file OK"
|
|
529
|
-
assert ! File.exist?("#{pid_file}.oldbin"), "oldbin pid file"
|
|
530
|
-
assert_nothing_raised { Process.kill('USR2', pid) }
|
|
531
|
-
assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) }
|
|
532
|
-
wait_for_file("#{pid_file}.oldbin")
|
|
533
|
-
wait_for_file(pid_file)
|
|
534
|
-
|
|
535
|
-
# kill old master process
|
|
536
|
-
assert_not_equal pid, File.read(pid_file).to_i
|
|
537
|
-
assert_equal pid, File.read("#{pid_file}.oldbin").to_i
|
|
538
|
-
assert_nothing_raised { Process.kill('QUIT', pid) }
|
|
539
|
-
assert_not_equal pid, File.read(pid_file).to_i
|
|
540
|
-
assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) }
|
|
541
|
-
wait_for_file(pid_file)
|
|
542
|
-
assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) }
|
|
543
|
-
assert_nothing_raised { Process.kill('QUIT', File.read(pid_file).to_i) }
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
def reexec_basic_test(pid, pid_file)
|
|
547
|
-
results = retry_hit(["http://#{@addr}:#{@port}/"])
|
|
548
|
-
assert_equal String, results[0].class
|
|
549
|
-
assert_nothing_raised { Process.kill(0, pid) }
|
|
550
|
-
master_log = "#{@tmpdir}/test_stderr.#{pid}.log"
|
|
551
|
-
wait_master_ready(master_log)
|
|
552
|
-
File.truncate(master_log, 0)
|
|
553
|
-
nr = 50
|
|
554
|
-
kill_point = 2
|
|
555
|
-
assert_nothing_raised do
|
|
556
|
-
nr.times do |i|
|
|
557
|
-
hit(["http://#{@addr}:#{@port}/#{i}"])
|
|
558
|
-
i == kill_point and Process.kill('HUP', pid)
|
|
559
|
-
end
|
|
560
|
-
end
|
|
561
|
-
wait_master_ready(master_log)
|
|
562
|
-
assert File.exist?(pid_file), "pid=#{pid_file} exists"
|
|
563
|
-
new_pid = File.read(pid_file).to_i
|
|
564
|
-
assert_not_equal pid, new_pid
|
|
565
|
-
assert_nothing_raised { Process.kill(0, new_pid) }
|
|
566
|
-
assert_nothing_raised { Process.kill('QUIT', new_pid) }
|
|
567
|
-
end
|
|
568
|
-
|
|
569
|
-
def wait_for_file(path)
|
|
570
|
-
tries = DEFAULT_TRIES
|
|
571
|
-
while (tries -= 1) > 0 && ! File.exist?(path)
|
|
572
|
-
sleep DEFAULT_RES
|
|
573
|
-
end
|
|
574
|
-
assert File.exist?(path), "path=#{path} exists #{caller.inspect}"
|
|
575
|
-
end
|
|
576
|
-
|
|
577
|
-
def xfork(&block)
|
|
578
|
-
fork do
|
|
579
|
-
ObjectSpace.each_object(Tempfile) do |tmp|
|
|
580
|
-
ObjectSpace.undefine_finalizer(tmp)
|
|
581
|
-
end
|
|
582
|
-
yield
|
|
583
|
-
end
|
|
584
|
-
end
|
|
585
|
-
|
|
586
470
|
end if do_test
|