semian 0.17.0 → 0.18.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/README.md +31 -0
- data/lib/semian/activerecord_trilogy_adapter.rb +121 -0
- data/lib/semian/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d24779fb714aedf9f83b9ea263d7a7bd0387bc487dbc2313fed0fa265be33b92
|
4
|
+
data.tar.gz: a505d6861420fe42f5f366184b73bdeb6aa8f3b4c5ee2a95ceb224f4a6770286
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e24a1414fb44190bde478cc02cf4c2f12dde3b73673446dee2225ee0155951be5121dbf077da05ce8d1746485f8ce2914b46a191ba09f05297f70177713d15f
|
7
|
+
data.tar.gz: d7e41a294ec3819216b4e5762b0ea98ca545002c528e64c291e606c0e1901b847c3d6ee31a4b927c2402b0249fed38faa5b46ecdbb5a4ae9e2ef767fa34702f7
|
data/README.md
CHANGED
@@ -73,6 +73,7 @@ version is the version of the public gem with the same name:
|
|
73
73
|
* [`semian/mysql2`][mysql-semian-adapter] (~> 0.3.16)
|
74
74
|
* [`semian/redis`][redis-semian-adapter] (~> 3.2.1)
|
75
75
|
* [`semian/net_http`][nethttp-semian-adapter]
|
76
|
+
* [`semian/activerecord_trilogy_adapter`][activerecord-trilogy-semian-adapter]
|
76
77
|
* [`semian-postgres`][postgres-semian-adapter]
|
77
78
|
|
78
79
|
### Creating Adapters
|
@@ -299,6 +300,35 @@ SEMIAN_PARAMETERS = { tickets: 1,
|
|
299
300
|
open_circuit_server_errors: true }
|
300
301
|
```
|
301
302
|
|
303
|
+
#### Active Record
|
304
|
+
|
305
|
+
Semian supports Active Record adapter `trilogy`.
|
306
|
+
It can be configured in the `database.yml`:
|
307
|
+
|
308
|
+
```yml
|
309
|
+
semian: &semian
|
310
|
+
success_threshold: 2
|
311
|
+
error_threshold: 3
|
312
|
+
error_timeout: 4
|
313
|
+
half_open_resource_timeout: 1
|
314
|
+
bulkhead: false # Disable bulkhead for Puma: https://github.com/shopify/semian#thread-safety
|
315
|
+
name: semian_identifier_name
|
316
|
+
|
317
|
+
default: &default
|
318
|
+
adapter: trilogy
|
319
|
+
username: root
|
320
|
+
password:
|
321
|
+
host: localhost
|
322
|
+
read_timeout: 2
|
323
|
+
write_timeout: 1
|
324
|
+
connect_timeout: 1
|
325
|
+
semian:
|
326
|
+
<<: *semian
|
327
|
+
```
|
328
|
+
|
329
|
+
Example cases for `activerecord-trilogy-adapter` can be run using
|
330
|
+
`BUNDLE_GEMFILE=gemfiles/activerecord_trilogy_adapter.gemfile bundle exec rake examples:activerecord_trilogy_adapter`
|
331
|
+
|
302
332
|
# Understanding Semian
|
303
333
|
|
304
334
|
Semian is a library with heuristics for failing fast. This section will explain
|
@@ -859,6 +889,7 @@ $ bundle install
|
|
859
889
|
[mysql-semian-adapter]: lib/semian/mysql2.rb
|
860
890
|
[postgres-semian-adapter]: https://github.com/mschoenlaub/semian-postgres
|
861
891
|
[redis-semian-adapter]: lib/semian/redis.rb
|
892
|
+
[activerecord-trilogy-semian-adapter]: lib/semian/activerecord_trilogy_adapter.rb
|
862
893
|
[semian-adapter]: lib/semian/adapter.rb
|
863
894
|
[nethttp-semian-adapter]: lib/semian/net_http.rb
|
864
895
|
[nethttp-default-errors]: lib/semian/net_http.rb#L35-L45
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian/adapter"
|
4
|
+
require "activerecord-trilogy-adapter"
|
5
|
+
require "active_record/connection_adapters/trilogy_adapter"
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionAdapters
|
9
|
+
class TrilogyAdapter
|
10
|
+
ActiveRecord::ActiveRecordError.include(::Semian::AdapterError)
|
11
|
+
|
12
|
+
class SemianError < StatementInvalid
|
13
|
+
def initialize(semian_identifier, *args)
|
14
|
+
super(*args)
|
15
|
+
@semian_identifier = semian_identifier
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ResourceBusyError = Class.new(SemianError)
|
20
|
+
CircuitOpenError = Class.new(SemianError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Semian
|
26
|
+
module ActiveRecordTrilogyAdapter
|
27
|
+
include Semian::Adapter
|
28
|
+
|
29
|
+
ResourceBusyError = ::ActiveRecord::ConnectionAdapters::TrilogyAdapter::ResourceBusyError
|
30
|
+
CircuitOpenError = ::ActiveRecord::ConnectionAdapters::TrilogyAdapter::CircuitOpenError
|
31
|
+
|
32
|
+
attr_reader :raw_semian_options, :semian_identifier
|
33
|
+
|
34
|
+
def initialize(*options)
|
35
|
+
*, config = options
|
36
|
+
config = config.dup
|
37
|
+
@raw_semian_options = config.delete(:semian)
|
38
|
+
@semian_identifier = begin
|
39
|
+
name = semian_options && semian_options[:name]
|
40
|
+
unless name
|
41
|
+
host = config[:host] || "localhost"
|
42
|
+
port = config[:port] || 3306
|
43
|
+
name = "#{host}:#{port}"
|
44
|
+
end
|
45
|
+
:"mysql_#{name}"
|
46
|
+
end
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def execute(sql, name = nil, async: false, allow_retry: false)
|
51
|
+
if query_allowlisted?(sql)
|
52
|
+
super(sql, name, async: async, allow_retry: allow_retry)
|
53
|
+
else
|
54
|
+
acquire_semian_resource(adapter: :trilogy_adapter, scope: :execute) do
|
55
|
+
super(sql, name, async: async, allow_retry: allow_retry)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def active?
|
61
|
+
acquire_semian_resource(adapter: :trilogy_adapter, scope: :ping) do
|
62
|
+
super
|
63
|
+
end
|
64
|
+
rescue ResourceBusyError, CircuitOpenError
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
def with_resource_timeout(temp_timeout)
|
69
|
+
if connection.nil?
|
70
|
+
prev_read_timeout = @config[:read_timeout] || 0
|
71
|
+
@config.merge!(read_timeout: temp_timeout) # Create new client with temp_timeout for read timeout
|
72
|
+
else
|
73
|
+
prev_read_timeout = connection.read_timeout
|
74
|
+
connection.read_timeout = temp_timeout
|
75
|
+
end
|
76
|
+
yield
|
77
|
+
ensure
|
78
|
+
@config.merge!(read_timeout: prev_read_timeout)
|
79
|
+
connection&.read_timeout = prev_read_timeout
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def acquire_semian_resource(**)
|
85
|
+
super
|
86
|
+
rescue ActiveRecord::StatementInvalid => error
|
87
|
+
if error.cause.is_a?(Trilogy::TimeoutError)
|
88
|
+
semian_resource.mark_failed(error)
|
89
|
+
error.semian_identifier = semian_identifier
|
90
|
+
end
|
91
|
+
raise
|
92
|
+
end
|
93
|
+
|
94
|
+
def resource_exceptions
|
95
|
+
[ActiveRecord::ConnectionNotEstablished]
|
96
|
+
end
|
97
|
+
|
98
|
+
# TODO: share this with Mysql2
|
99
|
+
QUERY_ALLOWLIST = Regexp.union(
|
100
|
+
%r{\A(?:/\*.*?\*/)?\s*ROLLBACK}i,
|
101
|
+
%r{\A(?:/\*.*?\*/)?\s*COMMIT}i,
|
102
|
+
%r{\A(?:/\*.*?\*/)?\s*RELEASE\s+SAVEPOINT}i,
|
103
|
+
)
|
104
|
+
|
105
|
+
def query_allowlisted?(sql, *)
|
106
|
+
QUERY_ALLOWLIST.match?(sql)
|
107
|
+
rescue ArgumentError
|
108
|
+
return false unless sql.valid_encoding?
|
109
|
+
|
110
|
+
raise
|
111
|
+
end
|
112
|
+
|
113
|
+
def connect(*args)
|
114
|
+
acquire_semian_resource(adapter: :trilogy_adapter, scope: :connection) do
|
115
|
+
super
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
ActiveRecord::ConnectionAdapters::TrilogyAdapter.prepend(Semian::ActiveRecordTrilogyAdapter)
|
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.18.0
|
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-02-
|
13
|
+
date: 2023-02-15 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
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- ext/semian/tickets.h
|
35
35
|
- ext/semian/types.h
|
36
36
|
- lib/semian.rb
|
37
|
+
- lib/semian/activerecord_trilogy_adapter.rb
|
37
38
|
- lib/semian/adapter.rb
|
38
39
|
- lib/semian/circuit_breaker.rb
|
39
40
|
- lib/semian/grpc.rb
|