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.
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