semian 0.25.2 → 0.26.6
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/lib/semian/configuration_validator.rb +1 -6
- data/lib/semian/instrumentable.rb +11 -9
- data/lib/semian/redis_client.rb +1 -0
- data/lib/semian/simple_integer.rb +22 -6
- data/lib/semian/simple_sliding_window.rb +40 -47
- data/lib/semian/version.rb +1 -1
- data/lib/semian.rb +40 -33
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c745b1aaf79e752138bf6cbfcff3e218022dfbaac8b3f6adba3dd9bd4091bbee
|
|
4
|
+
data.tar.gz: 03e8d192ffd5e7b86aadb3b87ad9846e4d0cc18724d67a14e72c35949a11e3f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: afa858cd11acdf8047f6dd38850419927915ff87b6dfeabd820ffa888b269aafc67415cce0a85543e3d49562b733a01ea8b22fd46c6b5fc24374236a333cf0ef
|
|
7
|
+
data.tar.gz: 9a087827f9abaefa6bdcdda853b3423e9a19fe9e0b6a35c2cea8650266857713ffeea39136560a6ed1f763940ff9f28868bcbb9e95aa84cac91764229cb2dfdb
|
|
@@ -7,12 +7,6 @@ module Semian
|
|
|
7
7
|
@configuration = configuration
|
|
8
8
|
@adapter = configuration[:adapter]
|
|
9
9
|
@force_config_validation = force_config_validation?
|
|
10
|
-
|
|
11
|
-
unless @force_config_validation
|
|
12
|
-
Semian.logger.info(
|
|
13
|
-
"Semian is running in log-mode for configuration validation. This means that Semian will not raise an error if the configuration is invalid. This is not recommended for production environments.\n\n[IMPORTANT] PLEASE UPDATE YOUR CONFIGURATION TO USE `force_config_validation: true` TO ENABLE STRICT CONFIGURATION VALIDATION.\n---\n",
|
|
14
|
-
)
|
|
15
|
-
end
|
|
16
10
|
end
|
|
17
11
|
|
|
18
12
|
def validate!
|
|
@@ -29,6 +23,7 @@ module Semian
|
|
|
29
23
|
end
|
|
30
24
|
|
|
31
25
|
def raise_or_log_validation_required!(message)
|
|
26
|
+
message = "Semian Resource #{@name}: #{message}"
|
|
32
27
|
if @force_config_validation
|
|
33
28
|
raise ArgumentError, message
|
|
34
29
|
else
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent-ruby"
|
|
4
|
+
|
|
3
5
|
module Semian
|
|
4
6
|
module Instrumentable
|
|
7
|
+
SUBSCRIBERS = Concurrent::Map.new
|
|
8
|
+
|
|
5
9
|
def subscribe(name = rand, &block)
|
|
6
|
-
|
|
10
|
+
SUBSCRIBERS[name] = block
|
|
7
11
|
name
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
def unsubscribe(name)
|
|
11
|
-
|
|
15
|
+
SUBSCRIBERS.delete(name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def subscribers
|
|
19
|
+
SUBSCRIBERS
|
|
12
20
|
end
|
|
13
21
|
|
|
14
22
|
# Args:
|
|
@@ -18,13 +26,7 @@ module Semian
|
|
|
18
26
|
# adapter (string)
|
|
19
27
|
# payload (optional)
|
|
20
28
|
def notify(*args)
|
|
21
|
-
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
def subscribers
|
|
27
|
-
@subscribers ||= {}
|
|
29
|
+
SUBSCRIBERS.values.each { |subscriber| subscriber.call(*args) }
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
end
|
data/lib/semian/redis_client.rb
CHANGED
|
@@ -126,6 +126,7 @@ module Semian
|
|
|
126
126
|
|
|
127
127
|
module RedisClientPool
|
|
128
128
|
include RedisClientCommon
|
|
129
|
+
|
|
129
130
|
define_method(:semian_resource, Semian::Adapter.instance_method(:semian_resource))
|
|
130
131
|
define_method(:clear_semian_resource, Semian::Adapter.instance_method(:clear_semian_resource))
|
|
131
132
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thread"
|
|
4
|
+
require "concurrent"
|
|
4
5
|
|
|
5
6
|
module Semian
|
|
6
7
|
module Simple
|
|
@@ -26,14 +27,29 @@ module Semian
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
module ThreadSafe
|
|
29
|
-
class Integer
|
|
30
|
-
def initialize
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
class Integer
|
|
31
|
+
def initialize
|
|
32
|
+
@atom = Concurrent::AtomicFixnum.new(0)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def value
|
|
36
|
+
@atom.value
|
|
33
37
|
end
|
|
34
38
|
|
|
35
|
-
def
|
|
36
|
-
@
|
|
39
|
+
def value=(new_value)
|
|
40
|
+
@atom.value = new_value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def increment(val = 1)
|
|
44
|
+
@atom.increment(val)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def reset
|
|
48
|
+
@atom.value = 0
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def destroy
|
|
52
|
+
reset
|
|
37
53
|
end
|
|
38
54
|
end
|
|
39
55
|
end
|
|
@@ -1,69 +1,62 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thread"
|
|
4
|
+
require "concurrent"
|
|
4
5
|
|
|
5
6
|
module Semian
|
|
6
|
-
module
|
|
7
|
-
|
|
8
|
-
extend Forwardable
|
|
7
|
+
module SlidingWindowBehavior
|
|
8
|
+
extend Forwardable
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
def_delegators :@window, :size, :last, :empty?
|
|
11
|
+
attr_reader :max_size
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
# A sliding window is a structure that stores the most @max_size recent timestamps
|
|
14
|
+
# like this: if @max_size = 4, current time is 10, @window =[5,7,9,10].
|
|
15
|
+
# Another push of (11) at 11 sec would make @window [7,9,10,11], shifting off 5.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
def reject!(&block)
|
|
18
|
+
@window.reject!(&block)
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
def push(value)
|
|
23
|
+
resize_to(@max_size - 1) # make room
|
|
24
|
+
@window << value
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
alias_method :<<, :push
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
alias_method :<<, :push
|
|
29
|
+
def clear
|
|
30
|
+
@window.clear
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
alias_method :destroy, :clear
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def resize_to(size)
|
|
38
|
+
@window = @window.last(size) if @window.size >= size
|
|
39
|
+
end
|
|
40
|
+
end
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
module Simple
|
|
43
|
+
class SlidingWindow # :nodoc:
|
|
44
|
+
include SlidingWindowBehavior
|
|
40
45
|
|
|
41
|
-
def
|
|
42
|
-
@
|
|
46
|
+
def initialize(max_size:)
|
|
47
|
+
@max_size = max_size
|
|
48
|
+
@window = []
|
|
43
49
|
end
|
|
44
50
|
end
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
module ThreadSafe
|
|
48
|
-
class SlidingWindow
|
|
49
|
-
|
|
50
|
-
super
|
|
51
|
-
@lock = Mutex.new
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# #size, #last, and #clear are not wrapped in a mutex. For the first two,
|
|
55
|
-
# the worst-case is a thread-switch at a timing where they'd receive an
|
|
56
|
-
# out-of-date value--which could happen with a mutex as well.
|
|
57
|
-
#
|
|
58
|
-
# As for clear, it's an all or nothing operation. Doesn't matter if we
|
|
59
|
-
# have the lock or not.
|
|
60
|
-
|
|
61
|
-
def reject!(*)
|
|
62
|
-
@lock.synchronize { super }
|
|
63
|
-
end
|
|
54
|
+
class SlidingWindow
|
|
55
|
+
include SlidingWindowBehavior
|
|
64
56
|
|
|
65
|
-
def
|
|
66
|
-
@
|
|
57
|
+
def initialize(max_size:)
|
|
58
|
+
@max_size = max_size
|
|
59
|
+
@window = Concurrent::Array.new
|
|
67
60
|
end
|
|
68
61
|
end
|
|
69
62
|
end
|
data/lib/semian/version.rb
CHANGED
data/lib/semian.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "forwardable"
|
|
|
4
4
|
require "logger"
|
|
5
5
|
require "weakref"
|
|
6
6
|
require "thread"
|
|
7
|
+
require "concurrent-ruby"
|
|
7
8
|
|
|
8
9
|
require "semian/version"
|
|
9
10
|
require "semian/instrumentable"
|
|
@@ -110,6 +111,20 @@ module Semian
|
|
|
110
111
|
self.default_permissions = 0660
|
|
111
112
|
self.default_force_config_validation = false
|
|
112
113
|
|
|
114
|
+
# We only allow disabling thread-safety for parts of the code that are on the hot path.
|
|
115
|
+
# Since locking there could have a significant impact. Everything else is enforced thread safety
|
|
116
|
+
def thread_safe?
|
|
117
|
+
return @thread_safe if defined?(@thread_safe)
|
|
118
|
+
|
|
119
|
+
@thread_safe = true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def thread_safe=(thread_safe)
|
|
123
|
+
@thread_safe = thread_safe
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
@reset_mutex = Mutex.new
|
|
127
|
+
|
|
113
128
|
def issue_disabled_semaphores_warning
|
|
114
129
|
return if defined?(@warning_issued)
|
|
115
130
|
|
|
@@ -199,9 +214,9 @@ module Semian
|
|
|
199
214
|
# If consumer who retrieved / registered by a Semian::Adapter, keep track
|
|
200
215
|
# of who the consumer was so that we can clear the resource reference if needed.
|
|
201
216
|
consumer = args.delete(:consumer)
|
|
202
|
-
if consumer&.class&.include?(Semian::Adapter)
|
|
203
|
-
consumers
|
|
204
|
-
|
|
217
|
+
if consumer&.class&.include?(Semian::Adapter) && !args[:dynamic]
|
|
218
|
+
consumer_set = consumers.compute_if_absent(name) { ObjectSpace::WeakMap.new }
|
|
219
|
+
consumer_set[consumer] = true
|
|
205
220
|
end
|
|
206
221
|
self[name] || register(name, **args)
|
|
207
222
|
end
|
|
@@ -233,14 +248,8 @@ module Semian
|
|
|
233
248
|
resource = resources.delete(name)
|
|
234
249
|
if resource
|
|
235
250
|
resource.bulkhead&.unregister_worker
|
|
236
|
-
consumers_for_resource = consumers.delete(name) ||
|
|
237
|
-
consumers_for_resource.
|
|
238
|
-
if consumer.weakref_alive?
|
|
239
|
-
consumer.clear_semian_resource
|
|
240
|
-
end
|
|
241
|
-
rescue WeakRef::RefError
|
|
242
|
-
next
|
|
243
|
-
end
|
|
251
|
+
consumers_for_resource = consumers.delete(name) || ObjectSpace::WeakMap.new
|
|
252
|
+
consumers_for_resource.each_key(&:clear_semian_resource)
|
|
244
253
|
end
|
|
245
254
|
end
|
|
246
255
|
|
|
@@ -251,29 +260,11 @@ module Semian
|
|
|
251
260
|
end
|
|
252
261
|
end
|
|
253
262
|
|
|
254
|
-
# Retrieves a hash of all registered resources.
|
|
255
|
-
def resources
|
|
256
|
-
@resources ||= LRUHash.new
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
# Retrieves a hash of all registered resource consumers.
|
|
260
|
-
def consumers
|
|
261
|
-
@consumers ||= {}
|
|
262
|
-
end
|
|
263
|
-
|
|
264
263
|
def reset!
|
|
265
|
-
@
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
def thread_safe?
|
|
270
|
-
return @thread_safe if defined?(@thread_safe)
|
|
271
|
-
|
|
272
|
-
@thread_safe = true
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def thread_safe=(thread_safe)
|
|
276
|
-
@thread_safe = thread_safe
|
|
264
|
+
@reset_mutex.synchronize do
|
|
265
|
+
@consumers = Concurrent::Map.new
|
|
266
|
+
@resources = LRUHash.new
|
|
267
|
+
end
|
|
277
268
|
end
|
|
278
269
|
|
|
279
270
|
THREAD_BULKHEAD_DISABLED_VAR = :semian_bulkheads_disabled
|
|
@@ -291,6 +282,22 @@ module Semian
|
|
|
291
282
|
thread.thread_variable_set(THREAD_BULKHEAD_DISABLED_VAR, old_value)
|
|
292
283
|
end
|
|
293
284
|
|
|
285
|
+
def resources
|
|
286
|
+
return @resources if defined?(@resources) && @resources
|
|
287
|
+
|
|
288
|
+
@reset_mutex.synchronize do
|
|
289
|
+
@resources ||= LRUHash.new
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def consumers
|
|
294
|
+
return @consumers if defined?(@consumers) && @consumers
|
|
295
|
+
|
|
296
|
+
@reset_mutex.synchronize do
|
|
297
|
+
@consumers ||= Concurrent::Map.new
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
294
301
|
private
|
|
295
302
|
|
|
296
303
|
def create_circuit_breaker(name, **options)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: semian
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.26.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Scott Francis
|
|
@@ -10,7 +10,21 @@ authors:
|
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
12
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
|
-
dependencies:
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: concurrent-ruby
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - ">="
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '0'
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - ">="
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '0'
|
|
14
28
|
description: |2
|
|
15
29
|
A Ruby C extention that is used to control access to shared resources
|
|
16
30
|
across process boundaries with SysV semaphores.
|
|
@@ -78,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
78
92
|
- !ruby/object:Gem::Version
|
|
79
93
|
version: '0'
|
|
80
94
|
requirements: []
|
|
81
|
-
rubygems_version: 3.7.
|
|
95
|
+
rubygems_version: 3.7.2
|
|
82
96
|
specification_version: 4
|
|
83
97
|
summary: Bulkheading for Ruby with SysV semaphores
|
|
84
98
|
test_files: []
|