fresh_connection 2.2.0 → 2.3.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/.travis.yml +2 -2
- data/README.md +183 -101
- data/benchmarks/benchmark.rb +1 -1
- data/bin/test +12 -1
- data/fresh_connection.gemspec +1 -1
- data/lib/fresh_connection/abstract_connection_manager.rb +27 -6
- data/lib/fresh_connection/access_control.rb +4 -4
- data/lib/fresh_connection/connection_factory.rb +13 -1
- data/lib/fresh_connection/connection_manager.rb +3 -3
- data/lib/fresh_connection/extend/ar_abstract_adapter.rb +2 -2
- data/lib/fresh_connection/extend/ar_base.rb +54 -13
- data/lib/fresh_connection/extend/ar_relation.rb +14 -4
- data/lib/fresh_connection/rack/connection_management.rb +2 -2
- data/lib/fresh_connection/{slave_connection_handler.rb → replica_connection_handler.rb} +6 -6
- data/lib/fresh_connection/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5209747ee5ebffd0721b5c2dbcf20940721867c4
|
4
|
+
data.tar.gz: 022c9dce7d03c2ca0bc0732022ea70099df547df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 700f794d9cb774036025d53b6cf471ffd83c8a9de8b1188c1d0bf153e292c98c69804473a1e20bf535e18fc4be771d264585db463affeeda1b7b2303942aa868
|
7
|
+
data.tar.gz: d4f6fbea8f6debe411aa22f603a3e463bc03986dbefd05ded39374fe645641811e862814f09c34de2d38881ceb6e946ab8b4b8cdaed638bbf226337c0bbb0505
|
data/.travis.yml
CHANGED
@@ -8,8 +8,8 @@ before_script:
|
|
8
8
|
- bundle update
|
9
9
|
- cp test/config/database_postgresql.yml.travis test/config/database_postgresql.yml
|
10
10
|
- psql -c 'create database fresh_connection_test_master;' -U postgres
|
11
|
-
- psql -c 'create database
|
12
|
-
- psql -c 'create database
|
11
|
+
- psql -c 'create database fresh_connection_test_replica1;' -U postgres
|
12
|
+
- psql -c 'create database fresh_connection_test_replica2;' -U postgres
|
13
13
|
cache: bundler
|
14
14
|
rvm:
|
15
15
|
- 2.1.10
|
data/README.md
CHANGED
@@ -1,68 +1,87 @@
|
|
1
1
|
# FreshConnection
|
2
2
|
[](http://badge.fury.io/rb/fresh_connection) [](https://travis-ci.org/tsukasaoishi/fresh_connection) [](https://codeclimate.com/github/tsukasaoishi/fresh_connection)
|
3
3
|
|
4
|
-
FreshConnection
|
4
|
+
**FreshConnection** provides access to one or more configured database replicas.
|
5
5
|
|
6
|
-
For example
|
6
|
+
For example:
|
7
7
|
```
|
8
|
-
Rails ------------ Master
|
8
|
+
Rails ------------ DB Master
|
9
9
|
|
|
10
|
-
|
10
|
+
+---- DB Replica
|
11
|
+
|
12
|
+
```
|
13
|
+
|
14
|
+
or:
|
15
|
+
|
16
|
+
```
|
17
|
+
Rails -------+---- DB Master
|
18
|
+
|
|
19
|
+
| +------ DB Replica1
|
11
20
|
| |
|
12
21
|
+---- Loadbalancer ---+
|
13
22
|
|
|
14
|
-
+------
|
23
|
+
+------ DB Replica2
|
15
24
|
```
|
16
25
|
|
17
|
-
FreshConnction connects
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
FreshConnction connects one or more configured DB replicas, or with multiple
|
27
|
+
replicas behind a DB query load balancer.
|
28
|
+
|
29
|
+
- Read queries go to the DB replica.
|
30
|
+
- Write queries go to the DB master.
|
31
|
+
- Within a transaction, all queries go to the DB master.
|
21
32
|
|
22
|
-
If you
|
33
|
+
If you wish to use multiple DB replicas on any given connection but not have
|
34
|
+
a load balancer (such as [`pgbouncer`](https://pgbouncer.github.io) for Posgres
|
35
|
+
databases), you can use [EbisuConnection](https://github.com/tsukasaoishi/ebisu_connection).
|
23
36
|
|
24
37
|
## Usage
|
25
|
-
### Access to
|
26
|
-
Read
|
38
|
+
### Access to the DB Replica
|
39
|
+
Read queries are automatically connected to the DB replica.
|
27
40
|
|
28
41
|
```ruby
|
29
|
-
Article.where(:
|
42
|
+
Article.where(id: 1)
|
43
|
+
|
44
|
+
Account.count
|
30
45
|
```
|
31
46
|
|
32
|
-
### Access to Master
|
33
|
-
If
|
34
|
-
|
47
|
+
### Access to the DB Master
|
48
|
+
If you wish to ensure that queries are directed to the DB master, call `read_master`.
|
49
|
+
Before version 0.4.3, `readonly(false)` must be used.
|
35
50
|
|
36
51
|
```ruby
|
37
|
-
Article.where(:
|
52
|
+
Article.where(id: 1).read_master
|
53
|
+
|
54
|
+
Account.count.read_master
|
38
55
|
```
|
39
56
|
|
40
|
-
|
57
|
+
Within transactions, all queries are connected to the DB master.
|
41
58
|
|
42
59
|
```ruby
|
43
60
|
Article.transaction do
|
44
|
-
Article.where(:
|
61
|
+
Article.where(id: 1)
|
45
62
|
end
|
46
63
|
```
|
47
64
|
|
48
|
-
Create,
|
65
|
+
Create, update and delete queries are connected to the DB master.
|
49
66
|
|
50
67
|
```ruby
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
68
|
+
new_article = Article.create(...)
|
69
|
+
new_article.title = "FreshConnection"
|
70
|
+
new_article.save
|
71
|
+
...
|
72
|
+
old_article.destroy
|
55
73
|
```
|
56
74
|
|
57
|
-
##
|
58
|
-
FreshConnection supports ActiveRecord version 4.
|
59
|
-
If you are using Rails
|
75
|
+
## ActiveRecord Versions Supported
|
76
|
+
FreshConnection supports ActiveRecord version 4.2 or later.
|
77
|
+
If you are using Rails 4.1 or 4.0, you can use FreshConnection version 2.1.2 or before.
|
78
|
+
If you are using Rails 3.2, you can use FreshConnection version 1.0.0 or before.
|
60
79
|
|
61
|
-
##
|
62
|
-
FreshConnection supports MySQL and PostgreSQL.
|
80
|
+
## Databases Supported
|
81
|
+
FreshConnection currently supports MySQL and PostgreSQL.
|
63
82
|
|
64
83
|
## Installation
|
65
|
-
Add this line to your application's Gemfile
|
84
|
+
Add this line to your application's `Gemfile`:
|
66
85
|
|
67
86
|
```ruby
|
68
87
|
gem "fresh_connection"
|
@@ -74,105 +93,158 @@ And then execute:
|
|
74
93
|
$ bundle
|
75
94
|
```
|
76
95
|
|
77
|
-
Or install it
|
96
|
+
Or install it manually with:
|
78
97
|
|
79
98
|
```
|
80
99
|
$ gem install fresh_connection
|
81
100
|
```
|
82
101
|
|
102
|
+
### Variant Installation For Use With Some Other ActiveRecord Gems
|
103
|
+
If you are using NewRelic or other gems that insert themselves into the
|
104
|
+
ActiveRecord call-chain using `method_alias`, then a slight variation on the
|
105
|
+
installation and configuration is required.
|
83
106
|
|
84
|
-
|
85
|
-
### config/database.yml
|
107
|
+
In the `Gemfile`, use:
|
86
108
|
|
87
|
-
```
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
database: kaeru
|
93
|
-
pool: 5
|
94
|
-
username: master
|
95
|
-
password: master
|
96
|
-
host: localhost
|
97
|
-
socket: /var/run/mysqld/mysqld.sock
|
109
|
+
```ruby
|
110
|
+
gem "fresh_connection", require: false
|
111
|
+
```
|
112
|
+
|
113
|
+
Then, in `config/application.rb`, add the following:
|
98
114
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
115
|
+
```ruby
|
116
|
+
config.after_initialize do
|
117
|
+
require 'fresh_connection'
|
118
|
+
end
|
103
119
|
```
|
104
120
|
|
105
|
-
|
106
|
-
|
121
|
+
## Configuration
|
122
|
+
|
123
|
+
The FreshConnection database replica is configured within the standard Rails
|
124
|
+
database configuration file, `config/database.yml`, using a `replica:` stanza.
|
125
|
+
|
126
|
+
*Security Note*:
|
127
|
+
|
128
|
+
> We strongly recommend against using secrets within the `config/database.yml`
|
129
|
+
> file. Instead, it is both convenient and advisable to use ERB substitutions with
|
130
|
+
> environment variables within the file.
|
107
131
|
|
108
|
-
|
109
|
-
|
132
|
+
> Using the [`dotenv`](https://github.com/bkeepers/dotenv) gem to keep secrets in a `.env` file that is never committed
|
133
|
+
> to the source management repository will help make secrets manageable.
|
134
|
+
|
135
|
+
Below is a sample such configuration file.
|
136
|
+
|
137
|
+
### `config/database.yml`
|
110
138
|
|
111
139
|
```yaml
|
112
140
|
production:
|
113
|
-
adapter:
|
114
|
-
encoding:
|
141
|
+
adapter: mysql2
|
142
|
+
encoding: utf8
|
115
143
|
reconnect: true
|
116
|
-
database:
|
117
|
-
pool:
|
118
|
-
username:
|
119
|
-
password:
|
120
|
-
host:
|
121
|
-
socket:
|
144
|
+
database: <%= ENV['DB_MASTER_NAME'] %>
|
145
|
+
pool: 5
|
146
|
+
username: <%= ENV['DB_MASTER_USER'] %>
|
147
|
+
password: <%= ENV['DB_MASTER_PASS'] %>
|
148
|
+
host: <%= ENV['DB_MASTER_HOST'] %>
|
149
|
+
socket: /var/run/mysqld/mysqld.sock
|
150
|
+
|
151
|
+
replica:
|
152
|
+
username: <%= ENV['DB_REPLICA_USER'] %>
|
153
|
+
password: <%= ENV['DB_REPLICA_PASS'] %>
|
154
|
+
host: <%= ENV['DB_REPLICA_HOST'] %>
|
155
|
+
```
|
156
|
+
|
157
|
+
`replica` is the configuration used for connecting read-only queries to the
|
158
|
+
database replica. All other connections will use the database master settings.
|
159
|
+
|
160
|
+
### Multiple DB Replicas
|
161
|
+
If you want to use multiple configured DB replicas, the configuration can
|
162
|
+
contain multiple `replica` stanzas in the configuration file `config/database.yml`.
|
122
163
|
|
123
|
-
|
124
|
-
username: slave
|
125
|
-
password: slave
|
126
|
-
host: slave
|
164
|
+
For example:
|
127
165
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
166
|
+
```yaml
|
167
|
+
production:
|
168
|
+
adapter: mysql2
|
169
|
+
encoding: utf8
|
170
|
+
reconnect: true
|
171
|
+
database: <%= ENV['DB_MASTER_NAME'] %>
|
172
|
+
pool: 5
|
173
|
+
username: <%= ENV['DB_MASTER_USER'] %>
|
174
|
+
password: <%= ENV['DB_MASTER_PASS'] %>
|
175
|
+
host: <%= ENV['DB_MASTER_HOST'] %>
|
176
|
+
socket: /var/run/mysqld/mysqld.sock
|
177
|
+
|
178
|
+
replica:
|
179
|
+
username: <%= ENV['DB_REPLICA_USER'] %>
|
180
|
+
password: <%= ENV['DB_REPLICA_PASS'] %>
|
181
|
+
host: <%= ENV['DB_REPLICA_HOST'] %>
|
182
|
+
|
183
|
+
admin_replica:
|
184
|
+
username: <%= ENV['DB_ADMIN_REPLICA_USER'] %>
|
185
|
+
password: <%= ENV['DB_ADMIN_REPLICA_PASS'] %>
|
186
|
+
host: <%= ENV['DB_ADMIN_REPLICA_HOST'] %>
|
132
187
|
```
|
133
188
|
|
134
|
-
|
189
|
+
The custom replica stanza can then be applied as an argument to the
|
190
|
+
`establish_fresh_connection` method in the models that should use it. For
|
191
|
+
example:
|
135
192
|
|
136
193
|
```ruby
|
137
194
|
class AdminUser < ActiveRecord::Base
|
138
|
-
establish_fresh_connection :
|
195
|
+
establish_fresh_connection :admin_replica
|
139
196
|
end
|
140
197
|
```
|
141
198
|
|
142
|
-
The
|
199
|
+
The child (sub) classes of the configured model will inherit the same access
|
200
|
+
as the parent class. Example:
|
143
201
|
|
144
202
|
```ruby
|
145
|
-
class
|
146
|
-
establish_fresh_connection :
|
203
|
+
class AdminBase < ActiveRecord::Base
|
204
|
+
establish_fresh_connection :admin_replica
|
205
|
+
end
|
206
|
+
|
207
|
+
class AdminUser < AdminBase
|
147
208
|
end
|
148
209
|
|
149
|
-
class
|
210
|
+
class Benefit < AdminBase
|
150
211
|
end
|
151
212
|
|
152
|
-
class
|
213
|
+
class Customer < ActiveRecord::Base
|
153
214
|
end
|
154
215
|
```
|
155
216
|
|
156
|
-
AdminUser and Benefit access
|
217
|
+
The `AdminUser` and `Benefit` models will access the database configured for
|
218
|
+
the `admin_replica` group.
|
157
219
|
|
220
|
+
The `Customer` model will use the default connections: read-only queries will
|
221
|
+
connect to the standard DB replica, and state-changing queries will connect to
|
222
|
+
the DB master.
|
158
223
|
|
159
|
-
|
224
|
+
|
225
|
+
### Master-only Models
|
226
|
+
|
227
|
+
It is possible to declare that specific models always use the DB master for all connections, using
|
228
|
+
the `master_db_only!` method:
|
160
229
|
|
161
230
|
```ruby
|
162
|
-
class
|
231
|
+
class CustomerState < ActiveRecord::Base
|
163
232
|
master_db_only!
|
164
233
|
end
|
165
234
|
```
|
166
235
|
|
167
|
-
|
168
|
-
|
236
|
+
All queries generated by methods on the `CustomerState` model will be directed to the DB master.
|
237
|
+
|
238
|
+
### Using FreshConnection With Unicorn
|
169
239
|
|
170
|
-
|
240
|
+
When using FreshConnection with Unicorn (or any other multi-processing web
|
241
|
+
server which restarts processes on the fly), connection management needs
|
242
|
+
special attention during startup:
|
171
243
|
|
172
244
|
```ruby
|
173
245
|
before_fork do |server, worker|
|
174
246
|
...
|
175
|
-
ActiveRecord::Base.
|
247
|
+
ActiveRecord::Base.clear_all_replica_connections!
|
176
248
|
...
|
177
249
|
end
|
178
250
|
|
@@ -183,37 +255,42 @@ after_fork do |server, worker|
|
|
183
255
|
end
|
184
256
|
```
|
185
257
|
|
186
|
-
###
|
187
|
-
|
188
|
-
If
|
258
|
+
### Replica Connection Manager
|
259
|
+
The default replica connection manager is `FreshConnection::ConnectionManager`.
|
260
|
+
If an alternative (custom) replica connection manager is desired, this can be done
|
261
|
+
with a simple assignment within a Rails initializer:
|
189
262
|
|
190
|
-
|
263
|
+
`config/initializers/fresh_connection.rb`:
|
191
264
|
|
192
265
|
```ruby
|
193
|
-
FreshConnection.connection_manager =
|
266
|
+
FreshConnection.connection_manager = MyOwnReplicaConnection
|
194
267
|
```
|
195
268
|
|
196
|
-
|
197
|
-
|
269
|
+
The `MyOwnReplicaConnection` class should inherit from
|
270
|
+
`FreshConnection::AbstractConnectionManager`, which has this interface:
|
198
271
|
|
199
272
|
```ruby
|
200
|
-
class
|
201
|
-
|
202
|
-
|
273
|
+
class MyOwnReplicaConnection < FreshConnection::AbstractConnectionManager
|
274
|
+
|
275
|
+
def replica_connection
|
276
|
+
# must return an instance of a subclass of ActiveRecord::ConnectionAdapters
|
277
|
+
# eg: ActiveRecord::ConnectionAdapter::Mysql2Adapter
|
278
|
+
# or: ActiveRecord::ConnectionAdapter::PostgresqlAdapter
|
203
279
|
end
|
204
280
|
|
205
281
|
def clear_all_connections!
|
206
|
-
# called
|
282
|
+
# called to disconnect all connections
|
207
283
|
end
|
208
|
-
|
284
|
+
|
209
285
|
def put_aside!
|
210
286
|
# called when end of Rails controller action
|
211
287
|
end
|
212
288
|
|
213
289
|
def recovery?
|
214
|
-
# called when
|
215
|
-
#
|
290
|
+
# called when raising exceptions on access to the DB replica
|
291
|
+
# access will be retried when this method returns true
|
216
292
|
end
|
293
|
+
|
217
294
|
end
|
218
295
|
```
|
219
296
|
|
@@ -228,17 +305,22 @@ end
|
|
228
305
|
|
229
306
|
## Test
|
230
307
|
|
231
|
-
I'm glad that you would
|
232
|
-
To run the test suite,
|
233
|
-
|
308
|
+
I'm glad that you would test!
|
309
|
+
To run the test suite, `mysql` must be installed.
|
310
|
+
|
311
|
+
### Test Configuration
|
234
312
|
|
235
|
-
First
|
313
|
+
First, configure the test `mysql` server in `spec/database.yml`.
|
314
|
+
|
315
|
+
Then, run:
|
236
316
|
|
237
317
|
```bash
|
238
318
|
./bin/setup
|
239
319
|
```
|
240
320
|
|
241
|
-
|
321
|
+
### Running Tests
|
322
|
+
|
323
|
+
To run the spec suite for all supported versions of rails:
|
242
324
|
|
243
325
|
```bash
|
244
326
|
./bin/test
|
data/benchmarks/benchmark.rb
CHANGED
data/bin/test
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
2
|
set -euo pipefail
|
3
3
|
IFS=$'\n\t'
|
4
|
-
set -vx
|
5
4
|
|
5
|
+
for dbname in fresh_connection_test_master fresh_connection_test_replica1 fresh_connection_test_replica2 ; do
|
6
|
+
if ! psql $dbname -c '\q' 2>/dev/null ; then
|
7
|
+
if [[ -n "`psql -l | grep \"$dbname\"`" ]]; then
|
8
|
+
echo "Dropping database $dbname .."
|
9
|
+
dropdb $dbname
|
10
|
+
fi
|
11
|
+
echo "Creating database $dbname .."
|
12
|
+
createdb $dbname
|
13
|
+
fi
|
14
|
+
done
|
15
|
+
|
16
|
+
set -vx
|
6
17
|
bundle exec appraisal rake test
|
data/fresh_connection.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Tsukasa OISHI"]
|
10
10
|
spec.email = ["tsukasa.oishi@gmail.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{FreshConnection supports
|
12
|
+
spec.summary = %q{FreshConnection supports connections with configured replica servers.}
|
13
13
|
spec.description = %q{https://github.com/tsukasaoishi/fresh_connection}
|
14
14
|
spec.homepage = "https://github.com/tsukasaoishi/fresh_connection"
|
15
15
|
spec.license = "MIT"
|
@@ -1,15 +1,36 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
|
1
3
|
module FreshConnection
|
2
4
|
class AbstractConnectionManager
|
3
|
-
|
5
|
+
class << self
|
6
|
+
def method_added(name)
|
7
|
+
return unless name == :slave_connection
|
8
|
+
|
9
|
+
ActiveSupport::Deprecation.warn(
|
10
|
+
"'slave_connection' has been deprecated. use 'replica_connection' insted."
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :replica_group
|
16
|
+
|
17
|
+
def initialize(replica_group = "replica")
|
18
|
+
replica_group = "replica" if replica_group.to_s == "slave"
|
19
|
+
@replica_group = replica_group.to_s
|
20
|
+
@replica_group = "replica" if @replica_group.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def slave_group
|
24
|
+
ActiveSupport::Deprecation.warn(
|
25
|
+
"'slave_group' is deprecated and will removed from version 2.4.0. use 'replica_group' insted."
|
26
|
+
)
|
4
27
|
|
5
|
-
|
6
|
-
@slave_group = slave_group.to_s
|
7
|
-
@slave_group = "slave" if @slave_group.empty?
|
28
|
+
replica_group
|
8
29
|
end
|
9
30
|
|
10
|
-
def
|
31
|
+
def replica_connection
|
11
32
|
end
|
12
|
-
undef_method :
|
33
|
+
undef_method :replica_connection
|
13
34
|
|
14
35
|
def clear_all_connections!
|
15
36
|
end
|
@@ -5,17 +5,17 @@ module FreshConnection
|
|
5
5
|
switch_to(:master, &block)
|
6
6
|
end
|
7
7
|
|
8
|
-
def access(
|
8
|
+
def access(enable_replica_access, &block)
|
9
9
|
if access_db
|
10
10
|
block.call
|
11
11
|
else
|
12
|
-
db =
|
12
|
+
db = enable_replica_access ? :replica : :master
|
13
13
|
switch_to(db, &block)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
access_db == :
|
17
|
+
def replica_access?
|
18
|
+
access_db == :replica
|
19
19
|
end
|
20
20
|
|
21
21
|
def catch_exceptions
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_support/deprecation'
|
1
2
|
require 'active_support/core_ext/hash/keys'
|
2
3
|
|
3
4
|
module FreshConnection
|
@@ -23,7 +24,18 @@ module FreshConnection
|
|
23
24
|
|
24
25
|
def build_spec
|
25
26
|
config = ar_spec.config.symbolize_keys
|
26
|
-
group_config =
|
27
|
+
group_config = config[@group]
|
28
|
+
|
29
|
+
# provide backward compatibility for older :slave usage
|
30
|
+
if !group_config && @group == :replica && config.key?(:slave)
|
31
|
+
ActiveSupport::Deprecation.warn(
|
32
|
+
"'slave' in database.yml is deprecated and will ignored from version 2.4.0. use 'replica' insted."
|
33
|
+
)
|
34
|
+
group_config = config[:slave]
|
35
|
+
end
|
36
|
+
|
37
|
+
group_config = (group_config || {}).symbolize_keys
|
38
|
+
|
27
39
|
config.merge(group_config).merge(@modify_spec)
|
28
40
|
end
|
29
41
|
|
@@ -9,7 +9,7 @@ module FreshConnection
|
|
9
9
|
@connections = Concurrent::Map.new
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def replica_connection
|
13
13
|
@connections.fetch_or_store(current_thread_id) do |_|
|
14
14
|
connection_factory.new_connection
|
15
15
|
end
|
@@ -29,7 +29,7 @@ module FreshConnection
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def recovery?
|
32
|
-
return false if
|
32
|
+
return false if replica_connection.active?
|
33
33
|
put_aside!
|
34
34
|
true
|
35
35
|
end
|
@@ -37,7 +37,7 @@ module FreshConnection
|
|
37
37
|
private
|
38
38
|
|
39
39
|
def connection_factory
|
40
|
-
@connection_factory ||= ConnectionFactory.new(@
|
40
|
+
@connection_factory ||= ConnectionFactory.new(@replica_group)
|
41
41
|
end
|
42
42
|
|
43
43
|
def current_thread_id
|
@@ -2,11 +2,11 @@ module FreshConnection
|
|
2
2
|
module Extend
|
3
3
|
module ArAbstractAdapter
|
4
4
|
def self.prepended(base)
|
5
|
-
base.send :attr_writer, :
|
5
|
+
base.send :attr_writer, :replica_group
|
6
6
|
end
|
7
7
|
|
8
8
|
def log(*args)
|
9
|
-
args[1] = "[#{@
|
9
|
+
args[1] = "[#{@replica_group}] #{args[1]}" if defined?(@replica_group)
|
10
10
|
super
|
11
11
|
end
|
12
12
|
end
|
@@ -1,15 +1,16 @@
|
|
1
|
+
require 'active_support/deprecation'
|
1
2
|
require 'fresh_connection/access_control'
|
2
|
-
require 'fresh_connection/
|
3
|
+
require 'fresh_connection/replica_connection_handler'
|
3
4
|
|
4
5
|
module FreshConnection
|
5
6
|
module Extend
|
6
7
|
module ArBase
|
7
8
|
def connection
|
8
|
-
if FreshConnection::AccessControl.
|
9
|
+
if FreshConnection::AccessControl.replica_access?
|
9
10
|
if logger && logger.debug?
|
10
|
-
|
11
|
+
replica_connection.tap{|c| c.replica_group = replica_group }
|
11
12
|
else
|
12
|
-
|
13
|
+
replica_connection
|
13
14
|
end
|
14
15
|
else
|
15
16
|
super
|
@@ -24,16 +25,32 @@ module FreshConnection
|
|
24
25
|
all.manage_access(false, &block)
|
25
26
|
end
|
26
27
|
|
27
|
-
def establish_fresh_connection(
|
28
|
-
|
28
|
+
def establish_fresh_connection(replica_group = nil)
|
29
|
+
replica_connection_handler.establish_connection(name, replica_group)
|
30
|
+
end
|
31
|
+
|
32
|
+
def replica_connection
|
33
|
+
replica_connection_handler.connection(self)
|
29
34
|
end
|
30
35
|
|
31
36
|
def slave_connection
|
32
|
-
|
37
|
+
ActiveSupport::Deprecation.warn(
|
38
|
+
"'slave_connection' is deprecated and will removed from version 2.4.0. use 'replica_connection' insted."
|
39
|
+
)
|
40
|
+
|
41
|
+
replica_connection
|
42
|
+
end
|
43
|
+
|
44
|
+
def clear_all_replica_connections!
|
45
|
+
replica_connection_handler.clear_all_connections!
|
33
46
|
end
|
34
47
|
|
35
48
|
def clear_all_slave_connections!
|
36
|
-
|
49
|
+
ActiveSupport::Deprecation.warn(
|
50
|
+
"'clear_all_slave_connections!' is deprecated and will removed from version 2.4.0. use 'clear_all_replica_connections!' insted."
|
51
|
+
)
|
52
|
+
|
53
|
+
clear_all_replica_connections!
|
37
54
|
end
|
38
55
|
|
39
56
|
def master_db_only!
|
@@ -45,22 +62,46 @@ module FreshConnection
|
|
45
62
|
(self != ActiveRecord::Base && superclass.master_db_only?)
|
46
63
|
end
|
47
64
|
|
65
|
+
def replica_connection_put_aside!
|
66
|
+
replica_connection_handler.put_aside!
|
67
|
+
end
|
68
|
+
|
48
69
|
def slave_connection_put_aside!
|
49
|
-
|
70
|
+
ActiveSupport::Deprecation.warn(
|
71
|
+
"'slave_connection_put_aside!' is deprecated and will removed from version 2.4.0. use 'replica_connection_put_aside!' insted."
|
72
|
+
)
|
73
|
+
|
74
|
+
replica_connection_put_aside!
|
75
|
+
end
|
76
|
+
|
77
|
+
def replica_connection_recovery?
|
78
|
+
replica_connection_handler.recovery?(self)
|
50
79
|
end
|
51
80
|
|
52
81
|
def slave_connection_recovery?
|
53
|
-
|
82
|
+
ActiveSupport::Deprecation.warn(
|
83
|
+
"'slave_connection_recovery?' is deprecated and will removed from version 2.4.0. use 'replica_connection_recovery?' insted."
|
84
|
+
)
|
85
|
+
|
86
|
+
replica_connection_recovery?
|
87
|
+
end
|
88
|
+
|
89
|
+
def replica_group
|
90
|
+
replica_connection_handler.replica_group(self)
|
54
91
|
end
|
55
92
|
|
56
93
|
def slave_group
|
57
|
-
|
94
|
+
ActiveSupport::Deprecation.warn(
|
95
|
+
"'slave_connection_recovery?' is deprecated and will removed from version 2.4.0. use 'replica_connection_recovery?' insted."
|
96
|
+
)
|
97
|
+
|
98
|
+
replica_group
|
58
99
|
end
|
59
100
|
|
60
101
|
private
|
61
102
|
|
62
|
-
def
|
63
|
-
@@
|
103
|
+
def replica_connection_handler
|
104
|
+
@@replica_connection_handler ||= FreshConnection::ReplicaConnectionHandler.new
|
64
105
|
end
|
65
106
|
end
|
66
107
|
end
|
@@ -1,18 +1,20 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
|
1
3
|
module FreshConnection
|
2
4
|
module Extend
|
3
5
|
module ArRelation
|
4
6
|
RETRY_LIMIT = 3
|
5
7
|
private_constant :RETRY_LIMIT
|
6
8
|
|
7
|
-
def manage_access(
|
9
|
+
def manage_access(replica_access = enable_replica_access, &block)
|
8
10
|
if @klass.master_db_only?
|
9
11
|
FreshConnection::AccessControl.force_master_access(&block)
|
10
12
|
else
|
11
13
|
retry_count = 0
|
12
14
|
begin
|
13
|
-
FreshConnection::AccessControl.access(
|
15
|
+
FreshConnection::AccessControl.access(replica_access, &block)
|
14
16
|
rescue *FreshConnection::AccessControl.catch_exceptions
|
15
|
-
if @klass.
|
17
|
+
if @klass.replica_connection_recovery?
|
16
18
|
retry_count += 1
|
17
19
|
retry if retry_count < RETRY_LIMIT
|
18
20
|
end
|
@@ -52,10 +54,18 @@ module FreshConnection
|
|
52
54
|
@values[:read_master] = value
|
53
55
|
end
|
54
56
|
|
55
|
-
def
|
57
|
+
def enable_replica_access
|
56
58
|
connection.open_transactions == 0 && !read_master_value
|
57
59
|
end
|
58
60
|
|
61
|
+
def enable_slave_access
|
62
|
+
ActiveSupport::Deprecation.warn(
|
63
|
+
"'enable_slave_access' is deprecated and will removed from version 2.4.0. use 'enable_replica_access' insted."
|
64
|
+
)
|
65
|
+
|
66
|
+
enable_replica_access
|
67
|
+
end
|
68
|
+
|
59
69
|
private
|
60
70
|
|
61
71
|
def exec_queries
|
@@ -10,12 +10,12 @@ module FreshConnection
|
|
10
10
|
|
11
11
|
response = @app.call(env)
|
12
12
|
response[2] = ::Rack::BodyProxy.new(response[2]) do
|
13
|
-
ActiveRecord::Base.
|
13
|
+
ActiveRecord::Base.replica_connection_put_aside! unless testing
|
14
14
|
end
|
15
15
|
|
16
16
|
response
|
17
17
|
rescue Exception
|
18
|
-
ActiveRecord::Base.
|
18
|
+
ActiveRecord::Base.replica_connection_put_aside! unless testing
|
19
19
|
raise
|
20
20
|
end
|
21
21
|
end
|
@@ -1,21 +1,21 @@
|
|
1
1
|
require 'concurrent'
|
2
2
|
|
3
3
|
module FreshConnection
|
4
|
-
class
|
4
|
+
class ReplicaConnectionHandler
|
5
5
|
def initialize
|
6
6
|
@class_to_pool = Concurrent::Map.new
|
7
7
|
end
|
8
8
|
|
9
|
-
def establish_connection(name,
|
9
|
+
def establish_connection(name, replica_group)
|
10
10
|
if cm = class_to_pool[name]
|
11
11
|
cm.put_aside!
|
12
12
|
end
|
13
13
|
|
14
|
-
class_to_pool[name] = FreshConnection.connection_manager.new(
|
14
|
+
class_to_pool[name] = FreshConnection.connection_manager.new(replica_group)
|
15
15
|
end
|
16
16
|
|
17
17
|
def connection(klass)
|
18
|
-
detect_connection_manager(klass).
|
18
|
+
detect_connection_manager(klass).replica_connection
|
19
19
|
end
|
20
20
|
|
21
21
|
def clear_all_connections!
|
@@ -34,8 +34,8 @@ module FreshConnection
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
38
|
-
detect_connection_manager(klass).
|
37
|
+
def replica_group(klass)
|
38
|
+
detect_connection_manager(klass).replica_group
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fresh_connection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tsukasa OISHI
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -223,7 +223,7 @@ files:
|
|
223
223
|
- lib/fresh_connection/extend/ar_statement_cache.rb
|
224
224
|
- lib/fresh_connection/rack/connection_management.rb
|
225
225
|
- lib/fresh_connection/railtie.rb
|
226
|
-
- lib/fresh_connection/
|
226
|
+
- lib/fresh_connection/replica_connection_handler.rb
|
227
227
|
- lib/fresh_connection/version.rb
|
228
228
|
- log/.gitkeep
|
229
229
|
homepage: https://github.com/tsukasaoishi/fresh_connection
|
@@ -249,5 +249,5 @@ rubyforge_project:
|
|
249
249
|
rubygems_version: 2.5.2
|
250
250
|
signing_key:
|
251
251
|
specification_version: 4
|
252
|
-
summary: FreshConnection supports
|
252
|
+
summary: FreshConnection supports connections with configured replica servers.
|
253
253
|
test_files: []
|