network_resiliency 0.5.1 → 0.6.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/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/lib/network_resiliency/adapter/http.rb +68 -14
- data/lib/network_resiliency/adapter/redis.rb +73 -15
- data/lib/network_resiliency/version.rb +1 -1
- data/lib/network_resiliency.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9cd120266db1dfb8d2e5b37155534f3d5dc37a560d329352fa1a061ffd19ec97
|
4
|
+
data.tar.gz: c09fb2b6dc81e74a838e7da9ae9bc7e4d177c9ba7104a3b8e6ecb6b8668ebf97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9caeeab2d22c4feea69b03438f8c66588179fe130ef1620935c475654a4661bbba36d82c79b4650fb3bcdead86cf0b5f3e3746513c40d16c8208b6f09ca32e5a
|
7
|
+
data.tar.gz: 05ad9ab8d5ec490cf6064cc83c54e9132b2a10f4bf6ab8ca0ab83f94d84db706622c951a49ad0de8e0ac7551ce32963fb86ca7aad5d6f47bb0c33162d3ba0e6d
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -15,19 +15,41 @@ module NetworkResiliency
|
|
15
15
|
(instance&.singleton_class || Net::HTTP).ancestors.include?(Instrumentation)
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
ID_REGEX = %r{/[0-9]+(?=/|$)}.freeze
|
19
|
+
UUID_REGEX = %r`/\h{8}-\h{4}-(\h{4})-\h{4}-\h{12}(?=/|$)`.freeze
|
20
|
+
refine Net::HTTP do
|
21
|
+
def normalize_path(path)
|
22
|
+
path.gsub(
|
23
|
+
Regexp.union(
|
24
|
+
NetworkResiliency::Adapter::HTTP::ID_REGEX,
|
25
|
+
NetworkResiliency::Adapter::HTTP::UUID_REGEX,
|
26
|
+
),
|
27
|
+
'/x',
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_resilience(action, destination, idempotent, &block)
|
32
|
+
if action == :connect
|
33
|
+
original_timeout = self.open_timeout
|
34
|
+
set_timeout = ->(timeout) { self.open_timeout = timeout }
|
35
|
+
else
|
36
|
+
original_timeout = self.read_timeout
|
37
|
+
set_timeout = ->(timeout) { self.read_timeout = timeout }
|
38
|
+
end
|
22
39
|
|
23
40
|
timeouts = NetworkResiliency.timeouts_for(
|
24
41
|
adapter: "http",
|
25
|
-
action:
|
26
|
-
destination:
|
42
|
+
action: action.to_s,
|
43
|
+
destination: destination,
|
27
44
|
max: original_timeout,
|
28
45
|
units: :seconds,
|
29
46
|
)
|
30
47
|
|
48
|
+
unless idempotent
|
49
|
+
# only try once, with most lenient timeout
|
50
|
+
timeouts = timeouts.last(1)
|
51
|
+
end
|
52
|
+
|
31
53
|
attempts = 0
|
32
54
|
ts = -NetworkResiliency.timestamp
|
33
55
|
|
@@ -35,10 +57,13 @@ module NetworkResiliency
|
|
35
57
|
attempts += 1
|
36
58
|
error = nil
|
37
59
|
|
38
|
-
|
60
|
+
set_timeout.call(timeouts.shift)
|
61
|
+
|
62
|
+
yield
|
63
|
+
rescue ::Timeout::Error,
|
64
|
+
defined?(OpenSSL::SSL) ? OpenSSL::OpenSSLError : IOError,
|
65
|
+
SystemCallError => e
|
39
66
|
|
40
|
-
super
|
41
|
-
rescue Net::OpenTimeout => e
|
42
67
|
# capture error
|
43
68
|
error = e.class
|
44
69
|
|
@@ -47,20 +72,49 @@ module NetworkResiliency
|
|
47
72
|
raise
|
48
73
|
ensure
|
49
74
|
ts += NetworkResiliency.timestamp
|
50
|
-
|
75
|
+
set_timeout.call(original_timeout)
|
51
76
|
|
52
77
|
NetworkResiliency.record(
|
53
78
|
adapter: "http",
|
54
|
-
action:
|
55
|
-
destination:
|
56
|
-
error: error,
|
79
|
+
action: action.to_s,
|
80
|
+
destination: destination,
|
57
81
|
duration: ts,
|
58
|
-
|
82
|
+
error: error,
|
83
|
+
timeout: original_timeout.to_f * 1_000,
|
59
84
|
attempts: attempts,
|
60
85
|
)
|
61
86
|
end
|
62
87
|
end
|
63
88
|
end
|
89
|
+
|
90
|
+
module Instrumentation
|
91
|
+
using NetworkResiliency::Adapter::HTTP
|
92
|
+
|
93
|
+
def connect
|
94
|
+
return super unless NetworkResiliency.enabled?(:http)
|
95
|
+
|
96
|
+
with_resilience(:connect, address, true) { super }
|
97
|
+
end
|
98
|
+
|
99
|
+
def transport_request(req, &block)
|
100
|
+
return super unless NetworkResiliency.enabled?(:http)
|
101
|
+
|
102
|
+
destination = [
|
103
|
+
req.method.downcase,
|
104
|
+
address,
|
105
|
+
normalize_path(req.path),
|
106
|
+
].join(":")
|
107
|
+
|
108
|
+
idepotent = Net::HTTP::IDEMPOTENT_METHODS_.include?(req.method)
|
109
|
+
|
110
|
+
retries = self.max_retries
|
111
|
+
self.max_retries = 0 # disable
|
112
|
+
|
113
|
+
with_resilience(:request, destination, idepotent) { super }
|
114
|
+
ensure
|
115
|
+
self.max_retries = retries
|
116
|
+
end
|
117
|
+
end
|
64
118
|
end
|
65
119
|
end
|
66
120
|
end
|
@@ -35,20 +35,44 @@ module NetworkResiliency
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
IDEMPOTENT_COMMANDS = [
|
39
|
+
"exists",
|
40
|
+
"expire",
|
41
|
+
"get",
|
42
|
+
"getex",
|
43
|
+
"getrange",
|
44
|
+
"mget",
|
45
|
+
"mset",
|
46
|
+
"ping",
|
47
|
+
"scard",
|
48
|
+
"sdiff",
|
49
|
+
"sdiffstore",
|
50
|
+
"set",
|
51
|
+
"sismember",
|
52
|
+
"smembers",
|
53
|
+
"smismember",
|
54
|
+
].freeze
|
55
|
+
|
56
|
+
refine ::Redis::Client do
|
57
|
+
private
|
58
|
+
|
59
|
+
def with_resilience(action, destination, idempotent, &block)
|
60
|
+
timeout_key = action == :connect ? :connect_timeout : :read_timeout
|
61
|
+
original_timeout = @options[timeout_key]
|
43
62
|
|
44
63
|
timeouts = NetworkResiliency.timeouts_for(
|
45
64
|
adapter: "redis",
|
46
|
-
action:
|
47
|
-
destination:
|
48
|
-
max:
|
65
|
+
action: action.to_s,
|
66
|
+
destination: destination,
|
67
|
+
max: @options[timeout_key],
|
49
68
|
units: :seconds,
|
50
69
|
)
|
51
70
|
|
71
|
+
unless idempotent
|
72
|
+
# only try once, with most lenient timeout
|
73
|
+
timeouts = timeouts.last(1)
|
74
|
+
end
|
75
|
+
|
52
76
|
attempts = 0
|
53
77
|
ts = -NetworkResiliency.timestamp
|
54
78
|
|
@@ -56,10 +80,10 @@ module NetworkResiliency
|
|
56
80
|
attempts += 1
|
57
81
|
error = nil
|
58
82
|
|
59
|
-
@options[
|
83
|
+
@options[timeout_key] = timeouts.shift
|
60
84
|
|
61
|
-
|
62
|
-
rescue ::Redis::
|
85
|
+
yield
|
86
|
+
rescue ::Redis::BaseConnectionError => e
|
63
87
|
# capture error
|
64
88
|
|
65
89
|
# grab underlying exception within Redis wrapper
|
@@ -70,19 +94,53 @@ module NetworkResiliency
|
|
70
94
|
raise
|
71
95
|
ensure
|
72
96
|
ts += NetworkResiliency.timestamp
|
73
|
-
@options[
|
97
|
+
@options[timeout_key] = original_timeout
|
74
98
|
|
75
99
|
NetworkResiliency.record(
|
76
100
|
adapter: "redis",
|
77
|
-
action:
|
78
|
-
destination:
|
101
|
+
action: action.to_s,
|
102
|
+
destination: destination,
|
79
103
|
duration: ts,
|
80
104
|
error: error,
|
81
|
-
timeout: @options[
|
105
|
+
timeout: @options[timeout_key].to_f * 1_000,
|
82
106
|
attempts: attempts,
|
83
107
|
)
|
84
108
|
end
|
85
109
|
end
|
110
|
+
|
111
|
+
def idempotent?(command)
|
112
|
+
NetworkResiliency::Adapter::Redis::IDEMPOTENT_COMMANDS.include?(command)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
module Instrumentation
|
117
|
+
using NetworkResiliency::Refinements
|
118
|
+
using NetworkResiliency::Adapter::Redis
|
119
|
+
|
120
|
+
def establish_connection
|
121
|
+
return super unless NetworkResiliency.enabled?(:redis)
|
122
|
+
|
123
|
+
with_resilience(:connect, host, true) { super }
|
124
|
+
end
|
125
|
+
|
126
|
+
def call(command)
|
127
|
+
return super unless NetworkResiliency.enabled?(:redis)
|
128
|
+
return super unless command.is_a?(Array)
|
129
|
+
|
130
|
+
command_key = command.first.to_s
|
131
|
+
|
132
|
+
# larger commands may have larger timeouts
|
133
|
+
command_size = command.size.order_of_magnitude
|
134
|
+
destination = [
|
135
|
+
host,
|
136
|
+
command_key,
|
137
|
+
(command_size if command_size > 1),
|
138
|
+
].compact.join(":")
|
139
|
+
|
140
|
+
idempotent = idempotent?(command_key)
|
141
|
+
|
142
|
+
with_resilience(:request, destination, idempotent) { super }
|
143
|
+
end
|
86
144
|
end
|
87
145
|
end
|
88
146
|
end
|
data/lib/network_resiliency.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: network_resiliency
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Pepper
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|