serverengine 2.3.0 → 2.3.2
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/.github/workflows/linux.yml +1 -1
- data/.github/workflows/windows.yml +1 -1
- data/Changelog +11 -0
- data/README.md +2 -2
- data/examples/server.rb +2 -2
- data/lib/serverengine/multi_process_server.rb +5 -1
- data/lib/serverengine/socket_manager.rb +32 -7
- data/lib/serverengine/socket_manager_win.rb +4 -3
- data/lib/serverengine/version.rb +1 -1
- data/serverengine.gemspec +4 -3
- data/spec/daemon_logger_spec.rb +2 -2
- data/spec/daemon_spec.rb +16 -5
- data/spec/multi_process_server_spec.rb +156 -10
- data/spec/multi_spawn_server_spec.rb +17 -1
- data/spec/socket_manager_spec.rb +32 -3
- data/spec/spec_helper.rb +1 -4
- data/spec/supervisor_spec.rb +25 -9
- metadata +22 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7350069b97302faf3d6c50118875bf055a7094bc31845185cd4e6b4d3e0a9f61
|
4
|
+
data.tar.gz: 02e1c1e261b6ea6dde6a77a984c8f94dccf237093dda7949174acd2658c9897b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66ae31cea3ff4779a648d64c00e54ff01e76912cd2a4ce1f6105b83657bdbdaea0e9b2c54972cc171ed2aa2a01794dd4c2c3b2e73fdb1754a7013cee2fd0c1a5
|
7
|
+
data.tar.gz: 8b023a4052650036480ce2f899ac367279dcc341a3d98536c7a3fc91cf227622a3707f7315a9eead3a0a2335792700723dda3a527aa422c5aa06d2ff27824b11
|
data/.github/workflows/linux.yml
CHANGED
data/Changelog
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
2023-03-14 version 2.3.2
|
2
|
+
|
3
|
+
* Accept `nil` for `ServerEngine::SocketManager::Server.open` to select path automatically
|
4
|
+
* Care excluded port ranges of Windows in `ServerEngine::SocketManager.generate_path`
|
5
|
+
* Update to Rake 13 and RSpec 3 to support running tests on Ruby 3.2
|
6
|
+
|
7
|
+
2022-12-22 version 2.3.1
|
8
|
+
|
9
|
+
* Don't treat as error when worker shuts down with exit status 0
|
10
|
+
|
1
11
|
2022-06-13 version 2.3.0
|
2
12
|
|
3
13
|
* Add restart_worker_interval option to prevent workers restart immediately
|
@@ -13,6 +23,7 @@
|
|
13
23
|
2022-01-13 version 2.2.5:
|
14
24
|
|
15
25
|
* Fix DLL load error on Ruby 3.1 on Windows
|
26
|
+
* Treat as error when worker shuts down unexpectedly
|
16
27
|
|
17
28
|
2021-05-24 version 2.2.4:
|
18
29
|
|
data/README.md
CHANGED
@@ -370,8 +370,8 @@ se.run
|
|
370
370
|
```ruby
|
371
371
|
module MyServer
|
372
372
|
def before_run
|
373
|
-
@
|
374
|
-
@
|
373
|
+
@socket_manager_server = ServerEngine::SocketManager::Server.open
|
374
|
+
@socket_manager_path = @socket_manager_server.path
|
375
375
|
end
|
376
376
|
|
377
377
|
def after_run
|
data/examples/server.rb
CHANGED
@@ -41,8 +41,8 @@ module MyServer
|
|
41
41
|
attr_reader :socket_manager_path
|
42
42
|
|
43
43
|
def before_run
|
44
|
-
@
|
45
|
-
@
|
44
|
+
@socket_manager_server = ServerEngine::SocketManager::Server.open
|
45
|
+
@socket_manager_path = @socket_manager_server.path
|
46
46
|
rescue Exception => e
|
47
47
|
logger.error "unexpected error in server, class #{e.class}: #{e.message}"
|
48
48
|
raise
|
@@ -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
|
@@ -77,19 +77,23 @@ module ServerEngine
|
|
77
77
|
port = ENV['SERVERENGINE_SOCKETMANAGER_PORT']
|
78
78
|
return port.to_i if port
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end
|
80
|
+
excluded_port_ranges = get_excluded_port_ranges
|
81
|
+
get_dynamic_port_range
|
82
|
+
.reject { |port| excluded_port_ranges.any? { |range| range.cover?(port) } }
|
83
|
+
.find { |port| `netstat -na | findstr "#{port}"`.length == 0 }
|
85
84
|
else
|
86
85
|
base_dir = (ENV['SERVERENGINE_SOCKETMANAGER_SOCK_DIR'] || '/tmp')
|
87
86
|
File.join(base_dir, 'SERVERENGINE_SOCKETMANAGER_' + Time.now.utc.iso8601 + '_' + Process.pid.to_s)
|
88
87
|
end
|
89
88
|
end
|
90
89
|
|
91
|
-
def self.open(path)
|
92
|
-
new(path)
|
90
|
+
def self.open(path = nil)
|
91
|
+
return new(path) unless path.nil?
|
92
|
+
if ServerEngine.windows?
|
93
|
+
new(0)
|
94
|
+
else
|
95
|
+
new(self.generate_path)
|
96
|
+
end
|
93
97
|
end
|
94
98
|
|
95
99
|
def initialize(path)
|
@@ -198,6 +202,27 @@ module ServerEngine
|
|
198
202
|
return 49152..65535
|
199
203
|
end
|
200
204
|
end
|
205
|
+
|
206
|
+
def self.get_excluded_port_ranges
|
207
|
+
# Example output of netsh:
|
208
|
+
#
|
209
|
+
# Protocol tcp Port Exclusion Ranges
|
210
|
+
#
|
211
|
+
# Start Port End Port
|
212
|
+
# ---------- --------
|
213
|
+
# 2869 2869
|
214
|
+
# 49152 49251
|
215
|
+
# 50000 50059 *
|
216
|
+
# 57095 57194
|
217
|
+
#
|
218
|
+
# * - Administered port exclusions.
|
219
|
+
#
|
220
|
+
`netsh int ipv4 show excludedportrange tcp`
|
221
|
+
.force_encoding("ASCII-8BIT")
|
222
|
+
.lines
|
223
|
+
.map { |line| line.match(/\s*(\d+)\s*(\d+)[\s\*]*/) ? $1.to_i..$2.to_i : nil }
|
224
|
+
.compact
|
225
|
+
end
|
201
226
|
end
|
202
227
|
end
|
203
228
|
|
@@ -108,8 +108,9 @@ module ServerEngine
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def start_server(addr)
|
111
|
-
#
|
112
|
-
#
|
111
|
+
# We need to take care about selecting an available port.
|
112
|
+
# By passing `nil` or `0` as `addr`, an available port is automatically selected.
|
113
|
+
# However, we should consider using NamedPipe instead of TCPServer.
|
113
114
|
@server = TCPServer.new("127.0.0.1", addr)
|
114
115
|
@thread = Thread.new do
|
115
116
|
begin
|
@@ -123,7 +124,7 @@ module ServerEngine
|
|
123
124
|
end
|
124
125
|
end
|
125
126
|
|
126
|
-
return
|
127
|
+
return @server.addr[1]
|
127
128
|
end
|
128
129
|
|
129
130
|
def stop_server
|
data/lib/serverengine/version.rb
CHANGED
data/serverengine.gemspec
CHANGED
@@ -16,16 +16,17 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
17
|
gem.require_paths = ["lib"]
|
18
18
|
|
19
|
-
gem.required_ruby_version = ">= 2.
|
19
|
+
gem.required_ruby_version = ">= 2.3.0"
|
20
20
|
|
21
21
|
gem.add_dependency "sigdump", ["~> 0.2.2"]
|
22
22
|
|
23
23
|
# rake v12.x doesn't work with rspec 2. rspec should be updated to 3
|
24
|
-
gem.add_development_dependency "rake", ["~>
|
25
|
-
gem.add_development_dependency "rspec", ["~>
|
24
|
+
gem.add_development_dependency "rake", ["~> 13.0"]
|
25
|
+
gem.add_development_dependency "rspec", ["~> 3.12.0"]
|
26
26
|
|
27
27
|
gem.add_development_dependency 'rake-compiler-dock', ['~> 0.5.0']
|
28
28
|
gem.add_development_dependency 'rake-compiler', ['~> 0.9.4']
|
29
29
|
|
30
30
|
gem.add_development_dependency "timecop", ["~> 0.9.5"]
|
31
|
+
gem.add_development_dependency "rr", ["~> 3.1"]
|
31
32
|
end
|
data/spec/daemon_logger_spec.rb
CHANGED
@@ -150,7 +150,7 @@ describe ServerEngine::DaemonLogger do
|
|
150
150
|
end
|
151
151
|
|
152
152
|
it 'inter-process locking on rotation' do
|
153
|
-
|
153
|
+
skip "fork is not implemented in Windows" if ServerEngine.windows?
|
154
154
|
|
155
155
|
log = DaemonLogger.new("tmp/se4.log", level: 'trace', log_rotate_age: 3, log_rotate_size: 10)
|
156
156
|
r, w = IO.pipe
|
@@ -175,7 +175,7 @@ describe ServerEngine::DaemonLogger do
|
|
175
175
|
end
|
176
176
|
|
177
177
|
it 'reopen log when path is renamed' do
|
178
|
-
|
178
|
+
skip "rename isn't supported on windows" if ServerEngine.windows?
|
179
179
|
|
180
180
|
log = DaemonLogger.new("tmp/rotate.log", { level: 'info', log_rotate_age: 0 })
|
181
181
|
|
data/spec/daemon_spec.rb
CHANGED
@@ -2,8 +2,16 @@
|
|
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
|
+
skip "not supported signal base commands on Windows" if ServerEngine.windows?
|
7
15
|
|
8
16
|
dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid", command_sender: "signal")
|
9
17
|
dm.main
|
@@ -25,7 +33,7 @@ describe ServerEngine::Daemon do
|
|
25
33
|
end
|
26
34
|
|
27
35
|
it 'signals' do
|
28
|
-
|
36
|
+
skip "not supported signal base commands on Windows" if ServerEngine.windows?
|
29
37
|
dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid", command_sender: "signal")
|
30
38
|
dm.main
|
31
39
|
|
@@ -104,13 +112,14 @@ describe ServerEngine::Daemon do
|
|
104
112
|
end
|
105
113
|
|
106
114
|
it 'exits with status 0 when it was stopped normally' do
|
107
|
-
|
115
|
+
skip "worker type process(fork) cannot be used in Windows" if ServerEngine.windows?
|
108
116
|
dm = Daemon.new(
|
109
117
|
TestServer,
|
110
118
|
TestWorker,
|
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],
|
@@ -126,7 +135,7 @@ describe ServerEngine::Daemon do
|
|
126
135
|
end
|
127
136
|
|
128
137
|
it 'exits with status of workers if worker exits with status specified in unrecoverable_exit_codes, without supervisor' do
|
129
|
-
|
138
|
+
skip "worker type process(fork) cannot be used in Windows" if ServerEngine.windows?
|
130
139
|
|
131
140
|
dm = Daemon.new(
|
132
141
|
TestServer,
|
@@ -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],
|
@@ -149,7 +159,7 @@ describe ServerEngine::Daemon do
|
|
149
159
|
end
|
150
160
|
|
151
161
|
it 'exits with status of workers if worker exits with status specified in unrecoverable_exit_codes, with supervisor' do
|
152
|
-
|
162
|
+
skip "worker type process(fork) cannot be used in Windows" if ServerEngine.windows?
|
153
163
|
|
154
164
|
dm = Daemon.new(
|
155
165
|
TestServer,
|
@@ -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
|
+
skip "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 }
|
@@ -33,9 +50,14 @@
|
|
33
50
|
end
|
34
51
|
|
35
52
|
it 'scale down' do
|
36
|
-
|
53
|
+
skip "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
|
-
|
65
|
-
pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
|
101
|
+
skip "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
|
-
|
89
|
-
pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
|
130
|
+
skip "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
|
+
skip "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
|
+
skip "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
|
+
skip "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/socket_manager_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'rr'
|
2
3
|
|
3
4
|
describe ServerEngine::SocketManager do
|
4
5
|
include_context 'test server and worker'
|
@@ -21,9 +22,23 @@ describe ServerEngine::SocketManager do
|
|
21
22
|
|
22
23
|
if ServerEngine.windows?
|
23
24
|
context 'Server.generate_path' do
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
context 'with socket path as port number' do
|
26
|
+
it 'returns a port in the dynamic port range' do
|
27
|
+
path = SocketManager::Server.generate_path
|
28
|
+
expect(path).to be_between(49152, 65535)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns a port which is not excluded' do
|
32
|
+
excluded_port_ranges = [
|
33
|
+
49152..49251,
|
34
|
+
50000..50059,
|
35
|
+
]
|
36
|
+
RR.stub(SocketManager::Server).get_excluded_port_ranges { excluded_port_ranges }
|
37
|
+
path = SocketManager::Server.generate_path
|
38
|
+
excluded_port_ranges.each do |range|
|
39
|
+
expect(path).not_to be_between(range.first, range.last)
|
40
|
+
end
|
41
|
+
end
|
27
42
|
end
|
28
43
|
|
29
44
|
it 'can be changed via environment variable' do
|
@@ -33,6 +48,13 @@ describe ServerEngine::SocketManager do
|
|
33
48
|
ENV.delete('SERVERENGINE_SOCKETMANAGER_PORT')
|
34
49
|
end
|
35
50
|
end
|
51
|
+
|
52
|
+
context 'Server.open' do
|
53
|
+
it 'returns server with automatically selected socket path as port number' do
|
54
|
+
server = SocketManager::Server.open
|
55
|
+
expect(server.path).to be_between(49152, 65535)
|
56
|
+
end
|
57
|
+
end
|
36
58
|
else
|
37
59
|
context 'Server.generate_path' do
|
38
60
|
it 'returns socket path under /tmp' do
|
@@ -47,6 +69,13 @@ describe ServerEngine::SocketManager do
|
|
47
69
|
ENV.delete('SERVERENGINE_SOCKETMANAGER_SOCK_DIR')
|
48
70
|
end
|
49
71
|
end
|
72
|
+
|
73
|
+
context 'Server.open' do
|
74
|
+
it 'returns server with automatically selected socket path under /tmp' do
|
75
|
+
server = SocketManager::Server.open
|
76
|
+
expect(server.path).to include('/tmp/SERVERENGINE_SOCKETMANAGER_')
|
77
|
+
end
|
78
|
+
end
|
50
79
|
end
|
51
80
|
|
52
81
|
context 'with thread' do
|
data/spec/spec_helper.rb
CHANGED
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
|
@@ -76,7 +90,7 @@ describe ServerEngine::Supervisor do
|
|
76
90
|
context "when using #{sender} as command_sender" do
|
77
91
|
|
78
92
|
it 'start and graceful stop' do
|
79
|
-
|
93
|
+
skip 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
80
94
|
|
81
95
|
sv, t = start_supervisor(command_sender: sender)
|
82
96
|
|
@@ -99,7 +113,7 @@ describe ServerEngine::Supervisor do
|
|
99
113
|
end
|
100
114
|
|
101
115
|
it 'immediate stop' do
|
102
|
-
|
116
|
+
skip 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
103
117
|
|
104
118
|
sv, t = start_supervisor(command_sender: sender)
|
105
119
|
|
@@ -117,7 +131,7 @@ describe ServerEngine::Supervisor do
|
|
117
131
|
end
|
118
132
|
|
119
133
|
it 'graceful restart' do
|
120
|
-
|
134
|
+
skip 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
121
135
|
|
122
136
|
sv, t = start_supervisor(command_sender: sender)
|
123
137
|
|
@@ -141,7 +155,7 @@ describe ServerEngine::Supervisor do
|
|
141
155
|
end
|
142
156
|
|
143
157
|
it 'immediate restart' do
|
144
|
-
|
158
|
+
skip 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
145
159
|
|
146
160
|
sv, t = start_supervisor(command_sender: sender)
|
147
161
|
|
@@ -165,7 +179,7 @@ describe ServerEngine::Supervisor do
|
|
165
179
|
end
|
166
180
|
|
167
181
|
it 'reload' do
|
168
|
-
|
182
|
+
skip 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
169
183
|
|
170
184
|
sv, t = start_supervisor(command_sender: sender)
|
171
185
|
|
@@ -186,7 +200,9 @@ describe ServerEngine::Supervisor do
|
|
186
200
|
# TODO detach
|
187
201
|
|
188
202
|
it 'auto restart in limited ratio' do
|
189
|
-
|
203
|
+
skip 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
204
|
+
|
205
|
+
RR.stub(ServerEngine).dump_uncaught_error
|
190
206
|
|
191
207
|
sv, t = start_supervisor(RunErrorWorker, server_restart_wait: 1, command_sender: sender)
|
192
208
|
|
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sigdump
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '13.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 3.12.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 3.12.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake-compiler-dock
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -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
|
@@ -161,14 +175,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
161
175
|
requirements:
|
162
176
|
- - ">="
|
163
177
|
- !ruby/object:Gem::Version
|
164
|
-
version: 2.
|
178
|
+
version: 2.3.0
|
165
179
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
180
|
requirements:
|
167
181
|
- - ">="
|
168
182
|
- !ruby/object:Gem::Version
|
169
183
|
version: '0'
|
170
184
|
requirements: []
|
171
|
-
rubygems_version: 3.
|
185
|
+
rubygems_version: 3.4.1
|
172
186
|
signing_key:
|
173
187
|
specification_version: 4
|
174
188
|
summary: ServerEngine - multiprocess server framework
|