unicorn 5.5.0 → 5.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.manifest +5 -3
  3. data/.olddoc.yml +12 -7
  4. data/Documentation/.gitignore +1 -3
  5. data/Documentation/unicorn.1 +222 -0
  6. data/Documentation/unicorn_rails.1 +207 -0
  7. data/FAQ +1 -1
  8. data/GIT-VERSION-FILE +1 -1
  9. data/GIT-VERSION-GEN +1 -1
  10. data/GNUmakefile +112 -57
  11. data/HACKING +1 -1
  12. data/ISSUES +16 -21
  13. data/KNOWN_ISSUES +2 -2
  14. data/LATEST +22 -29
  15. data/Links +5 -5
  16. data/NEWS +23 -0
  17. data/README +13 -6
  18. data/SIGNALS +1 -1
  19. data/Sandbox +2 -2
  20. data/archive/slrnpull.conf +1 -1
  21. data/bin/unicorn_rails +2 -2
  22. data/examples/big_app_gc.rb +1 -1
  23. data/examples/logrotate.conf +2 -2
  24. data/examples/nginx.conf +1 -1
  25. data/examples/unicorn.conf.minimal.rb +2 -2
  26. data/examples/unicorn.conf.rb +2 -2
  27. data/examples/unicorn@.service +7 -0
  28. data/ext/unicorn_http/extconf.rb +5 -0
  29. data/ext/unicorn_http/unicorn_http.c +253 -215
  30. data/ext/unicorn_http/unicorn_http.rl +43 -5
  31. data/lib/unicorn/configurator.rb +13 -3
  32. data/lib/unicorn/http_request.rb +11 -0
  33. data/lib/unicorn/http_server.rb +36 -4
  34. data/lib/unicorn/oob_gc.rb +2 -2
  35. data/lib/unicorn/tmpio.rb +8 -2
  36. data/lib/unicorn/version.rb +1 -1
  37. data/lib/unicorn.rb +4 -1
  38. data/man/man1/unicorn.1 +89 -88
  39. data/man/man1/unicorn_rails.1 +78 -83
  40. data/t/GNUmakefile +3 -72
  41. data/test/benchmark/README +14 -4
  42. data/test/benchmark/ddstream.ru +50 -0
  43. data/test/benchmark/readinput.ru +40 -0
  44. data/test/benchmark/uconnect.perl +66 -0
  45. data/test/exec/test_exec.rb +9 -7
  46. data/test/test_helper.rb +22 -30
  47. data/test/unit/test_http_parser_ng.rb +81 -0
  48. data/test/unit/test_server.rb +74 -0
  49. data/test/unit/test_upload.rb +4 -9
  50. data/test/unit/test_util.rb +1 -1
  51. data/unicorn.gemspec +8 -7
  52. metadata +12 -9
  53. data/Documentation/GNUmakefile +0 -30
  54. data/Documentation/unicorn.1.txt +0 -187
  55. data/Documentation/unicorn_rails.1.txt +0 -175
@@ -1,5 +1,3 @@
1
- .\" Automatically generated by Pandoc 1.17.2
2
- .\"
3
1
  .TH "UNICORN_RAILS" "1" "September 17, 2009" "Unicorn User Manual" ""
4
2
  .hy
5
3
  .SH NAME
@@ -10,17 +8,12 @@ unicorn_rails \- unicorn launcher for Rails 1.x and 2.x users
10
8
  unicorn_rails [\-c CONFIG_FILE] [\-E RAILS_ENV] [\-D] [RACKUP_FILE]
11
9
  .SH DESCRIPTION
12
10
  .PP
13
- A rackup(1)\-like command to launch Rails applications using Unicorn.
14
- It is expected to be started in your Rails application root
15
- (RAILS_ROOT), but the "working_directory" directive may be used in the
16
- CONFIG_FILE.
11
+ A rackup(1)\-like command to launch ancient Rails (2.x and earlier)
12
+ applications using Unicorn. Rails 3 (and later) support Rack natively,
13
+ so users are encouraged to use unicorn(1) instead of unicorn_rails(1).
17
14
  .PP
18
- It is designed to help Rails 1.x and 2.y users transition to Rack, but
19
- it is NOT needed for Rails 3 applications.
20
- Rails 3 users are encouraged to use unicorn(1) instead of
21
- unicorn_rails(1).
22
- Users of Rails 1.x/2.y may also use unicorn(1) instead of
23
- unicorn_rails(1).
15
+ It is expected to be started in your Rails application root (RAILS_ROOT),
16
+ but the "working_directory" directive may be used in the CONFIG_FILE.
24
17
  .PP
