semian 0.17.0 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|