fresh_connection 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/fresh_connection.svg)](http://badge.fury.io/rb/fresh_connection) [![Build Status](https://travis-ci.org/tsukasaoishi/fresh_connection.svg?branch=master)](https://travis-ci.org/tsukasaoishi/fresh_connection) [![Code Climate](https://codeclimate.com/github/tsukasaoishi/fresh_connection/badges/gpa.svg)](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: []
|