activerecord-trilogy-failover 0.1.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +79 -0
- data/Rakefile +7 -0
- data/lib/activerecord-trilogy-failover.rb +3 -0
- data/lib/activerecord_trilogy_failover/adapter_patch.rb +27 -0
- data/lib/activerecord_trilogy_failover/railtie.rb +11 -0
- data/lib/activerecord_trilogy_failover/version.rb +5 -0
- data/lib/activerecord_trilogy_failover.rb +5 -0
- metadata +83 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5e6f84d43760ea21937b7eb3818b3c117e2aa5a43716568dfb7c800fbff16a40
|
|
4
|
+
data.tar.gz: cc3629c63028c92df3f3285dbc3f219c3fa9c00df61944d6250aaf93460b7e77
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 675583f7a32b1253ce365502761e838950393fd529ca438ec7cefca5846cd62b702dff2f7fa74f89bc437e1addde5e4625c0c4f6163a46d1e73520a2cee2e32e
|
|
7
|
+
data.tar.gz: 5a037835f24cc566a69a954019d190c6a875d67e2e69c8d1e9277662bf7b8332b1bcf568551f0d2e5d21c37914942d2c01697336517df0845c801c5c856032eb
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# activerecord-trilogy-failover
|
|
2
|
+
|
|
3
|
+
Automatic reconnection on MySQL read-only errors for ActiveRecord's Trilogy adapter.
|
|
4
|
+
|
|
5
|
+
When a MySQL server switches to read-only mode (e.g., Aurora failover, ProxySQL routing change, RDS Multi-AZ switchover), existing connections receive `ER_OPTION_PREVENTS_STATEMENT (1290)` on write attempts. This gem translates that error into `ActiveRecord::ConnectionFailed`, enabling Rails' built-in retry mechanism to transparently reconnect to the new writer.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
The gem prepends a thin patch to `TrilogyAdapter#translate_exception`:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Write attempt → 1290 error → ConnectionFailed → Rails retry → reconnect! → new writer
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- **SELECT** (with `allow_retry: true`): Automatically retried and reconnected
|
|
16
|
+
- **INSERT / UPDATE / DELETE**: Error propagates to the caller (no double-execution risk)
|
|
17
|
+
|
|
18
|
+
This is the correct behavior — writes should not be silently retried.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Add to your Gemfile:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem "activerecord-trilogy-failover", github: "riseshia/activerecord-trilogy-failover"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
**With Rails**: No configuration needed. The Railtie automatically patches `TrilogyAdapter` on load.
|
|
31
|
+
|
|
32
|
+
**Without Rails**: Manually prepend the patch:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
require "activerecord_trilogy_failover"
|
|
36
|
+
|
|
37
|
+
ActiveRecord::ConnectionAdapters::TrilogyAdapter.prepend(
|
|
38
|
+
ActiveRecordTrilogyFailover::AdapterPatch
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### database.yml tuning
|
|
43
|
+
|
|
44
|
+
Rails' built-in retry settings work with this gem:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
production:
|
|
48
|
+
adapter: trilogy
|
|
49
|
+
connection_retries: 2 # default: 1
|
|
50
|
+
retry_deadline: 5 # seconds, default: none
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Compatibility
|
|
54
|
+
|
|
55
|
+
- Ruby >= 3.2
|
|
56
|
+
- ActiveRecord >= 7.1
|
|
57
|
+
- Trilogy >= 2.0
|
|
58
|
+
- MySQL 8.0 (other versions are not tested)
|
|
59
|
+
|
|
60
|
+
## Development
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
bundle install
|
|
64
|
+
bundle exec rspec
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Integration test
|
|
68
|
+
|
|
69
|
+
Integration tests run against a real MySQL instance using Docker:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
docker compose up -d # start MySQL on port 3307
|
|
73
|
+
MYSQL_PORT=3307 bundle exec rspec spec/integration/
|
|
74
|
+
docker compose down -v # cleanup
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT
|
data/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecordTrilogyFailover
|
|
4
|
+
module AdapterPatch
|
|
5
|
+
# MySQL error code for ER_OPTION_PREVENTS_STATEMENT
|
|
6
|
+
# Raised when the server is running with --read-only
|
|
7
|
+
MYSQL_READ_ONLY_ERROR_CODE = 1290
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def translate_exception(exception, message:, sql:, binds:)
|
|
12
|
+
if read_only_error?(exception)
|
|
13
|
+
return ActiveRecord::ConnectionFailed.new(
|
|
14
|
+
"#{exception.class}: #{exception.message}",
|
|
15
|
+
connection_pool: @pool
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read_only_error?(exception)
|
|
22
|
+
exception.respond_to?(:error_code) &&
|
|
23
|
+
exception.error_code == MYSQL_READ_ONLY_ERROR_CODE &&
|
|
24
|
+
exception.message.include?("--read-only")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecordTrilogyFailover
|
|
4
|
+
class Railtie < Rails::Railtie
|
|
5
|
+
initializer "activerecord_trilogy_failover.patch" do
|
|
6
|
+
ActiveSupport.on_load(:active_record_trilogyadapter) do
|
|
7
|
+
prepend ActiveRecordTrilogyFailover::AdapterPatch
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: activerecord-trilogy-failover
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Shia
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activerecord
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: trilogy
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
40
|
+
description: |
|
|
41
|
+
Handles MySQL ER_OPTION_PREVENTS_STATEMENT (1290) errors by translating them
|
|
42
|
+
into ActiveRecord::ConnectionFailed, enabling Rails' built-in retry mechanism
|
|
43
|
+
to transparently reconnect. Useful for Aurora failover, ProxySQL, RDS Multi-AZ,
|
|
44
|
+
or any MySQL read-only switchover scenario.
|
|
45
|
+
email:
|
|
46
|
+
- rise.and.and@gmail.com
|
|
47
|
+
executables: []
|
|
48
|
+
extensions: []
|
|
49
|
+
extra_rdoc_files: []
|
|
50
|
+
files:
|
|
51
|
+
- LICENSE.txt
|
|
52
|
+
- README.md
|
|
53
|
+
- Rakefile
|
|
54
|
+
- lib/activerecord-trilogy-failover.rb
|
|
55
|
+
- lib/activerecord_trilogy_failover.rb
|
|
56
|
+
- lib/activerecord_trilogy_failover/adapter_patch.rb
|
|
57
|
+
- lib/activerecord_trilogy_failover/railtie.rb
|
|
58
|
+
- lib/activerecord_trilogy_failover/version.rb
|
|
59
|
+
homepage: https://github.com/riseshia/activerecord-trilogy-failover
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata:
|
|
63
|
+
homepage_uri: https://github.com/riseshia/activerecord-trilogy-failover
|
|
64
|
+
source_code_uri: https://github.com/riseshia/activerecord-trilogy-failover
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 3.2.0
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 3.6.9
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: Automatic reconnection on MySQL read-only errors for ActiveRecord Trilogy
|
|
82
|
+
adapter
|
|
83
|
+
test_files: []
|