janus-ar 8.0.1 → 8.1.1
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/Gemfile +1 -3
- data/Gemfile.lock +27 -24
- data/README.md +69 -3
- data/Rakefile +0 -2
- data/janus-ar.gemspec +7 -6
- data/lib/janus-ar/active_record/connection_adapters/janus_mysql2_adapter.rb +0 -2
- data/lib/janus-ar/active_record/connection_adapters/janus_postgresql_adapter.rb +42 -0
- data/lib/janus-ar/active_record/connection_adapters/janus_trilogy_adapter.rb +0 -2
- data/lib/janus-ar/adapter_extensions.rb +21 -6
- data/lib/janus-ar/client.rb +5 -6
- data/lib/janus-ar/context.rb +0 -2
- data/lib/janus-ar/db_console_config.rb +15 -17
- data/lib/janus-ar/logging/logger.rb +13 -15
- data/lib/janus-ar/logging/subscriber.rb +25 -27
- data/lib/janus-ar/query_director.rb +15 -4
- data/lib/janus-ar/railtie.rb +0 -2
- data/lib/janus-ar/version.rb +1 -3
- data/lib/janus-ar.rb +0 -2
- metadata +23 -31
- data/.bundle/config +0 -5
- data/.github/dependabot.yml +0 -7
- data/.github/workflows/ci.yml +0 -68
- data/.github/workflows/publish.yml +0 -25
- data/.gitignore +0 -3
- data/.rspec +0 -3
- data/.rubocop.yml +0 -29
- data/assets/.gitkeep +0 -1
- data/assets/janus-logo.png +0 -0
- data/bin/release.sh +0 -4
- data/gemfiles/activerecord_8_0.gemfile +0 -7
- data/gemfiles/activerecord_8_1.gemfile +0 -7
- data/spec/lib/janus-ar/active_record/connection_adapters/janus_mysql_adapter_spec.rb +0 -78
- data/spec/lib/janus-ar/active_record/connection_adapters/janus_trilogy_adapter_spec.rb +0 -77
- data/spec/lib/janus-ar/client_spec.rb +0 -7
- data/spec/lib/janus-ar/context_spec.rb +0 -118
- data/spec/lib/janus-ar/db_console_config_spec.rb +0 -28
- data/spec/lib/janus-ar/logging/logger_spec.rb +0 -31
- data/spec/lib/janus-ar/logging/subscriber_spec.rb +0 -58
- data/spec/lib/janus-ar/query_director_spec.rb +0 -141
- data/spec/shared_examples/a_mysql_like_server.rb +0 -155
- data/spec/spec_helper.rb +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1720a88365b20f91d220065128ca260baf91380779a7a1d9a6f9aa8fffc13ae8
|
|
4
|
+
data.tar.gz: 2987d7e690b1038946a489a80df29917a192c1a07b85bc16932fb0bf8c2df531
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae79f4f2c54ea96d6191a2b7597982a558027ed911d894e66bbb205041d2339e0b33bb9004ac8331162c2766ac91c922f1ebd11e125ecd18e2f30db8b8cf173c
|
|
7
|
+
data.tar.gz: 1224b98a5c03641329d47f1eee868247141cfd6299f98a1f364aed01bef7eb4c3897c4a1e3675734a04aabde3b4ca8165ddb58fe07f36c4f97691c8fcec89e17
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
janus-ar (8.
|
|
4
|
+
janus-ar (8.1.1)
|
|
5
5
|
activerecord (>= 8.0, < 9.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
|
-
remote:
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
activemodel (8.1.3)
|
|
11
|
-
activesupport (= 8.1.3)
|
|
12
|
-
activerecord (8.1.3)
|
|
13
|
-
activemodel (= 8.1.3)
|
|
14
|
-
activesupport (= 8.1.3)
|
|
10
|
+
activemodel (8.1.3.1)
|
|
11
|
+
activesupport (= 8.1.3.1)
|
|
12
|
+
activerecord (8.1.3.1)
|
|
13
|
+
activemodel (= 8.1.3.1)
|
|
14
|
+
activesupport (= 8.1.3.1)
|
|
15
15
|
timeout (>= 0.4.0)
|
|
16
|
-
activesupport (8.1.3)
|
|
16
|
+
activesupport (8.1.3.1)
|
|
17
17
|
base64
|
|
18
18
|
bigdecimal
|
|
19
19
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
@@ -30,15 +30,15 @@ GEM
|
|
|
30
30
|
base64 (0.3.0)
|
|
31
31
|
bigdecimal (4.1.2)
|
|
32
32
|
coderay (1.1.3)
|
|
33
|
-
concurrent-ruby (1.3.
|
|
33
|
+
concurrent-ruby (1.3.8)
|
|
34
34
|
connection_pool (3.0.2)
|
|
35
35
|
diff-lcs (1.6.2)
|
|
36
36
|
drb (2.2.3)
|
|
37
|
-
i18n (1.
|
|
37
|
+
i18n (1.15.2)
|
|
38
38
|
concurrent-ruby (~> 1.0)
|
|
39
39
|
io-console (0.8.2)
|
|
40
|
-
json (2.
|
|
41
|
-
language_server-protocol (3.17.0.
|
|
40
|
+
json (2.21.2)
|
|
41
|
+
language_server-protocol (3.17.0.6)
|
|
42
42
|
lint_roller (1.1.0)
|
|
43
43
|
logger (1.7.0)
|
|
44
44
|
method_source (1.1.0)
|
|
@@ -48,16 +48,18 @@ GEM
|
|
|
48
48
|
mysql2 (0.5.7)
|
|
49
49
|
bigdecimal
|
|
50
50
|
parallel (1.28.0)
|
|
51
|
-
parser (3.3.
|
|
51
|
+
parser (3.3.12.0)
|
|
52
52
|
ast (~> 2.4.1)
|
|
53
53
|
racc
|
|
54
|
+
pg (1.6.3-arm64-darwin)
|
|
55
|
+
pg (1.6.3-x86_64-linux)
|
|
54
56
|
prism (1.9.0)
|
|
55
57
|
pry (0.16.0)
|
|
56
58
|
coderay (~> 1.1)
|
|
57
59
|
method_source (~> 1.0)
|
|
58
60
|
reline (>= 0.6.0)
|
|
59
61
|
racc (1.8.1)
|
|
60
|
-
rack (3.2.
|
|
62
|
+
rack (3.2.7)
|
|
61
63
|
rainbow (3.1.1)
|
|
62
64
|
rake (13.4.2)
|
|
63
65
|
regexp_parser (2.12.0)
|
|
@@ -76,8 +78,8 @@ GEM
|
|
|
76
78
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
77
79
|
rspec-support (~> 3.13.0)
|
|
78
80
|
rspec-support (3.13.7)
|
|
79
|
-
rubocop (1.
|
|
80
|
-
json (
|
|
81
|
+
rubocop (1.90.0)
|
|
82
|
+
json (>= 2.3)
|
|
81
83
|
language_server-protocol (~> 3.17.0.2)
|
|
82
84
|
lint_roller (~> 1.1.0)
|
|
83
85
|
parallel (>= 1.10)
|
|
@@ -87,18 +89,18 @@ GEM
|
|
|
87
89
|
rubocop-ast (>= 1.49.0, < 2.0)
|
|
88
90
|
ruby-progressbar (~> 1.7)
|
|
89
91
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
90
|
-
rubocop-ast (1.
|
|
92
|
+
rubocop-ast (1.50.0)
|
|
91
93
|
parser (>= 3.3.7.2)
|
|
92
94
|
prism (~> 1.7)
|
|
93
|
-
rubocop-performance (1.
|
|
95
|
+
rubocop-performance (1.27.0)
|
|
94
96
|
lint_roller (~> 1.1)
|
|
95
|
-
rubocop (>= 1.
|
|
97
|
+
rubocop (>= 1.89.0, < 2.0)
|
|
96
98
|
rubocop-ast (>= 1.47.1, < 2.0)
|
|
97
|
-
rubocop-rails (2.
|
|
99
|
+
rubocop-rails (2.37.0)
|
|
98
100
|
activesupport (>= 4.2.0)
|
|
99
101
|
lint_roller (~> 1.1)
|
|
100
102
|
rack (>= 1.1)
|
|
101
|
-
rubocop (>= 1.
|
|
103
|
+
rubocop (>= 1.89.0, < 2.0)
|
|
102
104
|
rubocop-ast (>= 1.44.0, < 2.0)
|
|
103
105
|
rubocop-rspec (3.10.2)
|
|
104
106
|
lint_roller (~> 1.1)
|
|
@@ -111,7 +113,7 @@ GEM
|
|
|
111
113
|
ruby-progressbar (1.13.0)
|
|
112
114
|
securerandom (0.4.1)
|
|
113
115
|
timeout (0.6.1)
|
|
114
|
-
trilogy (2.
|
|
116
|
+
trilogy (2.13.0)
|
|
115
117
|
bigdecimal
|
|
116
118
|
tzinfo (2.0.6)
|
|
117
119
|
concurrent-ruby (~> 1.0)
|
|
@@ -128,12 +130,13 @@ DEPENDENCIES
|
|
|
128
130
|
activesupport (>= 8.0)
|
|
129
131
|
janus-ar!
|
|
130
132
|
mysql2
|
|
133
|
+
pg
|
|
131
134
|
pry
|
|
132
135
|
rake
|
|
133
136
|
rspec (~> 3)
|
|
134
|
-
rubocop (~> 1.
|
|
137
|
+
rubocop (~> 1.90.0)
|
|
135
138
|
rubocop-performance
|
|
136
|
-
rubocop-rails (~> 2.
|
|
139
|
+
rubocop-rails (~> 2.37.0)
|
|
137
140
|
rubocop-rspec
|
|
138
141
|
rubocop-thread_safety
|
|
139
142
|
trilogy
|
data/README.md
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
[](https://github.com/OLIOEX/janus-ar/actions/workflows/ci.yml)
|
|
12
12
|
[](https://badge.fury.io/rb/janus-ar)
|
|
13
13
|
|
|
14
|
-
Janus ActiveRecord is a generic primary/replica proxy for ActiveRecord 8
|
|
14
|
+
Janus ActiveRecord is a generic primary/replica proxy for ActiveRecord 8, supporting MySQL (via `mysql2` and `trilogy`) and PostgreSQL (via `pg`). It handles the switching of connections between primary and replica database servers. It comes with an ActiveRecord database adapter implementation.
|
|
15
15
|
|
|
16
16
|
Janus is heavily inspired by [Makara](https://github.com/instacart/makara) from TaskRabbit and then Instacart. Unfortunately this project is unmaintained and broke for us with Rails 7.1. This is an attempt to start afresh on the project. It is definitely not as fully featured as Makara at this stage.
|
|
17
17
|
|
|
18
18
|
Learn more about its origins: [https://tech.olioex.com/ruby/2024/04/16/introducing-janus.html](https://tech.olioex.com/ruby/2024/04/16/introducing-janus.html).
|
|
19
19
|
|
|
20
|
-
Notes: the gem requires ActiveRecord `>= 8.0, < 9.0` and Ruby `>= 3.2`, and is tested against MySQL 8.
|
|
20
|
+
Notes: the gem requires ActiveRecord `>= 8.0, < 9.0` and Ruby `>= 3.2`, and is tested against MySQL 8 and PostgreSQL 16 and 17.
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
@@ -45,6 +45,8 @@ require 'rails/all'
|
|
|
45
45
|
ActiveRecord::ConnectionAdapters.register("janus_trilogy", "ActiveRecord::ConnectionAdapters::JanusTrilogyAdapter", 'janus-ar/active_record/connection_adapters/janus_trilogy_adapter')
|
|
46
46
|
# ...or...
|
|
47
47
|
ActiveRecord::ConnectionAdapters.register("janus_mysql2", "ActiveRecord::ConnectionAdapters::JanusMysql2Adapter", 'janus-ar/active_record/connection_adapters/janus_mysql2_adapter')
|
|
48
|
+
# ...or...
|
|
49
|
+
ActiveRecord::ConnectionAdapters.register("janus_postgresql", "ActiveRecord::ConnectionAdapters::JanusPostgreSQLAdapter", 'janus-ar/active_record/connection_adapters/janus_postgresql_adapter')
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
#### Rails <= 7.1
|
|
@@ -81,6 +83,45 @@ Note: For `trilogy` please use adapter "janus_trilogy". You'll probably need to
|
|
|
81
83
|
Otherwise you will get an error like the following (see https://github.com/trilogy-libraries/trilogy/issues/26):
|
|
82
84
|
> trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket: TRILOGY_UNSUPPORTED"
|
|
83
85
|
|
|
86
|
+
#### PostgreSQL
|
|
87
|
+
|
|
88
|
+
Use adapter `janus_postgresql`, and add the `pg` gem to your `Gemfile`:
|
|
89
|
+
|
|
90
|
+
```yml
|
|
91
|
+
development:
|
|
92
|
+
adapter: janus_postgresql
|
|
93
|
+
database: database_name
|
|
94
|
+
janus:
|
|
95
|
+
primary:
|
|
96
|
+
<<: *default
|
|
97
|
+
host: primary-host.local
|
|
98
|
+
username: app
|
|
99
|
+
password: primary_password
|
|
100
|
+
replica:
|
|
101
|
+
<<: *default
|
|
102
|
+
host: replica-host.local
|
|
103
|
+
username: app_readonly
|
|
104
|
+
password: replica_password
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Anything the adapter reads out of its own configuration — `pool`, `prepared_statements`,
|
|
108
|
+
`insert_returning`, `variables`, `schema_search_path`, SSL settings and so on — must go
|
|
109
|
+
inside the `primary:` and `replica:` blocks rather than alongside `adapter:`, because each
|
|
110
|
+
connection is built from its own block.
|
|
111
|
+
|
|
112
|
+
Two things worth knowing about the PostgreSQL adapter specifically:
|
|
113
|
+
|
|
114
|
+
* Unlike MySQL, PostgreSQL never inlines bind values into the statement: reads
|
|
115
|
+
reach the replica as `$1` placeholders plus a separate parameter list, and
|
|
116
|
+
prepared statements are cached per connection. The adapter forwards binds and
|
|
117
|
+
the prepare flag to the replica, so each connection maintains its own
|
|
118
|
+
statement cache.
|
|
119
|
+
* Type OIDs are resolved against whichever connection served the lookup. This is
|
|
120
|
+
correct for a physical (streaming) replica, where OIDs are identical to the
|
|
121
|
+
primary's by construction. If you point Janus at a logical replica whose custom
|
|
122
|
+
types, enums or extensions were created independently, the OIDs can diverge and
|
|
123
|
+
results may be cast with the wrong type.
|
|
124
|
+
|
|
84
125
|
### Forcing connections
|
|
85
126
|
|
|
86
127
|
A context is local to the current unit of work (thread or fiber, following ActiveRecord's configured isolation level). This allows you to stick to the primary safely within a single request or job, in systems such as Sidekiq for instance.
|
|
@@ -119,7 +160,9 @@ There are some edge cases:
|
|
|
119
160
|
* `SET` operations will be sent to all connections
|
|
120
161
|
* Execution of specific methods such as `connect!`, `disconnect!`, `reconnect!`, and `clear_cache!` are invoked on all underlying connections
|
|
121
162
|
* Calls inside a transaction will always be sent to the primary (otherwise changes from within the transaction could not be read back on most transaction isolation levels)
|
|
122
|
-
* Locking reads (e.g. `SELECT ... FOR UPDATE`) will always be sent to the primary
|
|
163
|
+
* Locking reads (e.g. `SELECT ... FOR UPDATE`, `FOR UPDATE SKIP LOCKED`, `FOR SHARE`, `FOR NO KEY UPDATE`, `FOR KEY SHARE`, `LOCK IN SHARE MODE`) will always be sent to the primary
|
|
164
|
+
* So will reads that call a locking or sequence function — `GET_LOCK(...)`, `IS_FREE_LOCK(...)`, `nextval(...)`, `setval(...)` and PostgreSQL's advisory lock family (`pg_advisory_lock`, `pg_try_advisory_xact_lock_shared`, `pg_advisory_unlock_all`, and so on)
|
|
165
|
+
* PostgreSQL cursor statements (`DECLARE`, `FETCH`, `MOVE`, `CLOSE`) go to the primary, so a cursor is always fetched on the connection that declared it
|
|
123
166
|
|
|
124
167
|
# Notes
|
|
125
168
|
|
|
@@ -128,3 +171,26 @@ Janus does not support Rails' read/write split or sharding using `with_connectio
|
|
|
128
171
|
# Acknowlegements
|
|
129
172
|
|
|
130
173
|
Amazing project logo by @undevelopedbruce.
|
|
174
|
+
|
|
175
|
+
## Releasing
|
|
176
|
+
|
|
177
|
+
Releases are cut from the [GitHub Releases UI](https://github.com/OLIOEX/janus-ar/releases/new).
|
|
178
|
+
Publishing to RubyGems is automatic — there is nothing to bump by hand, and no
|
|
179
|
+
API key to rotate (authentication uses RubyGems
|
|
180
|
+
[trusted publishing](https://guides.rubygems.org/trusted-publishing/) over OIDC).
|
|
181
|
+
|
|
182
|
+
To release:
|
|
183
|
+
|
|
184
|
+
1. Create a new release against the head of `main`, with a tag named
|
|
185
|
+
`vMAJOR.MINOR.PATCH` — e.g. `v8.1.0`. Pre-release suffixes use a dot, as
|
|
186
|
+
RubyGems requires: `v8.1.0.rc1`, not `v8.1.0-rc1`.
|
|
187
|
+
2. Publish it.
|
|
188
|
+
|
|
189
|
+
[`.github/workflows/publish.yml`](.github/workflows/publish.yml) then takes the
|
|
190
|
+
version from the tag, writes it into `lib/janus-ar/version.rb`, refreshes
|
|
191
|
+
`Gemfile.lock`, commits that bump to `main` as `Release vX.Y.Z`, moves the tag
|
|
192
|
+
onto that commit, and pushes the gem.
|
|
193
|
+
|
|
194
|
+
Because the bump is committed to `main`, releases must be created from its head
|
|
195
|
+
— the workflow refuses to publish if the tag sits anywhere else. You never need
|
|
196
|
+
to edit `version.rb` yourself; the tag is the source of truth.
|
data/Rakefile
CHANGED
data/janus-ar.gemspec
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
require File.expand_path('lib/janus-ar/version.rb', __dir__)
|
|
4
2
|
|
|
5
3
|
Gem::Specification.new do |gem|
|
|
@@ -13,8 +11,10 @@ Gem::Specification.new do |gem|
|
|
|
13
11
|
'source_code_uri' => 'https://github.com/olioex/janus-ar',
|
|
14
12
|
}
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
gem.
|
|
14
|
+
ignored = %r{^(bin|spec|assets|gemfiles|\.github)/|^\.}
|
|
15
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR).grep_v(ignored)
|
|
16
|
+
# The gem ships no executables; bin/ holds repository maintenance scripts.
|
|
17
|
+
gem.executables = []
|
|
18
18
|
gem.name = 'janus-ar'
|
|
19
19
|
gem.require_paths = %w(lib)
|
|
20
20
|
gem.version = Janus::VERSION
|
|
@@ -24,12 +24,13 @@ Gem::Specification.new do |gem|
|
|
|
24
24
|
gem.add_dependency 'activerecord', '>= 8.0', '< 9.0'
|
|
25
25
|
gem.add_development_dependency 'activesupport', '>= 8.0'
|
|
26
26
|
gem.add_development_dependency 'mysql2'
|
|
27
|
+
gem.add_development_dependency 'pg'
|
|
27
28
|
gem.add_development_dependency 'trilogy'
|
|
28
29
|
gem.add_development_dependency 'pry'
|
|
29
30
|
gem.add_development_dependency 'rake'
|
|
30
31
|
gem.add_development_dependency 'rspec', '~> 3'
|
|
31
|
-
gem.add_development_dependency 'rubocop', '~> 1.
|
|
32
|
-
gem.add_development_dependency 'rubocop-rails', '~> 2.
|
|
32
|
+
gem.add_development_dependency 'rubocop', '~> 1.90.0'
|
|
33
|
+
gem.add_development_dependency 'rubocop-rails', '~> 2.37.0'
|
|
33
34
|
gem.add_development_dependency 'rubocop-rspec'
|
|
34
35
|
gem.add_development_dependency 'rubocop-thread_safety'
|
|
35
36
|
gem.add_development_dependency 'rubocop-performance'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
|
4
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
|
5
|
+
require_relative '../../../janus-ar'
|
|
6
|
+
require_relative '../../adapter_extensions'
|
|
7
|
+
|
|
8
|
+
module ActiveRecord
|
|
9
|
+
module ConnectionHandling
|
|
10
|
+
def janus_postgresql_connection(config)
|
|
11
|
+
ActiveRecord::ConnectionAdapters::JanusPostgreSQLAdapter.new(config)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Base
|
|
16
|
+
def self.janus_postgresql_adapter_class
|
|
17
|
+
ActiveRecord::ConnectionAdapters::JanusPostgreSQLAdapter
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ConnectionAdapters
|
|
22
|
+
class JanusPostgreSQLAdapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
23
|
+
include Janus::AdapterExtensions
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def replica_adapter_class
|
|
28
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# PostgreSQL compiles statements to `$1` placeholders and passes the values
|
|
32
|
+
# separately, so the replica needs the whole call, not just the SQL.
|
|
33
|
+
#
|
|
34
|
+
# `raw_execute` is private on a stock adapter, hence the `send`. It is the
|
|
35
|
+
# right target rather than the public `execute`: the statement has already
|
|
36
|
+
# been through `preprocess_query`, which we do not want applied twice.
|
|
37
|
+
def forward_raw_execute(sql, ...)
|
|
38
|
+
replica_connection.send(:raw_execute, sql, ...)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
module Janus
|
|
4
2
|
# Behaviour shared by the Janus MySQL2 and Trilogy adapters.
|
|
5
3
|
#
|
|
@@ -34,17 +32,17 @@ module Janus
|
|
|
34
32
|
@connection_parameters ||= args[0]
|
|
35
33
|
end
|
|
36
34
|
|
|
37
|
-
# The argument lists below intentionally use
|
|
35
|
+
# The argument lists below intentionally use argument forwarding and a bare
|
|
38
36
|
# `super`: ActiveRecord's `raw_execute`/`execute` signatures differ between
|
|
39
37
|
# versions, so we forward whatever we are given unchanged rather than
|
|
40
38
|
# restating (and pinning ourselves to) the current signature.
|
|
41
|
-
def raw_execute(sql,
|
|
39
|
+
def raw_execute(sql, ...)
|
|
42
40
|
case where_to_send?(sql)
|
|
43
41
|
when :all
|
|
44
|
-
|
|
42
|
+
send_raw_to_replica(sql, :all, ...)
|
|
45
43
|
super
|
|
46
44
|
when :replica
|
|
47
|
-
|
|
45
|
+
send_raw_to_replica(sql, :replica, ...)
|
|
48
46
|
else
|
|
49
47
|
mark_primary(sql)
|
|
50
48
|
super
|
|
@@ -74,6 +72,11 @@ module Janus
|
|
|
74
72
|
super
|
|
75
73
|
end
|
|
76
74
|
|
|
75
|
+
def discard!(...)
|
|
76
|
+
replica_connection.discard!(...)
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
|
|
77
80
|
def disconnect!(...)
|
|
78
81
|
replica_connection.disconnect!(...)
|
|
79
82
|
super
|
|
@@ -103,5 +106,17 @@ module Janus
|
|
|
103
106
|
Janus::Context.used_connection(connection)
|
|
104
107
|
replica_connection.execute(sql)
|
|
105
108
|
end
|
|
109
|
+
|
|
110
|
+
def send_raw_to_replica(sql, connection, ...)
|
|
111
|
+
Janus::Context.used_connection(connection)
|
|
112
|
+
forward_raw_execute(sql, ...)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The MySQL adapters inline bind values into the SQL string, so replaying the
|
|
116
|
+
# statement alone is enough. Adapters that keep binds separate must override
|
|
117
|
+
# this to carry them across.
|
|
118
|
+
def forward_raw_execute(sql, *, **)
|
|
119
|
+
replica_connection.execute(sql)
|
|
120
|
+
end
|
|
106
121
|
end
|
|
107
122
|
end
|
data/lib/janus-ar/client.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
end
|
|
1
|
+
module Janus
|
|
2
|
+
class Client
|
|
3
|
+
FOUND_ROWS = 2
|
|
4
|
+
end
|
|
5
|
+
end
|
data/lib/janus-ar/context.rb
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
end
|
|
17
|
-
end
|
|
1
|
+
module Janus
|
|
2
|
+
class DbConsoleConfig
|
|
3
|
+
def initialize(config)
|
|
4
|
+
@_config = config.configuration_hash
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def configuration_hash
|
|
8
|
+
@_config[:janus]['replica'].symbolize_keys
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def database
|
|
12
|
+
@_config[:database]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
end
|
|
1
|
+
module Janus
|
|
2
|
+
module Logging
|
|
3
|
+
class Logger
|
|
4
|
+
class << self
|
|
5
|
+
def log(message, format = :info)
|
|
6
|
+
logger&.send(format, "[Janus] #{message}")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_accessor :logger
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
module
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
end
|
|
27
|
-
end
|
|
1
|
+
module Janus
|
|
2
|
+
module Logging
|
|
3
|
+
module Subscriber
|
|
4
|
+
IGNORE_PAYLOAD_NAMES = %w(SCHEMA EXPLAIN).freeze
|
|
5
|
+
|
|
6
|
+
def sql(event)
|
|
7
|
+
name = event.payload[:name]
|
|
8
|
+
unless IGNORE_PAYLOAD_NAMES.include?(name)
|
|
9
|
+
name = [current_wrapper_name(event), name].compact.join(' ')
|
|
10
|
+
event.payload[:name] = name
|
|
11
|
+
end
|
|
12
|
+
super(event)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
protected
|
|
16
|
+
|
|
17
|
+
def current_wrapper_name(_event)
|
|
18
|
+
connection = Janus::Context.last_used_connection
|
|
19
|
+
return nil unless connection
|
|
20
|
+
|
|
21
|
+
"[#{connection}]"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
1
|
module Janus
|
|
3
2
|
class QueryDirector
|
|
4
3
|
ALL = :all
|
|
5
4
|
REPLICA = :replica
|
|
6
5
|
PRIMARY = :primary
|
|
7
6
|
|
|
7
|
+
# Regexp fragments, not literal names: PostgreSQL's advisory lock family has
|
|
8
|
+
# too many members to list.
|
|
9
|
+
LOCK_FUNCTIONS = %w(
|
|
10
|
+
nextval currval lastval setval get_lock release_lock is_free_lock is_used_lock
|
|
11
|
+
pg_(?:try_)?advisory_(?:xact_)?(?:un)?lock(?:_shared|_all)?
|
|
12
|
+
).freeze
|
|
13
|
+
|
|
14
|
+
# A string rather than a Regexp: interpolating a Regexp below would wrap it in
|
|
15
|
+
# a group that resets the /im flags, so `FOR UPDATE` would stop matching.
|
|
16
|
+
ROW_LOCK_STRENGTHS = 'no\s+key\s+update|key\s+share|update|share'
|
|
17
|
+
|
|
8
18
|
SQL_PRIMARY_MATCHERS = [
|
|
9
|
-
/\A\s*select
|
|
10
|
-
/\A\s*select
|
|
11
|
-
/\A\s*
|
|
19
|
+
/\A\s*select\b.*\bfor\s+(?:#{ROW_LOCK_STRENGTHS})\b/im,
|
|
20
|
+
/\A\s*select\b.*\block\s+in\s+share\s+mode\b/im,
|
|
21
|
+
/\A\s*select\b.*\b(#{LOCK_FUNCTIONS.join('|')})\s*\(/im,
|
|
22
|
+
/\A\s*show\b/i,
|
|
12
23
|
].freeze
|
|
13
24
|
SQL_REPLICA_MATCHERS = [/\A\s*(select|with.+\)\s*select)\s/i].freeze
|
|
14
25
|
SQL_ALL_MATCHERS = [/\A\s*set\s/i].freeze
|
data/lib/janus-ar/railtie.rb
CHANGED
data/lib/janus-ar/version.rb
CHANGED