25
18
  The outward interface resembles rackup(1), the internals and default
26
19
  middleware loading is designed like the \f[C]script/server\f[] command
@@ -33,56 +26,55 @@ as much as possible.
33
26
  .SH UNICORN OPTIONS
34
27
  .TP
35
28
  .B \-c, \-\-config\-file CONFIG_FILE
36
- Path to the Unicorn\-specific config file.
37
- The config file is implemented as a Ruby DSL, so Ruby code may executed.
38
- See the RDoc/ri for the \f[I]Unicorn::Configurator\f[] class for the
39
- full list of directives available from the DSL.
40
- Using an absolute path for for CONFIG_FILE is recommended as it makes
41
- multiple instances of Unicorn easily distinguishable when viewing ps(1)
42
- output.
29
+ Path to the Unicorn\-specific config file. The config file is
30
+ implemented as a Ruby DSL, so Ruby code may executed.
31
+ See the RDoc/ri for the \f[I]Unicorn::Configurator\f[] class for the full
32
+ list of directives available from the DSL.
33
+ Using an absolute path for for CONFIG_FILE is recommended as it
34
+ makes multiple instances of Unicorn easily distinguishable when
35
+ viewing ps(1) output.
43
36
  .RS
44
37
  .RE
45
38
  .TP
46
39
  .B \-D, \-\-daemonize
47
- Run daemonized in the background.
48
- The process is detached from the controlling terminal and stdin is
49
- redirected to "/dev/null".
40
+ Run daemonized in the background. The process is detached from
41
+ the controlling terminal and stdin is redirected to "/dev/null".
50
42
  Unlike many common UNIX daemons, we do not chdir to "/" upon
51
- daemonization to allow more control over the startup/upgrade process.
52
- Unless specified in the CONFIG_FILE, stderr and stdout will also be
53
- redirected to "/dev/null".
43
+ daemonization to allow more control over the startup/upgrade
44
+ process.
45
+ Unless specified in the CONFIG_FILE, stderr and stdout will
46
+ also be redirected to "/dev/null".
54
47
  Daemonization will \f[I]skip\f[] loading of the
55
- \f[I]Rails::Rack::LogTailer\f[] middleware under Rails >= 2.3.x.
48
+ \f[I]Rails::Rack::LogTailer\f[]
49
+ middleware under Rails >= 2.3.x.
56
50
  By default, unicorn_rails(1) will create a PID file in
57
- \f[I]"RAILS_ROOT/tmp/pids/unicorn.pid"\f[].
58
- You may override this by specifying the "pid" directive to override this
59
- Unicorn config file.
51
+ \f[I]"RAILS_ROOT/tmp/pids/unicorn.pid"\f[]. You may override this
52
+ by specifying the "pid" directive to override this Unicorn config file.
60
53
  .RS
61
54
  .RE
62
55
  .TP
63
56
  .B \-E, \-\-env RAILS_ENV
64
- Run under the given RAILS_ENV.
65
- This sets the RAILS_ENV environment variable.
66
- Acceptable values are exactly those you expect in your Rails
57
+ Run under the given RAILS_ENV. This sets the RAILS_ENV environment
58
+ variable. Acceptable values are exactly those you expect in your Rails
67
59
  application, typically "development" or "production".
68
60
  .RS
69
61
  .RE
70
62
  .TP
71
63
  .B \-l, \-\-listen ADDRESS
72
- Listens on a given ADDRESS.
73
- ADDRESS may be in the form of HOST:PORT or PATH, HOST:PORT is taken to
74
- mean a TCP socket and PATH is meant to be a path to a UNIX domain
75
- socket.
64
+ Listens on a given ADDRESS. ADDRESS may be in the form of
65
+ HOST:PORT or PATH, HOST:PORT is taken to mean a TCP socket
66
+ and PATH is meant to be a path to a UNIX domain socket.
76
67
  Defaults to "0.0.0.0:8080" (all addresses on TCP port 8080).
77
68
  For production deployments, specifying the "listen" directive in
78
- CONFIG_FILE is recommended as it allows fine\-tuning of socket options.
69
+ CONFIG_FILE is recommended as it allows fine\-tuning of socket
70
+ options.
79
71
  .RS
