einhorn 0.8.2 → 1.0.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.
- checksums.yaml +4 -4
- data/Changes.md +15 -0
- data/README.md +7 -38
- 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 +75 -85
- data/lib/einhorn/compat.rb +7 -7
- data/lib/einhorn/event/abstract_text_descriptor.rb +32 -36
- 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 +20 -20
- 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 +37 -110
- 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
- /data/{LICENSE → LICENSE.txt} +0 -0
@@ -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
|