unicorn 0.2.3 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.document +1 -1
  2. data/.gitignore +1 -0
  3. data/CHANGELOG +1 -0
  4. data/DESIGN +4 -0
  5. data/GNUmakefile +30 -6
  6. data/Manifest +62 -3
  7. data/README +52 -42
  8. data/SIGNALS +17 -17
  9. data/TODO +27 -5
  10. data/bin/unicorn +15 -13
  11. data/bin/unicorn_rails +59 -22
  12. data/ext/unicorn/http11/http11.c +25 -104
  13. data/ext/unicorn/http11/http11_parser.c +24 -23
  14. data/ext/unicorn/http11/http11_parser.h +1 -3
  15. data/ext/unicorn/http11/http11_parser.rl +2 -1
  16. data/lib/unicorn.rb +58 -44
  17. data/lib/unicorn/app/old_rails.rb +23 -0
  18. data/lib/unicorn/app/old_rails/static.rb +58 -0
  19. data/lib/unicorn/cgi_wrapper.rb +151 -0
  20. data/lib/unicorn/configurator.rb +71 -31
  21. data/lib/unicorn/const.rb +9 -34
  22. data/lib/unicorn/http_request.rb +63 -66
  23. data/lib/unicorn/http_response.rb +6 -1
  24. data/lib/unicorn/socket.rb +15 -2
  25. data/test/benchmark/README +55 -0
  26. data/test/benchmark/big_request.rb +35 -0
  27. data/test/benchmark/dd.ru +18 -0
  28. data/test/benchmark/request.rb +47 -0
  29. data/test/benchmark/response.rb +29 -0
  30. data/test/exec/test_exec.rb +41 -157
  31. data/test/rails/app-1.2.3/.gitignore +2 -0
  32. data/test/rails/app-1.2.3/app/controllers/application.rb +4 -0
  33. data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +34 -0
  34. data/test/rails/app-1.2.3/app/helpers/application_helper.rb +2 -0
  35. data/test/rails/app-1.2.3/config/boot.rb +9 -0
  36. data/test/rails/app-1.2.3/config/database.yml +12 -0
  37. data/test/rails/app-1.2.3/config/environment.rb +10 -0
  38. data/test/rails/app-1.2.3/config/environments/development.rb +7 -0
  39. data/test/rails/app-1.2.3/config/environments/production.rb +3 -0
  40. data/test/rails/app-1.2.3/config/routes.rb +4 -0
  41. data/test/rails/app-1.2.3/db/.gitignore +0 -0
  42. data/test/rails/app-1.2.3/public/404.html +1 -0
  43. data/test/rails/app-1.2.3/public/500.html +1 -0
  44. data/test/rails/app-2.0.2/.gitignore +2 -0
  45. data/test/rails/app-2.0.2/app/controllers/application.rb +2 -0
  46. data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +34 -0
  47. data/test/rails/app-2.0.2/app/helpers/application_helper.rb +2 -0
  48. data/test/rails/app-2.0.2/config/boot.rb +9 -0
  49. data/test/rails/app-2.0.2/config/database.yml +12 -0
  50. data/test/rails/app-2.0.2/config/environment.rb +14 -0
  51. data/test/rails/app-2.0.2/config/environments/development.rb +6 -0
  52. data/test/rails/app-2.0.2/config/environments/production.rb +3 -0
  53. data/test/rails/app-2.0.2/config/routes.rb +4 -0
  54. data/test/rails/app-2.0.2/db/.gitignore +0 -0
  55. data/test/rails/app-2.0.2/public/404.html +1 -0
  56. data/test/rails/app-2.0.2/public/500.html +1 -0
  57. data/test/rails/app-2.2.2/.gitignore +2 -0
  58. data/test/rails/app-2.2.2/app/controllers/application.rb +2 -0
  59. data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +34 -0
  60. data/test/rails/app-2.2.2/app/helpers/application_helper.rb +2 -0
  61. data/test/rails/app-2.2.2/config/boot.rb +109 -0
  62. data/test/rails/app-2.2.2/config/database.yml +12 -0
  63. data/test/rails/app-2.2.2/config/environment.rb +14 -0
  64. data/test/rails/app-2.2.2/config/environments/development.rb +5 -0
  65. data/test/rails/app-2.2.2/config/environments/production.rb +3 -0
  66. data/test/rails/app-2.2.2/config/routes.rb +4 -0
  67. data/test/rails/app-2.2.2/db/.gitignore +0 -0
  68. data/test/rails/app-2.2.2/public/404.html +1 -0
  69. data/test/rails/app-2.2.2/public/500.html +1 -0
  70. data/test/rails/app-2.3.2.1/.gitignore +2 -0
  71. data/test/rails/app-2.3.2.1/app/controllers/application_controller.rb +3 -0
  72. data/test/rails/app-2.3.2.1/app/controllers/foo_controller.rb +34 -0
  73. data/test/rails/app-2.3.2.1/app/helpers/application_helper.rb +2 -0
  74. data/test/rails/app-2.3.2.1/config/boot.rb +107 -0
  75. data/test/rails/app-2.3.2.1/config/database.yml +12 -0
  76. data/test/rails/app-2.3.2.1/config/environment.rb +14 -0
  77. data/test/rails/app-2.3.2.1/config/environments/development.rb +5 -0
  78. data/test/rails/app-2.3.2.1/config/environments/production.rb +4 -0
  79. data/test/rails/app-2.3.2.1/config/routes.rb +4 -0
  80. data/test/rails/app-2.3.2.1/db/.gitignore +0 -0
  81. data/test/rails/app-2.3.2.1/public/404.html +1 -0
  82. data/test/rails/app-2.3.2.1/public/500.html +1 -0
  83. data/test/rails/test_rails.rb +243 -0
  84. data/test/test_helper.rb +149 -2
  85. data/test/unit/test_configurator.rb +46 -0
  86. data/test/unit/test_http_parser.rb +77 -36
  87. data/test/unit/test_request.rb +2 -0
  88. data/test/unit/test_response.rb +20 -4
  89. data/test/unit/test_server.rb +30 -1
  90. data/test/unit/test_socket_helper.rb +159 -0
  91. data/unicorn.gemspec +5 -5
  92. metadata +68 -5
  93. data/test/benchmark/previous.rb +0 -11
  94. data/test/benchmark/simple.rb +0 -11
  95. 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
