semian 0.18.1 → 0.19.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/README.md +34 -4
- data/lib/semian/activerecord_trilogy_adapter.rb +3 -3
- data/lib/semian/adapter.rb +15 -5
- data/lib/semian/net_http.rb +27 -5
- data/lib/semian/redis/v5.rb +8 -0
- data/lib/semian/redis.rb +8 -0
- data/lib/semian/redis_client.rb +8 -0
- data/lib/semian/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6bae2b0df6fceb44b91a57900ba40be6b53588866025c269627b08c2ccfa193
|
4
|
+
data.tar.gz: f11b8fb67e10cfbb5f1fe393225a45ba0836324768e5fa01366f760151b635b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b39cc0bfcf4341bd0a008f50109abb77f27b75021c28245d4c8e4b2af3bb78ca4611deef872c3b40714d4047a8ede6bcfc88cd426a8866f2e51249f3606aefb4
|
7
|
+
data.tar.gz: 89829e6fb002b9d96c2b96ede7339e10e7d3e1c6fee8d8d023b831747e969622d08342e07d7e0cd7ce31f433239e984b881417140e15a9a736501a62c941c79b
|
data/README.md
CHANGED
@@ -254,10 +254,40 @@ The `semian_options` passed apply to that resource. Semian creates the `semian_i
|
|
254
254
|
from the `name` to look up and store changes in the circuit breaker and bulkhead states
|
255
255
|
and associate successes, failures, errors with the protected resource.
|
256
256
|
|
257
|
-
We only require that
|
258
|
-
|
259
|
-
|
260
|
-
|
257
|
+
We only require that the `semian_configuration` be **set only once** over the lifetime of
|
258
|
+
the library.
|
259
|
+
|
260
|
+
If you need to return different values for the same pair of `host`/`port` value, you **must**
|
261
|
+
include the `dynamic: true` option. Returning different values for the same `host`/`port` values
|
262
|
+
without setting the `dynamic` option can lead to undesirable behavior.
|
263
|
+
|
264
|
+
A common example for dynamic options is the use of a thread local variable, such as
|
265
|
+
`ActiveSupport::CurrentAttributes`, for requests to a service acting as a proxy.
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
SEMIAN_PARAMETERS = {
|
269
|
+
# ...
|
270
|
+
dynamic: true,
|
271
|
+
}
|
272
|
+
|
273
|
+
class CurrentSemianSubResource < ActiveSupport::Attributes
|
274
|
+
attribute :name
|
275
|
+
end
|
276
|
+
|
277
|
+
Semian::NetHTTP.semian_configuration = proc do |host, port|
|
278
|
+
name = "#{host}_#{port}"
|
279
|
+
if (sub_resource_name = CurrentSemianSubResource.name)
|
280
|
+
name << "_#{name}"
|
281
|
+
end
|
282
|
+
SEMIAN_PARAMETERS.merge(name: name)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Two requests to example.com can use two different semian resources,
|
286
|
+
# as long as `CurrentSemianSubResource.name` is set accordingly:
|
287
|
+
# CurrentSemianSubResource.set(name: "sub_resource_1") { Net::HTTP.get_response(URI("http://example.com")) }
|
288
|
+
# and:
|
289
|
+
# CurrentSemianSubResource.set(name: "sub_resource_2") { Net::HTTP.get_response(URI("http://example.com")) }
|
290
|
+
```
|
261
291
|
|
262
292
|
For most purposes, `"#{host}_#{port}"` is a good default `name`. Custom `name` formats
|
263
293
|
can be useful to grouping related subdomains as one resource, so that they all
|
@@ -47,16 +47,16 @@ module Semian
|
|
47
47
|
super
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
50
|
+
def raw_execute(sql, *)
|
51
51
|
if query_allowlisted?(sql)
|
52
52
|
super
|
53
53
|
else
|
54
|
-
acquire_semian_resource(adapter: :trilogy_adapter, scope: :
|
54
|
+
acquire_semian_resource(adapter: :trilogy_adapter, scope: :query) do
|
55
55
|
super
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
59
|
-
ruby2_keywords :
|
59
|
+
ruby2_keywords :raw_execute
|
60
60
|
|
61
61
|
def active?
|
62
62
|
acquire_semian_resource(adapter: :trilogy_adapter, scope: :ping) do
|
data/lib/semian/adapter.rb
CHANGED
@@ -9,19 +9,25 @@ module Semian
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def semian_resource
|
12
|
-
@semian_resource
|
12
|
+
return @semian_resource if @semian_resource
|
13
|
+
|
14
|
+
case semian_options
|
13
15
|
when false
|
14
|
-
UnprotectedResource.new(semian_identifier)
|
16
|
+
@semian_resource = UnprotectedResource.new(semian_identifier)
|
15
17
|
when nil
|
16
18
|
Semian.logger.info("Semian is not configured for #{self.class.name}: #{semian_identifier}")
|
17
|
-
UnprotectedResource.new(semian_identifier)
|
19
|
+
@semian_resource = UnprotectedResource.new(semian_identifier)
|
18
20
|
else
|
19
21
|
options = semian_options.dup
|
20
22
|
options.delete(:name)
|
21
23
|
options[:consumer] = self
|
22
24
|
options[:exceptions] ||= []
|
23
25
|
options[:exceptions] += resource_exceptions
|
24
|
-
::Semian.retrieve_or_register(semian_identifier, **options)
|
26
|
+
resource = ::Semian.retrieve_or_register(semian_identifier, **options)
|
27
|
+
|
28
|
+
@semian_resource = resource unless options.fetch(:dynamic, false)
|
29
|
+
|
30
|
+
resource
|
25
31
|
end
|
26
32
|
end
|
27
33
|
|
@@ -53,7 +59,11 @@ module Semian
|
|
53
59
|
return @semian_options if defined? @semian_options
|
54
60
|
|
55
61
|
options = raw_semian_options
|
56
|
-
|
62
|
+
|
63
|
+
symbolized_options = options && options.transform_keys(&:to_sym) # rubocop:disable Style/SafeNavigation
|
64
|
+
symbolized_options.tap do
|
65
|
+
@semian_options = symbolized_options if !symbolized_options || !symbolized_options.fetch(:dynamic, false)
|
66
|
+
end
|
57
67
|
end
|
58
68
|
|
59
69
|
def raw_semian_options
|
data/lib/semian/net_http.rb
CHANGED
@@ -91,16 +91,20 @@ module Semian
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def connect
|
94
|
-
|
94
|
+
with_cleared_dynamic_options do
|
95
|
+
return super if disabled?
|
95
96
|
|
96
|
-
|
97
|
+
acquire_semian_resource(adapter: :http, scope: :connection) { super }
|
98
|
+
end
|
97
99
|
end
|
98
100
|
|
99
101
|
def transport_request(*)
|
100
|
-
|
102
|
+
with_cleared_dynamic_options do
|
103
|
+
return super if disabled?
|
101
104
|
|
102
|
-
|
103
|
-
|
105
|
+
acquire_semian_resource(adapter: :http, scope: :query) do
|
106
|
+
handle_error_responses(super)
|
107
|
+
end
|
104
108
|
end
|
105
109
|
end
|
106
110
|
|
@@ -125,6 +129,24 @@ module Semian
|
|
125
129
|
end
|
126
130
|
result
|
127
131
|
end
|
132
|
+
|
133
|
+
def with_cleared_dynamic_options
|
134
|
+
unless @resource_acquisition_in_progress
|
135
|
+
@resource_acquisition_in_progress = true
|
136
|
+
resource_acquisition_started = true
|
137
|
+
end
|
138
|
+
|
139
|
+
yield
|
140
|
+
ensure
|
141
|
+
if resource_acquisition_started
|
142
|
+
if @raw_semian_options&.fetch(:dynamic, false)
|
143
|
+
# Clear @raw_semian_options if the resource was flagged as dynamic.
|
144
|
+
@raw_semian_options = nil
|
145
|
+
end
|
146
|
+
|
147
|
+
@resource_acquisition_in_progress = false
|
148
|
+
end
|
149
|
+
end
|
128
150
|
end
|
129
151
|
end
|
130
152
|
|
data/lib/semian/redis/v5.rb
CHANGED
@@ -6,6 +6,14 @@ class Redis
|
|
6
6
|
BaseConnectionError.include(::Semian::AdapterError)
|
7
7
|
OutOfMemoryError.include(::Semian::AdapterError)
|
8
8
|
|
9
|
+
class ReadOnlyError < Redis::BaseConnectionError
|
10
|
+
# A ReadOnlyError is a fast failure and we don't want to track these errors so that we can reconnect
|
11
|
+
# to the new primary ASAP
|
12
|
+
def marks_semian_circuits?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
9
17
|
class SemianError < BaseConnectionError
|
10
18
|
def initialize(semian_identifier, *args)
|
11
19
|
super(*args)
|
data/lib/semian/redis.rb
CHANGED
@@ -32,6 +32,14 @@ class Redis
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
class ReadOnlyError < Redis::CommandError
|
36
|
+
# A ReadOnlyError is a fast failure and we don't want to track these errors so that we can reconnect
|
37
|
+
# to the new primary ASAP
|
38
|
+
def marks_semian_circuits?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
35
43
|
ResourceBusyError = Class.new(SemianError)
|
36
44
|
CircuitOpenError = Class.new(SemianError)
|
37
45
|
ResolveError = Class.new(SemianError)
|
data/lib/semian/redis_client.rb
CHANGED
@@ -14,6 +14,14 @@ class RedisClient
|
|
14
14
|
|
15
15
|
OutOfMemoryError.include(::Semian::AdapterError)
|
16
16
|
|
17
|
+
class ReadOnlyError < RedisClient::ConnectionError
|
18
|
+
# A ReadOnlyError is a fast failure and we don't want to track these errors so that we can reconnect
|
19
|
+
# to the new primary ASAP
|
20
|
+
def marks_semian_circuits?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
17
25
|
class SemianError < ConnectionError
|
18
26
|
def initialize(semian_identifier, *args)
|
19
27
|
super(*args)
|
data/lib/semian/version.rb
CHANGED
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.19.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Francis
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-05-
|
13
|
+
date: 2023-05-16 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: |2
|
16
16
|
A Ruby C extention that is used to control access to shared resources
|
@@ -79,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: '0'
|
81
81
|
requirements: []
|
82
|
-
rubygems_version: 3.4.
|
82
|
+
rubygems_version: 3.4.13
|
83
83
|
signing_key:
|
84
84
|
specification_version: 4
|
85
85
|
summary: Bulkheading for Ruby with SysV semaphores
|