semian 0.11.9 → 0.13.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.md +239 -0
- data/LICENSE.md +21 -0
- data/README.md +836 -0
- data/ext/semian/extconf.rb +21 -19
- data/lib/semian/adapter.rb +8 -4
- data/lib/semian/circuit_breaker.rb +16 -10
- data/lib/semian/grpc.rb +32 -10
- data/lib/semian/instrumentable.rb +2 -0
- data/lib/semian/lru_hash.rb +15 -14
- data/lib/semian/mysql2.rb +18 -9
- data/lib/semian/net_http.rb +10 -4
- data/lib/semian/platform.rb +3 -1
- data/lib/semian/protected_resource.rb +5 -3
- data/lib/semian/rails.rb +12 -6
- data/lib/semian/redis.rb +16 -14
- data/lib/semian/redis_client.rb +113 -0
- data/lib/semian/resource.rb +5 -3
- data/lib/semian/simple_integer.rb +4 -2
- data/lib/semian/simple_sliding_window.rb +5 -3
- data/lib/semian/simple_state.rb +3 -1
- data/lib/semian/unprotected_resource.rb +2 -0
- data/lib/semian/version.rb +3 -1
- data/lib/semian.rb +66 -45
- metadata +12 -187
data/lib/semian/resource.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Semian
|
2
|
-
class Resource
|
3
|
-
attr_reader :
|
4
|
+
class Resource # :nodoc:
|
5
|
+
attr_reader :name
|
4
6
|
|
5
7
|
class << Semian::Resource
|
6
8
|
# Ensure that there can only be one resource of a given type
|
@@ -55,7 +57,7 @@ module Semian
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def key
|
58
|
-
|
60
|
+
"0x00000000"
|
59
61
|
end
|
60
62
|
|
61
63
|
def in_use?
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thread"
|
2
4
|
|
3
5
|
module Semian
|
4
6
|
module Simple
|
5
|
-
class SlidingWindow
|
7
|
+
class SlidingWindow # :nodoc:
|
6
8
|
extend Forwardable
|
7
9
|
|
8
|
-
def_delegators :@window, :size, :last
|
10
|
+
def_delegators :@window, :size, :last, :empty?
|
9
11
|
attr_reader :max_size
|
10
12
|
|
11
13
|
# A sliding window is a structure that stores the most @max_size recent timestamps
|
data/lib/semian/simple_state.rb
CHANGED
data/lib/semian/version.rb
CHANGED
data/lib/semian.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
|
6
|
-
require
|
7
|
-
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
require "logger"
|
5
|
+
require "weakref"
|
6
|
+
require "thread"
|
7
|
+
|
8
|
+
require "semian/version"
|
9
|
+
require "semian/instrumentable"
|
10
|
+
require "semian/platform"
|
11
|
+
require "semian/resource"
|
12
|
+
require "semian/circuit_breaker"
|
13
|
+
require "semian/protected_resource"
|
14
|
+
require "semian/unprotected_resource"
|
15
|
+
require "semian/simple_sliding_window"
|
16
|
+
require "semian/simple_integer"
|
17
|
+
require "semian/simple_state"
|
18
|
+
require "semian/lru_hash"
|
17
19
|
|
18
20
|
#
|
19
21
|
# === Overview
|
@@ -56,9 +58,17 @@ require 'semian/lru_hash'
|
|
56
58
|
#
|
57
59
|
# ===== Registering a resource
|
58
60
|
#
|
59
|
-
# Semian.register(
|
61
|
+
# Semian.register(
|
62
|
+
# :mysql_shard0,
|
63
|
+
# tickets: 10,
|
64
|
+
# timeout: 0.5,
|
65
|
+
# error_threshold: 3,
|
66
|
+
# error_timeout: 10,
|
67
|
+
# success_threshold: 2,
|
68
|
+
# )
|
60
69
|
#
|
61
|
-
# This registers a new resource called <code>:mysql_shard0</code> that has 10 tickets and
|
70
|
+
# This registers a new resource called <code>:mysql_shard0</code> that has 10 tickets and
|
71
|
+
# a default timeout of 500 milliseconds.
|
62
72
|
#
|
63
73
|
# After 3 failures in the span of 10 seconds the circuit will be open.
|
64
74
|
# After an additional 10 seconds it will transition to half-open.
|
@@ -70,8 +80,8 @@ require 'semian/lru_hash'
|
|
70
80
|
# # Perform a MySQL query here
|
71
81
|
# end
|
72
82
|
#
|
73
|
-
# This acquires a ticket for the <code>:mysql_shard0</code> resource. If we use the example above,
|
74
|
-
# be lowered to 9 when block is executed, then raised to 10 when the block completes.
|
83
|
+
# This acquires a ticket for the <code>:mysql_shard0</code> resource. If we use the example above,
|
84
|
+
# the ticket count would be lowered to 9 when block is executed, then raised to 10 when the block completes.
|
75
85
|
#
|
76
86
|
# ===== Overriding the default timeout
|
77
87
|
#
|
@@ -79,7 +89,8 @@ require 'semian/lru_hash'
|
|
79
89
|
# # Perform a MySQL query here
|
80
90
|
# end
|
81
91
|
#
|
82
|
-
# This is the same as the previous example, but overrides the timeout
|
92
|
+
# This is the same as the previous example, but overrides the timeout
|
93
|
+
# from the default value of 500 milliseconds to 1 second.
|
83
94
|
module Semian
|
84
95
|
extend self
|
85
96
|
extend Instrumentable
|
@@ -91,12 +102,14 @@ module Semian
|
|
91
102
|
OpenCircuitError = Class.new(BaseError)
|
92
103
|
|
93
104
|
attr_accessor :maximum_lru_size, :minimum_lru_time, :default_permissions, :namespace
|
105
|
+
|
94
106
|
self.maximum_lru_size = 500
|
95
107
|
self.minimum_lru_time = 300
|
96
108
|
self.default_permissions = 0660
|
97
109
|
|
98
110
|
def issue_disabled_semaphores_warning
|
99
111
|
return if defined?(@warning_issued)
|
112
|
+
|
100
113
|
@warning_issued = true
|
101
114
|
if !sysv_semaphores_supported?
|
102
115
|
logger.info("Semian sysv semaphores are not supported on #{RUBY_PLATFORM} - all operations will no-op")
|
@@ -119,7 +132,7 @@ module Semian
|
|
119
132
|
|
120
133
|
attr_accessor :logger
|
121
134
|
|
122
|
-
self.logger = Logger.new(
|
135
|
+
self.logger = Logger.new($stderr)
|
123
136
|
|
124
137
|
# Registers a resource.
|
125
138
|
#
|
@@ -161,7 +174,7 @@ module Semian
|
|
161
174
|
bulkhead = create_bulkhead(name, **options)
|
162
175
|
|
163
176
|
if circuit_breaker.nil? && bulkhead.nil?
|
164
|
-
raise ArgumentError,
|
177
|
+
raise ArgumentError, "Both bulkhead and circuitbreaker cannot be disabled."
|
165
178
|
end
|
166
179
|
|
167
180
|
resources[name] = ProtectedResource.new(name, bulkhead, circuit_breaker)
|
@@ -170,11 +183,10 @@ module Semian
|
|
170
183
|
def retrieve_or_register(name, **args)
|
171
184
|
# If consumer who retrieved / registered by a Semian::Adapter, keep track
|
172
185
|
# of who the consumer was so that we can clear the resource reference if needed.
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
end
|
186
|
+
consumer = args.delete(:consumer)
|
187
|
+
if consumer&.class&.include?(Semian::Adapter)
|
188
|
+
consumers[name] ||= []
|
189
|
+
consumers[name] << WeakRef.new(consumer)
|
178
190
|
end
|
179
191
|
self[name] || register(name, **args)
|
180
192
|
end
|
@@ -185,9 +197,13 @@ module Semian
|
|
185
197
|
end
|
186
198
|
|
187
199
|
def destroy(name)
|
188
|
-
|
189
|
-
|
190
|
-
|
200
|
+
resource = resources.delete(name)
|
201
|
+
resource&.destroy
|
202
|
+
end
|
203
|
+
|
204
|
+
def destroy_all_resources
|
205
|
+
resources.values.each(&:destroy)
|
206
|
+
resources.clear
|
191
207
|
end
|
192
208
|
|
193
209
|
# Unregister will not destroy the semian resource, but it will
|
@@ -199,17 +215,16 @@ module Semian
|
|
199
215
|
# Also clears any semian_resources
|
200
216
|
# in use by any semian adapters if the weak reference is still alive.
|
201
217
|
def unregister(name)
|
202
|
-
|
203
|
-
|
218
|
+
resource = resources.delete(name)
|
219
|
+
if resource
|
220
|
+
resource.bulkhead&.unregister_worker
|
204
221
|
consumers_for_resource = consumers.delete(name) || []
|
205
222
|
consumers_for_resource.each do |consumer|
|
206
|
-
|
207
|
-
|
208
|
-
consumer.clear_semian_resource
|
209
|
-
end
|
210
|
-
rescue WeakRef::RefError
|
211
|
-
next
|
223
|
+
if consumer.weakref_alive?
|
224
|
+
consumer.clear_semian_resource
|
212
225
|
end
|
226
|
+
rescue WeakRef::RefError
|
227
|
+
next
|
213
228
|
end
|
214
229
|
end
|
215
230
|
end
|
@@ -238,6 +253,7 @@ module Semian
|
|
238
253
|
|
239
254
|
def thread_safe?
|
240
255
|
return @thread_safe if defined?(@thread_safe)
|
256
|
+
|
241
257
|
@thread_safe = true
|
242
258
|
end
|
243
259
|
|
@@ -250,6 +266,7 @@ module Semian
|
|
250
266
|
def create_circuit_breaker(name, **options)
|
251
267
|
circuit_breaker = options.fetch(:circuit_breaker, true)
|
252
268
|
return unless circuit_breaker
|
269
|
+
|
253
270
|
require_keys!([:success_threshold, :error_threshold, :error_timeout], options)
|
254
271
|
|
255
272
|
exceptions = options[:exceptions] || []
|
@@ -272,8 +289,8 @@ module Semian
|
|
272
289
|
unless options[:thread_safety_disabled].nil?
|
273
290
|
logger.info(
|
274
291
|
"NOTE: thread_safety_disabled will be replaced by a global setting" \
|
275
|
-
|
276
|
-
|
292
|
+
"Semian is thread safe by default. It is possible" \
|
293
|
+
"to modify the value by using Semian.thread_safe=",
|
277
294
|
)
|
278
295
|
end
|
279
296
|
|
@@ -287,7 +304,11 @@ module Semian
|
|
287
304
|
|
288
305
|
permissions = options[:permissions] || default_permissions
|
289
306
|
timeout = options[:timeout] || 0
|
290
|
-
Resource.new(name,
|
307
|
+
::Semian::Resource.new(name,
|
308
|
+
tickets: options[:tickets],
|
309
|
+
quota: options[:quota],
|
310
|
+
permissions: permissions,
|
311
|
+
timeout: timeout)
|
291
312
|
end
|
292
313
|
|
293
314
|
def require_keys!(required, options)
|
@@ -299,13 +320,13 @@ module Semian
|
|
299
320
|
end
|
300
321
|
|
301
322
|
if Semian.semaphores_enabled?
|
302
|
-
require
|
323
|
+
require "semian/semian"
|
303
324
|
else
|
304
325
|
Semian::MAX_TICKETS = 0
|
305
326
|
end
|
306
327
|
|
307
328
|
if defined? ActiveSupport
|
308
|
-
ActiveSupport.on_load
|
309
|
-
require
|
329
|
+
ActiveSupport.on_load(:active_record) do
|
330
|
+
require "semian/rails"
|
310
331
|
end
|
311
332
|
end
|
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.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Francis
|
@@ -10,199 +10,20 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-
|
14
|
-
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
name: rake-compiler
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
18
|
-
requirements:
|
19
|
-
- - ">="
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :development
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
requirements:
|
26
|
-
- - ">="
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
version: '0'
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: rake
|
31
|
-
requirement: !ruby/object:Gem::Requirement
|
32
|
-
requirements:
|
33
|
-
- - ">="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: '0'
|
36
|
-
type: :development
|
37
|
-
prerelease: false
|
38
|
-
version_requirements: !ruby/object:Gem::Requirement
|
39
|
-
requirements:
|
40
|
-
- - ">="
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: '0'
|
43
|
-
- !ruby/object:Gem::Dependency
|
44
|
-
name: timecop
|
45
|
-
requirement: !ruby/object:Gem::Requirement
|
46
|
-
requirements:
|
47
|
-
- - ">="
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '0'
|
50
|
-
type: :development
|
51
|
-
prerelease: false
|
52
|
-
version_requirements: !ruby/object:Gem::Requirement
|
53
|
-
requirements:
|
54
|
-
- - ">="
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: '0'
|
57
|
-
- !ruby/object:Gem::Dependency
|
58
|
-
name: minitest
|
59
|
-
requirement: !ruby/object:Gem::Requirement
|
60
|
-
requirements:
|
61
|
-
- - ">="
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
version: '0'
|
64
|
-
type: :development
|
65
|
-
prerelease: false
|
66
|
-
version_requirements: !ruby/object:Gem::Requirement
|
67
|
-
requirements:
|
68
|
-
- - ">="
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
version: '0'
|
71
|
-
- !ruby/object:Gem::Dependency
|
72
|
-
name: pry-byebug
|
73
|
-
requirement: !ruby/object:Gem::Requirement
|
74
|
-
requirements:
|
75
|
-
- - ">="
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '0'
|
78
|
-
type: :development
|
79
|
-
prerelease: false
|
80
|
-
version_requirements: !ruby/object:Gem::Requirement
|
81
|
-
requirements:
|
82
|
-
- - ">="
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
version: '0'
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: mysql2
|
87
|
-
requirement: !ruby/object:Gem::Requirement
|
88
|
-
requirements:
|
89
|
-
- - ">="
|
90
|
-
- !ruby/object:Gem::Version
|
91
|
-
version: '0'
|
92
|
-
type: :development
|
93
|
-
prerelease: false
|
94
|
-
version_requirements: !ruby/object:Gem::Requirement
|
95
|
-
requirements:
|
96
|
-
- - ">="
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
version: '0'
|
99
|
-
- !ruby/object:Gem::Dependency
|
100
|
-
name: redis
|
101
|
-
requirement: !ruby/object:Gem::Requirement
|
102
|
-
requirements:
|
103
|
-
- - ">="
|
104
|
-
- !ruby/object:Gem::Version
|
105
|
-
version: '0'
|
106
|
-
type: :development
|
107
|
-
prerelease: false
|
108
|
-
version_requirements: !ruby/object:Gem::Requirement
|
109
|
-
requirements:
|
110
|
-
- - ">="
|
111
|
-
- !ruby/object:Gem::Version
|
112
|
-
version: '0'
|
113
|
-
- !ruby/object:Gem::Dependency
|
114
|
-
name: webrick
|
115
|
-
requirement: !ruby/object:Gem::Requirement
|
116
|
-
requirements:
|
117
|
-
- - ">="
|
118
|
-
- !ruby/object:Gem::Version
|
119
|
-
version: '0'
|
120
|
-
type: :development
|
121
|
-
prerelease: false
|
122
|
-
version_requirements: !ruby/object:Gem::Requirement
|
123
|
-
requirements:
|
124
|
-
- - ">="
|
125
|
-
- !ruby/object:Gem::Version
|
126
|
-
version: '0'
|
127
|
-
- !ruby/object:Gem::Dependency
|
128
|
-
name: toxiproxy
|
129
|
-
requirement: !ruby/object:Gem::Requirement
|
130
|
-
requirements:
|
131
|
-
- - "~>"
|
132
|
-
- !ruby/object:Gem::Version
|
133
|
-
version: 1.0.0
|
134
|
-
type: :development
|
135
|
-
prerelease: false
|
136
|
-
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
requirements:
|
138
|
-
- - "~>"
|
139
|
-
- !ruby/object:Gem::Version
|
140
|
-
version: 1.0.0
|
141
|
-
- !ruby/object:Gem::Dependency
|
142
|
-
name: grpc
|
143
|
-
requirement: !ruby/object:Gem::Requirement
|
144
|
-
requirements:
|
145
|
-
- - ">="
|
146
|
-
- !ruby/object:Gem::Version
|
147
|
-
version: '0'
|
148
|
-
type: :development
|
149
|
-
prerelease: false
|
150
|
-
version_requirements: !ruby/object:Gem::Requirement
|
151
|
-
requirements:
|
152
|
-
- - ">="
|
153
|
-
- !ruby/object:Gem::Version
|
154
|
-
version: '0'
|
155
|
-
- !ruby/object:Gem::Dependency
|
156
|
-
name: mocha
|
157
|
-
requirement: !ruby/object:Gem::Requirement
|
158
|
-
requirements:
|
159
|
-
- - ">="
|
160
|
-
- !ruby/object:Gem::Version
|
161
|
-
version: '0'
|
162
|
-
type: :development
|
163
|
-
prerelease: false
|
164
|
-
version_requirements: !ruby/object:Gem::Requirement
|
165
|
-
requirements:
|
166
|
-
- - ">="
|
167
|
-
- !ruby/object:Gem::Version
|
168
|
-
version: '0'
|
169
|
-
- !ruby/object:Gem::Dependency
|
170
|
-
name: memory_profiler
|
171
|
-
requirement: !ruby/object:Gem::Requirement
|
172
|
-
requirements:
|
173
|
-
- - ">="
|
174
|
-
- !ruby/object:Gem::Version
|
175
|
-
version: '0'
|
176
|
-
type: :development
|
177
|
-
prerelease: false
|
178
|
-
version_requirements: !ruby/object:Gem::Requirement
|
179
|
-
requirements:
|
180
|
-
- - ">="
|
181
|
-
- !ruby/object:Gem::Version
|
182
|
-
version: '0'
|
183
|
-
- !ruby/object:Gem::Dependency
|
184
|
-
name: benchmark-memory
|
185
|
-
requirement: !ruby/object:Gem::Requirement
|
186
|
-
requirements:
|
187
|
-
- - ">="
|
188
|
-
- !ruby/object:Gem::Version
|
189
|
-
version: '0'
|
190
|
-
type: :development
|
191
|
-
prerelease: false
|
192
|
-
version_requirements: !ruby/object:Gem::Requirement
|
193
|
-
requirements:
|
194
|
-
- - ">="
|
195
|
-
- !ruby/object:Gem::Version
|
196
|
-
version: '0'
|
13
|
+
date: 2022-08-16 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
197
15
|
description: |2
|
198
16
|
A Ruby C extention that is used to control access to shared resources
|
199
17
|
across process boundaries with SysV semaphores.
|
200
|
-
email:
|
18
|
+
email: opensource@shopify.com
|
201
19
|
executables: []
|
202
20
|
extensions:
|
203
21
|
- ext/semian/extconf.rb
|
204
22
|
extra_rdoc_files: []
|
205
23
|
files:
|
24
|
+
- CHANGELOG.md
|
25
|
+
- LICENSE.md
|
26
|
+
- README.md
|
206
27
|
- ext/semian/extconf.rb
|
207
28
|
- ext/semian/resource.c
|
208
29
|
- ext/semian/resource.h
|
@@ -225,6 +46,7 @@ files:
|
|
225
46
|
- lib/semian/protected_resource.rb
|
226
47
|
- lib/semian/rails.rb
|
227
48
|
- lib/semian/redis.rb
|
49
|
+
- lib/semian/redis_client.rb
|
228
50
|
- lib/semian/resource.rb
|
229
51
|
- lib/semian/simple_integer.rb
|
230
52
|
- lib/semian/simple_sliding_window.rb
|
@@ -236,6 +58,9 @@ licenses:
|
|
236
58
|
- MIT
|
237
59
|
metadata:
|
238
60
|
allowed_push_host: https://rubygems.org
|
61
|
+
bug_tracker_uri: https://github.com/Shopify/semian/issues
|
62
|
+
changelog_uri: https://github.com/Shopify/semian/blob/master/CHANGELOG.md
|
63
|
+
source_code_uri: https://github.com/Shopify/semian
|
239
64
|
post_install_message:
|
240
65
|
rdoc_options: []
|
241
66
|
require_paths:
|
@@ -251,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
251
76
|
- !ruby/object:Gem::Version
|
252
77
|
version: '0'
|
253
78
|
requirements: []
|
254
|
-
rubygems_version: 3.
|
79
|
+
rubygems_version: 3.3.3
|
255
80
|
signing_key:
|
256
81
|
specification_version: 4
|
257
82
|
summary: Bulkheading for Ruby with SysV semaphores
|