80
72
  .RE
81
73
  .SH RACKUP COMPATIBILITY OPTIONS
82
74
  .TP
83
75
  .B \-o, \-\-host HOST
84
- Listen on a TCP socket belonging to HOST, default is "0.0.0.0" (all
85
- addresses).
76
+ Listen on a TCP socket belonging to HOST, default is
77
+ "0.0.0.0" (all addresses).
86
78
  If specified multiple times on the command\-line, only the
87
79
  last\-specified value takes effect.
88
80
  This option only exists for compatibility with the rackup(1) command,
@@ -92,8 +84,8 @@ use of "\-l"/"\-\-listen" switch is recommended instead.
92
84
  .TP
93
85
  .B \-p, \-\-port PORT
94
86
  Listen on the specified TCP PORT, default is 8080.
95
- If specified multiple times on the command\-line, only the
96
- last\-specified value takes effect.
87
+ If specified multiple times on the command\-line, only the last\-specified
88
+ value takes effect.
97
89
  This option only exists for compatibility with the rackup(1) command,
98
90
  use of "\-l"/"\-\-listen" switch is recommended instead.
99
91
  .RS
@@ -101,17 +93,16 @@ use of "\-l"/"\-\-listen" switch is recommended instead.
101
93
  .TP
102
94
  .B \-\-path PATH
103
95
  Mounts the Rails application at the given PATH (instead of "/").
104
- This is equivalent to setting the RAILS_RELATIVE_URL_ROOT environment
105
- variable.
106
- This is only supported under Rails 2.3 or later at the moment.
96
+ This is equivalent to setting the RAILS_RELATIVE_URL_ROOT
97
+ environment variable. This is only supported under Rails 2.3
98
+ or later at the moment.
107
99
  .RS
108
100
  .RE
109
101
  .SH RUBY OPTIONS
110
102
  .TP
111
103
  .B \-e, \-\-eval LINE
112
- Evaluate a LINE of Ruby code.
113
- This evaluation happens immediately as the command\-line is being
114
- parsed.
104
+ Evaluate a LINE of Ruby code. This evaluation happens
105
+ immediately as the command\-line is being parsed.
115
106
  .RS
116
107
  .RE
117
108
  .TP
@@ -128,46 +119,42 @@ Turn on verbose warnings, the $VERBOSE variable is set to true.
128
119
  .RE
129
120
  .TP
130
121
  .B \-I, \-\-include PATH
131
- specify $LOAD_PATH.
132
- PATH will be prepended to $LOAD_PATH.
122
+ specify $LOAD_PATH. PATH will be prepended to $LOAD_PATH.
133
123
  The \[aq]:\[aq] character may be used to delimit multiple directories.
134
- This directive may be used more than once.
135
- Modifications to $LOAD_PATH take place immediately and in the order they
136
- were specified on the command\-line.
124
+ This directive may be used more than once. Modifications to
125
+ $LOAD_PATH take place immediately and in the order they were
126
+ specified on the command\-line.
137
127
  .RS
138
128
  .RE
139
129
  .TP
140
130
  .B \-r, \-\-require LIBRARY
141
- require a specified LIBRARY before executing the application.
142
- The "require" statement will be executed immediately and in the order
131
+ require a specified LIBRARY before executing the application. The
132
+ "require" statement will be executed immediately and in the order
143
133
  they were specified on the command\-line.
144
134
  .RS
145
135
  .RE
146
136
  .SH RACKUP FILE
147
137
  .PP
148
- This defaults to "config.ru" in RAILS_ROOT.
149
- It should be the same file used by rackup(1) and other Rack launchers,
150
- it uses the \f[I]Rack::Builder\f[] DSL.
151
- Unlike many other Rack applications, RACKUP_FILE is completely
152
- \f[I]optional\f[] for Rails, but may be used to disable some of the
153
- default middleware for performance.
138
+ This defaults to "config.ru" in RAILS_ROOT. It should be the same
139
+ file used by rackup(1) and other Rack launchers, it uses the
140
+ \f[I]Rack::Builder\f[] DSL. Unlike many other Rack applications, RACKUP_FILE
141
+ is completely \f[I]optional\f[] for Rails, but may be used to disable
142
+ some of the default middleware for performance.
154
143
  .PP
