service_skeleton 2.0.2 → 2.2.0
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/ci.yml +28 -13
- data/.rubocop.yml +1 -1
- data/README.md +1 -1
- data/lib/service_skeleton/hurriable_timer.rb +61 -0
- data/lib/service_skeleton/hurriable_timer_sequence.rb +35 -0
- data/lib/service_skeleton.rb +2 -0
- data/service_skeleton.gemspec +2 -2
- data/ultravisor/lib/ultravisor/child/call_receiver.rb +1 -0
- data/ultravisor/lib/ultravisor/child/cast_receiver.rb +1 -0
- data/ultravisor/spec/spec_helper.rb +4 -2
- data/ultravisor/spec/ultravisor/child/cast_spec.rb +24 -0
- data/ultravisor/spec/ultravisor/child/restart_delay_spec.rb +1 -1
- data/ultravisor/spec/ultravisor/child/shutdown_spec.rb +1 -1
- data/ultravisor/spec/ultravisor/child/unsafe_instance_spec.rb +1 -1
- data/ultravisor/spec/ultravisor/run_spec.rb +3 -3
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f06ab46da1ee2c8366933e34efe02686cfca1758cff0667fb05b6ef5a42e9db6
|
4
|
+
data.tar.gz: 17cc7ba27c8cd63f6e74831e69da68c860803af15e77eb6f4a52686754107138
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3137b6133e59bd2c9aa990bd85216b6acdf09a1d6174ae0fa06a1c2ff55e898f266f131a69457dd39f363fcdef35d67d0f9fd1e9255f6a8e9b4ebdd2f4a5ed9
|
7
|
+
data.tar.gz: dbba001733bcf0717673335d1ff52d6eb33b0a88b602c7f3be2d378addb37c0da585fba5c15d6ddff106f0dd69663935d541583ea521376b8136711eef53f3b8
|
data/.github/workflows/ci.yml
CHANGED
@@ -4,23 +4,41 @@ on:
|
|
4
4
|
pull_request:
|
5
5
|
push:
|
6
6
|
branches:
|
7
|
-
- master
|
8
7
|
- main
|
9
8
|
|
10
9
|
jobs:
|
10
|
+
lint:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v4
|
15
|
+
|
16
|
+
- name: Setup ruby
|
17
|
+
uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: "3.2"
|
20
|
+
bundler-cache: true
|
21
|
+
|
22
|
+
- name: Lint
|
23
|
+
run: bundle exec rubocop
|
24
|
+
|
11
25
|
build:
|
12
26
|
runs-on: ubuntu-latest
|
13
27
|
|
14
28
|
strategy:
|
29
|
+
fail-fast: false
|
30
|
+
|
15
31
|
matrix:
|
16
32
|
ruby:
|
17
|
-
- 2.5
|
18
|
-
- 2.6
|
19
|
-
- 2.7
|
20
|
-
- 3.0
|
33
|
+
- "2.5"
|
34
|
+
- "2.6"
|
35
|
+
- "2.7"
|
36
|
+
- "3.0"
|
37
|
+
- "3.1"
|
38
|
+
- "3.2"
|
21
39
|
|
22
40
|
steps:
|
23
|
-
- uses: actions/checkout@
|
41
|
+
- uses: actions/checkout@v4
|
24
42
|
|
25
43
|
- name: Setup ruby
|
26
44
|
uses: ruby/setup-ruby@v1
|
@@ -28,22 +46,19 @@ jobs:
|
|
28
46
|
ruby-version: ${{ matrix.ruby }}
|
29
47
|
bundler-cache: true
|
30
48
|
|
31
|
-
- name: Lint
|
32
|
-
run: bundle exec rubocop
|
33
|
-
|
34
49
|
- name: Tests
|
35
50
|
run: bundle exec rake test
|
36
51
|
|
37
52
|
publish:
|
38
|
-
if: github.event_name == 'push' &&
|
39
|
-
needs: build
|
53
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
54
|
+
needs: [lint, build]
|
40
55
|
runs-on: ubuntu-latest
|
41
56
|
|
42
57
|
steps:
|
43
|
-
- uses: actions/checkout@
|
58
|
+
- uses: actions/checkout@v4
|
44
59
|
|
45
60
|
- name: Release Gem
|
46
|
-
uses: discourse/publish-rubygems-action@
|
61
|
+
uses: discourse/publish-rubygems-action@v3
|
47
62
|
env:
|
48
63
|
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
49
64
|
GIT_EMAIL: team@discourse.org
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -268,7 +268,7 @@ default for a config variable, like so:
|
|
268
268
|
class GenericHelloService
|
269
269
|
include ServiceSkeleton
|
270
270
|
|
271
|
-
string :RECIPIENT, match: /\
|
271
|
+
string :RECIPIENT, match: /\A\w+\z/, default: "Anonymous Coward"
|
272
272
|
|
273
273
|
# ...
|
274
274
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A mechanism for waiting until a timer expires or until another thread signals
|
4
|
+
# readiness.
|
5
|
+
class ServiceSkeleton::HurriableTimer
|
6
|
+
def initialize(timeout)
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@condition = ConditionVariable.new
|
9
|
+
@end_time = now + timeout
|
10
|
+
@hurried = false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Wait for the timer to elapse
|
14
|
+
#
|
15
|
+
# Any number of threads can wait on the same HurriableTimer
|
16
|
+
def wait(t = nil)
|
17
|
+
end_time =
|
18
|
+
if t
|
19
|
+
[@end_time, now + t].min
|
20
|
+
else
|
21
|
+
@end_time
|
22
|
+
end
|
23
|
+
|
24
|
+
@mutex.synchronize {
|
25
|
+
while true
|
26
|
+
remaining = end_time - now
|
27
|
+
|
28
|
+
if remaining < 0 || @hurried
|
29
|
+
break
|
30
|
+
else
|
31
|
+
@condition.wait(@mutex, remaining)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Cause the timer to trigger early if it hasn't already expired
|
40
|
+
#
|
41
|
+
# This method is idempotent
|
42
|
+
def hurry!
|
43
|
+
@mutex.synchronize {
|
44
|
+
@hurried = true
|
45
|
+
@condition.broadcast
|
46
|
+
}
|
47
|
+
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def expired?
|
52
|
+
@hurried || @end_time - now < 0
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def now
|
58
|
+
# Using this instead of Time.now, because it isn't affected by NTP updates
|
59
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# HurribleTimerSequence is a resettable version of HurriableTimer, designed for
|
4
|
+
# cases where some action needs to happen at at least some frequency, but may
|
5
|
+
# happen more often when other threads trigger the process early.
|
6
|
+
#
|
7
|
+
# It would have been possible to implement this without requiring allocation on
|
8
|
+
# reset, by reusing the mutex and condition variable in the normal timer, but
|
9
|
+
# this version is more obviously correct.
|
10
|
+
class ServiceSkeleton::HurriableTimerSequence
|
11
|
+
def initialize(timeout)
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@timeout = timeout
|
14
|
+
@latest = ServiceSkeleton::HurriableTimer.new(@timeout)
|
15
|
+
end
|
16
|
+
|
17
|
+
def reset!
|
18
|
+
@mutex.synchronize {
|
19
|
+
@latest.hurry!
|
20
|
+
@latest = ServiceSkeleton::HurriableTimer.new(@timeout)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait(t = nil)
|
25
|
+
@mutex.synchronize { @latest }.wait(t)
|
26
|
+
end
|
27
|
+
|
28
|
+
def hurry!
|
29
|
+
@mutex.synchronize { @latest }.hurry!
|
30
|
+
end
|
31
|
+
|
32
|
+
def expired?
|
33
|
+
@mutex.synchronize { @latest }.expired?
|
34
|
+
end
|
35
|
+
end
|
data/lib/service_skeleton.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require_relative "service_skeleton/config_class"
|
4
4
|
require_relative "service_skeleton/config_variables"
|
5
5
|
require_relative "service_skeleton/generator"
|
6
|
+
require_relative "service_skeleton/hurriable_timer"
|
7
|
+
require_relative "service_skeleton/hurriable_timer_sequence"
|
6
8
|
require_relative "service_skeleton/logging_helpers"
|
7
9
|
require_relative "service_skeleton/metrics_methods"
|
8
10
|
require_relative "service_skeleton/service_name"
|
data/service_skeleton.gemspec
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "service_skeleton"
|
5
5
|
|
6
|
-
s.version = '2.0
|
6
|
+
s.version = '2.2.0'
|
7
7
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
|
@@ -39,7 +39,7 @@ Gem::Specification.new do |s|
|
|
39
39
|
s.add_development_dependency 'rake'
|
40
40
|
s.add_development_dependency 'redcarpet'
|
41
41
|
s.add_development_dependency 'rspec'
|
42
|
-
s.add_development_dependency 'rubocop-discourse', '~>
|
42
|
+
s.add_development_dependency 'rubocop-discourse', '~> 3.4.1'
|
43
43
|
s.add_development_dependency 'simplecov'
|
44
44
|
s.add_development_dependency 'yard'
|
45
45
|
end
|
@@ -106,6 +106,30 @@ describe Ultravisor::Child do
|
|
106
106
|
|
107
107
|
expect { child.cast.to_s }.to_not raise_error
|
108
108
|
end
|
109
|
+
|
110
|
+
context "with keyword arguments" do
|
111
|
+
class Child
|
112
|
+
def run
|
113
|
+
end
|
114
|
+
|
115
|
+
def process
|
116
|
+
process_castcall
|
117
|
+
end
|
118
|
+
|
119
|
+
def kwarg(message:)
|
120
|
+
raise "#{message} was passed"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
let(:args) do
|
125
|
+
{ id: :child, klass: Child, method: :run, enable_castcall: true, access: :unsafe }
|
126
|
+
end
|
127
|
+
|
128
|
+
it "forwards them" do
|
129
|
+
child.cast.kwarg(message: "hey")
|
130
|
+
expect { child.unsafe_instance.process }.to raise_error("hey was passed")
|
131
|
+
end
|
132
|
+
end
|
109
133
|
end
|
110
134
|
end
|
111
135
|
end
|
@@ -10,7 +10,7 @@ describe Ultravisor::Child do
|
|
10
10
|
let(:mock_class) { Class.new.tap { |k| k.class_eval { def run; end } } }
|
11
11
|
|
12
12
|
describe "#restart_delay" do
|
13
|
-
context "
|
13
|
+
context "with default args" do
|
14
14
|
let(:args) { base_args }
|
15
15
|
|
16
16
|
it "returns the default delay" do
|
@@ -77,7 +77,7 @@ describe Ultravisor::Child do
|
|
77
77
|
expect(@thread).to have_received(:join).with(0.05)
|
78
78
|
end
|
79
79
|
|
80
|
-
context "the worker doesn't finish quickly enough" do
|
80
|
+
context "when the worker doesn't finish quickly enough" do
|
81
81
|
before(:each) do
|
82
82
|
allow(mock_instance).to receive(:run) { sleep 15 }
|
83
83
|
end
|
@@ -10,7 +10,7 @@ describe Ultravisor::Child do
|
|
10
10
|
let(:child) { Ultravisor::Child.new(**args) }
|
11
11
|
|
12
12
|
describe "#unsafe_instance" do
|
13
|
-
context "
|
13
|
+
context "with default args" do
|
14
14
|
it "explodes" do
|
15
15
|
expect { child.unsafe_instance }.to raise_error(Ultravisor::ThreadSafetyError)
|
16
16
|
end
|
@@ -69,13 +69,13 @@ describe Ultravisor do
|
|
69
69
|
ultravisor.run
|
70
70
|
end
|
71
71
|
|
72
|
-
context "
|
72
|
+
context "when the child terminates" do
|
73
73
|
before(:each) do
|
74
74
|
allow(ultravisor.instance_variable_get(:@queue)).to receive(:pop).and_return(child, :shutdown)
|
75
75
|
allow(ultravisor).to receive(:sleep)
|
76
76
|
end
|
77
77
|
|
78
|
-
context "
|
78
|
+
context "when in the limits of its restart policy" do
|
79
79
|
it "spawns the child again" do
|
80
80
|
expect(child).to receive(:spawn).exactly(:twice)
|
81
81
|
|
@@ -89,7 +89,7 @@ describe Ultravisor do
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
context "too often for its restart policy" do
|
92
|
+
context "when it terminates too often for its restart policy" do
|
93
93
|
before(:each) do
|
94
94
|
allow(child).to receive(:restart?).and_raise(Ultravisor::BlownRestartPolicyError)
|
95
95
|
allow(logger).to receive(:error)
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: service_skeleton
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Palmer
|
8
8
|
- Sam Saffron
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-12-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: frankenstein
|
@@ -199,14 +199,14 @@ dependencies:
|
|
199
199
|
requirements:
|
200
200
|
- - "~>"
|
201
201
|
- !ruby/object:Gem::Version
|
202
|
-
version:
|
202
|
+
version: 3.4.1
|
203
203
|
type: :development
|
204
204
|
prerelease: false
|
205
205
|
version_requirements: !ruby/object:Gem::Requirement
|
206
206
|
requirements:
|
207
207
|
- - "~>"
|
208
208
|
- !ruby/object:Gem::Version
|
209
|
-
version:
|
209
|
+
version: 3.4.1
|
210
210
|
- !ruby/object:Gem::Dependency
|
211
211
|
name: simplecov
|
212
212
|
requirement: !ruby/object:Gem::Requirement
|
@@ -275,6 +275,8 @@ files:
|
|
275
275
|
- lib/service_skeleton/error.rb
|
276
276
|
- lib/service_skeleton/filtering_logger.rb
|
277
277
|
- lib/service_skeleton/generator.rb
|
278
|
+
- lib/service_skeleton/hurriable_timer.rb
|
279
|
+
- lib/service_skeleton/hurriable_timer_sequence.rb
|
278
280
|
- lib/service_skeleton/logging_helpers.rb
|
279
281
|
- lib/service_skeleton/metric_method_name.rb
|
280
282
|
- lib/service_skeleton/metrics_methods.rb
|
@@ -319,7 +321,7 @@ files:
|
|
319
321
|
homepage: https://github.com/discourse/service_skeleton
|
320
322
|
licenses: []
|
321
323
|
metadata: {}
|
322
|
-
post_install_message:
|
324
|
+
post_install_message:
|
323
325
|
rdoc_options: []
|
324
326
|
require_paths:
|
325
327
|
- lib
|
@@ -334,8 +336,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
334
336
|
- !ruby/object:Gem::Version
|
335
337
|
version: '0'
|
336
338
|
requirements: []
|
337
|
-
rubygems_version: 3.
|
338
|
-
signing_key:
|
339
|
+
rubygems_version: 3.4.10
|
340
|
+
signing_key:
|
339
341
|
specification_version: 4
|
340
342
|
summary: The bare bones of a service
|
341
343
|
test_files: []
|