serverengine 2.3.0 → 2.3.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/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
|