unicorn 2.0.0pre3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -2
- data/examples/unicorn.conf.rb +4 -1
- data/lib/unicorn/configurator.rb +15 -7
- data/lib/unicorn/const.rb +2 -2
- data/lib/unicorn/http_server.rb +20 -26
- data/t/t0012-reload-empty-config.sh +82 -0
- data/unicorn.gemspec +2 -2
- metadata +15 -17
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -175,7 +175,7 @@ NEWS: GIT-VERSION-FILE .manifest
|
|
175
175
|
$(RAKE) -s news_rdoc > $@+
|
176
176
|
mv $@+ $@
|
177
177
|
|
178
|
-
SINCE = 1.1.
|
178
|
+
SINCE = 1.1.5
|
179
179
|
ChangeLog: LOG_VERSION = \
|
180
180
|
$(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
|
181
181
|
echo $(GIT_VERSION) || git describe)
|
@@ -195,7 +195,7 @@ atom = <link rel="alternate" title="Atom feed" href="$(1)" \
|
|
195
195
|
doc: .document $(ext)/unicorn_http.c NEWS ChangeLog
|
196
196
|
for i in $(man1_rdoc); do echo > $$i; done
|
197
197
|
find bin lib -type f -name '*.rbc' -exec rm -f '{}' ';'
|
198
|
-
rdoc -
|
198
|
+
rdoc -t "$(shell sed -ne '1s/^= //p' README)"
|
199
199
|
install -m644 COPYING doc/COPYING
|
200
200
|
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
201
201
|
$(MAKE) -C Documentation install-html install-man
|
data/examples/unicorn.conf.rb
CHANGED
@@ -63,7 +63,10 @@ before_fork do |server, worker|
|
|
63
63
|
# end
|
64
64
|
# end
|
65
65
|
#
|
66
|
-
#
|
66
|
+
# Throttle the master from forking too quickly by sleeping. Due
|
67
|
+
# to the implementation of standard Unix signal handlers, this
|
68
|
+
# helps (but does not completely) prevent identical, repeated signals
|
69
|
+
# from being lost when the receiving process is busy.
|
67
70
|
# sleep 1
|
68
71
|
end
|
69
72
|
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -8,7 +8,9 @@ require 'logger'
|
|
8
8
|
# example configuration files. An example config file for use with
|
9
9
|
# nginx is also available at
|
10
10
|
# http://unicorn.bogomips.org/examples/nginx.conf
|
11
|
-
class Unicorn::Configurator
|
11
|
+
class Unicorn::Configurator
|
12
|
+
attr_accessor :set, :config_file, :after_reload
|
13
|
+
|
12
14
|
# :stopdoc:
|
13
15
|
# used to stash stuff for deferred processing of cli options in
|
14
16
|
# config.ru after "working_directory" is bound. Do not rely on
|
@@ -42,21 +44,24 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
|
|
42
44
|
|
43
45
|
def initialize(defaults = {}) #:nodoc:
|
44
46
|
self.set = Hash.new(:unset)
|
45
|
-
use_defaults = defaults.delete(:use_defaults)
|
47
|
+
@use_defaults = defaults.delete(:use_defaults)
|
46
48
|
self.config_file = defaults.delete(:config_file)
|
47
49
|
|
48
50
|
# after_reload is only used by unicorn_rails, unsupported otherwise
|
49
51
|
self.after_reload = defaults.delete(:after_reload)
|
50
52
|
|
51
|
-
set.merge!(DEFAULTS) if use_defaults
|
52
|
-
defaults.each { |key, value| self.
|
53
|
+
set.merge!(DEFAULTS) if @use_defaults
|
54
|
+
defaults.each { |key, value| self.__send__(key, value) }
|
53
55
|
Hash === set[:listener_opts] or
|
54
56
|
set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} }
|
55
57
|
Array === set[:listeners] or set[:listeners] = []
|
56
|
-
reload
|
58
|
+
reload(false)
|
57
59
|
end
|
58
60
|
|
59
|
-
def reload #:nodoc:
|
61
|
+
def reload(merge_defaults = true) #:nodoc:
|
62
|
+
if merge_defaults && @use_defaults
|
63
|
+
set.merge!(DEFAULTS) if @use_defaults
|
64
|
+
end
|
60
65
|
instance_eval(File.read(config_file), config_file) if config_file
|
61
66
|
|
62
67
|
parse_rackup_file
|
@@ -401,7 +406,10 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
|
|
401
406
|
|
402
407
|
# sets the working directory for Unicorn. This ensures SIGUSR2 will
|
403
408
|
# start a new instance of Unicorn in this directory. This may be
|
404
|
-
# a symlink, a common scenario for Capistrano users.
|
409
|
+
# a symlink, a common scenario for Capistrano users. Unlike
|
410
|
+
# all other Unicorn configuration directives, this binds immediately
|
411
|
+
# for error checking and cannot be undone by unsetting it in the
|
412
|
+
# configuration file and reloading.
|
405
413
|
def working_directory(path)
|
406
414
|
# just let chdir raise errors
|
407
415
|
path = File.expand_path(path)
|
data/lib/unicorn/const.rb
CHANGED
@@ -7,8 +7,8 @@
|
|
7
7
|
# improve things much compared to constants.
|
8
8
|
module Unicorn::Const
|
9
9
|
|
10
|
-
# The current version of Unicorn, currently 2.0.
|
11
|
-
UNICORN_VERSION = "2.0.
|
10
|
+
# The current version of Unicorn, currently 2.0.0
|
11
|
+
UNICORN_VERSION = "2.0.0"
|
12
12
|
|
13
13
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
14
14
|
DEFAULT_HOST = "0.0.0.0"
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -50,6 +50,10 @@ class Unicorn::HttpServer
|
|
50
50
|
# signal queue used for self-piping
|
51
51
|
SIG_QUEUE = []
|
52
52
|
|
53
|
+
# list of signals we care about and trap in master.
|
54
|
+
QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
|
55
|
+
|
56
|
+
# :startdoc:
|
53
57
|
# We populate this at startup so we can figure out how to reexecute
|
54
58
|
# and upgrade the currently running instance of Unicorn
|
55
59
|
# This Hash is considered a stable interface and changing its contents
|
@@ -82,7 +86,6 @@ class Unicorn::HttpServer
|
|
82
86
|
}.call,
|
83
87
|
0 => $0.dup,
|
84
88
|
}
|
85
|
-
# :startdoc:
|
86
89
|
|
87
90
|
# Creates a working server on host:port (strange things happen if
|
88
91
|
# port isn't a Number). Use HttpServer::run to start the server and
|
@@ -151,8 +154,8 @@ class Unicorn::HttpServer
|
|
151
154
|
# setup signal handlers before writing pid file in case people get
|
152
155
|
# trigger happy and send signals as soon as the pid file exists.
|
153
156
|
# Note that signals don't actually get handled until the #join method
|
154
|
-
QUEUE_SIGS.each { |sig|
|
155
|
-
trap(:CHLD) {
|
157
|
+
QUEUE_SIGS.each { |sig| trap(sig) { SIG_QUEUE << sig; awaken_master } }
|
158
|
+
trap(:CHLD) { awaken_master }
|
156
159
|
self.pid = config[:pid]
|
157
160
|
|
158
161
|
self.master_pid = $$
|
@@ -287,14 +290,14 @@ class Unicorn::HttpServer
|
|
287
290
|
when nil
|
288
291
|
# avoid murdering workers after our master process (or the
|
289
292
|
# machine) comes out of suspend/hibernation
|
290
|
-
if (last_check + timeout) >= (last_check = Time.now)
|
291
|
-
murder_lazy_workers
|
293
|
+
if (last_check + @timeout) >= (last_check = Time.now)
|
294
|
+
sleep_time = murder_lazy_workers
|
292
295
|
else
|
293
296
|
# wait for workers to wakeup on suspend
|
294
|
-
|
297
|
+
sleep_time = @timeout/2.0 + 1
|
295
298
|
end
|
296
299
|
maintain_worker_count if respawn
|
297
|
-
master_sleep(
|
300
|
+
master_sleep(sleep_time)
|
298
301
|
when :QUIT # graceful shutdown
|
299
302
|
break
|
300
303
|
when :TERM, :INT # immediate shutdown
|
@@ -354,24 +357,7 @@ class Unicorn::HttpServer
|
|
354
357
|
|
355
358
|
private
|
356
359
|
|
357
|
-
# list of signals we care about and trap in master.
|
358
|
-
QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP,
|
359
|
-
:TTIN, :TTOU ]
|
360
|
-
|
361
|
-
# defer a signal for later processing in #join (master process)
|
362
|
-
def trap_deferred(signal)
|
363
|
-
trap(signal) do |sig_nr|
|
364
|
-
if SIG_QUEUE.size < 5
|
365
|
-
SIG_QUEUE << signal
|
366
|
-
awaken_master
|
367
|
-
else
|
368
|
-
logger.error "ignoring SIG#{signal}, queue=#{SIG_QUEUE.inspect}"
|
369
|
-
end
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
360
|
# wait for a signal hander to wake us up and then consume the pipe
|
374
|
-
# Wake up every second anyways to run murder_lazy_workers
|
375
361
|
def master_sleep(sec)
|
376
362
|
IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return
|
377
363
|
SELF_PIPE[0].kgio_tryread(11)
|
@@ -457,15 +443,23 @@ class Unicorn::HttpServer
|
|
457
443
|
# is stale for >timeout seconds, then we'll kill the corresponding
|
458
444
|
# worker.
|
459
445
|
def murder_lazy_workers
|
446
|
+
t = @timeout
|
447
|
+
next_sleep = 1
|
460
448
|
WORKERS.dup.each_pair do |wpid, worker|
|
461
449
|
stat = worker.tmp.stat
|
462
450
|
# skip workers that disable fchmod or have never fchmod-ed
|
463
451
|
stat.mode == 0100600 and next
|
464
|
-
|
452
|
+
diff = Time.now - stat.ctime
|
453
|
+
if diff <= t
|
454
|
+
tmp = t - diff
|
455
|
+
next_sleep < tmp and next_sleep = tmp
|
456
|
+
next
|
457
|
+
end
|
465
458
|
logger.error "worker=#{worker.nr} PID:#{wpid} timeout " \
|
466
|
-
"(#{diff}s > #{
|
459
|
+
"(#{diff}s > #{t}s), killing"
|
467
460
|
kill_worker(:KILL, wpid) # take no prisoners for timeout violations
|
468
461
|
end
|
462
|
+
next_sleep
|
469
463
|
end
|
470
464
|
|
471
465
|
def spawn_missing_workers
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 9 "reloading unset config resets defaults"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
rtmpfiles unicorn_config_orig before_reload after_reload
|
8
|
+
cat $unicorn_config > $unicorn_config_orig
|
9
|
+
cat >> $unicorn_config <<EOF
|
10
|
+
logger Logger.new(STDOUT)
|
11
|
+
preload_app true
|
12
|
+
timeout 0x7fffffff
|
13
|
+
worker_processes 2
|
14
|
+
after_fork { |s,w| }
|
15
|
+
\$dump_cfg = lambda { |fp,srv|
|
16
|
+
defaults = Unicorn::Configurator::DEFAULTS
|
17
|
+
defaults.keys.map { |x| x.to_s }.sort.each do |key|
|
18
|
+
next if key =~ %r{\Astd(?:err|out)_path\z}
|
19
|
+
key = key.to_sym
|
20
|
+
def_value = defaults[key]
|
21
|
+
srv_value = srv.__send__(key)
|
22
|
+
fp << "#{key}|#{srv_value}|#{def_value}\\n"
|
23
|
+
end
|
24
|
+
}
|
25
|
+
before_fork { |s,w|
|
26
|
+
File.open("$before_reload", "a") { |fp| \$dump_cfg.call(fp, s) }
|
27
|
+
}
|
28
|
+
before_exec { |s| }
|
29
|
+
EOF
|
30
|
+
unicorn -D -c $unicorn_config env.ru
|
31
|
+
unicorn_wait_start
|
32
|
+
}
|
33
|
+
|
34
|
+
t_begin "ensure worker is started" && {
|
35
|
+
curl -sSf http://$listen/ > $tmp
|
36
|
+
}
|
37
|
+
|
38
|
+
t_begin "replace config file with original(-ish)" && {
|
39
|
+
grep -v ^pid < $unicorn_config_orig > $unicorn_config
|
40
|
+
cat >> $unicorn_config <<EOF
|
41
|
+
before_fork { |s,w|
|
42
|
+
File.open("$after_reload", "a") { |fp| \$dump_cfg.call(fp, s) }
|
43
|
+
}
|
44
|
+
EOF
|
45
|
+
}
|
46
|
+
|
47
|
+
t_begin "reload signal succeeds" && {
|
48
|
+
kill -HUP $unicorn_pid
|
49
|
+
while ! egrep '(done|error) reloading' $r_err >/dev/null
|
50
|
+
do
|
51
|
+
sleep 1
|
52
|
+
done
|
53
|
+
|
54
|
+
grep 'done reloading' $r_err >/dev/null
|
55
|
+
}
|
56
|
+
|
57
|
+
t_begin "ensure worker is started" && {
|
58
|
+
curl -sSf http://$listen/ > $tmp
|
59
|
+
}
|
60
|
+
|
61
|
+
t_begin "pid file no longer exists" && {
|
62
|
+
if test -f $pid
|
63
|
+
then
|
64
|
+
die "pid=$pid should not exist"
|
65
|
+
fi
|
66
|
+
}
|
67
|
+
|
68
|
+
t_begin "killing succeeds" && {
|
69
|
+
kill $unicorn_pid
|
70
|
+
}
|
71
|
+
|
72
|
+
t_begin "check stderr" && {
|
73
|
+
check_stderr
|
74
|
+
}
|
75
|
+
|
76
|
+
t_begin "ensure reloading restored settings" && {
|
77
|
+
awk < $after_reload -F'|' '
|
78
|
+
$1 != "before_fork" && $2 != $3 { print $0; exit(1) }
|
79
|
+
'
|
80
|
+
}
|
81
|
+
|
82
|
+
t_done
|
data/unicorn.gemspec
CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
|
|
36
36
|
s.homepage = %q{http://unicorn.bogomips.org/}
|
37
37
|
|
38
38
|
summary = %q{Rack HTTP server for fast clients and Unix}
|
39
|
-
s.rdoc_options = [ "-
|
39
|
+
s.rdoc_options = [ "-t", "Unicorn: #{summary}" ]
|
40
40
|
s.require_paths = %w(lib ext)
|
41
41
|
s.rubyforge_project = %q{mongrel}
|
42
42
|
s.summary = summary
|
@@ -50,7 +50,7 @@ Gem::Specification.new do |s|
|
|
50
50
|
s.add_dependency(%q<rack>)
|
51
51
|
s.add_dependency(%q<kgio>, '~> 1.3.1')
|
52
52
|
|
53
|
-
s.add_development_dependency('isolate', '~>
|
53
|
+
s.add_development_dependency('isolate', '~> 3.0.0')
|
54
54
|
|
55
55
|
# s.licenses = %w(GPLv2 Ruby) # licenses= method is not in older RubyGems
|
56
56
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 15
|
5
|
+
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 2.0.
|
9
|
+
- 0
|
10
|
+
version: 2.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Unicorn hackers
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-27 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -56,12 +56,12 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - ~>
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
hash:
|
59
|
+
hash: 7
|
60
60
|
segments:
|
61
|
-
-
|
62
|
-
-
|
63
|
-
-
|
64
|
-
version:
|
61
|
+
- 3
|
62
|
+
- 0
|
63
|
+
- 0
|
64
|
+
version: 3.0.0
|
65
65
|
type: :development
|
66
66
|
version_requirements: *id003
|
67
67
|
description: |-
|
@@ -240,6 +240,7 @@ files:
|
|
240
240
|
- t/t0009-winch_ttin.sh
|
241
241
|
- t/t0010-reap-logging.sh
|
242
242
|
- t/t0011-active-unix-socket.sh
|
243
|
+
- t/t0012-reload-empty-config.sh
|
243
244
|
- t/t0300-rails3-basic.sh
|
244
245
|
- t/t0301-rails3-missing-config-ru.sh
|
245
246
|
- t/t0302-rails3-alt-working_directory.sh
|
@@ -349,7 +350,6 @@ licenses: []
|
|
349
350
|
|
350
351
|
post_install_message:
|
351
352
|
rdoc_options:
|
352
|
-
- -Na
|
353
353
|
- -t
|
354
354
|
- "Unicorn: Rack HTTP server for fast clients and Unix"
|
355
355
|
require_paths:
|
@@ -367,14 +367,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
367
367
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
368
368
|
none: false
|
369
369
|
requirements:
|
370
|
-
- - "
|
370
|
+
- - ">="
|
371
371
|
- !ruby/object:Gem::Version
|
372
|
-
hash:
|
372
|
+
hash: 3
|
373
373
|
segments:
|
374
|
-
-
|
375
|
-
|
376
|
-
- 1
|
377
|
-
version: 1.3.1
|
374
|
+
- 0
|
375
|
+
version: "0"
|
378
376
|
requirements: []
|
379
377
|
|
380
378
|
rubyforge_project: mongrel
|