155
- Embedded command\-line options are mostly parsed for compatibility with
156
- rackup(1) but strongly discouraged.
144
+ Embedded command\-line options are mostly parsed for compatibility
145
+ with rackup(1) but strongly discouraged.
157
146
  .SH ENVIRONMENT VARIABLES
158
147
  .PP
159
- The RAILS_ENV variable is set by the aforementioned \-E switch.
160
- The RAILS_RELATIVE_URL_ROOT is set by the aforementioned \-\-path
161
- switch.
148
+ The RAILS_ENV variable is set by the aforementioned \-E switch. The
149
+ RAILS_RELATIVE_URL_ROOT is set by the aforementioned \-\-path switch.
162
150
  Either of these variables may also be set in the shell or the Unicorn
163
- CONFIG_FILE.
164
- All application or library\-specific environment variables (e.g.
165
- TMPDIR, RAILS_ASSET_ID) may always be set in the Unicorn CONFIG_FILE in
166
- addition to the spawning shell.
167
- When transparently upgrading Unicorn, all environment variables set in
168
- the old master process are inherited by the new master process.
169
- Unicorn only uses (and will overwrite) the UNICORN_FD environment
170
- variable internally when doing transparent upgrades.
151
+ CONFIG_FILE. All application or library\-specific environment variables
152
+ (e.g. TMPDIR, RAILS_ASSET_ID) may always be set in the Unicorn
153
+ CONFIG_FILE in addition to the spawning shell. When transparently
154
+ upgrading Unicorn, all environment variables set in the old master
155
+ process are inherited by the new master process. Unicorn only uses (and
156
+ will overwrite) the UNICORN_FD environment variable internally when
157
+ doing transparent upgrades.
171
158
  .SH SIGNALS
172
159
  .PP
173
160
  The following UNIX signals may be sent to the master process:
@@ -179,12 +166,12 @@ INT/TERM \- quick shutdown, kills all workers immediately
179
166
  QUIT \- graceful shutdown, waits for workers to finish their current
180
167
  request before finishing.
181
168
  .IP \[bu] 2
182
- USR1 \- reopen all logs owned by the master and all workers See
183
- Unicorn::Util.reopen_logs for what is considered a log.
169
+ USR1 \- reopen all logs owned by the master and all workers
170
+ See Unicorn::Util.reopen_logs for what is considered a log.
184
171
  .IP \[bu] 2
185
- USR2 \- reexecute the running binary.
186
- A separate QUIT should be sent to the original process once the child is
187
- verified to be up and running.
172
+ USR2 \- reexecute the running binary. A separate QUIT
173
+ should be sent to the original process once the child is verified to
174
+ be up and running.
188
175
  .IP \[bu] 2
189
176
  WINCH \- gracefully stops workers but keep the master running.
190
177
  This will only work for daemonized processes.
@@ -193,7 +180,7 @@ TTIN \- increment the number of worker processes by one
193
180
  .IP \[bu] 2
194
181
  TTOU \- decrement the number of worker processes by one
195
182
  .PP
196
- See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
183
+ See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
197
184
  full description of all signals used by Unicorn.
198
185
  .SH SEE ALSO
199
186
  .IP \[bu] 2
@@ -202,11 +189,19 @@ unicorn(1)
202
189
  \f[I]Rack::Builder\f[] ri/RDoc
203
190
  .IP \[bu] 2
204
191
  \f[I]Unicorn::Configurator\f[] ri/RDoc
192
+ .UR https://yhbt.net/unicorn/Unicorn/Configurator.html
193
+ .UE
205
194
  .IP \[bu] 2
