einhorn 0.7.4 → 1.0.0
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.
- checksums.yaml +7 -0
- data/Changes.md +10 -0
- data/README.md +36 -30
- data/bin/einhorn +17 -2
- data/einhorn.gemspec +23 -21
- data/example/pool_worker.rb +1 -1
- data/example/thin_example +8 -8
- data/example/time_server +5 -5
- data/lib/einhorn/client.rb +8 -9
- data/lib/einhorn/command/interface.rb +100 -95
- data/lib/einhorn/command.rb +167 -88
- data/lib/einhorn/compat.rb +7 -7
- data/lib/einhorn/event/abstract_text_descriptor.rb +31 -35
- data/lib/einhorn/event/ack_timer.rb +2 -2
- data/lib/einhorn/event/command_server.rb +7 -9
- data/lib/einhorn/event/connection.rb +1 -3
- data/lib/einhorn/event/loop_breaker.rb +2 -1
- data/lib/einhorn/event/persistent.rb +2 -2
- data/lib/einhorn/event/timer.rb +4 -4
- data/lib/einhorn/event.rb +29 -20
- data/lib/einhorn/prctl.rb +26 -0
- data/lib/einhorn/prctl_linux.rb +48 -0
- data/lib/einhorn/safe_yaml.rb +17 -0
- data/lib/einhorn/version.rb +1 -1
- data/lib/einhorn/worker.rb +67 -49
- data/lib/einhorn/worker_pool.rb +9 -9
- data/lib/einhorn.rb +155 -126
- metadata +42 -137
- data/.gitignore +0 -17
- data/.travis.yml +0 -10
- data/CONTRIBUTORS +0 -6
- data/Gemfile +0 -11
- data/History.txt +0 -4
- data/README.md.in +0 -76
- data/Rakefile +0 -27
- data/test/_lib.rb +0 -12
- data/test/integration/_lib/fixtures/env_printer/env_printer.rb +0 -26
- data/test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb +0 -22
- data/test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb +0 -6
- data/test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb +0 -22
- data/test/integration/_lib/helpers/einhorn_helpers.rb +0 -143
- data/test/integration/_lib/helpers.rb +0 -4
- data/test/integration/_lib.rb +0 -6
- data/test/integration/startup.rb +0 -31
- data/test/integration/upgrading.rb +0 -157
- data/test/unit/einhorn/client.rb +0 -88
- data/test/unit/einhorn/command/interface.rb +0 -49
- data/test/unit/einhorn/command.rb +0 -21
- data/test/unit/einhorn/event.rb +0 -89
- data/test/unit/einhorn/worker_pool.rb +0 -39
- data/test/unit/einhorn.rb +0 -58
- /data/{LICENSE → LICENSE.txt} +0 -0
@@ -2,7 +2,7 @@ module Einhorn::Event
|
|
2
2
|
class ACKTimer < Timer
|
3
3
|
include Persistent
|
4
4
|
|
5
|
-
def initialize(time, pid, start=nil)
|
5
|
+
def initialize(time, pid, start = nil)
|
6
6
|
super(time, start) do
|
7
7
|
Einhorn::Command.register_timer_ack(time, pid)
|
8
8
|
end
|
@@ -10,7 +10,7 @@ module Einhorn::Event
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def to_state
|
13
|
-
{:
|
13
|
+
{class: self.class.to_s, time: @time, start: @start, pid: @pid}
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.from_state(state)
|
@@ -3,7 +3,7 @@ module Einhorn::Event
|
|
3
3
|
include Persistent
|
4
4
|
|
5
5
|
def self.open(server)
|
6
|
-
|
6
|
+
new(server)
|
7
7
|
end
|
8
8
|
|
9
9
|
def initialize(server)
|
@@ -15,14 +15,12 @@ module Einhorn::Event
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def notify_readable
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
Connection.open(sock)
|
23
|
-
end
|
24
|
-
rescue Errno::EAGAIN
|
18
|
+
loop do
|
19
|
+
return if @closed
|
20
|
+
sock = Einhorn::Compat.accept_nonblock(@server)
|
21
|
+
Connection.open(sock)
|
25
22
|
end
|
23
|
+
rescue Errno::EAGAIN
|
26
24
|
end
|
27
25
|
|
28
26
|
def to_io
|
@@ -30,7 +28,7 @@ module Einhorn::Event
|
|
30
28
|
end
|
31
29
|
|
32
30
|
def to_state
|
33
|
-
{:
|
31
|
+
{class: self.class.to_s, server: @server.fileno}
|
34
32
|
end
|
35
33
|
|
36
34
|
def self.from_state(state)
|
@@ -11,8 +11,6 @@ module Einhorn::Event
|
|
11
11
|
split = @read_buffer.split("\n", 2)
|
12
12
|
if split.length > 1
|
13
13
|
split
|
14
|
-
else
|
15
|
-
nil
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
@@ -21,7 +19,7 @@ module Einhorn::Event
|
|
21
19
|
end
|
22
20
|
|
23
21
|
def to_state
|
24
|
-
state = {:
|
22
|
+
state = {class: self.class.to_s, socket: @socket.fileno}
|
25
23
|
# Don't include by default because it's not that pretty
|
26
24
|
state[:read_buffer] = @read_buffer if @read_buffer.length > 0
|
27
25
|
state[:write_buffer] = @write_buffer if @write_buffer.length > 0
|
@@ -8,7 +8,7 @@ module Einhorn::Event
|
|
8
8
|
|
9
9
|
def self.from_state(state)
|
10
10
|
klass_name = state[:class]
|
11
|
-
if klass = @@persistent[klass_name]
|
11
|
+
if (klass = @@persistent[klass_name])
|
12
12
|
klass.from_state(state)
|
13
13
|
else
|
14
14
|
Einhorn.log_error("Unrecognized persistent descriptor class #{klass_name.inspect}. Ignoring. This most likely indicates that your Einhorn version has upgraded. Everything should still be working, but it may be worth a restart.", :upgrade)
|
@@ -17,7 +17,7 @@ module Einhorn::Event
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.persistent?(descriptor)
|
20
|
-
@@persistent.values.any? {|klass| descriptor.
|
20
|
+
@@persistent.values.any? { |klass| descriptor.is_a?(klass) }
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/lib/einhorn/event/timer.rb
CHANGED
@@ -2,7 +2,7 @@ module Einhorn::Event
|
|
2
2
|
class Timer
|
3
3
|
attr_reader :time
|
4
4
|
|
5
|
-
def initialize(time, start=nil, &blk)
|
5
|
+
def initialize(time, start = nil, &blk)
|
6
6
|
@time = time
|
7
7
|
@start = start || Time.now
|
8
8
|
@blk = blk
|
@@ -10,7 +10,7 @@ module Einhorn::Event
|
|
10
10
|
|
11
11
|
# TODO: abstract into some interface
|
12
12
|
def self.open(*args, &blk)
|
13
|
-
instance =
|
13
|
+
instance = new(*args, &blk)
|
14
14
|
instance.register!
|
15
15
|
instance
|
16
16
|
end
|
@@ -27,12 +27,12 @@ module Einhorn::Event
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def register!
|
30
|
-
Einhorn.log_debug("Scheduling a new #{
|
30
|
+
Einhorn.log_debug("Scheduling a new #{time}s timer")
|
31
31
|
Einhorn::Event.register_timer(self)
|
32
32
|
end
|
33
33
|
|
34
34
|
def deregister!
|
35
|
-
Einhorn.log_debug("Nuking timer that expired #{Time.now -
|
35
|
+
Einhorn.log_debug("Nuking timer that expired #{Time.now - expires_at}s ago")
|
36
36
|
Einhorn::Event.deregister_timer(self)
|
37
37
|
end
|
38
38
|
end
|
data/lib/einhorn/event.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require "set"
|
2
2
|
|
3
3
|
module Einhorn
|
4
4
|
module Event
|
5
5
|
@@loopbreak_reader = nil
|
6
6
|
@@loopbreak_writer = nil
|
7
|
+
@@default_timeout = nil
|
7
8
|
@@signal_actions = []
|
8
9
|
@@readable = {}
|
9
10
|
@@writeable = {}
|
@@ -36,8 +37,8 @@ module Einhorn
|
|
36
37
|
|
37
38
|
def self.persistent_descriptors
|
38
39
|
descriptor_sets = @@readable.values + @@writeable.values + @@timers.values
|
39
|
-
descriptors = descriptor_sets.inject {|a, b| a | b}
|
40
|
-
descriptors.select {|descriptor| Einhorn::Event::Persistent.persistent?(descriptor)}
|
40
|
+
descriptors = descriptor_sets.inject { |a, b| a | b }
|
41
|
+
descriptors.select { |descriptor| Einhorn::Event::Persistent.persistent?(descriptor) }
|
41
42
|
end
|
42
43
|
|
43
44
|
def self.restore_persistent_descriptors(persistent_descriptors)
|
@@ -80,8 +81,8 @@ module Einhorn
|
|
80
81
|
|
81
82
|
def self.writeable_fds
|
82
83
|
writers = @@writeable.select do |io, writers|
|
83
|
-
writers.any? {|writer| writer.write_pending?}
|
84
|
-
end.map {|io, writers| io}
|
84
|
+
writers.any? { |writer| writer.write_pending? }
|
85
|
+
end.map { |io, writers| io }
|
85
86
|
Einhorn.log_debug("Writeable fds are #{writers.inspect}")
|
86
87
|
writers
|
87
88
|
end
|
@@ -117,10 +118,10 @@ module Einhorn
|
|
117
118
|
|
118
119
|
def self.timeout
|
119
120
|
# (expires_at of the next timer) - now
|
120
|
-
if expires_at = @@timers.keys.
|
121
|
+
if (expires_at = @@timers.keys.min)
|
121
122
|
expires_at - Time.now
|
122
123
|
else
|
123
|
-
|
124
|
+
@@default_timeout
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
@@ -129,7 +130,7 @@ module Einhorn
|
|
129
130
|
# handlers. Since it's just an array we push to/shift from, we
|
130
131
|
# can be sure there's no race (such as adding hash keys during
|
131
132
|
# iteration.)
|
132
|
-
while blk = @@signal_actions.shift
|
133
|
+
while (blk = @@signal_actions.shift)
|
133
134
|
blk.call
|
134
135
|
end
|
135
136
|
end
|
@@ -142,37 +143,45 @@ module Einhorn
|
|
142
143
|
|
143
144
|
readable, writeable, _ = IO.select(readable_fds, writeable_fds, nil, time)
|
144
145
|
(readable || []).each do |io|
|
145
|
-
@@readable[io].each {|reader| reader.notify_readable}
|
146
|
+
@@readable[io].each { |reader| reader.notify_readable }
|
146
147
|
end
|
147
148
|
|
148
149
|
(writeable || []).each do |io|
|
149
|
-
@@writeable[io].each {|writer| writer.notify_writeable}
|
150
|
+
@@writeable[io].each { |writer| writer.notify_writeable }
|
150
151
|
end
|
151
152
|
end
|
152
153
|
|
153
154
|
def self.run_timers
|
154
|
-
@@timers.select {|expires_at, _| expires_at <= Time.now}.each do |expires_at, timers|
|
155
|
+
@@timers.select { |expires_at, _| expires_at <= Time.now }.each do |expires_at, timers|
|
155
156
|
# Going to be modifying the set, so let's dup it.
|
156
|
-
timers.dup.each {|timer| timer.ring!}
|
157
|
+
timers.dup.each { |timer| timer.ring! }
|
157
158
|
end
|
158
159
|
end
|
159
160
|
|
160
161
|
def self.break_loop
|
161
162
|
Einhorn.log_debug("Breaking the loop")
|
162
163
|
begin
|
163
|
-
@@loopbreak_writer.write_nonblock(
|
164
|
+
@@loopbreak_writer.write_nonblock("a")
|
164
165
|
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
165
166
|
Einhorn.log_error("Loop break pipe is full -- probably means that we are quite backlogged")
|
166
167
|
end
|
167
168
|
end
|
169
|
+
|
170
|
+
def self.default_timeout=(val)
|
171
|
+
@@default_timeout = val.to_i == 0 ? nil : val.to_i
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.default_timeout
|
175
|
+
@@default_timeout
|
176
|
+
end
|
168
177
|
end
|
169
178
|
end
|
170
179
|
|
171
|
-
require
|
172
|
-
require
|
180
|
+
require "einhorn/event/persistent"
|
181
|
+
require "einhorn/event/timer"
|
173
182
|
|
174
|
-
require
|
175
|
-
require
|
176
|
-
require
|
177
|
-
require
|
178
|
-
require
|
183
|
+
require "einhorn/event/abstract_text_descriptor"
|
184
|
+
require "einhorn/event/ack_timer"
|
185
|
+
require "einhorn/event/command_server"
|
186
|
+
require "einhorn/event/connection"
|
187
|
+
require "einhorn/event/loop_breaker"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Einhorn
|
2
|
+
class PrctlAbstract
|
3
|
+
def self.get_pdeathsig
|
4
|
+
raise NotImplementedError
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.set_pdeathsig(signal)
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class PrctlUnimplemented < PrctlAbstract
|
13
|
+
# Deliberately empty; NotImplementedError is intended
|
14
|
+
end
|
15
|
+
|
16
|
+
if RUBY_PLATFORM.match?(/linux/)
|
17
|
+
begin
|
18
|
+
require "einhorn/prctl_linux"
|
19
|
+
Prctl = PrctlLinux
|
20
|
+
rescue LoadError
|
21
|
+
Prctl = PrctlUnimplemented
|
22
|
+
end
|
23
|
+
else
|
24
|
+
Prctl = PrctlUnimplemented
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "fiddle"
|
2
|
+
require "fiddle/import"
|
3
|
+
|
4
|
+
module Einhorn
|
5
|
+
module PrctlRaw
|
6
|
+
extend Fiddle::Importer
|
7
|
+
dlload Fiddle.dlopen(nil) # libc
|
8
|
+
extern "int prctl(int, unsigned long, unsigned long, unsigned long, unsigned long)"
|
9
|
+
|
10
|
+
# From linux/prctl.h
|
11
|
+
SET_PDEATHSIG = 1
|
12
|
+
GET_PDEATHSIG = 2
|
13
|
+
end
|
14
|
+
|
15
|
+
class PrctlLinux < PrctlAbstract
|
16
|
+
# Reading integers is hard with fiddle. :(
|
17
|
+
IntStruct = Fiddle::CStructBuilder.create(Fiddle::CStruct, [Fiddle::TYPE_INT], ["i"])
|
18
|
+
|
19
|
+
def self.get_pdeathsig
|
20
|
+
out = IntStruct.malloc
|
21
|
+
out.i = 0
|
22
|
+
if PrctlRaw.prctl(PrctlRaw::GET_PDEATHSIG, out.to_i, 0, 0, 0) != 0
|
23
|
+
raise SystemCallError.new("get_pdeathsig", Fiddle.last_error)
|
24
|
+
end
|
25
|
+
|
26
|
+
signo = out.i
|
27
|
+
if signo == 0
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
Signal.signame(signo)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.set_pdeathsig(signal)
|
35
|
+
signo = if signal.nil?
|
36
|
+
0
|
37
|
+
elsif signal.instance_of?(String)
|
38
|
+
Signal.list.fetch(signal)
|
39
|
+
else
|
40
|
+
signal
|
41
|
+
end
|
42
|
+
|
43
|
+
if PrctlRaw.prctl(PrctlRaw::SET_PDEATHSIG, signo, 0, 0, 0) != 0
|
44
|
+
raise SystemCallError.new("set_pdeathsig(#{signal})", Fiddle.last_error)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Einhorn
|
4
|
+
module SafeYAML
|
5
|
+
begin
|
6
|
+
YAML.safe_load("---", permitted_classes: [])
|
7
|
+
rescue ArgumentError
|
8
|
+
def self.load(payload)
|
9
|
+
YAML.safe_load(payload, [Set, Symbol, Time], [], true)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
def self.load(payload) # rubocop:disable Lint/DuplicateMethods
|
13
|
+
YAML.safe_load(payload, permitted_classes: [Set, Symbol, Time], aliases: true)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/einhorn/version.rb
CHANGED
data/lib/einhorn/worker.rb
CHANGED
@@ -1,23 +1,21 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "einhorn/client"
|
2
|
+
require "einhorn/command/interface"
|
3
3
|
|
4
4
|
module Einhorn
|
5
5
|
module Worker
|
6
6
|
class WorkerError < RuntimeError; end
|
7
7
|
|
8
8
|
def self.is_worker?
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
true
|
15
|
-
end
|
9
|
+
ensure_worker!
|
10
|
+
rescue WorkerError
|
11
|
+
false
|
12
|
+
else
|
13
|
+
true
|
16
14
|
end
|
17
15
|
|
18
16
|
def self.ensure_worker!
|
19
17
|
# Make sure that EINHORN_MASTER_PID is my parent
|
20
|
-
if ppid_s = ENV[
|
18
|
+
if (ppid_s = ENV["EINHORN_MASTER_PID"])
|
21
19
|
ppid = ppid_s.to_i
|
22
20
|
raise WorkerError.new("EINHORN_MASTER_PID environment variable is #{ppid_s.inspect}, but my parent's pid is #{Process.ppid.inspect}. This probably means that I am a subprocess of an Einhorn worker, but am not one myself.") unless Process.ppid == ppid
|
23
21
|
true
|
@@ -27,10 +25,8 @@ module Einhorn
|
|
27
25
|
end
|
28
26
|
|
29
27
|
def self.ack(*args)
|
30
|
-
|
31
|
-
|
32
|
-
rescue WorkerError
|
33
|
-
end
|
28
|
+
ack!(*args)
|
29
|
+
rescue WorkerError
|
34
30
|
end
|
35
31
|
|
36
32
|
# Returns the index of this Einhorn child process.
|
@@ -42,7 +38,7 @@ module Einhorn
|
|
42
38
|
# Returns nil if not running in Einhorn, or running on a version
|
43
39
|
# of Einhorn that does not support indexing children.
|
44
40
|
def self.einhorn_child_index
|
45
|
-
index = ENV[
|
41
|
+
index = ENV["EINHORN_CHILD_INDEX"]
|
46
42
|
if index.nil? || index !~ /\A \d+ \z/x
|
47
43
|
index
|
48
44
|
else
|
@@ -65,45 +61,40 @@ module Einhorn
|
|
65
61
|
# TODO: add a :fileno option? Easy to implement; not sure if it'd
|
66
62
|
# be useful for anything. Maybe if it's always fd 3, because then
|
67
63
|
# the user wouldn't have to provide an arg.
|
68
|
-
def self.ack!(discovery
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
case discovery
|
73
|
-
when :env
|
74
|
-
socket = ENV['EINHORN_SOCK_PATH']
|
75
|
-
client = Einhorn::Client.for_path(socket)
|
76
|
-
when :fd
|
77
|
-
raise "No EINHORN_SOCK_FD provided in environment. Did you run einhorn with the -g flag?" unless fd_str = ENV['EINHORN_SOCK_FD']
|
78
|
-
|
79
|
-
fd = Integer(fd_str)
|
80
|
-
client = Einhorn::Client.for_fd(fd)
|
81
|
-
close_after_use = false if arg
|
82
|
-
when :direct
|
83
|
-
socket = arg
|
84
|
-
client = Einhorn::Client.for_path(socket)
|
85
|
-
else
|
86
|
-
raise "Unrecognized socket discovery mechanism: #{discovery.inspect}. Must be one of :filesystem, :argv, or :direct"
|
64
|
+
def self.ack!(discovery = :env, arg = nil)
|
65
|
+
handle_command_socket(discovery, arg) do |client|
|
66
|
+
client.send_command("command" => "worker:ack", "pid" => $$)
|
87
67
|
end
|
68
|
+
end
|
88
69
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
70
|
+
# Call this to indicate your child process is up and in a healthy state.
|
71
|
+
# Arguments:
|
72
|
+
#
|
73
|
+
# @request_id: Identifies the request ID of the worker, can be used to debug wedged workers.
|
74
|
+
#
|
75
|
+
# @discovery: How to discover the master process's command socket.
|
76
|
+
# :env: Discover the path from ENV['EINHORN_SOCK_PATH']
|
77
|
+
# :fd: Just use the file descriptor in ENV['EINHORN_SOCK_FD'].
|
78
|
+
# Must run the master with the -g flag. This is mostly
|
79
|
+
# useful if you don't have a nice library like Einhorn::Worker.
|
80
|
+
# Then @arg being true causes the FD to be left open after ACK;
|
81
|
+
# otherwise it is closed.
|
82
|
+
# :direct: Provide the path to the command socket in @arg.
|
83
|
+
def self.ping!(request_id, discovery = :env, arg = nil)
|
84
|
+
handle_command_socket(discovery, arg) do |client|
|
85
|
+
client.send_command("command" => "worker:ping", "pid" => $$, "request_id" => request_id)
|
86
|
+
end
|
96
87
|
end
|
97
88
|
|
98
|
-
def self.socket(number=nil)
|
89
|
+
def self.socket(number = nil)
|
99
90
|
number ||= 0
|
100
91
|
einhorn_fd(number)
|
101
92
|
end
|
102
93
|
|
103
|
-
def self.socket!(number=nil)
|
94
|
+
def self.socket!(number = nil)
|
104
95
|
number ||= 0
|
105
96
|
|
106
|
-
unless count = einhorn_fd_count
|
97
|
+
unless (count = einhorn_fd_count)
|
107
98
|
raise "No EINHORN_FD_COUNT provided in environment. Are you running under Einhorn?"
|
108
99
|
end
|
109
100
|
|
@@ -111,7 +102,7 @@ module Einhorn
|
|
111
102
|
raise "Only #{count} FDs available, but FD #{number} was requested"
|
112
103
|
end
|
113
104
|
|
114
|
-
unless fd = einhorn_fd(number)
|
105
|
+
unless (fd = einhorn_fd(number))
|
115
106
|
raise "No EINHORN_FD_#{number} provided in environment. That's pretty weird"
|
116
107
|
end
|
117
108
|
|
@@ -119,14 +110,14 @@ module Einhorn
|
|
119
110
|
end
|
120
111
|
|
121
112
|
def self.einhorn_fd(n)
|
122
|
-
unless raw_fd = ENV["EINHORN_FD_#{n}"]
|
113
|
+
unless (raw_fd = ENV["EINHORN_FD_#{n}"])
|
123
114
|
return nil
|
124
115
|
end
|
125
116
|
Integer(raw_fd)
|
126
117
|
end
|
127
118
|
|
128
119
|
def self.einhorn_fd_count
|
129
|
-
unless raw_count = ENV[
|
120
|
+
unless (raw_count = ENV["EINHORN_FD_COUNT"])
|
130
121
|
return 0
|
131
122
|
end
|
132
123
|
Integer(raw_count)
|
@@ -134,15 +125,42 @@ module Einhorn
|
|
134
125
|
|
135
126
|
# Call this to handle graceful shutdown requests to your app.
|
136
127
|
def self.graceful_shutdown(&blk)
|
137
|
-
Signal.trap(
|
128
|
+
Signal.trap("USR2", &blk)
|
138
129
|
end
|
139
130
|
|
140
|
-
|
131
|
+
def self.handle_command_socket(discovery, contextual_arg)
|
132
|
+
ensure_worker!
|
133
|
+
close_after_use = true
|
134
|
+
|
135
|
+
case discovery
|
136
|
+
when :env
|
137
|
+
socket = ENV["EINHORN_SOCK_PATH"]
|
138
|
+
client = Einhorn::Client.for_path(socket)
|
139
|
+
when :fd
|
140
|
+
raise "No EINHORN_SOCK_FD provided in environment. Did you run einhorn with the -g flag?" unless (fd_str = ENV["EINHORN_SOCK_FD"])
|
141
|
+
|
142
|
+
fd = Integer(fd_str)
|
143
|
+
client = Einhorn::Client.for_fd(fd)
|
144
|
+
close_after_use = false if contextual_arg
|
145
|
+
when :direct
|
146
|
+
socket = contextual_arg
|
147
|
+
client = Einhorn::Client.for_path(socket)
|
148
|
+
else
|
149
|
+
raise "Unrecognized socket discovery mechanism: #{discovery.inspect}. Must be one of :filesystem, :argv, or :direct"
|
150
|
+
end
|
151
|
+
|
152
|
+
yield client
|
153
|
+
client.close if close_after_use
|
154
|
+
|
155
|
+
true
|
156
|
+
end
|
157
|
+
private_class_method :handle_command_socket
|
141
158
|
|
142
159
|
def self.socket_from_filesystem(cmd_name)
|
143
160
|
ppid = Process.ppid
|
144
161
|
socket_path_file = Einhorn::Command::Interface.socket_path_file(ppid)
|
145
162
|
File.read(socket_path_file)
|
146
163
|
end
|
164
|
+
private_class_method :socket_from_filesystem
|
147
165
|
end
|
148
166
|
end
|
data/lib/einhorn/worker_pool.rb
CHANGED
@@ -7,13 +7,13 @@ module Einhorn
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.workers
|
10
|
-
workers_with_state.map {|pid, _| pid}
|
10
|
+
workers_with_state.map { |pid, _| pid }
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.unsignaled_workers
|
14
14
|
workers_with_state.select do |pid, spec|
|
15
15
|
spec[:signaled].length == 0
|
16
|
-
end.map {|pid, _| pid}
|
16
|
+
end.map { |pid, _| pid }
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.modern_workers_with_state
|
@@ -23,15 +23,15 @@ module Einhorn
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.acked_modern_workers_with_state
|
26
|
-
modern_workers_with_state.select {|pid, spec| spec[:acked]}
|
26
|
+
modern_workers_with_state.select { |pid, spec| spec[:acked] }
|
27
27
|
end
|
28
28
|
|
29
29
|
def self.modern_workers
|
30
|
-
modern_workers_with_state.map {|pid, _| pid}
|
30
|
+
modern_workers_with_state.map { |pid, _| pid }
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.acked_modern_workers
|
34
|
-
acked_modern_workers_with_state.map {|pid, _| pid}
|
34
|
+
acked_modern_workers_with_state.map { |pid, _| pid }
|
35
35
|
end
|
36
36
|
|
37
37
|
def self.unsignaled_modern_workers_with_state
|
@@ -43,23 +43,23 @@ module Einhorn
|
|
43
43
|
def self.acked_unsignaled_modern_workers
|
44
44
|
acked_modern_workers_with_state.select do |_, spec|
|
45
45
|
spec[:signaled].length == 0
|
46
|
-
end.map {|pid, _| pid}
|
46
|
+
end.map { |pid, _| pid }
|
47
47
|
end
|
48
48
|
|
49
49
|
def self.unsignaled_modern_workers_with_priority
|
50
50
|
unsignaled_modern_workers_with_state.sort_by do |pid, spec|
|
51
51
|
spec[:acked] ? 1 : 0
|
52
|
-
end.map {|pid, _| pid}
|
52
|
+
end.map { |pid, _| pid }
|
53
53
|
end
|
54
54
|
|
55
55
|
def self.unacked_unsignaled_modern_workers_with_state
|
56
|
-
modern_workers_with_state.select {|pid, spec|
|
56
|
+
modern_workers_with_state.select { |pid, spec|
|
57
57
|
!spec[:acked] && spec[:signaled].length == 0
|
58
58
|
}
|
59
59
|
end
|
60
60
|
|
61
61
|
def self.unacked_unsignaled_modern_workers
|
62
|
-
unacked_unsignaled_modern_workers_with_state.map {|pid, _| pid}
|
62
|
+
unacked_unsignaled_modern_workers_with_state.map { |pid, _| pid }
|
63
63
|
end
|
64
64
|
|
65
65
|
# Use the number of modern workers, rather than unsignaled modern
|