semian 0.12.0 → 0.13.2
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 +243 -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 +13 -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 +33 -6
- data/lib/semian/redis.rb +16 -14
- data/lib/semian/redis_client.rb +5 -3
- 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 +61 -45
- metadata +11 -201
data/ext/semian/extconf.rb
CHANGED
@@ -1,33 +1,35 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__))
|
4
|
+
|
5
|
+
require "semian/platform"
|
4
6
|
|
5
7
|
unless Semian.sysv_semaphores_supported?
|
6
|
-
File.write
|
7
|
-
all:
|
8
|
-
clean:
|
9
|
-
install:
|
10
|
-
MAKEFILE
|
8
|
+
File.write("Makefile", <<~MAKEFILE)
|
9
|
+
all:
|
10
|
+
clean:
|
11
|
+
install:
|
12
|
+
MAKEFILE
|
11
13
|
exit
|
12
14
|
end
|
13
15
|
|
14
|
-
require
|
16
|
+
require "mkmf"
|
15
17
|
|
16
|
-
abort
|
17
|
-
abort
|
18
|
+
abort "openssl is missing. please install openssl." unless find_header("openssl/sha.h")
|
19
|
+
abort "openssl is missing. please install openssl." unless find_library("crypto", "SHA1")
|
18
20
|
|
19
|
-
have_header
|
20
|
-
have_header
|
21
|
-
have_header
|
21
|
+
have_header "sys/ipc.h"
|
22
|
+
have_header "sys/sem.h"
|
23
|
+
have_header "sys/types.h"
|
22
24
|
|
23
|
-
have_func
|
24
|
-
have_func
|
25
|
+
have_func "rb_thread_blocking_region"
|
26
|
+
have_func "rb_thread_call_without_gvl"
|
25
27
|
|
26
28
|
$CFLAGS = "-D_GNU_SOURCE -Werror -Wall "
|
27
|
-
if ENV.key?(
|
28
|
-
|
29
|
+
$CFLAGS += if ENV.key?("DEBUG")
|
30
|
+
"-O0 -g -DDEBUG"
|
29
31
|
else
|
30
|
-
|
32
|
+
"-O3"
|
31
33
|
end
|
32
34
|
|
33
|
-
create_makefile(
|
35
|
+
create_makefile("semian/semian")
|
data/lib/semian/adapter.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian"
|
2
4
|
|
3
5
|
module Semian
|
4
6
|
module Adapter
|
5
7
|
def semian_identifier
|
6
|
-
raise NotImplementedError
|
8
|
+
raise NotImplementedError, "Semian adapters must implement a `semian_identifier` method"
|
7
9
|
end
|
8
10
|
|
9
11
|
def semian_resource
|
@@ -31,6 +33,7 @@ module Semian
|
|
31
33
|
|
32
34
|
def acquire_semian_resource(scope:, adapter:, &block)
|
33
35
|
return yield if resource_already_acquired?
|
36
|
+
|
34
37
|
semian_resource.acquire(scope: scope, adapter: adapter, resource: self) do
|
35
38
|
mark_resource_as_acquired(&block)
|
36
39
|
end
|
@@ -48,16 +51,17 @@ module Semian
|
|
48
51
|
|
49
52
|
def semian_options
|
50
53
|
return @semian_options if defined? @semian_options
|
54
|
+
|
51
55
|
options = raw_semian_options
|
52
56
|
@semian_options = options && options.map { |k, v| [k.to_sym, v] }.to_h
|
53
57
|
end
|
54
58
|
|
55
59
|
def raw_semian_options
|
56
|
-
raise NotImplementedError
|
60
|
+
raise NotImplementedError, "Semian adapters must implement a `raw_semian_options` method"
|
57
61
|
end
|
58
62
|
|
59
63
|
def resource_exceptions
|
60
|
-
raise NotImplementedError
|
64
|
+
raise NotImplementedError, "Semian adapters must implement a `resource_exceptions` method"
|
61
65
|
end
|
62
66
|
|
63
67
|
def resource_already_acquired?
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Semian
|
2
|
-
class CircuitBreaker
|
4
|
+
class CircuitBreaker # :nodoc:
|
3
5
|
extend Forwardable
|
4
6
|
|
5
7
|
def_delegators :@state, :closed?, :open?, :half_open?
|
@@ -7,7 +9,9 @@ module Semian
|
|
7
9
|
attr_reader :name, :half_open_resource_timeout, :error_timeout, :state, :last_error
|
8
10
|
|
9
11
|
def initialize(name, exceptions:, success_threshold:, error_threshold:,
|
10
|
-
|
12
|
+
error_timeout:, implementation:, half_open_resource_timeout: nil,
|
13
|
+
error_threshold_timeout: nil)
|
14
|
+
|
11
15
|
@name = name.to_sym
|
12
16
|
@success_count_threshold = success_threshold
|
13
17
|
@error_count_threshold = error_threshold
|
@@ -25,6 +29,7 @@ module Semian
|
|
25
29
|
|
26
30
|
def acquire(resource = nil, &block)
|
27
31
|
return yield if disabled?
|
32
|
+
|
28
33
|
transition_to_half_open if transition_to_half_open?
|
29
34
|
|
30
35
|
raise OpenCircuitError unless request_allowed?
|
@@ -63,6 +68,7 @@ module Semian
|
|
63
68
|
|
64
69
|
def mark_success
|
65
70
|
return unless half_open?
|
71
|
+
|
66
72
|
@successes.increment
|
67
73
|
transition_to_close if success_threshold_reached?
|
68
74
|
end
|
@@ -80,8 +86,7 @@ module Semian
|
|
80
86
|
end
|
81
87
|
|
82
88
|
def in_use?
|
83
|
-
|
84
|
-
@errors.size > 0
|
89
|
+
!error_timeout_expired? && !@errors.empty?
|
85
90
|
end
|
86
91
|
|
87
92
|
private
|
@@ -117,6 +122,7 @@ module Semian
|
|
117
122
|
def error_timeout_expired?
|
118
123
|
last_error_time = @errors.last
|
119
124
|
return false unless last_error_time
|
125
|
+
|
120
126
|
Time.at(last_error_time) + @error_timeout < Time.now
|
121
127
|
end
|
122
128
|
|
@@ -133,12 +139,12 @@ module Semian
|
|
133
139
|
return if @state.nil? || new_state == @state.value
|
134
140
|
|
135
141
|
str = "[#{self.class.name}] State transition from #{@state.value} to #{new_state}."
|
136
|
-
str
|
137
|
-
str
|
138
|
-
str
|
139
|
-
str
|
142
|
+
str += " success_count=#{@successes.value} error_count=#{@errors.size}"
|
143
|
+
str += " success_count_threshold=#{@success_count_threshold} error_count_threshold=#{@error_count_threshold}"
|
144
|
+
str += " error_timeout=#{@error_timeout} error_last_at=\"#{@errors.last}\""
|
145
|
+
str += " name=\"#{@name}\""
|
140
146
|
if new_state == :open && @last_error
|
141
|
-
str
|
147
|
+
str += " last_error_message=#{@last_error.message.inspect}"
|
142
148
|
end
|
143
149
|
|
144
150
|
Semian.logger.info(str)
|
@@ -149,7 +155,7 @@ module Semian
|
|
149
155
|
end
|
150
156
|
|
151
157
|
def disabled?
|
152
|
-
ENV[
|
158
|
+
ENV["SEMIAN_CIRCUIT_BREAKER_DISABLED"] || ENV["SEMIAN_DISABLED"]
|
153
159
|
end
|
154
160
|
|
155
161
|
def maybe_with_half_open_resource_timeout(resource, &block)
|
data/lib/semian/grpc.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian/adapter"
|
4
|
+
require "grpc"
|
3
5
|
|
4
6
|
module GRPC
|
5
7
|
GRPC::Unavailable.include(::Semian::AdapterError)
|
@@ -22,7 +24,6 @@ end
|
|
22
24
|
|
23
25
|
module Semian
|
24
26
|
module GRPC
|
25
|
-
attr_reader :raw_semian_options
|
26
27
|
include Semian::Adapter
|
27
28
|
|
28
29
|
ResourceBusyError = ::GRPC::ResourceBusyError
|
@@ -40,6 +41,7 @@ module Semian
|
|
40
41
|
|
41
42
|
def semian_configuration=(configuration)
|
42
43
|
raise Semian::GRPC::SemianConfigurationChangedError unless @semian_configuration.nil?
|
44
|
+
|
43
45
|
@semian_configuration = configuration
|
44
46
|
end
|
45
47
|
|
@@ -52,10 +54,10 @@ module Semian
|
|
52
54
|
@raw_semian_options ||= begin
|
53
55
|
# If the host is empty, it's possible that the adapter was initialized
|
54
56
|
# with the channel. Therefore, we look into the channel to find the host
|
55
|
-
if @host.empty?
|
56
|
-
|
57
|
+
host = if @host.empty?
|
58
|
+
@ch.target
|
57
59
|
else
|
58
|
-
|
60
|
+
@host
|
59
61
|
end
|
60
62
|
@raw_semian_options = Semian::GRPC.retrieve_semian_configuration(host)
|
61
63
|
@raw_semian_options = @raw_semian_options.dup unless @raw_semian_options.nil?
|
@@ -81,22 +83,42 @@ module Semian
|
|
81
83
|
|
82
84
|
def request_response(*, **)
|
83
85
|
return super if disabled?
|
84
|
-
|
86
|
+
|
87
|
+
acquire_semian_resource_grpc(scope: :request_response) { super }
|
85
88
|
end
|
86
89
|
|
87
90
|
def client_streamer(*, **)
|
88
91
|
return super if disabled?
|
89
|
-
|
92
|
+
|
93
|
+
acquire_semian_resource_grpc(scope: :client_streamer) { super }
|
90
94
|
end
|
91
95
|
|
92
96
|
def server_streamer(*, **)
|
93
97
|
return super if disabled?
|
94
|
-
|
98
|
+
|
99
|
+
acquire_semian_resource_grpc(scope: :server_streamer) { super }
|
95
100
|
end
|
96
101
|
|
97
102
|
def bidi_streamer(*, **)
|
98
103
|
return super if disabled?
|
99
|
-
|
104
|
+
|
105
|
+
acquire_semian_resource_grpc(scope: :bidi_streamer) { super }
|
106
|
+
end
|
107
|
+
|
108
|
+
def acquire_semian_resource_grpc(scope:)
|
109
|
+
acquire_semian_resource(adapter: :grpc, scope: scope) do
|
110
|
+
result = yield
|
111
|
+
handle_operation(result, scope) if result.is_a?(::GRPC::ActiveCall::Operation)
|
112
|
+
result
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def handle_operation(operation, scope)
|
117
|
+
execute = operation.singleton_method(:execute)
|
118
|
+
operation.instance_variable_set(:@semian, self)
|
119
|
+
operation.define_singleton_method(:execute) do
|
120
|
+
@semian.send(:acquire_semian_resource, **{ adapter: :grpc, scope: scope }) { execute.call }
|
121
|
+
end
|
100
122
|
end
|
101
123
|
end
|
102
124
|
end
|
data/lib/semian/lru_hash.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class LRUHash
|
2
4
|
# This LRU (Least Recently Used) hash will allow
|
3
5
|
# the cleaning of resources as time goes on.
|
@@ -120,10 +122,10 @@ class LRUHash
|
|
120
122
|
|
121
123
|
def clear_unused_resources
|
122
124
|
payload = {
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
size: @table.size,
|
126
|
+
examined: 0,
|
127
|
+
cleared: 0,
|
128
|
+
elapsed: nil,
|
127
129
|
}
|
128
130
|
timer_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
129
131
|
|
@@ -155,20 +157,19 @@ class LRUHash
|
|
155
157
|
end
|
156
158
|
end
|
157
159
|
|
158
|
-
EXCEPTION_NEVER = {Exception => :never}.freeze
|
159
|
-
EXCEPTION_IMMEDIATE = {Exception => :immediate}.freeze
|
160
|
+
EXCEPTION_NEVER = { Exception => :never }.freeze
|
161
|
+
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
|
160
162
|
private_constant :EXCEPTION_NEVER
|
161
163
|
private_constant :EXCEPTION_IMMEDIATE
|
162
164
|
|
163
|
-
def try_synchronize
|
165
|
+
def try_synchronize(&block)
|
164
166
|
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
167
|
+
return false unless @lock.try_lock
|
168
|
+
|
169
|
+
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
170
|
+
true
|
171
|
+
ensure
|
172
|
+
@lock.unlock if @lock.owned?
|
172
173
|
end
|
173
174
|
end
|
174
175
|
end
|
data/lib/semian/mysql2.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian/adapter"
|
4
|
+
require "mysql2"
|
3
5
|
|
4
6
|
module Mysql2
|
5
7
|
Mysql2::Error.include(::Semian::AdapterError)
|
@@ -32,13 +34,13 @@ module Semian
|
|
32
34
|
CircuitOpenError = ::Mysql2::CircuitOpenError
|
33
35
|
PingFailure = Class.new(::Mysql2::Error)
|
34
36
|
|
35
|
-
DEFAULT_HOST =
|
37
|
+
DEFAULT_HOST = "localhost"
|
36
38
|
DEFAULT_PORT = 3306
|
37
39
|
|
38
40
|
QUERY_WHITELIST = Regexp.union(
|
39
|
-
|
40
|
-
|
41
|
-
|
41
|
+
%r{\A(?:/\*.*?\*/)?\s*ROLLBACK}i,
|
42
|
+
%r{\A(?:/\*.*?\*/)?\s*COMMIT}i,
|
43
|
+
%r{\A(?:/\*.*?\*/)?\s*RELEASE\s+SAVEPOINT}i,
|
42
44
|
)
|
43
45
|
|
44
46
|
# The naked methods are exposed as `raw_query` and `raw_connect` for instrumentation purpose
|
@@ -55,7 +57,8 @@ module Semian
|
|
55
57
|
|
56
58
|
def semian_identifier
|
57
59
|
@semian_identifier ||= begin
|
58
|
-
|
60
|
+
name = semian_options && semian_options[:name]
|
61
|
+
unless name
|
59
62
|
host = query_options[:host] || DEFAULT_HOST
|
60
63
|
port = query_options[:port] || DEFAULT_PORT
|
61
64
|
name = "#{host}:#{port}"
|
@@ -68,7 +71,7 @@ module Semian
|
|
68
71
|
result = nil
|
69
72
|
acquire_semian_resource(adapter: :mysql, scope: :ping) do
|
70
73
|
result = raw_ping
|
71
|
-
raise PingFailure
|
74
|
+
raise PingFailure, result.to_s unless result
|
72
75
|
end
|
73
76
|
result
|
74
77
|
rescue ResourceBusyError, CircuitOpenError, PingFailure
|
@@ -113,6 +116,7 @@ module Semian
|
|
113
116
|
# data that is not recognized as a valid encoding, in which case we just
|
114
117
|
# return false.
|
115
118
|
return false unless sql.valid_encoding?
|
119
|
+
|
116
120
|
raise
|
117
121
|
end
|
118
122
|
|
@@ -132,7 +136,7 @@ module Semian
|
|
132
136
|
|
133
137
|
def raw_semian_options
|
134
138
|
return query_options[:semian] if query_options.key?(:semian)
|
135
|
-
return query_options[
|
139
|
+
return query_options["semian"] if query_options.key?("semian")
|
136
140
|
end
|
137
141
|
end
|
138
142
|
end
|
data/lib/semian/net_http.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian/adapter"
|
4
|
+
require "net/http"
|
3
5
|
|
4
6
|
module Net
|
5
7
|
ProtocolError.include(::Semian::AdapterError)
|
@@ -40,10 +42,11 @@ module Semian
|
|
40
42
|
::Net::ProtocolError,
|
41
43
|
::EOFError,
|
42
44
|
::IOError,
|
43
|
-
::SystemCallError, # includes ::Errno::EINVAL, ::Errno::ECONNRESET,
|
45
|
+
::SystemCallError, # includes ::Errno::EINVAL, ::Errno::ECONNRESET,
|
46
|
+
# ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT, and more
|
44
47
|
].freeze # Net::HTTP can throw many different errors, this tries to capture most of them
|
45
48
|
|
46
|
-
module ClassMethods
|
49
|
+
module ClassMethods
|
47
50
|
def new(*args, semian: true)
|
48
51
|
http = super(*args)
|
49
52
|
http.instance_variable_set(:@semian_enabled, semian)
|
@@ -57,6 +60,7 @@ module Semian
|
|
57
60
|
|
58
61
|
def semian_configuration=(configuration)
|
59
62
|
raise Semian::NetHTTP::SemianConfigurationChangedError unless @semian_configuration.nil?
|
63
|
+
|
60
64
|
@semian_configuration = configuration
|
61
65
|
end
|
62
66
|
|
@@ -88,11 +92,13 @@ module Semian
|
|
88
92
|
|
89
93
|
def connect
|
90
94
|
return super if disabled?
|
95
|
+
|
91
96
|
acquire_semian_resource(adapter: :http, scope: :connection) { super }
|
92
97
|
end
|
93
98
|
|
94
99
|
def transport_request(*)
|
95
100
|
return super if disabled?
|
101
|
+
|
96
102
|
acquire_semian_resource(adapter: :http, scope: :query) do
|
97
103
|
handle_error_responses(super)
|
98
104
|
end
|
data/lib/semian/platform.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Semian
|
2
4
|
extend self
|
3
5
|
|
@@ -11,6 +13,6 @@ module Semian
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def disabled?
|
14
|
-
ENV[
|
16
|
+
ENV["SEMIAN_SEMAPHORES_DISABLED"] || ENV["SEMIAN_DISABLED"]
|
15
17
|
end
|
16
18
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Semian
|
2
4
|
class ProtectedResource
|
3
5
|
extend Forwardable
|
4
6
|
|
5
7
|
def_delegators :@bulkhead, :destroy, :count, :semid, :tickets, :registered_workers
|
6
8
|
def_delegators :@circuit_breaker, :reset, :mark_failed, :mark_success, :request_allowed?,
|
7
|
-
|
9
|
+
:open?, :closed?, :half_open?
|
8
10
|
|
9
11
|
attr_reader :bulkhead, :circuit_breaker, :name
|
10
12
|
attr_accessor :updated_at
|
@@ -17,8 +19,8 @@ module Semian
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def destroy
|
20
|
-
@bulkhead
|
21
|
-
@circuit_breaker
|
22
|
+
@bulkhead&.destroy
|
23
|
+
@circuit_breaker&.destroy
|
22
24
|
end
|
23
25
|
|
24
26
|
def acquire(timeout: nil, scope: nil, adapter: nil, resource: nil)
|
data/lib/semian/rails.rb
CHANGED
@@ -1,9 +1,36 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
require "active_record/connection_adapters/abstract_adapter"
|
4
|
+
|
5
|
+
module Semian
|
6
|
+
module Rails
|
7
|
+
def semian_resource
|
8
|
+
@semian_resource ||= client_connection.semian_resource
|
9
|
+
end
|
10
|
+
|
11
|
+
def reconnect
|
12
|
+
@semian_resource = nil
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# client_connection is an instance of a Mysql2::Client
|
19
|
+
#
|
20
|
+
# The conditionals here support multiple Rails versions.
|
21
|
+
# - valid_raw_connection is for 7.1.x and above
|
22
|
+
# - @raw_connection is for 7.0.x
|
23
|
+
# - @connection is for versions below 6.1.x and below
|
24
|
+
def client_connection
|
25
|
+
if respond_to?(:valid_raw_connection)
|
26
|
+
valid_raw_connection
|
27
|
+
elsif instance_variable_defined?(:@raw_connection)
|
28
|
+
@raw_connection
|
29
|
+
else
|
30
|
+
@connection
|
31
|
+
end
|
32
|
+
end
|
8
33
|
end
|
9
34
|
end
|
35
|
+
|
36
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(Semian::Rails)
|
data/lib/semian/redis.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian/adapter"
|
4
|
+
require "redis"
|
3
5
|
|
4
6
|
class Redis
|
5
7
|
Redis::BaseConnectionError.include(::Semian::AdapterError)
|
@@ -17,7 +19,7 @@ class Redis
|
|
17
19
|
end
|
18
20
|
|
19
21
|
class ConnectionError < Redis::BaseConnectionError
|
20
|
-
# A Connection Reset is a fast failure and we don't want to track these errors in
|
22
|
+
# A Connection Reset is a fast failure and we don't want to track these errors in
|
21
23
|
# semian
|
22
24
|
def marks_semian_circuits?
|
23
25
|
message != "Connection lost (ECONNRESET)"
|
@@ -90,12 +92,11 @@ module Semian
|
|
90
92
|
|
91
93
|
def connect
|
92
94
|
acquire_semian_resource(adapter: :redis, scope: :connection) do
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
95
|
+
raw_connect
|
96
|
+
rescue SocketError, RuntimeError => e
|
97
|
+
raise ResolveError, semian_identifier if dns_resolve_failure?(e.cause || e)
|
98
|
+
|
99
|
+
raise
|
99
100
|
end
|
100
101
|
end
|
101
102
|
|
@@ -107,7 +108,7 @@ module Semian
|
|
107
108
|
|
108
109
|
begin
|
109
110
|
connection.timeout = temp_timeout if connected?
|
110
|
-
options[:timeout] = Float(temp_timeout)
|
111
|
+
options[:timeout] = Float(temp_timeout)
|
111
112
|
options[:connect_timeout] = Float(temp_timeout)
|
112
113
|
options[:read_timeout] = Float(temp_timeout)
|
113
114
|
options[:write_timeout] = Float(temp_timeout)
|
@@ -133,17 +134,18 @@ module Semian
|
|
133
134
|
|
134
135
|
def raw_semian_options
|
135
136
|
return options[:semian] if options.key?(:semian)
|
136
|
-
return options[
|
137
|
+
return options["semian"] if options.key?("semian")
|
137
138
|
end
|
138
139
|
|
139
140
|
def raise_if_out_of_memory(reply)
|
140
141
|
return unless reply.is_a?(::Redis::CommandError)
|
141
|
-
return unless reply.message
|
142
|
-
|
142
|
+
return unless reply.message =~ /OOM command not allowed when used memory > 'maxmemory'/
|
143
|
+
|
144
|
+
raise ::Redis::OutOfMemoryError, reply.message
|
143
145
|
end
|
144
146
|
|
145
147
|
def dns_resolve_failure?(e)
|
146
|
-
e.to_s.match?(/(can't resolve)|(name or service not known)|(nodename nor servname provided, or not known)|(failure in name resolution)/i)
|
148
|
+
e.to_s.match?(/(can't resolve)|(name or service not known)|(nodename nor servname provided, or not known)|(failure in name resolution)/i) # rubocop:disable Layout/LineLength
|
147
149
|
end
|
148
150
|
end
|
149
151
|
end
|
data/lib/semian/redis_client.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian/adapter"
|
4
|
+
require "redis-client"
|
3
5
|
|
4
6
|
class RedisClient
|
5
7
|
ConnectionError.include(::Semian::AdapterError)
|
@@ -104,7 +106,7 @@ module Semian
|
|
104
106
|
include RedisClientCommon
|
105
107
|
define_method(:semian_resource, Semian::Adapter.instance_method(:semian_resource))
|
106
108
|
define_method(:clear_semian_resource, Semian::Adapter.instance_method(:clear_semian_resource))
|
107
|
-
|
109
|
+
end
|
108
110
|
end
|
109
111
|
|
110
112
|
RedisClient.prepend(Semian::RedisClient)
|
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