206
- Unicorn RDoc (https://bogomips.org/unicorn/)
195
+ unicorn RDoc
196
+ .UR https://yhbt.net/unicorn/
197
+ .UE
207
198
  .IP \[bu] 2
208
- Rack RDoc (https://www.rubydoc.info/github/rack/rack/)
199
+ Rack RDoc
200
+ .UR https://www.rubydoc.info/github/rack/rack/
201
+ .UE
209
202
  .IP \[bu] 2
210
- Rackup HowTo (https://github.com/rack/rack/wiki/tutorial-rackup-howto)
203
+ Rackup HowTo
204
+ .UR https://github.com/rack/rack/wiki/(tutorial)-rackup-howto
205
+ .UE
211
206
  .SH AUTHORS
212
- The Unicorn Community <unicorn-public@bogomips.org>.
207
+ The Unicorn Community <unicorn-public@yhbt.net>.
data/t/GNUmakefile CHANGED
@@ -1,74 +1,5 @@
1
- # we can run tests in parallel with GNU make
1
+ # there used to be more, here, but we stopped relying on recursive make
2
2
  all::
3
+ $(MAKE) -C .. test-integration
3
4
 
4
- pid := $(shell echo $$PPID)
5
-
6
- RUBY = ruby
7
- RAKE = rake
8
- -include ../local.mk
9
- ifeq ($(RUBY_VERSION),)
10
- RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
11
- endif
12
-
13
- ifeq ($(RUBY_VERSION),)
14
- $(error unable to detect RUBY_VERSION)
15
- endif
16
-
17
- RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
18
- export RUBY_ENGINE
19
-
20
- MYLIBS := $(RUBYLIB)
21
-
22
- T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
23
-
24
- all:: $(T)
25
-
26
- # can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
27
- t_pfx = trash/$@-$(RUBY_ENGINE)-$(RUBY_VERSION)
28
- TEST_OPTS =
29
- # TRACER = strace -f -o $(t_pfx).strace -s 100000
30
- # TRACER = /usr/bin/time -o $(t_pfx).time
31
-
32
- ifdef V
33
- ifeq ($(V),2)
34
- TEST_OPTS += --trace
35
- else
36
- TEST_OPTS += --verbose
37
- endif
38
- endif
39
-
40
- random_blob:
41
- dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
42
- mv $@.$(pid) $@
43
-
44
- $(T): random_blob
45
-
46
- dependencies := socat curl
47
- deps := $(addprefix .dep+,$(dependencies))
48
- $(deps): dep_bin = $(lastword $(subst +, ,$@))
49
- $(deps):
50
- @which $(dep_bin) > $@.$(pid) 2>/dev/null || :
51
- @test -s $@.$(pid) || \
52
- { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
53
- @mv $@.$(pid) $@
54
- dep: $(deps)
55
-
56
- test_prefix := $(CURDIR)/../test/$(RUBY_ENGINE)-$(RUBY_VERSION)
57
- $(test_prefix)/.stamp:
58
- $(MAKE) -C .. test-install
59
-
60
- $(T): export RUBY := $(RUBY)
61
- $(T): export RAKE := $(RAKE)
62
- $(T): export PATH := $(test_prefix)/bin:$(PATH)
63
- $(T): export RUBYLIB := $(test_prefix)/lib:$(MYLIBS)
64
- $(T): dep $(test_prefix)/.stamp trash/.gitignore
65
- $(TRACER) $(SHELL) $(SH_TEST_OPTS) $@ $(TEST_OPTS)
66
-
67
- trash/.gitignore:
68
- mkdir -p $(@D)
69
- echo '*' > $@
70
-
71
- clean:
72
- $(RM) -r trash/*
73
-
74
- .PHONY: $(T) clean
5
+ .PHONY: all
@@ -42,9 +42,19 @@ The benchmark client is usually httperf.
42
42
  Another gentle reminder: performance with slow networks/clients
43
43
  is NOT our problem. That is the job of nginx (or similar).
44
44
 
45
+ == ddstream.ru
46
+
47
+ Standalone Rack app intended to show how BAD we are at slow clients.
48
+ See usage in comments.
49
+
50
+ == readinput.ru
51
+
52
+ Standalone Rack app intended to show how bad we are with slow uploaders.
53
+ See usage in comments.
54
+
45
55
  == Contributors
46
56
 
47
- This directory is maintained independently in the "benchmark" branch
48
- based against v0.1.0. Only changes to this directory (test/benchmarks)
49
- are committed to this branch although the master branch may merge this
50
- branch occassionaly.
57
+ This directory is intended to remain stable. Do not make changes
58
+ to benchmarking code which can change performance and invalidate
59
+ results across revisions. Instead, write new benchmarks and update
60
+ coments/documentation as necessary.
@@ -0,0 +1,50 @@
1
+ # This app is intended to test large HTTP responses with or without
2
+ # a fully-buffering reverse proxy such as nginx. Without a fully-buffering
3
+ # reverse proxy, unicorn will be unresponsive when client count exceeds
4
+ # worker_processes.
5
+ #
6
+ # To demonstrate how bad unicorn is at slowly reading clients:
7
+ #
8
+ # # in one terminal, start unicorn with one worker:
9
+ # unicorn -E none -l 127.0.0.1:8080 test/benchmark/ddstream.ru
10
+ #
11
+ # # in a different terminal, start more slow curl processes than
12
+ # # unicorn workers and watch time outputs
13
+ # curl --limit-rate 8K --trace-time -vsN http://127.0.0.1:8080/ >/dev/null &
14
+ # curl --limit-rate 8K --trace-time -vsN http://127.0.0.1:8080/ >/dev/null &
15
+ # wait
16
+ #
17
+ # The last client won't see a response until the first one is done reading
18
+ #
19
+ # nginx note: do not change the default "proxy_buffering" behavior.
20
+ # Setting "proxy_buffering off" prevents nginx from protecting unicorn.
21
+
22
+ # totally standalone rack app to stream a giant response
23
+ class BigResponse
24
+ def initialize(bs, count)
25
+ @buf = "#{bs.to_s(16)}\r\n#{' ' * bs}\r\n"
26
+ @count = count
27
+ @res = [ 200,
28
+ { 'Transfer-Encoding' => -'chunked', 'Content-Type' => 'text/plain' },
29
+ self
30
+ ]
31
+ end
32
+
33
+ # rack response body iterator
34
+ def each
35
+ (1..@count).each { yield @buf }
36
+ yield -"0\r\n\r\n"
37
+ end
38
+
39
+ # rack app entry endpoint
40
+ def call(_env)
41
+ @res
42
+ end
43
+ end
44
+
45
+ # default to a giant (128M) response because kernel socket buffers
46
+ # can be ridiculously large on some systems
47
+ bs = ENV['bs'] ? ENV['bs'].to_i : 65536
48
+ count = ENV['count'] ? ENV['count'].to_i : 2048
49
+ warn "serving response with bs=#{bs} count=#{count} (#{bs*count} bytes)"
50
+ run BigResponse.new(bs, count)
@@ -0,0 +1,40 @@
1
+ # This app is intended to test large HTTP requests with or without
2
+ # a fully-buffering reverse proxy such as nginx. Without a fully-buffering
3
+ # reverse proxy, unicorn will be unresponsive when client count exceeds
4
+ # worker_processes.
5
+
6
+ DOC = <<DOC
7
+ To demonstrate how bad unicorn is at slowly uploading clients:
8
+
9
+ # in one terminal, start unicorn with one worker:
10
+ unicorn -E none -l 127.0.0.1:8080 test/benchmark/readinput.ru
11
+
12
+ # in a different terminal, upload 45M from multiple curl processes:
13
+ dd if=/dev/zero bs=45M count=1 | curl -T- -HExpect: --limit-rate 1M \
14
+ --trace-time -v http://127.0.0.1:8080/ &
15
+ dd if=/dev/zero bs=45M count=1 | curl -T- -HExpect: --limit-rate 1M \
16
+ --trace-time -v http://127.0.0.1:8080/ &
17
+ wait
18
+
19
+ # The last client won't see a response until the first one is done uploading
20
+ # You also won't be able to make GET requests to view this documentation
21
+ # while clients are uploading. You can also view the stderr debug output
22
+ # of unicorn (see logging code in #{__FILE__}).
23
+ DOC
24
+
25
+ run(lambda do |env|
26
+ input = env['rack.input']
27
+ buf = ''.b
28
+
29
+ # default logger contains timestamps, rely on that so users can
30
+ # see what the server is doing
31
+ l = env['rack.logger']
32
+
33
+ l.debug('BEGIN reading input ...') if l
34
+ :nop while input.read(16384, buf)
35
+ l.debug('DONE reading input ...') if l
36
+
37
+ buf.clear
38
+ [ 200, [ %W(Content-Length #{DOC.size}), %w(Content-Type text/plain) ],
39
+ [ DOC ] ]
40
+ end)
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/perl -w
2
+ # Benchmark script to spawn some processes and hammer a local unicorn
3
+ # to test accept loop performance. This only does Unix sockets.
4
+ # There's plenty of TCP benchmarking tools out there, and TCP port reuse
5
+ # has predictability problems since unicorn can't do persistent connections.
6
+ # Written in Perl for the same reason: predictability.
7
+ # Ruby GC is not as predictable as Perl refcounting.
8
+ use strict;
9
+ use Socket qw(AF_UNIX SOCK_STREAM sockaddr_un);
10
+ use POSIX qw(:sys_wait_h);
11
+ use Getopt::Std;
12
+ # -c / -n switches stolen from ab(1)
13
+ my $usage = "$0 [-c CONCURRENCY] [-n NUM_REQUESTS] SOCKET_PATH\n";
14
+ our $opt_c = 2;
15
+ our $opt_n = 1000;
16
+ getopts('c:n:') or die $usage;
17
+ my $unix_path = shift or die $usage;
18
+ use constant REQ => "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
19
+ use constant REQ_LEN => length(REQ);
20
+ use constant BUFSIZ => 8192;
21
+ $^F = 99; # don't waste syscall time with FD_CLOEXEC
22
+
23
+ my %workers; # pid => worker num
24
+ die "-n $opt_n not evenly divisible by -c $opt_c\n" if $opt_n % $opt_c;
25
+ my $n_per_worker = $opt_n / $opt_c;
26
+ my $addr = sockaddr_un($unix_path);
27
+
28
+ for my $num (1..$opt_c) {
29
+ defined(my $pid = fork) or die "fork failed: $!\n";
30
+ if ($pid) {
31
+ $workers{$pid} = $num;
32
+ } else {
33
+ work($n_per_worker);
34
+ }
35
+ }
36
+
37
+ reap_worker(0) while scalar keys %workers;
38
+ exit;
39
+
40
+ sub work {
41
+ my ($n) = @_;
42
+ my ($buf, $x);
43
+ for (1..$n) {
44
+ socket(S, AF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
45
+ connect(S, $addr) or die "connect: $!";
46
+ defined($x = syswrite(S, REQ)) or die "write: $!";
47
+ $x == REQ_LEN or die "short write: $x != ".REQ_LEN."\n";
48
+ do {
49
+ $x = sysread(S, $buf, BUFSIZ);
50
+ unless (defined $x) {
51
+ next if $!{EINTR};
52
+ die "sysread: $!\n";
53
+ }
54
+ } until ($x == 0);
55
+ }
56
+ exit 0;
57
+ }
58
+
59
+ sub reap_worker {
60
+ my ($flags) = @_;
61
+ my $pid = waitpid(-1, $flags);
62
+ return if !defined $pid || $pid <= 0;
63
+ my $p = delete $workers{$pid} || '(unknown)';
64
+ warn("$pid [$p] exited with $?\n") if $?;
65
+ $p;
66
+ }
@@ -45,8 +45,9 @@ end
45
45
 
46
46
  COMMON_TMP = Tempfile.new('unicorn_tmp') unless defined?(COMMON_TMP)
47
47
 
48
+ HEAVY_WORKERS = 2
48
49
  HEAVY_CFG = <<-EOS
49
- worker_processes 4
50
+ worker_processes #{HEAVY_WORKERS}
50
51
  timeout 30
51
52
  logger Logger.new('#{COMMON_TMP.path}')
52
53
  before_fork do |server, worker|
@@ -606,6 +607,7 @@ EOF
606
607
  def test_weird_config_settings
607
608
  File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
608
609
  ucfg = Tempfile.new('unicorn_test_config')
610
+ proc_total = HEAVY_WORKERS + 1 # + 1 for master
609
611
  ucfg.syswrite(HEAVY_CFG)
610
612
  pid = xfork do
611
613
  redirect_test_io do
@@ -616,9 +618,9 @@ EOF
616
618
  results = retry_hit(["http://#{@addr}:#{@port}/"])
617
619
  assert_equal String, results[0].class
618
620
  wait_master_ready(COMMON_TMP.path)
619
- wait_workers_ready(COMMON_TMP.path, 4)
621
+ wait_workers_ready(COMMON_TMP.path, HEAVY_WORKERS)
620
622
  bf = File.readlines(COMMON_TMP.path).grep(/\bbefore_fork: worker=/)
621
- assert_equal 4, bf.size
623
+ assert_equal HEAVY_WORKERS, bf.size
622
624
  rotate = Tempfile.new('unicorn_rotate')
623
625
 
624
626
  File.rename(COMMON_TMP.path, rotate.path)
@@ -630,20 +632,20 @@ EOF
630
632
  tries = DEFAULT_TRIES
631
633
  log = File.readlines(rotate.path)
632
634
  while (tries -= 1) > 0 &&
633
- log.grep(/reopening logs\.\.\./).size < 5
635
+ log.grep(/reopening logs\.\.\./).size < proc_total
634
636
  sleep DEFAULT_RES
635
637
  log = File.readlines(rotate.path)
636
638
  end
637
- assert_equal 5, log.grep(/reopening logs\.\.\./).size
639
+ assert_equal proc_total, log.grep(/reopening logs\.\.\./).size
638
640
  assert_equal 0, log.grep(/done reopening logs/).size
639
641
 
640
642
  tries = DEFAULT_TRIES
641
643
  log = File.readlines(COMMON_TMP.path)
642
- while (tries -= 1) > 0 && log.grep(/done reopening logs/).size < 5
644
+ while (tries -= 1) > 0 && log.grep(/done reopening logs/).size < proc_total
643
645
  sleep DEFAULT_RES
644
646
  log = File.readlines(COMMON_TMP.path)
645
647
  end
646
- assert_equal 5, log.grep(/done reopening logs/).size
648
+ assert_equal proc_total, log.grep(/done reopening logs/).size
647
649
  assert_equal 0, log.grep(/reopening logs\.\.\./).size
648
650
 
649
651
  Process.kill(:QUIT, pid)
data/test/test_helper.rb CHANGED
@@ -34,16 +34,33 @@ if ENV['DEBUG']
34
34
  Debugger.start
35
35
  end
36
36
 
37
+ unless RUBY_VERSION < '3.1'
38
+ warn "Unicorn was only tested against MRI up to 3.0.\n" \
39
+ "It might not properly work with #{RUBY_VERSION}"
40
+ end
41
+
37
42
  def redirect_test_io
38
43
  orig_err = STDERR.dup
39
44
  orig_out = STDOUT.dup
40
- STDERR.reopen("test_stderr.#{$$}.log", "a")
41
- STDOUT.reopen("test_stdout.#{$$}.log", "a")
45
+ new_out = File.open("test_stdout.#$$.log", "a")
46
+ new_err = File.open("test_stderr.#$$.log", "a")
47
+ new_out.sync = new_err.sync = true
48
+
49
+ if tail = ENV['TAIL'] # "tail -F" if GNU, "tail -f" otherwise
50
+ require 'shellwords'
51
+ cmd = tail.shellsplit
52
+ cmd << new_out.path
53
+ cmd << new_err.path
54
+ pid = Process.spawn(*cmd, { 1 => 2, :pgroup => true })
55
+ sleep 0.1 # wait for tail(1) to startup
56
+ end
57
+ STDERR.reopen(new_err)
58
+ STDOUT.reopen(new_out)
42
59
  STDERR.sync = STDOUT.sync = true
43
60
 
44
61
  at_exit do
45
- File.unlink("test_stderr.#{$$}.log") rescue nil
46
- File.unlink("test_stdout.#{$$}.log") rescue nil
62
+ File.unlink(new_out.path) rescue nil
63
+ File.unlink(new_err.path) rescue nil
47
64
  end
48
65
 
49
66
  begin
@@ -51,6 +68,7 @@ def redirect_test_io
51
68
  ensure
52
69
  STDERR.reopen(orig_err)
53
70
  STDOUT.reopen(orig_out)
71
+ Process.kill(:TERM, pid) if pid
54
72
  end
55
73
  end
56
74
 
@@ -265,32 +283,6 @@ def wait_for_death(pid)
265
283
  raise "PID:#{pid} never died!"
266
284
  end
267
285
 
268
- # executes +cmd+ and chunks its STDOUT
269
- def chunked_spawn(stdout, *cmd)
270
- fork {
271
- crd, cwr = IO.pipe
272
- crd.binmode
273
- cwr.binmode
274
- crd.sync = cwr.sync = true
275
-
276
- pid = fork {
277
- STDOUT.reopen(cwr)
278
- crd.close
279
- cwr.close
280
- exec(*cmd)
281
- }
282
- cwr.close
283
- begin
284
- buf = crd.readpartial(16384)
285
- stdout.write("#{'%x' % buf.size}\r\n#{buf}")
286
- rescue EOFError
287
- stdout.write("0\r\n")
288
- pid, status = Process.waitpid(pid)
289
- exit status.exitstatus
290
- end while true
291
- }
292
- end
293
-
294
286
  def reset_sig_handlers
295
287
  %w(WINCH QUIT INT TERM USR1 USR2 HUP TTIN TTOU CHLD).each do |sig|
296
288
  trap(sig, "DEFAULT")