serverengine 2.3.0 → 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +5 -0
- data/lib/serverengine/multi_process_server.rb +5 -1
- data/lib/serverengine/version.rb +1 -1
- data/serverengine.gemspec +1 -0
- data/spec/daemon_spec.rb +11 -0
- data/spec/multi_process_server_spec.rb +152 -6
- data/spec/multi_spawn_server_spec.rb +17 -1
- data/spec/supervisor_spec.rb +19 -3
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b802588a6e93b69c2dc9810ab33b9e3195e14d93c5f977961a04cc6d88cacf1
|
4
|
+
data.tar.gz: 9921383ec5ae39cfe982da9ce8c0b432bbc553c2ea5d2090a2d849ec2500c230
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea4c0e4b34490665e4287c0f43e4ea1f32c10fe022464a69326505ec21aad2650e53b842c2aaeec2a969d914f73715ee059b2bdbb3d72d14b0bce51192bf217c
|
7
|
+
data.tar.gz: 3e30810b08b8667f2295b41fca60c4683126d8936e32169d0789b8ec02ba3965fc631dbe069c9a8cf057e109ccaa9ad3ad14f4507b2388dcd9ae27463ce39651
|
data/Changelog
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
2022-12-22 version 2.3.1
|
2
|
+
|
3
|
+
* Don't treat as error when worker shuts down with exit status 0
|
4
|
+
|
1
5
|
2022-06-13 version 2.3.0
|
2
6
|
|
3
7
|
* Add restart_worker_interval option to prevent workers restart immediately
|
@@ -13,6 +17,7 @@
|
|
13
17
|
2022-01-13 version 2.2.5:
|
14
18
|
|
15
19
|
* Fix DLL load error on Ruby 3.1 on Windows
|
20
|
+
* Treat as error when worker shuts down unexpectedly
|
16
21
|
|
17
22
|
2021-05-24 version 2.2.4:
|
18
23
|
|
@@ -147,7 +147,11 @@ module ServerEngine
|
|
147
147
|
if @stop
|
148
148
|
@worker.logger.info "Worker #{@wid} finished with #{ServerEngine.format_join_status(stat)}"
|
149
149
|
else
|
150
|
-
|
150
|
+
if stat.is_a?(Process::Status) && stat.success?
|
151
|
+
@worker.logger.info "Worker #{@wid} exited with #{ServerEngine.format_join_status(stat)}"
|
152
|
+
else
|
153
|
+
@worker.logger.error "Worker #{@wid} exited unexpectedly with #{ServerEngine.format_join_status(stat)}"
|
154
|
+
end
|
151
155
|
end
|
152
156
|
if stat.is_a?(Process::Status) && stat.exited? && @unrecoverable_exit_codes.include?(stat.exitstatus)
|
153
157
|
@unrecoverable_exit = true
|
data/lib/serverengine/version.rb
CHANGED
data/serverengine.gemspec
CHANGED
data/spec/daemon_spec.rb
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
describe ServerEngine::Daemon do
|
3
3
|
include_context 'test server and worker'
|
4
4
|
|
5
|
+
before do
|
6
|
+
@log_path = "tmp/multi-worker-test-#{SecureRandom.hex(10)}.log"
|
7
|
+
end
|
8
|
+
|
9
|
+
after do
|
10
|
+
FileUtils.rm_rf(@log_path)
|
11
|
+
end
|
12
|
+
|
5
13
|
it 'run and graceful stop by signal' do
|
6
14
|
pending "not supported signal base commands on Windows" if ServerEngine.windows?
|
7
15
|
|
@@ -111,6 +119,7 @@ describe ServerEngine::Daemon do
|
|
111
119
|
daemonize: false,
|
112
120
|
supervisor: false,
|
113
121
|
pid_path: "tmp/pid",
|
122
|
+
logger: ServerEngine::DaemonLogger.new(@log_path),
|
114
123
|
log_stdout: false,
|
115
124
|
log_stderr: false,
|
116
125
|
unrecoverable_exit_codes: [3,4,5],
|
@@ -135,6 +144,7 @@ describe ServerEngine::Daemon do
|
|
135
144
|
supervisor: false,
|
136
145
|
worker_type: 'process',
|
137
146
|
pid_path: "tmp/pid",
|
147
|
+
logger: ServerEngine::DaemonLogger.new(@log_path),
|
138
148
|
log_stdout: false,
|
139
149
|
log_stderr: false,
|
140
150
|
unrecoverable_exit_codes: [3,4,5],
|
@@ -158,6 +168,7 @@ describe ServerEngine::Daemon do
|
|
158
168
|
supervisor: true,
|
159
169
|
worker_type: 'process',
|
160
170
|
pid_path: "tmp/pid",
|
171
|
+
logger: ServerEngine::DaemonLogger.new(@log_path),
|
161
172
|
log_stdout: false,
|
162
173
|
log_stderr: false,
|
163
174
|
unrecoverable_exit_codes: [3,4,5],
|
@@ -1,13 +1,30 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'securerandom'
|
3
|
+
|
1
4
|
[ServerEngine::MultiThreadServer, ServerEngine::MultiProcessServer].each do |impl_class|
|
2
5
|
# MultiProcessServer uses fork(2) internally, then it doesn't support Windows.
|
3
6
|
|
4
7
|
describe impl_class do
|
5
8
|
include_context 'test server and worker'
|
6
9
|
|
10
|
+
before do
|
11
|
+
@log_path = "tmp/multi-worker-test-#{SecureRandom.hex(10)}.log"
|
12
|
+
@logger = ServerEngine::DaemonLogger.new(@log_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
FileUtils.rm_rf(@log_path)
|
17
|
+
end
|
18
|
+
|
7
19
|
it 'scale up' do
|
8
20
|
pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
|
9
21
|
|
10
|
-
config = {
|
22
|
+
config = {
|
23
|
+
workers: 2,
|
24
|
+
logger: @logger,
|
25
|
+
log_stdout: false,
|
26
|
+
log_stderr: false,
|
27
|
+
}
|
11
28
|
|
12
29
|
s = impl_class.new(TestWorker) { config.dup }
|
13
30
|
t = Thread.new { s.main }
|
@@ -35,7 +52,12 @@
|
|
35
52
|
it 'scale down' do
|
36
53
|
pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
|
37
54
|
|
38
|
-
config = {
|
55
|
+
config = {
|
56
|
+
workers: 2,
|
57
|
+
logger: @logger,
|
58
|
+
log_stdout: false,
|
59
|
+
log_stderr: false
|
60
|
+
}
|
39
61
|
|
40
62
|
s = impl_class.new(TestWorker) { config.dup }
|
41
63
|
t = Thread.new { s.main }
|
@@ -59,12 +81,32 @@
|
|
59
81
|
|
60
82
|
test_state(:worker_stop).should == 3
|
61
83
|
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
[ServerEngine::MultiProcessServer].each do |impl_class|
|
88
|
+
describe impl_class do
|
89
|
+
include_context 'test server and worker'
|
90
|
+
|
91
|
+
before do
|
92
|
+
@log_path = "tmp/multi-worker-test-#{SecureRandom.hex(10)}.log"
|
93
|
+
@logger = ServerEngine::DaemonLogger.new(@log_path)
|
94
|
+
end
|
95
|
+
|
96
|
+
after do
|
97
|
+
FileUtils.rm_rf(@log_path)
|
98
|
+
end
|
62
99
|
|
63
100
|
it 'raises SystemExit when all workers exit with specified code by unrecoverable_exit_codes' do
|
64
|
-
pending "unrecoverable_exit_codes supported only for multi process workers" if impl_class == ServerEngine::MultiThreadServer
|
65
101
|
pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
|
66
102
|
|
67
|
-
config = {
|
103
|
+
config = {
|
104
|
+
workers: 4,
|
105
|
+
logger: @logger,
|
106
|
+
log_stdout: false,
|
107
|
+
log_stderr: false,
|
108
|
+
unrecoverable_exit_codes: [3, 4, 5]
|
109
|
+
}
|
68
110
|
|
69
111
|
s = impl_class.new(TestExitWorker) { config.dup }
|
70
112
|
raised_error = nil
|
@@ -85,10 +127,16 @@
|
|
85
127
|
end
|
86
128
|
|
87
129
|
it 'raises SystemExit immediately when a worker exits if stop_immediately_at_unrecoverable_exit specified' do
|
88
|
-
pending "unrecoverable_exit_codes supported only for multi process workers" if impl_class == ServerEngine::MultiThreadServer
|
89
130
|
pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
|
90
131
|
|
91
|
-
config = {
|
132
|
+
config = {
|
133
|
+
workers: 4,
|
134
|
+
logger: @logger,
|
135
|
+
log_stdout: false,
|
136
|
+
log_stderr: false,
|
137
|
+
unrecoverable_exit_codes: [3, 4, 5],
|
138
|
+
stop_immediately_at_unrecoverable_exit: true
|
139
|
+
}
|
92
140
|
|
93
141
|
s = impl_class.new(TestExitWorker) { config.dup }
|
94
142
|
raised_error = nil
|
@@ -111,3 +159,101 @@
|
|
111
159
|
end
|
112
160
|
end
|
113
161
|
end
|
162
|
+
|
163
|
+
describe "log level for exited proccess" do
|
164
|
+
include_context 'test server and worker'
|
165
|
+
|
166
|
+
before do
|
167
|
+
@log_path = "tmp/multi-process-log-level-test-#{SecureRandom.hex(10)}.log"
|
168
|
+
end
|
169
|
+
|
170
|
+
after do
|
171
|
+
FileUtils.rm_rf(@log_path)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'stop' do
|
175
|
+
pending "Windows environment does not support fork" if ServerEngine.windows?
|
176
|
+
|
177
|
+
config = {
|
178
|
+
workers: 1,
|
179
|
+
logger: ServerEngine::DaemonLogger.new(@log_path),
|
180
|
+
log_stdout: false,
|
181
|
+
log_stderr: false,
|
182
|
+
}
|
183
|
+
|
184
|
+
s = ServerEngine::MultiProcessServer.new(TestWorker) { config.dup }
|
185
|
+
t = Thread.new { s.main }
|
186
|
+
|
187
|
+
begin
|
188
|
+
wait_for_fork
|
189
|
+
test_state(:worker_run).should == 1
|
190
|
+
ensure
|
191
|
+
s.stop(true)
|
192
|
+
t.join
|
193
|
+
end
|
194
|
+
|
195
|
+
log_lines = File.read(@log_path).split("\n")
|
196
|
+
expect(log_lines[2]).to match(/^I, \[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+ #\d+\] INFO -- : Worker 0 finished with status 0$/)
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'non zero exit status' do
|
200
|
+
pending "Windows environment does not support fork" if ServerEngine.windows?
|
201
|
+
|
202
|
+
config = {
|
203
|
+
workers: 1,
|
204
|
+
logger: ServerEngine::DaemonLogger.new(@log_path),
|
205
|
+
log_stdout: false,
|
206
|
+
log_stderr: false,
|
207
|
+
unrecoverable_exit_codes: [5],
|
208
|
+
}
|
209
|
+
|
210
|
+
s = ServerEngine::MultiProcessServer.new(TestExitWorker) { config.dup }
|
211
|
+
raised_error = nil
|
212
|
+
Thread.new do
|
213
|
+
begin
|
214
|
+
s.main
|
215
|
+
rescue SystemExit => e
|
216
|
+
raised_error = e
|
217
|
+
end
|
218
|
+
end.join
|
219
|
+
|
220
|
+
test_state(:worker_stop).to_i.should == 0
|
221
|
+
raised_error.status.should == 5
|
222
|
+
log_lines = File.read(@log_path).split("\n")
|
223
|
+
expect(log_lines[1]).to match(/^E, \[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+ #\d+\] ERROR -- : Worker 0 exited unexpectedly with status 5$/)
|
224
|
+
end
|
225
|
+
|
226
|
+
module TestNormalExitWorker
|
227
|
+
include TestExitWorker
|
228
|
+
def initialize
|
229
|
+
super
|
230
|
+
@exit_code = 0
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'zero exit status' do
|
235
|
+
pending "Windows environment does not support fork" if ServerEngine.windows?
|
236
|
+
|
237
|
+
config = {
|
238
|
+
workers: 1,
|
239
|
+
logger: ServerEngine::DaemonLogger.new(@log_path),
|
240
|
+
log_stdout: false,
|
241
|
+
log_stderr: false,
|
242
|
+
}
|
243
|
+
|
244
|
+
s = ServerEngine::MultiProcessServer.new(TestNormalExitWorker) { config.dup }
|
245
|
+
t = Thread.new { s.main }
|
246
|
+
|
247
|
+
begin
|
248
|
+
Timeout.timeout(5) do
|
249
|
+
sleep 1 until File.read(@log_path).include?("INFO -- : Worker 0 exited with status 0")
|
250
|
+
end
|
251
|
+
ensure
|
252
|
+
s.stop(true)
|
253
|
+
t.join
|
254
|
+
end
|
255
|
+
|
256
|
+
log_lines = File.read(@log_path).split("\n")
|
257
|
+
expect(log_lines[1]).to match(/^I, \[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+ #\d+\] INFO -- : Worker 0 exited with status 0$/)
|
258
|
+
end
|
259
|
+
end
|
@@ -4,10 +4,25 @@ require 'timecop'
|
|
4
4
|
describe ServerEngine::MultiSpawnServer do
|
5
5
|
include_context 'test server and worker'
|
6
6
|
|
7
|
+
before do
|
8
|
+
@log_path = "tmp/multi-worker-test-#{SecureRandom.hex(10)}.log"
|
9
|
+
@logger = ServerEngine::DaemonLogger.new(@log_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
FileUtils.rm_rf(@log_path)
|
14
|
+
end
|
15
|
+
|
7
16
|
describe 'starts worker processes' do
|
8
17
|
context 'with command_sender=pipe' do
|
9
18
|
it do
|
10
|
-
config = {
|
19
|
+
config = {
|
20
|
+
workers: 2,
|
21
|
+
command_sender: 'pipe',
|
22
|
+
logger: @logger,
|
23
|
+
log_stdout: false,
|
24
|
+
log_stderr: false
|
25
|
+
}
|
11
26
|
|
12
27
|
s = ServerEngine::MultiSpawnServer.new(TestWorker) { config.dup }
|
13
28
|
t = Thread.new { s.main }
|
@@ -32,6 +47,7 @@ describe ServerEngine::MultiSpawnServer do
|
|
32
47
|
{
|
33
48
|
workers: workers,
|
34
49
|
command_sender: 'pipe',
|
50
|
+
logger: @logger,
|
35
51
|
log_stdout: false,
|
36
52
|
log_stderr: false,
|
37
53
|
start_worker_delay: start_worker_delay,
|
data/spec/supervisor_spec.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
require 'rr'
|
1
2
|
|
2
3
|
describe ServerEngine::Supervisor do
|
3
4
|
include_context 'test server and worker'
|
4
5
|
|
5
6
|
def start_supervisor(worker = nil, **config)
|
7
|
+
config[:log] ||= @log_path
|
8
|
+
config[:log_stdout] ||= false
|
9
|
+
config[:log_stderr] ||= false
|
6
10
|
if ServerEngine.windows?
|
7
11
|
config[:windows_daemon_cmdline] = windows_supervisor_cmdline(nil, worker, config)
|
8
12
|
end
|
@@ -13,6 +17,8 @@ describe ServerEngine::Supervisor do
|
|
13
17
|
end
|
14
18
|
|
15
19
|
def start_daemon(**config)
|
20
|
+
config[:log_stdout] ||= false
|
21
|
+
config[:log_stderr] ||= false
|
16
22
|
if ServerEngine.windows?
|
17
23
|
config[:windows_daemon_cmdline] = windows_daemon_cmdline
|
18
24
|
end
|
@@ -22,9 +28,17 @@ describe ServerEngine::Supervisor do
|
|
22
28
|
return daemon, t
|
23
29
|
end
|
24
30
|
|
31
|
+
before do
|
32
|
+
@log_path = "tmp/supervisor-test-#{SecureRandom.hex(10)}.log"
|
33
|
+
end
|
34
|
+
|
35
|
+
after do
|
36
|
+
FileUtils.rm_rf(@log_path)
|
37
|
+
end
|
38
|
+
|
25
39
|
context 'when :log=IO option is given' do
|
26
40
|
it 'can start' do
|
27
|
-
daemon, t = start_daemon(log:
|
41
|
+
daemon, t = start_daemon(log: File.open(@log_path, "wb"))
|
28
42
|
|
29
43
|
begin
|
30
44
|
wait_for_fork
|
@@ -40,7 +54,7 @@ describe ServerEngine::Supervisor do
|
|
40
54
|
|
41
55
|
context 'when :logger option is given' do
|
42
56
|
it 'uses specified logger instance' do
|
43
|
-
logger = ServerEngine::DaemonLogger.new(
|
57
|
+
logger = ServerEngine::DaemonLogger.new(@log_path)
|
44
58
|
daemon, t = start_daemon(logger: logger)
|
45
59
|
|
46
60
|
begin
|
@@ -57,7 +71,7 @@ describe ServerEngine::Supervisor do
|
|
57
71
|
|
58
72
|
context 'when both :logger and :log options are given' do
|
59
73
|
it 'start ignoring :log' do
|
60
|
-
logger = ServerEngine::DaemonLogger.new(
|
74
|
+
logger = ServerEngine::DaemonLogger.new(@log_path)
|
61
75
|
daemon, t = start_daemon(logger: logger, log: STDERR)
|
62
76
|
|
63
77
|
begin
|
@@ -188,6 +202,8 @@ describe ServerEngine::Supervisor do
|
|
188
202
|
it 'auto restart in limited ratio' do
|
189
203
|
pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
190
204
|
|
205
|
+
RR.stub(ServerEngine).dump_uncaught_error
|
206
|
+
|
191
207
|
sv, t = start_supervisor(RunErrorWorker, server_restart_wait: 1, command_sender: sender)
|
192
208
|
|
193
209
|
begin
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serverengine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.
|
4
|
+
version: 2.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sigdump
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.9.5
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rr
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.1'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.1'
|
97
111
|
description: A framework to implement robust multiprocess servers like Unicorn
|
98
112
|
email:
|
99
113
|
- frsyuki@gmail.com
|