einhorn 0.8.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +10 -0
- data/{LICENSE → LICENSE.txt} +0 -0
- data/README.md +5 -36
- data/einhorn.gemspec +23 -21
- data/example/pool_worker.rb +2 -2
- data/example/thin_example +8 -8
- data/example/time_server +5 -5
- data/lib/einhorn/client.rb +8 -8
- data/lib/einhorn/command/interface.rb +92 -98
- data/lib/einhorn/command.rb +76 -85
- 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 +19 -19
- data/lib/einhorn/prctl.rb +2 -2
- data/lib/einhorn/prctl_linux.rb +13 -14
- data/lib/einhorn/safe_yaml.rb +17 -0
- data/lib/einhorn/version.rb +1 -1
- data/lib/einhorn/worker.rb +26 -30
- data/lib/einhorn/worker_pool.rb +9 -9
- data/lib/einhorn.rb +120 -125
- metadata +33 -117
- 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 -94
- 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 -23
- data/test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb +0 -6
- data/test/integration/_lib/fixtures/pdeathsig_printer/pdeathsig_printer.rb +0 -29
- data/test/integration/_lib/fixtures/signal_timeout/sleepy_server.rb +0 -23
- data/test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb +0 -24
- data/test/integration/_lib/helpers/einhorn_helpers.rb +0 -148
- data/test/integration/_lib/helpers.rb +0 -4
- data/test/integration/_lib.rb +0 -6
- data/test/integration/pdeathsig.rb +0 -26
- data/test/integration/startup.rb +0 -31
- data/test/integration/upgrading.rb +0 -204
- data/test/unit/_lib/bad_worker.rb +0 -7
- data/test/unit/_lib/sleep_worker.rb +0 -5
- data/test/unit/einhorn/client.rb +0 -88
- data/test/unit/einhorn/command/interface.rb +0 -49
- data/test/unit/einhorn/command.rb +0 -135
- data/test/unit/einhorn/event.rb +0 -89
- data/test/unit/einhorn/worker_pool.rb +0 -39
- data/test/unit/einhorn.rb +0 -96
@@ -1,204 +0,0 @@
|
|
1
|
-
require(File.expand_path('_lib', File.dirname(__FILE__)))
|
2
|
-
require 'socket'
|
3
|
-
require 'einhorn/client'
|
4
|
-
|
5
|
-
class UpgradeTests < EinhornIntegrationTestCase
|
6
|
-
include Helpers::EinhornHelpers
|
7
|
-
|
8
|
-
describe 'when upgrading a running einhorn without preloading' do
|
9
|
-
before do
|
10
|
-
@dir = prepare_fixture_directory('upgrade_project')
|
11
|
-
@port = find_free_port
|
12
|
-
@server_program = File.join(@dir, "upgrading_server.rb")
|
13
|
-
@socket_path = File.join(@dir, "einhorn.sock")
|
14
|
-
end
|
15
|
-
after { cleanup_fixtured_directories }
|
16
|
-
|
17
|
-
it 'can restart' do
|
18
|
-
File.open(File.join(@dir, "version"), 'w') { |f| f.write("0") }
|
19
|
-
with_running_einhorn(%W{einhorn -m manual -b 127.0.0.1:#{@port} -d #{@socket_path} -- ruby #{@server_program}}) do |process|
|
20
|
-
wait_for_open_port
|
21
|
-
assert_equal("0", read_from_port, "Should report the initial version")
|
22
|
-
|
23
|
-
File.open(File.join(@dir, "version"), 'w') { |f| f.write("1") }
|
24
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
25
|
-
assert_equal("1", read_from_port, "Should report the upgraded version")
|
26
|
-
|
27
|
-
process.terminate
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe 'handling environments on upgrade' do
|
33
|
-
before do
|
34
|
-
@dir = prepare_fixture_directory('env_printer')
|
35
|
-
@port = find_free_port
|
36
|
-
@server_program = File.join(@dir, "env_printer.rb")
|
37
|
-
@socket_path = File.join(@dir, "einhorn.sock")
|
38
|
-
end
|
39
|
-
after { cleanup_fixtured_directories }
|
40
|
-
|
41
|
-
describe 'when running with --reexec-as' do
|
42
|
-
it 'preserves environment variables across restarts' do
|
43
|
-
# exec the new einhorn with the same environment:
|
44
|
-
reexec_cmdline = 'env VAR=a bundle exec --keep-file-descriptors einhorn'
|
45
|
-
|
46
|
-
with_running_einhorn(%W{einhorn -m manual -b 127.0.0.1:#{@port} --reexec-as=#{reexec_cmdline} -d #{@socket_path} -- ruby #{@server_program} VAR},
|
47
|
-
:env => ENV.to_hash.merge({'VAR' => 'a'})) do |process|
|
48
|
-
|
49
|
-
wait_for_open_port
|
50
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
51
|
-
assert_equal("a", read_from_port, "Should report the upgraded version")
|
52
|
-
|
53
|
-
process.terminate
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'cleans up if a child dies during the reexec' do
|
58
|
-
# attempt to setup a scenario where a child exits in the
|
59
|
-
# interlude after old einhorn has execed the reexec-as
|
60
|
-
# command, but before the reexec-as command execs new einhorn
|
61
|
-
|
62
|
-
@dir = prepare_fixture_directory('exit_during_upgrade')
|
63
|
-
@server_program = File.join(@dir, "exiting_server.rb")
|
64
|
-
@socket_path = File.join(@dir, "einhorn.sock")
|
65
|
-
|
66
|
-
reexec_cmdline = File.join(@dir, 'upgrade_reexec.rb')
|
67
|
-
|
68
|
-
with_running_einhorn(%W{einhorn -m manual -b 127.0.0.1:#{@port} --reexec-as=#{reexec_cmdline} -d #{@socket_path} -- ruby #{@server_program}}) do |process|
|
69
|
-
wait_for_open_port
|
70
|
-
|
71
|
-
Process.kill('USR2', read_from_port.to_i)
|
72
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
73
|
-
|
74
|
-
client = Einhorn::Client.for_path(@socket_path)
|
75
|
-
client.send_command('command' => 'state')
|
76
|
-
resp = client.receive_message
|
77
|
-
|
78
|
-
state = YAML.load(resp['message'])
|
79
|
-
assert_equal(1, state[:state][:children].count)
|
80
|
-
|
81
|
-
child = state[:state][:children].first.last
|
82
|
-
assert_in_delta(child[:pinged_at], Time.now, 60)
|
83
|
-
assert_equal("id-1", child[:pinged_request_id])
|
84
|
-
|
85
|
-
process.terminate
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe 'without preloading' do
|
90
|
-
it 'can update environment variables when the reexec command line says to' do
|
91
|
-
# exec the new einhorn with the same environment:
|
92
|
-
reexec_cmdline = 'env VAR=b OINK=b bundle exec --keep-file-descriptors einhorn'
|
93
|
-
|
94
|
-
with_running_einhorn(%W{einhorn -m manual -b 127.0.0.1:#{@port} --reexec-as=#{reexec_cmdline} -d #{@socket_path} -- ruby #{@server_program} VAR},
|
95
|
-
:env => ENV.to_hash.merge({'VAR' => 'a'})) do |process|
|
96
|
-
|
97
|
-
wait_for_open_port
|
98
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
99
|
-
assert_equal("b", read_from_port, "Should report the upgraded version")
|
100
|
-
|
101
|
-
process.terminate
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
describe 'with preloading' do
|
107
|
-
it 'can update environment variables on preloaded code when the reexec command line says to' do
|
108
|
-
# exec the new einhorn with the same environment:
|
109
|
-
reexec_cmdline = 'env VAR=b OINK=b bundle exec --keep-file-descriptors einhorn'
|
110
|
-
|
111
|
-
with_running_einhorn(%W{einhorn -m manual -p #{@server_program} -b 127.0.0.1:#{@port} --reexec-as=#{reexec_cmdline} -d #{@socket_path} -- ruby #{@server_program} VAR},
|
112
|
-
:env => ENV.to_hash.merge({'VAR' => 'a'})) do |process|
|
113
|
-
|
114
|
-
wait_for_open_port
|
115
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
116
|
-
assert_equal("b", read_from_port, "Should report the upgraded version")
|
117
|
-
|
118
|
-
process.terminate
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
describe 'when invoked with --drop-env-var' do
|
126
|
-
before do
|
127
|
-
@dir = prepare_fixture_directory('env_printer')
|
128
|
-
@port = find_free_port
|
129
|
-
@server_program = File.join(@dir, "env_printer.rb")
|
130
|
-
@socket_path = File.join(@dir, "einhorn.sock")
|
131
|
-
end
|
132
|
-
after { cleanup_fixtured_directories }
|
133
|
-
|
134
|
-
it %{removes the variable from its children's environment} do
|
135
|
-
with_running_einhorn(%W{einhorn -m manual -b 127.0.0.1:#{@port} --drop-env-var=VAR -d #{@socket_path} -- ruby #{@server_program} VAR},
|
136
|
-
:env => ENV.to_hash.merge({'VAR' => 'a'})) do |process|
|
137
|
-
wait_for_open_port
|
138
|
-
assert_equal("a", read_from_port, "Should report $VAR initially")
|
139
|
-
|
140
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
141
|
-
assert_equal("", read_from_port, "Should have dropped the variable post-upgrade")
|
142
|
-
|
143
|
-
process.terminate
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
it %{causes an upgrade with --reexec-as to not clobber the new environment} do
|
148
|
-
reexec_cmdline = 'env VAR2=b bundle exec --keep-file-descriptors einhorn'
|
149
|
-
with_running_einhorn(%W{einhorn -m manual -b 127.0.0.1:#{@port} --drop-env-var=VAR1 --drop-env-var=VAR2 -d #{@socket_path} --reexec-as=#{reexec_cmdline} -- ruby #{@server_program} VAR1 VAR2},
|
150
|
-
:env => ENV.to_hash.merge({'VAR1' => 'a', 'VAR2' => 'a'})) do |process|
|
151
|
-
wait_for_open_port
|
152
|
-
assert_equal("aa", read_from_port, "Should report both $VAR1 and $VAR2 initially")
|
153
|
-
|
154
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
155
|
-
assert_equal("b", read_from_port, "Should have dropped $VAR1 post-upgrade and re-set $VAR2")
|
156
|
-
|
157
|
-
process.terminate
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
describe "with --signal-timeout" do
|
163
|
-
before do
|
164
|
-
@dir = prepare_fixture_directory('signal_timeout')
|
165
|
-
@port = find_free_port
|
166
|
-
@server_program = File.join(@dir, "sleepy_server.rb")
|
167
|
-
@socket_path = File.join(@dir, "einhorn.sock")
|
168
|
-
end
|
169
|
-
|
170
|
-
after { cleanup_fixtured_directories }
|
171
|
-
|
172
|
-
it 'issues a SIGKILL to outdated children when signal-timeout has passed' do
|
173
|
-
signal_timeout = 2
|
174
|
-
sleep_for = 10
|
175
|
-
cmd = %W{
|
176
|
-
einhorn
|
177
|
-
-b 127.0.0.1:#{@port}
|
178
|
-
-d #{@socket_path}
|
179
|
-
--signal-timeout #{signal_timeout}
|
180
|
-
-- ruby #{@server_program}
|
181
|
-
}
|
182
|
-
|
183
|
-
with_running_einhorn(cmd, env: ENV.to_h.merge({'TRAP_SLEEP' => sleep_for.to_s})) do |process|
|
184
|
-
wait_for_open_port
|
185
|
-
client = Einhorn::Client.for_path(@socket_path)
|
186
|
-
einhornsh(%W{-d #{@socket_path} -e upgrade})
|
187
|
-
|
188
|
-
state = get_state(client)
|
189
|
-
assert_equal(2, state[:children].count)
|
190
|
-
signaled_children = state[:children].select{|_,c| c[:signaled].length > 0}
|
191
|
-
assert_equal(1, signaled_children.length)
|
192
|
-
|
193
|
-
sleep(signal_timeout * 2)
|
194
|
-
|
195
|
-
state = get_state(client)
|
196
|
-
assert_equal(1, state[:children].count)
|
197
|
-
signaled_children = state[:children].select{|_,c| c[:signaled].length > 0}
|
198
|
-
assert_equal(0, signaled_children.length)
|
199
|
-
|
200
|
-
process.terminate
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
data/test/unit/einhorn/client.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '../../_lib'))
|
2
|
-
|
3
|
-
require 'einhorn'
|
4
|
-
|
5
|
-
class ClientTest < EinhornTestCase
|
6
|
-
def unserialized_message
|
7
|
-
{:foo => ['%bar', '%baz']}
|
8
|
-
end
|
9
|
-
|
10
|
-
def serialized_1_8
|
11
|
-
"--- %0A:foo: %0A- \"%25bar\"%0A- \"%25baz\"%0A\n"
|
12
|
-
end
|
13
|
-
|
14
|
-
def serialized_1_9
|
15
|
-
"---%0A:foo:%0A- ! '%25bar'%0A- ! '%25baz'%0A\n"
|
16
|
-
end
|
17
|
-
|
18
|
-
def serialized_2_0
|
19
|
-
"---%0A:foo:%0A- '%25bar'%0A- '%25baz'%0A\n"
|
20
|
-
end
|
21
|
-
|
22
|
-
def serialized_2_1
|
23
|
-
"---%0A:foo:%0A- \"%25bar\"%0A- \"%25baz\"%0A\n"
|
24
|
-
end
|
25
|
-
|
26
|
-
def serialized_options
|
27
|
-
[serialized_1_8, serialized_1_9, serialized_2_0, serialized_2_1]
|
28
|
-
end
|
29
|
-
|
30
|
-
describe "when sending a message" do
|
31
|
-
it "writes a serialized line" do
|
32
|
-
socket = mock
|
33
|
-
socket.expects(:write).with {|write| serialized_options.include?(write)}
|
34
|
-
Einhorn::Client::Transport.send_message(socket, unserialized_message)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "when receiving a message" do
|
39
|
-
it "deserializes a single 1.8-style line" do
|
40
|
-
socket = mock
|
41
|
-
socket.expects(:readline).returns(serialized_1_8)
|
42
|
-
result = Einhorn::Client::Transport.receive_message(socket)
|
43
|
-
assert_equal(result, unserialized_message)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "deserializes a single 1.9-style line" do
|
47
|
-
socket = mock
|
48
|
-
socket.expects(:readline).returns(serialized_1_9)
|
49
|
-
result = Einhorn::Client::Transport.receive_message(socket)
|
50
|
-
assert_equal(result, unserialized_message)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
describe "when {de,}serializing a message" do
|
55
|
-
it "serializes and escape a message as expected" do
|
56
|
-
actual = Einhorn::Client::Transport.serialize_message(unserialized_message)
|
57
|
-
assert(serialized_options.include?(actual), "Actual message is #{actual.inspect}")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "deserializes and unescape a 1.8-style message as expected" do
|
61
|
-
actual = Einhorn::Client::Transport.deserialize_message(serialized_1_8)
|
62
|
-
assert_equal(unserialized_message, actual)
|
63
|
-
end
|
64
|
-
|
65
|
-
it "deserializes and unescape a 1.9-style message as expected" do
|
66
|
-
actual = Einhorn::Client::Transport.deserialize_message(serialized_1_9)
|
67
|
-
assert_equal(unserialized_message, actual)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "deserializes and unescapes a 2.0-style message as expected" do
|
71
|
-
actual = Einhorn::Client::Transport.deserialize_message(serialized_2_0)
|
72
|
-
assert_equal(unserialized_message, actual)
|
73
|
-
end
|
74
|
-
|
75
|
-
it "deserializes and unescapes a 2.1-style message as expected" do
|
76
|
-
actual = Einhorn::Client::Transport.deserialize_message(serialized_2_1)
|
77
|
-
assert_equal(unserialized_message, actual)
|
78
|
-
end
|
79
|
-
|
80
|
-
it "raises an error when deserializing invalid YAML" do
|
81
|
-
invalid_serialized = "-%0A\t-"
|
82
|
-
begin
|
83
|
-
Einhorn::Client::Transport.deserialize_message(invalid_serialized)
|
84
|
-
rescue Einhorn::Client::Transport::ParseError
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '../../../_lib'))
|
2
|
-
|
3
|
-
require 'einhorn'
|
4
|
-
|
5
|
-
class InterfaceTest < EinhornTestCase
|
6
|
-
include Einhorn::Command
|
7
|
-
|
8
|
-
describe "when a command is received" do
|
9
|
-
it "calls that command" do
|
10
|
-
conn = stub(:log_debug => nil)
|
11
|
-
conn.expects(:write).once.with do |message|
|
12
|
-
# Remove trailing newline
|
13
|
-
message = message[0...-1]
|
14
|
-
parsed = YAML.load(URI.unescape(message))
|
15
|
-
parsed['message'] =~ /Welcome, gdb/
|
16
|
-
end
|
17
|
-
request = {
|
18
|
-
'command' => 'ehlo',
|
19
|
-
'user' => 'gdb'
|
20
|
-
}
|
21
|
-
Interface.process_command(conn, YAML.dump(request))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "when an unrecognized command is received" do
|
26
|
-
it "calls the unrecognized_command method" do
|
27
|
-
conn = stub(:log_debug => nil)
|
28
|
-
Interface.expects(:unrecognized_command).once
|
29
|
-
request = {
|
30
|
-
'command' => 'made-up',
|
31
|
-
}
|
32
|
-
Interface.process_command(conn, YAML.dump(request))
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "when a worker ack is received" do
|
37
|
-
it "registers ack and close the connection" do
|
38
|
-
conn = stub(:log_debug => nil)
|
39
|
-
conn.expects(:close).once
|
40
|
-
conn.expects(:write).never
|
41
|
-
request = {
|
42
|
-
'command' => 'worker:ack',
|
43
|
-
'pid' => 1234
|
44
|
-
}
|
45
|
-
Einhorn::Command.expects(:register_manual_ack).once.with(1234)
|
46
|
-
Interface.process_command(conn, YAML.dump(request))
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '../../_lib'))
|
2
|
-
|
3
|
-
require 'einhorn'
|
4
|
-
|
5
|
-
class CommandTest < EinhornTestCase
|
6
|
-
include Einhorn
|
7
|
-
|
8
|
-
describe "when running quieter" do
|
9
|
-
it "increases the verbosity threshold" do
|
10
|
-
Einhorn::State.stubs(:verbosity => 1)
|
11
|
-
Einhorn::State.expects(:verbosity=).once.with(2).returns(2)
|
12
|
-
Command.quieter
|
13
|
-
end
|
14
|
-
|
15
|
-
it "maxes out at 2" do
|
16
|
-
Einhorn::State.stubs(:verbosity => 2)
|
17
|
-
Einhorn::State.expects(:verbosity=).never
|
18
|
-
Command.quieter
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "resignal_timeout" do
|
23
|
-
it "does not kill any children" do
|
24
|
-
Einhorn::State.stubs(signal_timeout: 5 * 60)
|
25
|
-
Einhorn::State.stubs(children: {
|
26
|
-
12345 => {last_signaled_at: nil},
|
27
|
-
12346 => {signaled: Set.new(["USR1"]), last_signaled_at: Time.now - (2 * 60)},
|
28
|
-
})
|
29
|
-
|
30
|
-
Process.expects(:kill).never
|
31
|
-
Einhorn::Command.kill_expired_signaled_workers
|
32
|
-
|
33
|
-
refute(Einhorn::State.children[12346][:signaled].include?("KILL"), "Process was KILLed when it shouldn't have been")
|
34
|
-
end
|
35
|
-
|
36
|
-
it "KILLs stuck child processes" do
|
37
|
-
Time.stub :now, Time.at(0) do
|
38
|
-
Process.stub(:kill, true) do
|
39
|
-
Einhorn::State.stubs(signal_timeout: 60)
|
40
|
-
Einhorn::State.stubs(children: {
|
41
|
-
12346 => {signaled: Set.new(["USR2"]), last_signaled_at: Time.now - (2 * 60)},
|
42
|
-
})
|
43
|
-
|
44
|
-
Einhorn::Command.kill_expired_signaled_workers
|
45
|
-
|
46
|
-
child = Einhorn::State.children[12346]
|
47
|
-
assert(child[:signaled].include?("KILL"), "Process was not KILLed as expected")
|
48
|
-
assert(child[:last_signaled_at] == Time.now, "The last_processed_at was not updated as expected")
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
describe "trigger_spinup?" do
|
55
|
-
it "is true by default" do
|
56
|
-
assert(Einhorn::Command.trigger_spinup?(1))
|
57
|
-
end
|
58
|
-
|
59
|
-
it "is false if unacked >= max_unacked" do
|
60
|
-
Einhorn::State.stubs(children: {12346 => {type: :worker, acked: false, signaled: Set.new}})
|
61
|
-
assert(Einhorn::Command.trigger_spinup?(1))
|
62
|
-
end
|
63
|
-
|
64
|
-
it "is false if capacity is exceeded" do
|
65
|
-
Einhorn::State.stubs(config: {max_upgrade_additional: 1, number: 1})
|
66
|
-
Einhorn::State.stubs(
|
67
|
-
children: {
|
68
|
-
1 => {type: :worker, acked: true, signaled: Set.new},
|
69
|
-
2 => {type: :worker, acked: true, signaled: Set.new},
|
70
|
-
3 => {type: :worker, acked: true, signaled: Set.new},
|
71
|
-
}
|
72
|
-
)
|
73
|
-
refute(Einhorn::Command.trigger_spinup?(1))
|
74
|
-
end
|
75
|
-
|
76
|
-
it "is true if under capacity" do
|
77
|
-
Einhorn::State.stubs(config: {max_upgrade_additional: 2, number: 1})
|
78
|
-
Einhorn::State.stubs(children: {1 => {type: :worker, acked: true, signaled: Set.new}})
|
79
|
-
assert(Einhorn::Command.trigger_spinup?(1))
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
describe "replenish_gradually" do
|
84
|
-
it "does nothing if an outstanding spinup timer exists" do
|
85
|
-
Einhorn::TransientState.stubs(has_outstanding_spinup_timer: true)
|
86
|
-
Einhorn::Command.expects(:spinup).never
|
87
|
-
Einhorn::Command.replenish_gradually
|
88
|
-
end
|
89
|
-
it "does nothing if the worker pool is full" do
|
90
|
-
Einhorn::TransientState.stubs(has_outstanding_spinup_timer: false)
|
91
|
-
Einhorn::WorkerPool.stubs(missing_worker_count: 0)
|
92
|
-
Einhorn::Command.expects(:spinup).never
|
93
|
-
Einhorn::Command.replenish_gradually
|
94
|
-
end
|
95
|
-
|
96
|
-
it "does nothing if we have not reached the spinup interval" do
|
97
|
-
Einhorn::TransientState.stubs(has_outstanding_spinup_timer: false)
|
98
|
-
Einhorn::WorkerPool.stubs(missing_worker_count: 1)
|
99
|
-
Einhorn::State.stubs(last_spinup: Time.now)
|
100
|
-
Einhorn::Command.expects(:spinup).never
|
101
|
-
Einhorn::Command.replenish_gradually
|
102
|
-
end
|
103
|
-
|
104
|
-
it "calls trigger_spinup? if we have reached the spinup interval" do
|
105
|
-
Einhorn::TransientState.stubs(has_outstanding_spinup_timer: false)
|
106
|
-
Einhorn::State.stubs(config: {seconds: 1, max_unacked: 2, number: 1})
|
107
|
-
Einhorn::WorkerPool.stubs(missing_worker_count: 1)
|
108
|
-
Einhorn::State.stubs(last_spinup: Time.now - 2) # 2 seconds ago
|
109
|
-
Einhorn::Command.expects(:trigger_spinup?).with(2).returns(false)
|
110
|
-
Einhorn::Command.replenish_gradually
|
111
|
-
end
|
112
|
-
|
113
|
-
it "can handle sub-second spinup intervals" do
|
114
|
-
Einhorn::TransientState.stubs(has_outstanding_spinup_timer: false)
|
115
|
-
Einhorn::State.stubs(config: {seconds: 0.1, max_unacked: 2, number: 1})
|
116
|
-
Einhorn::WorkerPool.stubs(missing_worker_count: 1)
|
117
|
-
Einhorn::State.stubs(last_spinup: Time.now - 0.5) # Half a second ago
|
118
|
-
Einhorn::Command.stubs(trigger_spinup?: true)
|
119
|
-
Einhorn::Command.expects(:spinup)
|
120
|
-
Einhorn::Command.replenish_gradually
|
121
|
-
end
|
122
|
-
|
123
|
-
it "registers a timer to run again at spinup interval" do
|
124
|
-
Einhorn::TransientState.stubs(has_outstanding_spinup_timer: false)
|
125
|
-
Einhorn::State.stubs(config: {seconds: 0.1, max_unacked: 2, number: 1})
|
126
|
-
Einhorn::WorkerPool.stubs(missing_worker_count: 1)
|
127
|
-
Einhorn::State.stubs(last_spinup: Time.now - 1)
|
128
|
-
Einhorn::Command.stubs(trigger_spinup?: true)
|
129
|
-
Einhorn::Command.stubs(:spinup)
|
130
|
-
Einhorn::TransientState.expects(:has_outstanding_spinup_timer=).with(true)
|
131
|
-
Einhorn::Event::Timer.expects(:open).with(0.1)
|
132
|
-
Einhorn::Command.replenish_gradually
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
data/test/unit/einhorn/event.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '../../_lib'))
|
2
|
-
|
3
|
-
require 'set'
|
4
|
-
require 'einhorn'
|
5
|
-
|
6
|
-
module Einhorn::Event
|
7
|
-
def self.reset
|
8
|
-
@@loopbreak_reader = nil
|
9
|
-
@@loopbreak_writer = nil
|
10
|
-
@@readable = {}
|
11
|
-
@@writeable = {}
|
12
|
-
@@timers = {}
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class EventTest < EinhornTestCase
|
17
|
-
describe "when running the event loop" do
|
18
|
-
before do
|
19
|
-
Einhorn::Event.reset
|
20
|
-
end
|
21
|
-
|
22
|
-
after do
|
23
|
-
Einhorn::Event.reset
|
24
|
-
end
|
25
|
-
|
26
|
-
it "selects on readable descriptors" do
|
27
|
-
sock1 = stub(:fileno => 4)
|
28
|
-
sock2 = stub(:fileno => 5)
|
29
|
-
|
30
|
-
conn1 = Einhorn::Event::Connection.open(sock1)
|
31
|
-
conn2 = Einhorn::Event::Connection.open(sock2)
|
32
|
-
|
33
|
-
IO.expects(:select).once.with do |readers, writers, errs, timeout|
|
34
|
-
Set.new(readers) == Set.new([sock1, sock2]) &&
|
35
|
-
writers == [] &&
|
36
|
-
errs == nil &&
|
37
|
-
timeout == nil
|
38
|
-
end.returns([[], [], []])
|
39
|
-
|
40
|
-
Einhorn::Event.loop_once
|
41
|
-
end
|
42
|
-
|
43
|
-
it "selects on writeable descriptors" do
|
44
|
-
sock1 = stub(:fileno => 4)
|
45
|
-
sock2 = stub(:fileno => 5)
|
46
|
-
|
47
|
-
conn1 = Einhorn::Event::Connection.open(sock1)
|
48
|
-
conn2 = Einhorn::Event::Connection.open(sock2)
|
49
|
-
|
50
|
-
sock2.expects(:write_nonblock).once.raises(Errno::EWOULDBLOCK.new)
|
51
|
-
conn2.write('Hello!')
|
52
|
-
|
53
|
-
IO.expects(:select).once.with do |readers, writers, errs, timeout|
|
54
|
-
Set.new(readers) == Set.new([sock1, sock2]) &&
|
55
|
-
writers == [sock2] &&
|
56
|
-
errs == nil &&
|
57
|
-
timeout == nil
|
58
|
-
end.returns([[], [], []])
|
59
|
-
|
60
|
-
Einhorn::Event.loop_once
|
61
|
-
end
|
62
|
-
|
63
|
-
it "runs callbacks for ready selectables" do
|
64
|
-
sock1 = stub(:fileno => 4)
|
65
|
-
sock2 = stub(:fileno => 5)
|
66
|
-
|
67
|
-
conn1 = Einhorn::Event::Connection.open(sock1)
|
68
|
-
conn2 = Einhorn::Event::Connection.open(sock2)
|
69
|
-
|
70
|
-
sock2.expects(:write_nonblock).once.raises(Errno::EWOULDBLOCK.new)
|
71
|
-
conn2.write('Hello!')
|
72
|
-
|
73
|
-
IO.expects(:select).once.with do |readers, writers, errs, timeout|
|
74
|
-
Set.new(readers) == Set.new([sock1, sock2]) &&
|
75
|
-
writers == [sock2] &&
|
76
|
-
errs == nil &&
|
77
|
-
timeout == nil
|
78
|
-
end.returns([[sock1], [sock2], []])
|
79
|
-
|
80
|
-
conn1.expects(:notify_readable).once
|
81
|
-
conn2.expects(:notify_writeable).never
|
82
|
-
|
83
|
-
conn1.expects(:notify_readable).never
|
84
|
-
conn2.expects(:notify_writeable).once
|
85
|
-
|
86
|
-
Einhorn::Event.loop_once
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '../../_lib'))
|
2
|
-
|
3
|
-
require 'einhorn'
|
4
|
-
|
5
|
-
class WorkerPoolTest < EinhornTestCase
|
6
|
-
def stub_children
|
7
|
-
Einhorn::State.stubs(:children).returns(
|
8
|
-
1234 => {:type => :worker, :signaled => Set.new(['INT'])},
|
9
|
-
1235 => {:type => :state_passer},
|
10
|
-
1236 => {:type => :worker, :signaled => Set.new}
|
11
|
-
)
|
12
|
-
end
|
13
|
-
|
14
|
-
describe "#workers_with_state" do
|
15
|
-
before do
|
16
|
-
stub_children
|
17
|
-
end
|
18
|
-
|
19
|
-
it "selects only the workers" do
|
20
|
-
workers_with_state = Einhorn::WorkerPool.workers_with_state
|
21
|
-
# Sort only needed for Ruby 1.8
|
22
|
-
assert_equal([
|
23
|
-
[1234, {:type => :worker, :signaled => Set.new(['INT'])}],
|
24
|
-
[1236, {:type => :worker, :signaled => Set.new}]
|
25
|
-
], workers_with_state.sort)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "#unsignaled_workers" do
|
30
|
-
before do
|
31
|
-
stub_children
|
32
|
-
end
|
33
|
-
|
34
|
-
it "selects unsignaled workers" do
|
35
|
-
unsignaled_workers = Einhorn::WorkerPool.unsignaled_workers
|
36
|
-
assert_equal([1236], unsignaled_workers)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|