semian_extension 0.11.4.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 +7 -0
- data/ext/semian/extconf.rb +33 -0
- data/ext/semian/resource.c +394 -0
- data/ext/semian/resource.h +133 -0
- data/ext/semian/semian.c +72 -0
- data/ext/semian/semian.h +14 -0
- data/ext/semian/sysv_semaphores.c +271 -0
- data/ext/semian/sysv_semaphores.h +122 -0
- data/ext/semian/tickets.c +76 -0
- data/ext/semian/tickets.h +13 -0
- data/ext/semian/types.h +41 -0
- data/lib/semian/adapter.rb +75 -0
- data/lib/semian/circuit_breaker.rb +167 -0
- data/lib/semian/grpc.rb +104 -0
- data/lib/semian/instrumentable.rb +28 -0
- data/lib/semian/lru_hash.rb +174 -0
- data/lib/semian/mysql2.rb +135 -0
- data/lib/semian/net_http.rb +117 -0
- data/lib/semian/platform.rb +16 -0
- data/lib/semian/protected_resource.rb +65 -0
- data/lib/semian/rails.rb +7 -0
- data/lib/semian/redis.rb +143 -0
- data/lib/semian/resource.rb +65 -0
- data/lib/semian/simple_integer.rb +38 -0
- data/lib/semian/simple_sliding_window.rb +68 -0
- data/lib/semian/simple_state.rb +50 -0
- data/lib/semian/typhoeus.rb +103 -0
- data/lib/semian/unprotected_resource.rb +73 -0
- data/lib/semian/version.rb +3 -0
- data/lib/semian.rb +310 -0
- metadata +260 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
module Semian
|
2
|
+
# This class acts as a replacement for `ProtectedResource` when
|
3
|
+
# the semian configuration of an `Adapter` is missing or explicitly disabled
|
4
|
+
class UnprotectedResource
|
5
|
+
attr_reader :name
|
6
|
+
attr_accessor :updated_at
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@updated_at = Time.now
|
11
|
+
end
|
12
|
+
|
13
|
+
def registered_workers
|
14
|
+
0
|
15
|
+
end
|
16
|
+
|
17
|
+
def tickets
|
18
|
+
-1
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy
|
22
|
+
end
|
23
|
+
|
24
|
+
def acquire(*)
|
25
|
+
yield self
|
26
|
+
end
|
27
|
+
|
28
|
+
def count
|
29
|
+
0
|
30
|
+
end
|
31
|
+
|
32
|
+
def semid
|
33
|
+
0
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset
|
37
|
+
end
|
38
|
+
|
39
|
+
def open?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def closed?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def half_open?
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def request_allowed?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def mark_failed(_error)
|
56
|
+
end
|
57
|
+
|
58
|
+
def mark_success
|
59
|
+
end
|
60
|
+
|
61
|
+
def bulkhead
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def circuit_breaker
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def in_use?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/semian.rb
ADDED
@@ -0,0 +1,310 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'logger'
|
3
|
+
require 'weakref'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
require 'semian/version'
|
7
|
+
require 'semian/instrumentable'
|
8
|
+
require 'semian/platform'
|
9
|
+
require 'semian/resource'
|
10
|
+
require 'semian/circuit_breaker'
|
11
|
+
require 'semian/protected_resource'
|
12
|
+
require 'semian/unprotected_resource'
|
13
|
+
require 'semian/simple_sliding_window'
|
14
|
+
require 'semian/simple_integer'
|
15
|
+
require 'semian/simple_state'
|
16
|
+
require 'semian/lru_hash'
|
17
|
+
|
18
|
+
#
|
19
|
+
# === Overview
|
20
|
+
#
|
21
|
+
# Semian is a library that can be used to control access to external services.
|
22
|
+
#
|
23
|
+
# It's desirable to control access to external services so that in the case that one
|
24
|
+
# is slow or not responding, the performance of an entire system is not compromised.
|
25
|
+
#
|
26
|
+
# Semian uses the concept of a "resource" as an identifier that controls access
|
27
|
+
# to some external service. So for example, "mysql" or "redis" would be considered
|
28
|
+
# resources. If a system is sharded, like a database, you would typically create
|
29
|
+
# a resource for every shard.
|
30
|
+
#
|
31
|
+
# Resources are visible across an IPC namespace. This means that you can register a
|
32
|
+
# resource in one process and access it from another. This is useful in application
|
33
|
+
# servers like Unicorn that are multi-process. A resource is persistent. It will
|
34
|
+
# continue to exist even after the application exits, and will only be destroyed by
|
35
|
+
# manually removing it with the <code>ipcrm</code> command, calling Resource.destroy,
|
36
|
+
# or rebooting the machine.
|
37
|
+
#
|
38
|
+
# Each resource has a configurable number of tickets. Tickets are what controls
|
39
|
+
# access to the external service. If a client does not have a ticket, it cannot access
|
40
|
+
# a service. If there are no tickets available, the client will block for a configurable
|
41
|
+
# amount of time until a ticket is available. If there are no tickets available after
|
42
|
+
# the timeout period has elapsed, the client will be unable to access the service and
|
43
|
+
# an error will be raised.
|
44
|
+
#
|
45
|
+
# Resources also integrate a circuit breaker in order to fail faster and to let the
|
46
|
+
# resource the time to recover. If `error_threshold` errors happen in the span of `error_timeout`
|
47
|
+
# then the circuit will be opened and every attempt to acquire the resource will immediately fail.
|
48
|
+
#
|
49
|
+
# Once in open state, after `error_timeout` is elapsed, the circuit will transition in the half-open state.
|
50
|
+
# In that state a single error will fully re-open the circuit, and the circuit will transition back to the closed
|
51
|
+
# state only after the resource is acquired `success_threshold` consecutive times.
|
52
|
+
#
|
53
|
+
# A resource is registered by using the Semian.register method.
|
54
|
+
#
|
55
|
+
# ==== Examples
|
56
|
+
#
|
57
|
+
# ===== Registering a resource
|
58
|
+
#
|
59
|
+
# Semian.register(:mysql_shard0, tickets: 10, timeout: 0.5, error_threshold: 3, error_timeout: 10, success_threshold: 2)
|
60
|
+
#
|
61
|
+
# This registers a new resource called <code>:mysql_shard0</code> that has 10 tickets and a default timeout of 500 milliseconds.
|
62
|
+
#
|
63
|
+
# After 3 failures in the span of 10 seconds the circuit will be open.
|
64
|
+
# After an additional 10 seconds it will transition to half-open.
|
65
|
+
# And finally after 2 successful acquisitions of the resource it will transition back to the closed state.
|
66
|
+
#
|
67
|
+
# ===== Using a resource
|
68
|
+
#
|
69
|
+
# Semian[:mysql_shard0].acquire do
|
70
|
+
# # Perform a MySQL query here
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# This acquires a ticket for the <code>:mysql_shard0</code> resource. If we use the example above, the ticket count would
|
74
|
+
# be lowered to 9 when block is executed, then raised to 10 when the block completes.
|
75
|
+
#
|
76
|
+
# ===== Overriding the default timeout
|
77
|
+
#
|
78
|
+
# Semian[:mysql_shard0].acquire(timeout: 1) do
|
79
|
+
# # Perform a MySQL query here
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# This is the same as the previous example, but overrides the timeout from the default value of 500 milliseconds to 1 second.
|
83
|
+
module Semian
|
84
|
+
extend self
|
85
|
+
extend Instrumentable
|
86
|
+
|
87
|
+
BaseError = Class.new(StandardError)
|
88
|
+
SyscallError = Class.new(BaseError)
|
89
|
+
TimeoutError = Class.new(BaseError)
|
90
|
+
InternalError = Class.new(BaseError)
|
91
|
+
OpenCircuitError = Class.new(BaseError)
|
92
|
+
|
93
|
+
attr_accessor :maximum_lru_size, :minimum_lru_time, :default_permissions, :namespace
|
94
|
+
self.maximum_lru_size = 500
|
95
|
+
self.minimum_lru_time = 300
|
96
|
+
self.default_permissions = 0660
|
97
|
+
|
98
|
+
def issue_disabled_semaphores_warning
|
99
|
+
return if defined?(@warning_issued)
|
100
|
+
@warning_issued = true
|
101
|
+
if !sysv_semaphores_supported?
|
102
|
+
logger.info("Semian sysv semaphores are not supported on #{RUBY_PLATFORM} - all operations will no-op")
|
103
|
+
elsif disabled?
|
104
|
+
logger.info("Semian semaphores are disabled, is this what you really want? - all operations will no-op")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module AdapterError
|
109
|
+
attr_accessor :semian_identifier
|
110
|
+
|
111
|
+
def to_s
|
112
|
+
if @semian_identifier
|
113
|
+
"[#{@semian_identifier}] #{super}"
|
114
|
+
else
|
115
|
+
super
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
attr_accessor :logger
|
121
|
+
|
122
|
+
self.logger = Logger.new(STDERR)
|
123
|
+
|
124
|
+
# Registers a resource.
|
125
|
+
#
|
126
|
+
# +name+: Name of the resource - this can be either a string or symbol. (required)
|
127
|
+
#
|
128
|
+
# +circuit_breaker+: The boolean if you want a circuit breaker acquired for your resource. Default true.
|
129
|
+
#
|
130
|
+
# +bulkhead+: The boolean if you want a bulkhead to be acquired for your resource. Default true.
|
131
|
+
#
|
132
|
+
# +tickets+: Number of tickets. If this value is 0, the ticket count will not be set,
|
133
|
+
# but the resource must have been previously registered otherwise an error will be raised.
|
134
|
+
# Mutually exclusive with the 'quota' argument.
|
135
|
+
#
|
136
|
+
# +quota+: Calculate tickets as a ratio of the number of registered workers.
|
137
|
+
# Must be greater than 0, less than or equal to 1. There will always be at least 1 ticket, as it
|
138
|
+
# is calculated as (workers * quota).ceil
|
139
|
+
# Mutually exclusive with the 'ticket' argument.
|
140
|
+
# but the resource must have been previously registered otherwise an error will be raised. (bulkhead)
|
141
|
+
#
|
142
|
+
# +permissions+: Octal permissions of the resource. Default to +Semian.default_permissions+ (0660). (bulkhead)
|
143
|
+
#
|
144
|
+
# +timeout+: Default timeout in seconds. Default 0. (bulkhead)
|
145
|
+
#
|
146
|
+
# +error_threshold+: The amount of errors that must happen within error_timeout amount of time to open
|
147
|
+
# the circuit. (circuit breaker required)
|
148
|
+
#
|
149
|
+
# +error_timeout+: The duration in seconds since the last error after which the error count is reset to 0.
|
150
|
+
# (circuit breaker required)
|
151
|
+
#
|
152
|
+
# +success_threshold+: The number of consecutive success after which an half-open circuit will be fully closed.
|
153
|
+
# (circuit breaker required)
|
154
|
+
#
|
155
|
+
# +exceptions+: An array of exception classes that should be accounted as resource errors. Default [].
|
156
|
+
# (circuit breaker)
|
157
|
+
#
|
158
|
+
# Returns the registered resource.
|
159
|
+
def register(name, **options)
|
160
|
+
circuit_breaker = create_circuit_breaker(name, **options)
|
161
|
+
bulkhead = create_bulkhead(name, **options)
|
162
|
+
|
163
|
+
if circuit_breaker.nil? && bulkhead.nil?
|
164
|
+
raise ArgumentError, 'Both bulkhead and circuitbreaker cannot be disabled.'
|
165
|
+
end
|
166
|
+
|
167
|
+
resources[name] = ProtectedResource.new(name, bulkhead, circuit_breaker)
|
168
|
+
end
|
169
|
+
|
170
|
+
def retrieve_or_register(name, **args)
|
171
|
+
# If consumer who retrieved / registered by a Semian::Adapter, keep track
|
172
|
+
# of who the consumer was so that we can clear the resource reference if needed.
|
173
|
+
if consumer = args.delete(:consumer)
|
174
|
+
if consumer.class.include?(Semian::Adapter)
|
175
|
+
consumers[name] ||= []
|
176
|
+
consumers[name] << WeakRef.new(consumer)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
self[name] || register(name, **args)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Retrieves a resource by name.
|
183
|
+
def [](name)
|
184
|
+
resources[name]
|
185
|
+
end
|
186
|
+
|
187
|
+
def destroy(name)
|
188
|
+
if resource = resources.delete(name)
|
189
|
+
resource.destroy
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Unregister will not destroy the semian resource, but it will
|
194
|
+
# remove it from the hash of registered resources, and decrease
|
195
|
+
# the number of registered workers.
|
196
|
+
# Semian.destroy removes the underlying resource, but
|
197
|
+
# Semian.unregister will remove all references, while preserving
|
198
|
+
# the underlying semian resource (and sysV semaphore).
|
199
|
+
# Also clears any semian_resources
|
200
|
+
# in use by any semian adapters if the weak reference is still alive.
|
201
|
+
def unregister(name)
|
202
|
+
if resource = resources.delete(name)
|
203
|
+
resource.bulkhead.unregister_worker if resource.bulkhead
|
204
|
+
consumers_for_resource = consumers.delete(name) || []
|
205
|
+
consumers_for_resource.each do |consumer|
|
206
|
+
begin
|
207
|
+
if consumer.weakref_alive?
|
208
|
+
consumer.clear_semian_resource
|
209
|
+
end
|
210
|
+
rescue WeakRef::RefError
|
211
|
+
next
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Unregisters all resources
|
218
|
+
def unregister_all_resources
|
219
|
+
resources.keys.each do |resource|
|
220
|
+
unregister(resource)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Retrieves a hash of all registered resources.
|
225
|
+
def resources
|
226
|
+
@resources ||= LRUHash.new
|
227
|
+
end
|
228
|
+
|
229
|
+
# Retrieves a hash of all registered resource consumers.
|
230
|
+
def consumers
|
231
|
+
@consumers ||= {}
|
232
|
+
end
|
233
|
+
|
234
|
+
def reset!
|
235
|
+
@consumers = {}
|
236
|
+
@resources = LRUHash.new
|
237
|
+
end
|
238
|
+
|
239
|
+
def thread_safe?
|
240
|
+
return @thread_safe if defined?(@thread_safe)
|
241
|
+
@thread_safe = true
|
242
|
+
end
|
243
|
+
|
244
|
+
def thread_safe=(thread_safe)
|
245
|
+
@thread_safe = thread_safe
|
246
|
+
end
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
def create_circuit_breaker(name, **options)
|
251
|
+
circuit_breaker = options.fetch(:circuit_breaker, true)
|
252
|
+
return unless circuit_breaker
|
253
|
+
require_keys!([:success_threshold, :error_threshold, :error_timeout], options)
|
254
|
+
|
255
|
+
exceptions = options[:exceptions] || []
|
256
|
+
CircuitBreaker.new(
|
257
|
+
name,
|
258
|
+
success_threshold: options[:success_threshold],
|
259
|
+
error_threshold: options[:error_threshold],
|
260
|
+
error_timeout: options[:error_timeout],
|
261
|
+
exceptions: Array(exceptions) + [::Semian::BaseError],
|
262
|
+
half_open_resource_timeout: options[:half_open_resource_timeout],
|
263
|
+
implementation: implementation(**options),
|
264
|
+
)
|
265
|
+
end
|
266
|
+
|
267
|
+
def implementation(**options)
|
268
|
+
# thread_safety_disabled will be replaced by a global setting
|
269
|
+
# Semian is thread safe by default. It is possible
|
270
|
+
# to modify the value by using Semian.thread_safe=
|
271
|
+
unless options[:thread_safety_disabled].nil?
|
272
|
+
logger.info(
|
273
|
+
"NOTE: thread_safety_disabled will be replaced by a global setting" \
|
274
|
+
"Semian is thread safe by default. It is possible" \
|
275
|
+
"to modify the value by using Semian.thread_safe=",
|
276
|
+
)
|
277
|
+
end
|
278
|
+
|
279
|
+
thread_safe = options[:thread_safety_disabled].nil? ? Semian.thread_safe? : !options[:thread_safety_disabled]
|
280
|
+
thread_safe ? ::Semian::ThreadSafe : ::Semian::Simple
|
281
|
+
end
|
282
|
+
|
283
|
+
def create_bulkhead(name, **options)
|
284
|
+
bulkhead = options.fetch(:bulkhead, true)
|
285
|
+
return unless bulkhead
|
286
|
+
|
287
|
+
permissions = options[:permissions] || default_permissions
|
288
|
+
timeout = options[:timeout] || 0
|
289
|
+
Resource.new(name, tickets: options[:tickets], quota: options[:quota], permissions: permissions, timeout: timeout)
|
290
|
+
end
|
291
|
+
|
292
|
+
def require_keys!(required, options)
|
293
|
+
diff = required - options.keys
|
294
|
+
unless diff.empty?
|
295
|
+
raise ArgumentError, "Missing required arguments for Semian: #{diff}"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
if Semian.semaphores_enabled?
|
301
|
+
require 'semian/semian'
|
302
|
+
else
|
303
|
+
Semian::MAX_TICKETS = 0
|
304
|
+
end
|
305
|
+
|
306
|
+
if defined? ActiveSupport
|
307
|
+
ActiveSupport.on_load :active_record do
|
308
|
+
require 'semian/rails'
|
309
|
+
end
|
310
|
+
end
|
metadata
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: semian_extension
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.11.4.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Scott Francis
|
8
|
+
- Simon Eskildsen
|
9
|
+
- Dale Hamel
|
10
|
+
- Sajan Gupta
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2021-11-01 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rake-compiler
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: timecop
|
46
|
+
requirement: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: minitest
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: pry-byebug
|
74
|
+
requirement: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: mysql2
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
type: :development
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
- !ruby/object:Gem::Dependency
|
101
|
+
name: redis
|
102
|
+
requirement: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
type: :development
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
- !ruby/object:Gem::Dependency
|
115
|
+
name: webrick
|
116
|
+
requirement: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
type: :development
|
122
|
+
prerelease: false
|
123
|
+
version_requirements: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
- !ruby/object:Gem::Dependency
|
129
|
+
name: toxiproxy
|
130
|
+
requirement: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - "~>"
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 1.0.0
|
135
|
+
type: :development
|
136
|
+
prerelease: false
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - "~>"
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 1.0.0
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: grpc
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
type: :development
|
150
|
+
prerelease: false
|
151
|
+
version_requirements: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
- !ruby/object:Gem::Dependency
|
157
|
+
name: mocha
|
158
|
+
requirement: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
type: :development
|
164
|
+
prerelease: false
|
165
|
+
version_requirements: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
- !ruby/object:Gem::Dependency
|
171
|
+
name: memory_profiler
|
172
|
+
requirement: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
177
|
+
type: :development
|
178
|
+
prerelease: false
|
179
|
+
version_requirements: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - ">="
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
- !ruby/object:Gem::Dependency
|
185
|
+
name: benchmark-memory
|
186
|
+
requirement: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
type: :development
|
192
|
+
prerelease: false
|
193
|
+
version_requirements: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
description: |2
|
199
|
+
A Ruby C extention that is used to control access to shared resources
|
200
|
+
across process boundaries with SysV semaphores.
|
201
|
+
email: root@1mg.com
|
202
|
+
executables: []
|
203
|
+
extensions:
|
204
|
+
- ext/semian/extconf.rb
|
205
|
+
extra_rdoc_files: []
|
206
|
+
files:
|
207
|
+
- ext/semian/extconf.rb
|
208
|
+
- ext/semian/resource.c
|
209
|
+
- ext/semian/resource.h
|
210
|
+
- ext/semian/semian.c
|
211
|
+
- ext/semian/semian.h
|
212
|
+
- ext/semian/sysv_semaphores.c
|
213
|
+
- ext/semian/sysv_semaphores.h
|
214
|
+
- ext/semian/tickets.c
|
215
|
+
- ext/semian/tickets.h
|
216
|
+
- ext/semian/types.h
|
217
|
+
- lib/semian.rb
|
218
|
+
- lib/semian/adapter.rb
|
219
|
+
- lib/semian/circuit_breaker.rb
|
220
|
+
- lib/semian/grpc.rb
|
221
|
+
- lib/semian/instrumentable.rb
|
222
|
+
- lib/semian/lru_hash.rb
|
223
|
+
- lib/semian/mysql2.rb
|
224
|
+
- lib/semian/net_http.rb
|
225
|
+
- lib/semian/platform.rb
|
226
|
+
- lib/semian/protected_resource.rb
|
227
|
+
- lib/semian/rails.rb
|
228
|
+
- lib/semian/redis.rb
|
229
|
+
- lib/semian/resource.rb
|
230
|
+
- lib/semian/simple_integer.rb
|
231
|
+
- lib/semian/simple_sliding_window.rb
|
232
|
+
- lib/semian/simple_state.rb
|
233
|
+
- lib/semian/typhoeus.rb
|
234
|
+
- lib/semian/unprotected_resource.rb
|
235
|
+
- lib/semian/version.rb
|
236
|
+
homepage: https://github.com/1mgOfficial/semian
|
237
|
+
licenses:
|
238
|
+
- MIT
|
239
|
+
metadata:
|
240
|
+
allowed_push_host: https://rubygems.org
|
241
|
+
post_install_message:
|
242
|
+
rdoc_options: []
|
243
|
+
require_paths:
|
244
|
+
- lib
|
245
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
246
|
+
requirements:
|
247
|
+
- - ">="
|
248
|
+
- !ruby/object:Gem::Version
|
249
|
+
version: '0'
|
250
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
251
|
+
requirements:
|
252
|
+
- - ">="
|
253
|
+
- !ruby/object:Gem::Version
|
254
|
+
version: '0'
|
255
|
+
requirements: []
|
256
|
+
rubygems_version: 3.0.3
|
257
|
+
signing_key:
|
258
|
+
specification_version: 4
|
259
|
+
summary: Bulkheading for Ruby with SysV semaphores
|
260
|
+
test_files: []
|