semian 0.17.0 → 0.18.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +32 -2
- data/lib/semian/activerecord_trilogy_adapter.rb +122 -0
- data/lib/semian/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c0cecc8c8e42f223f2cde75e3818ad4bc33897107b6a99186beb48c413c6d30
|
4
|
+
data.tar.gz: 9202b9e1cf15ff54d68f8d3fe11800ace1624828d1aa840840b88180f65e5355
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bd71610d96984ada73fe625966f66ecc13cc921ac659a78c9010eec952f61e1ea56aa4c9f3e6de0572a2b2059e0cd919e1063907c4d39c9d630dd536bf4a89e
|
7
|
+
data.tar.gz: 20513585134a89dc3a33bcc2895728a64fa5e35e978dbcf83ac44fccc00ae9428e0f8bd8988fe3941e4236d4d18daed380014843b0ad071f945164e68eda68af
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
## Semian ![Build Status](https://github.com/Shopify/semian/actions/workflows/
|
2
|
-
|
1
|
+
## Semian ![Build Status](https://github.com/Shopify/semian/actions/workflows/test.yml/badge.svg)
|
3
2
|
|
4
3
|
![](http://i.imgur.com/7Vn2ibF.png)
|
5
4
|
|
@@ -73,6 +72,7 @@ version is the version of the public gem with the same name:
|
|
73
72
|
* [`semian/mysql2`][mysql-semian-adapter] (~> 0.3.16)
|
74
73
|
* [`semian/redis`][redis-semian-adapter] (~> 3.2.1)
|
75
74
|
* [`semian/net_http`][nethttp-semian-adapter]
|
75
|
+
* [`semian/activerecord_trilogy_adapter`][activerecord-trilogy-semian-adapter]
|
76
76
|
* [`semian-postgres`][postgres-semian-adapter]
|
77
77
|
|
78
78
|
### Creating Adapters
|
@@ -299,6 +299,35 @@ SEMIAN_PARAMETERS = { tickets: 1,
|
|
299
299
|
open_circuit_server_errors: true }
|
300
300
|
```
|
301
301
|
|
302
|
+
#### Active Record
|
303
|
+
|
304
|
+
Semian supports Active Record adapter `trilogy`.
|
305
|
+
It can be configured in the `database.yml`:
|
306
|
+
|
307
|
+
```yml
|
308
|
+
semian: &semian
|
309
|
+
success_threshold: 2
|
310
|
+
error_threshold: 3
|
311
|
+
error_timeout: 4
|
312
|
+
half_open_resource_timeout: 1
|
313
|
+
bulkhead: false # Disable bulkhead for Puma: https://github.com/shopify/semian#thread-safety
|
314
|
+
name: semian_identifier_name
|
315
|
+
|
316
|
+
default: &default
|
317
|
+
adapter: trilogy
|
318
|
+
username: root
|
319
|
+
password:
|
320
|
+
host: localhost
|
321
|
+
read_timeout: 2
|
322
|
+
write_timeout: 1
|
323
|
+
connect_timeout: 1
|
324
|
+
semian:
|
325
|
+
<<: *semian
|
326
|
+
```
|
327
|
+
|
328
|
+
Example cases for `activerecord-trilogy-adapter` can be run using
|
329
|
+
`BUNDLE_GEMFILE=gemfiles/activerecord_trilogy_adapter.gemfile bundle exec rake examples:activerecord_trilogy_adapter`
|
330
|
+
|
302
331
|
# Understanding Semian
|
303
332
|
|
304
333
|
Semian is a library with heuristics for failing fast. This section will explain
|
@@ -859,6 +888,7 @@ $ bundle install
|
|
859
888
|
[mysql-semian-adapter]: lib/semian/mysql2.rb
|
860
889
|
[postgres-semian-adapter]: https://github.com/mschoenlaub/semian-postgres
|
861
890
|
[redis-semian-adapter]: lib/semian/redis.rb
|
891
|
+
[activerecord-trilogy-semian-adapter]: lib/semian/activerecord_trilogy_adapter.rb
|
862
892
|
[semian-adapter]: lib/semian/adapter.rb
|
863
893
|
[nethttp-semian-adapter]: lib/semian/net_http.rb
|
864
894
|
[nethttp-default-errors]: lib/semian/net_http.rb#L35-L45
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semian/adapter"
|
4
|
+
require "active_record"
|
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, *)
|
51
|
+
if query_allowlisted?(sql)
|
52
|
+
super
|
53
|
+
else
|
54
|
+
acquire_semian_resource(adapter: :trilogy_adapter, scope: :execute) do
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
ruby2_keywords :execute
|
60
|
+
|
61
|
+
def active?
|
62
|
+
acquire_semian_resource(adapter: :trilogy_adapter, scope: :ping) do
|
63
|
+
super
|
64
|
+
end
|
65
|
+
rescue ResourceBusyError, CircuitOpenError
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
def with_resource_timeout(temp_timeout)
|
70
|
+
if connection.nil?
|
71
|
+
prev_read_timeout = @config[:read_timeout] || 0
|
72
|
+
@config.merge!(read_timeout: temp_timeout) # Create new client with temp_timeout for read timeout
|
73
|
+
else
|
74
|
+
prev_read_timeout = connection.read_timeout
|
75
|
+
connection.read_timeout = temp_timeout
|
76
|
+
end
|
77
|
+
yield
|
78
|
+
ensure
|
79
|
+
@config.merge!(read_timeout: prev_read_timeout)
|
80
|
+
connection&.read_timeout = prev_read_timeout
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def acquire_semian_resource(**)
|
86
|
+
super
|
87
|
+
rescue ActiveRecord::StatementInvalid => error
|
88
|
+
if error.cause.is_a?(Trilogy::TimeoutError)
|
89
|
+
semian_resource.mark_failed(error)
|
90
|
+
error.semian_identifier = semian_identifier
|
91
|
+
end
|
92
|
+
raise
|
93
|
+
end
|
94
|
+
|
95
|
+
def resource_exceptions
|
96
|
+
[ActiveRecord::ConnectionNotEstablished]
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO: share this with Mysql2
|
100
|
+
QUERY_ALLOWLIST = Regexp.union(
|
101
|
+
%r{\A(?:/\*.*?\*/)?\s*ROLLBACK}i,
|
102
|
+
%r{\A(?:/\*.*?\*/)?\s*COMMIT}i,
|
103
|
+
%r{\A(?:/\*.*?\*/)?\s*RELEASE\s+SAVEPOINT}i,
|
104
|
+
)
|
105
|
+
|
106
|
+
def query_allowlisted?(sql, *)
|
107
|
+
QUERY_ALLOWLIST.match?(sql)
|
108
|
+
rescue ArgumentError
|
109
|
+
return false unless sql.valid_encoding?
|
110
|
+
|
111
|
+
raise
|
112
|
+
end
|
113
|
+
|
114
|
+
def connect(*args)
|
115
|
+
acquire_semian_resource(adapter: :trilogy_adapter, scope: :connection) do
|
116
|
+
super
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
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.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-02
|
13
|
+
date: 2023-05-02 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
|
@@ -78,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
79
|
- !ruby/object:Gem::Version
|
79
80
|
version: '0'
|
80
81
|
requirements: []
|
81
|
-
rubygems_version: 3.
|
82
|
+
rubygems_version: 3.4.12
|
82
83
|
signing_key:
|
83
84
|
specification_version: 4
|
84
85
|
summary: Bulkheading for Ruby with SysV semaphores
|