service_skeleton 0.0.0.1.ENOTAG → 0.0.0.2.g46c1e0e
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 +5 -5
- data/.gitignore +0 -2
- data/.rubocop.yml +114 -9
- data/.travis.yml +11 -0
- data/README.md +153 -279
- data/lib/service_skeleton/background_worker.rb +80 -0
- data/lib/service_skeleton/config.rb +18 -78
- data/lib/service_skeleton/config_variable.rb +8 -29
- data/lib/service_skeleton/config_variables.rb +68 -54
- data/lib/service_skeleton/error.rb +3 -5
- data/lib/service_skeleton/filtering_logger.rb +0 -2
- data/lib/service_skeleton/logging_helpers.rb +3 -10
- data/lib/service_skeleton/metrics_methods.rb +13 -28
- data/lib/service_skeleton/signal_handler.rb +183 -0
- data/lib/service_skeleton.rb +145 -22
- data/service_skeleton.gemspec +9 -10
- metadata +19 -102
- data/.editorconfig +0 -7
- data/.git-blame-ignore-revs +0 -2
- data/.github/workflows/ci.yml +0 -50
- data/lib/service_skeleton/config_class.rb +0 -16
- data/lib/service_skeleton/config_variable/boolean.rb +0 -21
- data/lib/service_skeleton/config_variable/enum.rb +0 -27
- data/lib/service_skeleton/config_variable/float.rb +0 -25
- data/lib/service_skeleton/config_variable/integer.rb +0 -25
- data/lib/service_skeleton/config_variable/kv_list.rb +0 -26
- data/lib/service_skeleton/config_variable/path_list.rb +0 -13
- data/lib/service_skeleton/config_variable/string.rb +0 -18
- data/lib/service_skeleton/config_variable/url.rb +0 -36
- data/lib/service_skeleton/config_variable/yaml_file.rb +0 -42
- data/lib/service_skeleton/generator.rb +0 -165
- data/lib/service_skeleton/metric_method_name.rb +0 -9
- data/lib/service_skeleton/runner.rb +0 -46
- data/lib/service_skeleton/service_name.rb +0 -20
- data/lib/service_skeleton/signal_manager.rb +0 -202
- data/lib/service_skeleton/signals_methods.rb +0 -15
- data/lib/service_skeleton/ultravisor_children.rb +0 -20
- data/lib/service_skeleton/ultravisor_loggerstash.rb +0 -11
- data/ultravisor/.yardopts +0 -1
- data/ultravisor/Guardfile +0 -9
- data/ultravisor/README.md +0 -404
- data/ultravisor/lib/ultravisor/child/call.rb +0 -21
- data/ultravisor/lib/ultravisor/child/call_receiver.rb +0 -14
- data/ultravisor/lib/ultravisor/child/cast.rb +0 -16
- data/ultravisor/lib/ultravisor/child/cast_receiver.rb +0 -11
- data/ultravisor/lib/ultravisor/child/process_cast_call.rb +0 -39
- data/ultravisor/lib/ultravisor/child.rb +0 -481
- data/ultravisor/lib/ultravisor/error.rb +0 -25
- data/ultravisor/lib/ultravisor/logging_helpers.rb +0 -32
- data/ultravisor/lib/ultravisor.rb +0 -216
- data/ultravisor/spec/example_group_methods.rb +0 -19
- data/ultravisor/spec/example_methods.rb +0 -8
- data/ultravisor/spec/spec_helper.rb +0 -52
- data/ultravisor/spec/ultravisor/add_child_spec.rb +0 -79
- data/ultravisor/spec/ultravisor/child/call_spec.rb +0 -121
- data/ultravisor/spec/ultravisor/child/cast_spec.rb +0 -111
- data/ultravisor/spec/ultravisor/child/id_spec.rb +0 -21
- data/ultravisor/spec/ultravisor/child/new_spec.rb +0 -152
- data/ultravisor/spec/ultravisor/child/restart_delay_spec.rb +0 -40
- data/ultravisor/spec/ultravisor/child/restart_spec.rb +0 -70
- data/ultravisor/spec/ultravisor/child/run_spec.rb +0 -95
- data/ultravisor/spec/ultravisor/child/shutdown_spec.rb +0 -124
- data/ultravisor/spec/ultravisor/child/spawn_spec.rb +0 -107
- data/ultravisor/spec/ultravisor/child/unsafe_instance_spec.rb +0 -55
- data/ultravisor/spec/ultravisor/child/wait_spec.rb +0 -32
- data/ultravisor/spec/ultravisor/new_spec.rb +0 -71
- data/ultravisor/spec/ultravisor/remove_child_spec.rb +0 -49
- data/ultravisor/spec/ultravisor/run_spec.rb +0 -334
- data/ultravisor/spec/ultravisor/shutdown_spec.rb +0 -106
@@ -1,216 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "logger"
|
4
|
-
|
5
|
-
require_relative "./ultravisor/child"
|
6
|
-
require_relative "./ultravisor/error"
|
7
|
-
require_relative "./ultravisor/logging_helpers"
|
8
|
-
|
9
|
-
# A super-dooOOOoooper supervisor.
|
10
|
-
#
|
11
|
-
class Ultravisor
|
12
|
-
include LoggingHelpers
|
13
|
-
|
14
|
-
def initialize(children: [], strategy: :one_for_one, logger: Logger.new("/dev/null"))
|
15
|
-
@queue, @logger = Queue.new, logger
|
16
|
-
|
17
|
-
@strategy = strategy
|
18
|
-
validate_strategy
|
19
|
-
|
20
|
-
@op_m, @op_cv = Mutex.new, ConditionVariable.new
|
21
|
-
@running_thread = nil
|
22
|
-
|
23
|
-
initialize_children(children)
|
24
|
-
end
|
25
|
-
|
26
|
-
def run
|
27
|
-
logger.debug(logloc) { "called" }
|
28
|
-
|
29
|
-
@op_m.synchronize do
|
30
|
-
if @running_thread
|
31
|
-
raise AlreadyRunningError,
|
32
|
-
"This ultravisor is already running"
|
33
|
-
end
|
34
|
-
|
35
|
-
@queue.clear
|
36
|
-
@running_thread = Thread.current
|
37
|
-
Thread.current.name = "Ultravisor"
|
38
|
-
end
|
39
|
-
|
40
|
-
logger.debug(logloc) { "Going to start children #{@children.map(&:first).inspect}" }
|
41
|
-
@children.each { |c| c.last.spawn(@queue) }
|
42
|
-
|
43
|
-
process_events
|
44
|
-
|
45
|
-
@op_m.synchronize do
|
46
|
-
logger.debug(logloc) { "Shutdown time for #{@children.reverse.map(&:first).inspect}" }
|
47
|
-
@children.reverse.each { |c| c.last.shutdown }
|
48
|
-
|
49
|
-
@running_thread = nil
|
50
|
-
@op_cv.broadcast
|
51
|
-
end
|
52
|
-
|
53
|
-
self
|
54
|
-
end
|
55
|
-
|
56
|
-
def shutdown(wait: true, force: false)
|
57
|
-
@op_m.synchronize do
|
58
|
-
return self unless @running_thread
|
59
|
-
if force
|
60
|
-
@children.reverse.each { |c| c.last.shutdown(force: true) }
|
61
|
-
@running_thread.kill
|
62
|
-
@running_thread = nil
|
63
|
-
@op_cv.broadcast
|
64
|
-
else
|
65
|
-
@queue << :shutdown
|
66
|
-
if wait
|
67
|
-
@op_cv.wait(@op_m) while @running_thread
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
self
|
72
|
-
end
|
73
|
-
|
74
|
-
def [](id)
|
75
|
-
@children.assoc(id)&.last
|
76
|
-
end
|
77
|
-
|
78
|
-
def add_child(**args)
|
79
|
-
logger.debug(logloc) { "Adding child #{args[:id].inspect}" }
|
80
|
-
args[:logger] ||= logger
|
81
|
-
|
82
|
-
@op_m.synchronize do
|
83
|
-
c = Ultravisor::Child.new(**args)
|
84
|
-
|
85
|
-
if @children.assoc(c.id)
|
86
|
-
raise DuplicateChildError,
|
87
|
-
"Child with ID #{c.id.inspect} already exists"
|
88
|
-
end
|
89
|
-
|
90
|
-
@children << [c.id, c]
|
91
|
-
|
92
|
-
if @running_thread
|
93
|
-
logger.debug(logloc) { "Auto-starting new child #{args[:id].inspect}" }
|
94
|
-
c.spawn(@queue)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def remove_child(id)
|
100
|
-
logger.debug(logloc) { "Removing child #{id.inspect}" }
|
101
|
-
|
102
|
-
@op_m.synchronize do
|
103
|
-
c = @children.assoc(id)
|
104
|
-
|
105
|
-
return nil if c.nil?
|
106
|
-
|
107
|
-
@children.delete(c)
|
108
|
-
if @running_thread
|
109
|
-
logger.debug(logloc) { "Shutting down removed child #{id.inspect}" }
|
110
|
-
c.last.shutdown
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
private
|
116
|
-
|
117
|
-
def validate_strategy
|
118
|
-
unless %i{one_for_one all_for_one rest_for_one}.include?(@strategy)
|
119
|
-
raise ArgumentError,
|
120
|
-
"Invalid strategy #{@strategy.inspect}"
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def initialize_children(children)
|
125
|
-
unless children.is_a?(Array)
|
126
|
-
raise ArgumentError,
|
127
|
-
"children must be an Array"
|
128
|
-
end
|
129
|
-
|
130
|
-
@children = []
|
131
|
-
|
132
|
-
children.each do |cfg|
|
133
|
-
cfg[:logger] ||= logger
|
134
|
-
c = Ultravisor::Child.new(**cfg)
|
135
|
-
if @children.assoc(c.id)
|
136
|
-
raise DuplicateChildError,
|
137
|
-
"Duplicate child ID: #{c.id.inspect}"
|
138
|
-
end
|
139
|
-
|
140
|
-
@children << [c.id, c]
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def process_events
|
145
|
-
loop do
|
146
|
-
qe = @queue.pop
|
147
|
-
|
148
|
-
case qe
|
149
|
-
when Ultravisor::Child
|
150
|
-
logger.debug(logloc) { "Received Ultravisor::Child queue entry for #{qe.id}" }
|
151
|
-
@op_m.synchronize { child_exited(qe) }
|
152
|
-
when :shutdown
|
153
|
-
logger.debug(logloc) { "Received :shutdown queue entry" }
|
154
|
-
break
|
155
|
-
else
|
156
|
-
logger.error(logloc) { "Unknown queue entry: #{qe.inspect}" }
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def child_exited(child)
|
162
|
-
if child.termination_exception
|
163
|
-
log_exception(child.termination_exception, "Ultravisor::Child(#{child.id.inspect})") { "Thread terminated by unhandled exception" }
|
164
|
-
end
|
165
|
-
|
166
|
-
if @running_thread.nil?
|
167
|
-
logger.debug(logloc) { "Child termination after shutdown" }
|
168
|
-
# Child termination processed after we've shut down... nope
|
169
|
-
return
|
170
|
-
end
|
171
|
-
|
172
|
-
begin
|
173
|
-
return unless child.restart?
|
174
|
-
rescue Ultravisor::BlownRestartPolicyError
|
175
|
-
# Uh oh...
|
176
|
-
logger.error(logloc) { "Child #{child.id} has exceeded its restart policy. Shutting down the Ultravisor." }
|
177
|
-
@queue << :shutdown
|
178
|
-
return
|
179
|
-
end
|
180
|
-
|
181
|
-
case @strategy
|
182
|
-
when :all_for_one
|
183
|
-
@children.reverse.each do |id, c|
|
184
|
-
# Don't need to shut down the child that has caused all this mess
|
185
|
-
next if child.id == id
|
186
|
-
|
187
|
-
c.shutdown
|
188
|
-
end
|
189
|
-
when :rest_for_one
|
190
|
-
@children.reverse.each do |id, c|
|
191
|
-
# Don't go past the child that caused the problems
|
192
|
-
break if child.id == id
|
193
|
-
|
194
|
-
c.shutdown
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
sleep child.restart_delay
|
199
|
-
|
200
|
-
case @strategy
|
201
|
-
when :all_for_one
|
202
|
-
@children.each do |_, c|
|
203
|
-
c.spawn(@queue)
|
204
|
-
end
|
205
|
-
when :rest_for_one
|
206
|
-
s = false
|
207
|
-
@children.each do |id, c|
|
208
|
-
s = true if child.id == id
|
209
|
-
|
210
|
-
c.spawn(@queue) if s
|
211
|
-
end
|
212
|
-
when :one_for_one
|
213
|
-
child.spawn(@queue)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
module ExampleGroupMethods
|
6
|
-
def uses_logger
|
7
|
-
let(:logger) { instance_double(Logger, 'mock') }
|
8
|
-
|
9
|
-
before(:each) do
|
10
|
-
allow(Logger).to receive(:new).and_return(logger)
|
11
|
-
allow(logger).to receive(:debug).with(instance_of(String))
|
12
|
-
allow(logger).to receive(:info).with(instance_of(String))
|
13
|
-
allow(logger).to receive(:error) { |p, &m| puts "#{p}: #{m.call}" }
|
14
|
-
allow(logger).to receive(:level=).with(Logger::INFO)
|
15
|
-
allow(logger).to receive(:formatter=).with(an_instance_of(Proc))
|
16
|
-
allow(logger).to receive(:kind_of?).with(Logger).and_return(true)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'bundler'
|
3
|
-
Bundler.setup(:default, :development)
|
4
|
-
require 'rspec/core'
|
5
|
-
require 'rspec/mocks'
|
6
|
-
|
7
|
-
Thread.report_on_exception = false
|
8
|
-
|
9
|
-
require 'simplecov'
|
10
|
-
SimpleCov.start do
|
11
|
-
add_filter('spec')
|
12
|
-
end
|
13
|
-
|
14
|
-
class ListIncompletelyCoveredFiles
|
15
|
-
def format(result)
|
16
|
-
incompletes = result.files.select { |f| f.covered_percent < 100 }
|
17
|
-
|
18
|
-
unless incompletes.empty?
|
19
|
-
puts
|
20
|
-
puts "Files with incomplete test coverage:"
|
21
|
-
incompletes.each do |f|
|
22
|
-
printf " %2.01f%% %s\n", f.covered_percent, f.filename
|
23
|
-
end
|
24
|
-
puts; puts
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([
|
30
|
-
SimpleCov::Formatter::HTMLFormatter,
|
31
|
-
ListIncompletelyCoveredFiles
|
32
|
-
])
|
33
|
-
|
34
|
-
require_relative "./example_group_methods"
|
35
|
-
require_relative "./example_methods"
|
36
|
-
|
37
|
-
RSpec.configure do |config|
|
38
|
-
config.extend ExampleGroupMethods
|
39
|
-
config.include ExampleMethods
|
40
|
-
|
41
|
-
config.order = :random
|
42
|
-
config.fail_fast = !!ENV["RSPEC_CONFIG_FAIL_FAST"]
|
43
|
-
config.full_backtrace = !!ENV["RSPEC_CONFIG_FULL_BACKTRACE"]
|
44
|
-
|
45
|
-
config.expect_with :rspec do |c|
|
46
|
-
c.syntax = :expect
|
47
|
-
end
|
48
|
-
|
49
|
-
config.mock_with :rspec do |mocks|
|
50
|
-
mocks.verify_partial_doubles = true
|
51
|
-
end
|
52
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative "../spec_helper"
|
3
|
-
|
4
|
-
require_relative "../../lib/ultravisor"
|
5
|
-
|
6
|
-
describe Ultravisor do
|
7
|
-
let(:args) { {} }
|
8
|
-
let(:ultravisor) { Ultravisor.new(**args) }
|
9
|
-
let!(:child) { Ultravisor::Child.new(id: :xtra, klass: Class, method: :to_s) }
|
10
|
-
|
11
|
-
describe "#add_child" do
|
12
|
-
before(:each) do
|
13
|
-
allow(Ultravisor::Child).to receive(:new).and_return(child)
|
14
|
-
end
|
15
|
-
|
16
|
-
context "when the ultravisor isn't running" do
|
17
|
-
it "creates a new Child object" do
|
18
|
-
expect(Ultravisor::Child).to receive(:new).with(id: :xtra, klass: Class, method: :to_s, logger: instance_of(Logger))
|
19
|
-
|
20
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "registers the new child" do
|
24
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
25
|
-
|
26
|
-
expect(ultravisor[:xtra]).to be_a(Ultravisor::Child)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "explodes if a dupe child ID is used" do
|
30
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
31
|
-
|
32
|
-
expect do
|
33
|
-
ultravisor.add_child(id: :xtra, klass: Object, method: :to_s)
|
34
|
-
end.to raise_error(Ultravisor::DuplicateChildError)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "doesn't spawn a child thread" do
|
38
|
-
expect(child).to_not receive(:spawn)
|
39
|
-
|
40
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context "while the ultravisor *is* running" do
|
45
|
-
let(:mock_thread) { instance_double(Thread) }
|
46
|
-
|
47
|
-
before(:each) do
|
48
|
-
allow(child).to receive(:spawn)
|
49
|
-
ultravisor.instance_variable_set(:@running_thread, mock_thread)
|
50
|
-
end
|
51
|
-
|
52
|
-
it "creates a new Child object" do
|
53
|
-
expect(Ultravisor::Child).to receive(:new).with(id: :xtra, klass: Class, method: :to_s, logger: instance_of(Logger))
|
54
|
-
|
55
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
56
|
-
end
|
57
|
-
|
58
|
-
it "registers the new child" do
|
59
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
60
|
-
|
61
|
-
expect(ultravisor[:xtra]).to be_a(Ultravisor::Child)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "explodes if a dupe child ID is used" do
|
65
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
66
|
-
|
67
|
-
expect do
|
68
|
-
ultravisor.add_child(id: :xtra, klass: Object, method: :to_s)
|
69
|
-
end.to raise_error(Ultravisor::DuplicateChildError)
|
70
|
-
end
|
71
|
-
|
72
|
-
it "spawns a child thread" do
|
73
|
-
ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
|
74
|
-
|
75
|
-
expect(child).to have_received(:spawn)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,121 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../../spec_helper"
|
4
|
-
|
5
|
-
require_relative "../../../lib/ultravisor/child"
|
6
|
-
require_relative "../../../lib/ultravisor/error"
|
7
|
-
|
8
|
-
class CastCallTest
|
9
|
-
class FakeError < RuntimeError; end
|
10
|
-
|
11
|
-
def run
|
12
|
-
end
|
13
|
-
|
14
|
-
def poke_processor
|
15
|
-
process_castcall
|
16
|
-
end
|
17
|
-
|
18
|
-
def failing_method
|
19
|
-
raise FakeError
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
describe Ultravisor::Child do
|
24
|
-
let(:child) { Ultravisor::Child.new(**args) }
|
25
|
-
let(:instance) { child.__send__(:new_instance) }
|
26
|
-
|
27
|
-
describe "#call" do
|
28
|
-
context "without enable_castcall" do
|
29
|
-
let(:args) do
|
30
|
-
{
|
31
|
-
id: :call_child,
|
32
|
-
klass: CastCallTest,
|
33
|
-
method: :run,
|
34
|
-
}
|
35
|
-
end
|
36
|
-
|
37
|
-
it "does not accept calls to #call" do
|
38
|
-
expect { child.call }.to raise_error(NoMethodError)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "with enable_castcall" do
|
43
|
-
let(:args) do
|
44
|
-
{
|
45
|
-
id: :call_child,
|
46
|
-
klass: CastCallTest,
|
47
|
-
method: :run,
|
48
|
-
enable_castcall: true,
|
49
|
-
}
|
50
|
-
end
|
51
|
-
|
52
|
-
before(:each) do
|
53
|
-
# Got to have an instance otherwise all hell breaks loose
|
54
|
-
child.instance_variable_set(:@instance, instance)
|
55
|
-
|
56
|
-
# So we can check if and when it's been called
|
57
|
-
allow(instance).to receive(:to_s).and_call_original
|
58
|
-
allow(instance).to receive(:failing_method).and_call_original
|
59
|
-
end
|
60
|
-
|
61
|
-
it "accepts calls to #call" do
|
62
|
-
expect { child.call }.to_not raise_error
|
63
|
-
end
|
64
|
-
|
65
|
-
it "does not accept call calls to methods that do not exist on the worker object" do
|
66
|
-
expect { child.call.flibbetygibbets }.to raise_error(NoMethodError)
|
67
|
-
end
|
68
|
-
|
69
|
-
it "does not accept call calls to private methods on the worker object" do
|
70
|
-
expect { child.call.eval }.to raise_error(NoMethodError)
|
71
|
-
end
|
72
|
-
|
73
|
-
it "accepts calls to methods that exist on the worker object" do
|
74
|
-
th = Thread.new { child.call.to_s }
|
75
|
-
th.join(0.01)
|
76
|
-
instance.poke_processor
|
77
|
-
expect { th.value }.to_not raise_error
|
78
|
-
end
|
79
|
-
|
80
|
-
it "calls the instance method only when process_castcall is called" do
|
81
|
-
th = Thread.new { child.call.to_s }
|
82
|
-
th.join(0.001)
|
83
|
-
|
84
|
-
# Thread should be ticking along, not dead
|
85
|
-
expect(th.status).to eq("sleep")
|
86
|
-
expect(instance).to_not have_received(:to_s)
|
87
|
-
instance.poke_processor
|
88
|
-
expect(instance).to have_received(:to_s)
|
89
|
-
expect(th.value).to be_a(String)
|
90
|
-
end
|
91
|
-
|
92
|
-
it "raises a relevant error if the call itself causes an exception is dying" do
|
93
|
-
th = Thread.new { child.call.failing_method }
|
94
|
-
th.join(0.001)
|
95
|
-
|
96
|
-
expect(instance).to_not have_received(:failing_method)
|
97
|
-
expect { instance.poke_processor }.to raise_error(CastCallTest::FakeError)
|
98
|
-
expect(instance).to have_received(:failing_method)
|
99
|
-
|
100
|
-
expect { th.value }.to raise_error(Ultravisor::ChildRestartedError)
|
101
|
-
end
|
102
|
-
|
103
|
-
it "raises a relevant error if the instance is dying" do
|
104
|
-
instance.instance_variable_get(:@ultravisor_child_castcall_queue).close
|
105
|
-
|
106
|
-
expect { child.call.to_s }.to raise_error(Ultravisor::ChildRestartedError)
|
107
|
-
end
|
108
|
-
|
109
|
-
it "raises an error to all incomplete calls if the instance terminates" do
|
110
|
-
th = Thread.new { child.call.to_s }
|
111
|
-
|
112
|
-
th.join(0.001) until th.status == "sleep"
|
113
|
-
|
114
|
-
expect(instance).to_not have_received(:to_s)
|
115
|
-
child.instance_variable_get(:@spawn_m).synchronize { child.__send__(:termination_cleanup) }
|
116
|
-
|
117
|
-
expect { th.value }.to raise_error(Ultravisor::ChildRestartedError)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
@@ -1,111 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../../spec_helper"
|
4
|
-
|
5
|
-
require_relative "../../../lib/ultravisor/child"
|
6
|
-
require_relative "../../../lib/ultravisor/error"
|
7
|
-
|
8
|
-
class CastCallTest
|
9
|
-
def run
|
10
|
-
end
|
11
|
-
|
12
|
-
def poke_processor
|
13
|
-
process_castcall
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe Ultravisor::Child do
|
18
|
-
let(:child) { Ultravisor::Child.new(**args) }
|
19
|
-
let(:instance) { child.__send__(:new_instance) }
|
20
|
-
|
21
|
-
describe "#cast" do
|
22
|
-
context "without enable_castcall" do
|
23
|
-
let(:args) do
|
24
|
-
{
|
25
|
-
id: :cast_child,
|
26
|
-
klass: CastCallTest,
|
27
|
-
method: :run,
|
28
|
-
}
|
29
|
-
end
|
30
|
-
|
31
|
-
it "does not accept calls to #cast" do
|
32
|
-
expect { child.cast }.to raise_error(NoMethodError)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context "with enable_castcall" do
|
37
|
-
let(:args) do
|
38
|
-
{
|
39
|
-
id: :cast_child,
|
40
|
-
klass: CastCallTest,
|
41
|
-
method: :run,
|
42
|
-
enable_castcall: true,
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
before(:each) do
|
47
|
-
# Got to have an instance otherwise all hell breaks loose
|
48
|
-
child.instance_variable_set(:@instance, instance)
|
49
|
-
|
50
|
-
# So we can check if and when it's been called
|
51
|
-
allow(instance).to receive(:to_s).and_call_original
|
52
|
-
end
|
53
|
-
|
54
|
-
it "accepts calls to #cast" do
|
55
|
-
expect { child.cast }.to_not raise_error
|
56
|
-
end
|
57
|
-
|
58
|
-
it "does not accept cast calls to methods that do not exist on the worker object" do
|
59
|
-
expect { child.cast.flibbetygibbets }.to raise_error(NoMethodError)
|
60
|
-
end
|
61
|
-
|
62
|
-
it "does not accept cast calls to private methods on the worker object" do
|
63
|
-
expect { child.cast.eval }.to raise_error(NoMethodError)
|
64
|
-
end
|
65
|
-
|
66
|
-
it "accepts calls to methods that exist on the worker object" do
|
67
|
-
expect { child.cast.to_s }.to_not raise_error
|
68
|
-
end
|
69
|
-
|
70
|
-
it "calls the instance method only when process_castcall is called" do
|
71
|
-
child.cast.to_s
|
72
|
-
expect(instance).to_not have_received(:to_s)
|
73
|
-
instance.poke_processor
|
74
|
-
expect(instance).to have_received(:to_s)
|
75
|
-
end
|
76
|
-
|
77
|
-
it "processes all the queued method calls" do
|
78
|
-
child.cast.to_s
|
79
|
-
child.cast.to_s
|
80
|
-
child.cast.to_s
|
81
|
-
child.cast.to_s
|
82
|
-
child.cast.to_s
|
83
|
-
expect(instance).to_not have_received(:to_s)
|
84
|
-
instance.poke_processor
|
85
|
-
expect(instance).to have_received(:to_s).exactly(5).times
|
86
|
-
end
|
87
|
-
|
88
|
-
let(:cc_fd) { instance.__send__(:castcall_fd) }
|
89
|
-
it "marks the castcall_fd as readable only after cast is called" do
|
90
|
-
expect(IO.select([cc_fd], nil, nil, 0)).to eq(nil)
|
91
|
-
|
92
|
-
child.cast.to_s
|
93
|
-
|
94
|
-
expect(IO.select([cc_fd], nil, nil, 0)).to eq([[cc_fd], [], []])
|
95
|
-
end
|
96
|
-
|
97
|
-
it "does not have a readable castcall_fd after process_castcall" do
|
98
|
-
child.cast.to_s
|
99
|
-
expect(IO.select([cc_fd], nil, nil, 0)).to eq([[cc_fd], [], []])
|
100
|
-
instance.poke_processor
|
101
|
-
expect(IO.select([cc_fd], nil, nil, 0)).to eq(nil)
|
102
|
-
end
|
103
|
-
|
104
|
-
it "does not explode if the instance is dying" do
|
105
|
-
instance.instance_variable_get(:@ultravisor_child_castcall_queue).close
|
106
|
-
|
107
|
-
expect { child.cast.to_s }.to_not raise_error
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../../spec_helper"
|
4
|
-
|
5
|
-
require_relative "../../../lib/ultravisor/child"
|
6
|
-
require_relative "../../../lib/ultravisor/error"
|
7
|
-
|
8
|
-
describe Ultravisor::Child do
|
9
|
-
let(:child) { Ultravisor::Child.new(**args) }
|
10
|
-
let(:mock_class) { Class.new.tap { |k| k.class_eval { def run; end } } }
|
11
|
-
|
12
|
-
describe "#id" do
|
13
|
-
context "with minimal arguments" do
|
14
|
-
let(:args) { { id: :bob, klass: mock_class, method: :run } }
|
15
|
-
|
16
|
-
it "returns the child's ID" do
|
17
|
-
expect(child.id).to eq(:bob)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|