@@ -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
- STDERR.puts "#{$unicorn_bin} not found in PATH=#{ENV['PATH']}, " \
19
- "skipping this test"
11
+ warn "#{$unicorn_bin} not found in PATH=#{ENV['PATH']}, " \
12
+ "skipping this test"
20
13
  end
21
14
 
22
- begin
23
- require 'rack'
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('QUIT', 'IGNORE')
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("listeners %w(#{@addr}:#{@port})\n")
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('USR2', File.read(pid_file).to_i)
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
- Process.kill('QUIT', File.read(old_file).to_i)
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('USR2', current_pid)
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("listeners %w(#{@addr}:#{@port} #{@addr}:#{port2})\n")
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('USR2', current_pid)
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('QUIT', current_pid)
174
- Process.kill('QUIT', new_pid)
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('USR2', File.read(pid_file).to_i)
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
- Process.kill('QUIT', File.read(old_file).to_i)
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('USR2', current_pid)
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('USR2', current_pid)
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('QUIT', current_pid)
234
- Process.kill('QUIT', new_pid)
229
+ Process.kill(:QUIT, current_pid)
230
+ Process.kill(:QUIT, new_pid)
235
231
  end
236
232
  end
237
233
 
238
- def test_unicorn_config_listeners_overrides_cli
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("listeners %w(#{@addr}:#{@port})\n")
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 do
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('USR1', pid)
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 && log.grep(/rotating logs\.\.\./).size < 4
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 4, log.grep(/worker=\d+ rotating logs\.\.\./).size
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 < 4
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 4, log.grep(/worker=\d+ done rotating logs/).size
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('QUIT', pid) }
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('HUP', pid)
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