fresh_connection 1.0.1 → 2.0.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 +27 -3
- data/Appraisals +11 -11
- data/Gemfile +1 -1
- data/README.md +31 -24
- data/Rakefile +16 -4
- data/bin/console +14 -0
- data/bin/setup +9 -0
- data/bin/test +6 -0
- data/fresh_connection.gemspec +4 -3
- data/gemfiles/rails40.gemfile +1 -1
- data/gemfiles/rails41.gemfile +1 -1
- data/gemfiles/rails42.gemfile +1 -1
- data/gemfiles/{rails3.gemfile → rails50.gemfile} +2 -2
- data/lib/fresh_connection/abstract_connection_manager.rb +2 -25
- data/lib/fresh_connection/access_control.rb +8 -0
- data/lib/fresh_connection/connection_factory.rb +3 -6
- data/lib/fresh_connection/connection_manager.rb +21 -20
- data/lib/fresh_connection/extend/ar_abstract_adapter.rb +14 -0
- data/lib/fresh_connection/extend/ar_base.rb +21 -23
- data/lib/fresh_connection/extend/ar_relation.rb +36 -12
- data/lib/fresh_connection/extend/ar_statement_cache.rb +1 -3
- data/lib/fresh_connection/extend.rb +12 -21
- data/lib/fresh_connection/slave_connection_handler.rb +15 -9
- data/lib/fresh_connection/version.rb +1 -1
- data/lib/fresh_connection.rb +8 -3
- metadata +60 -46
- data/lib/fresh_connection/extend/ar_relation/for_rails3.rb +0 -31
- data/lib/fresh_connection/extend/ar_relation/for_rails4.rb +0 -20
- data/lib/fresh_connection/extend/connection_handler.rb +0 -12
- data/lib/fresh_connection/extend/mysql2_adapter.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc5e646490d77de912ba3add1040b0acecf88d7d
|
4
|
+
data.tar.gz: 1bf31e8b593012fd1ee11835cca259b8739ed9c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51b0eb074205141d96030ad1274229b5ebe7078bd5e0a348850ccc2623d0178edbb3694a7ee098a5bcef3ce32f7e547dfbff0fda1a3f3926439216179d80b601
|
7
|
+
data.tar.gz: 7e4f445e3a8acf489d7a726d03c127ab4e6b82e7da28a4deda02228823cdbc55932e2d953ca226ff752905576b5d5e59f6cab1015315900e071095c2fa312a15
|
data/.travis.yml
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
language: ruby
|
2
2
|
before_install:
|
3
3
|
- gem install bundler
|
4
|
+
services:
|
5
|
+
- mysql
|
6
|
+
- postgresql
|
4
7
|
before_script:
|
5
|
-
- mysql -e 'create database fresh_connection_test;'
|
6
|
-
- mysql -e 'create database fresh_connection_test_master;'
|
7
|
-
- mysql -e 'create database fresh_connection_test_slave;'
|
8
8
|
- bundle update
|
9
|
+
- cp test/config/database_postgresql.yml.travis test/config/database_postgresql.yml
|
10
|
+
- psql -c 'create database fresh_connection_test_master;' -U postgres
|
11
|
+
- psql -c 'create database fresh_connection_test_slave1;' -U postgres
|
12
|
+
- psql -c 'create database fresh_connection_test_slave2;' -U postgres
|
9
13
|
cache: bundler
|
10
14
|
rvm:
|
11
15
|
- 2.0.0
|
@@ -18,6 +22,26 @@ gemfile:
|
|
18
22
|
- gemfiles/rails40.gemfile
|
19
23
|
- gemfiles/rails41.gemfile
|
20
24
|
- gemfiles/rails42.gemfile
|
25
|
+
- gemfiles/rails50.gemfile
|
26
|
+
matrix:
|
27
|
+
exclude:
|
28
|
+
- rvm: 2.3.0
|
29
|
+
gemfile: gemfiles/rails3.gemfile
|
30
|
+
- rvm: ruby-head
|
31
|
+
gemfile: gemfiles/rails3.gemfile
|
32
|
+
- rvm: 2.3.0
|
33
|
+
gemfile: gemfiles/rails40.gemfile
|
34
|
+
- rvm: ruby-head
|
35
|
+
gemfile: gemfiles/rails40.gemfile
|
36
|
+
- rvm: 2.0.0
|
37
|
+
gemfile: gemfiles/rails50.gemfile
|
38
|
+
- rvm: 2.1.8
|
39
|
+
gemfile: gemfiles/rails50.gemfile
|
40
|
+
allow_failures:
|
41
|
+
- rvm: ruby-head
|
42
|
+
- gemfile: gemfiles/rails50.gemfile
|
43
|
+
fast_finish: true
|
21
44
|
notifications:
|
22
45
|
emails:
|
23
46
|
- tsukasa.oishi@gmail.com
|
47
|
+
bundler_args: --jobs 3 --retry 3
|
data/Appraisals
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
appraise "rails3" do
|
2
|
-
gem "activerecord", "~> 3.2.0"
|
3
|
-
gem "activesupport", "~> 3.2.0"
|
4
|
-
end
|
5
|
-
|
6
1
|
appraise "rails40" do
|
7
|
-
gem
|
8
|
-
gem
|
2
|
+
gem 'activerecord', '~> 4.0.0'
|
3
|
+
gem 'mysql2', '~> 0.3.10'
|
9
4
|
end
|
10
5
|
|
11
6
|
appraise "rails41" do
|
12
|
-
gem
|
13
|
-
gem
|
7
|
+
gem 'activerecord', '~> 4.1.0'
|
8
|
+
gem 'mysql2', '~> 0.3.13'
|
14
9
|
end
|
15
10
|
|
16
11
|
appraise "rails42" do
|
17
|
-
gem
|
18
|
-
gem
|
12
|
+
gem 'activerecord', '~> 4.2.0'
|
13
|
+
gem 'mysql2', '~> 0.3.13'
|
14
|
+
end
|
15
|
+
|
16
|
+
appraise "rails50" do
|
17
|
+
gem 'activerecord', '5.0.0.beta3'
|
18
|
+
gem 'mysql2', '>= 0.3.18', '< 0.5'
|
19
19
|
end
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,43 +1,42 @@
|
|
1
1
|
# FreshConnection
|
2
|
-
|
3
|
-
FreshConnection allows access to Mysql slave servers in Rails.
|
4
|
-
|
5
2
|
[](http://badge.fury.io/rb/fresh_connection) [](https://travis-ci.org/tsukasaoishi/fresh_connection) [](https://codeclimate.com/github/tsukasaoishi/fresh_connection)
|
6
3
|
|
7
|
-
ActiveRecord
|
8
|
-
FreshConnection can access to
|
4
|
+
ActiveRecord accesses a single server by default.
|
5
|
+
FreshConnection can access to slave servers via a load balancer.
|
9
6
|
|
10
7
|
For example.
|
11
8
|
```
|
12
|
-
Rails ------------
|
9
|
+
Rails ------------ Master DB
|
13
10
|
|
|
14
|
-
| +------
|
11
|
+
| +------ Slave1 DB
|
15
12
|
| |
|
16
13
|
+---- Loadbalancer ---+
|
17
14
|
|
|
18
|
-
+------
|
15
|
+
+------ Slave2 DB
|
19
16
|
```
|
20
17
|
|
21
|
-
|
22
|
-
Read query goes to the slave server
|
23
|
-
Write query goes to the master server.
|
24
|
-
|
18
|
+
FreshConnction connects with one of slave servers behind the load balancer.
|
19
|
+
Read query goes to the slave server.
|
20
|
+
Write query goes to the master server.
|
21
|
+
Inside transaction, all queries go to the master server.
|
25
22
|
|
23
|
+
If you can't use a load balancer, could use [EbisuConnection](https://github.com/tsukasaoishi/ebisu_connection).
|
26
24
|
|
27
25
|
## Usage
|
28
|
-
|
26
|
+
### Access to Slave
|
29
27
|
Read query goes to the slave server.
|
30
28
|
|
31
29
|
```ruby
|
32
30
|
Article.where(:id => 1)
|
33
31
|
```
|
34
32
|
|
35
|
-
|
33
|
+
### Access to Master
|
34
|
+
If read query want to access to the master server, use `read_master`.
|
35
|
+
In before version 0.4.3, can use `readonly(false)`.
|
36
36
|
|
37
37
|
```ruby
|
38
38
|
Article.where(:id => 1).read_master
|
39
39
|
```
|
40
|
-
If you would like to use readonly(false), use version before 1.0.0.
|
41
40
|
|
42
41
|
In transaction, All queries go to the master server.
|
43
42
|
|
@@ -47,7 +46,7 @@ Article.transaction do
|
|
47
46
|
end
|
48
47
|
```
|
49
48
|
|
50
|
-
Create, Update
|
49
|
+
Create, Update and Delete queries go to the master server.
|
51
50
|
|
52
51
|
```ruby
|
53
52
|
article = Article.create(...)
|
@@ -56,9 +55,14 @@ article.save
|
|
56
55
|
article.destory
|
57
56
|
```
|
58
57
|
|
58
|
+
## Support Rails version
|
59
|
+
FreshConnection supports Rails version 4.0 or later.
|
60
|
+
If you are using Rails 3.2, could use FreshConnection version 1.0.0 or before.
|
59
61
|
|
60
|
-
##
|
62
|
+
## Support DB
|
63
|
+
FreshConnection supports MySQL and PostgreSQL.
|
61
64
|
|
65
|
+
## Installation
|
62
66
|
Add this line to your application's Gemfile:
|
63
67
|
|
64
68
|
```ruby
|
@@ -100,7 +104,7 @@ production:
|
|
100
104
|
```
|
101
105
|
|
102
106
|
```slave``` is a config to connect to slave servers.
|
103
|
-
Others will use the master server
|
107
|
+
Others will use the master server settings.
|
104
108
|
|
105
109
|
### use multiple slave servers group
|
106
110
|
If you may want to use multiple slave groups, write the config to ```config/database.yml```.
|
@@ -136,7 +140,7 @@ class AdminUser < ActiveRecord::Base
|
|
136
140
|
end
|
137
141
|
```
|
138
142
|
|
139
|
-
The
|
143
|
+
The children class will access to same slave group as the parent.
|
140
144
|
|
141
145
|
```ruby
|
142
146
|
class Parent < ActiveRecord::Base
|
@@ -199,12 +203,17 @@ class MySlaveConnection < FreshConnection::AbstractConnectionManager
|
|
199
203
|
# must return object of ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
200
204
|
end
|
201
205
|
|
206
|
+
def clear_all_connections!
|
207
|
+
# called when all connections disconnect
|
208
|
+
end
|
209
|
+
|
202
210
|
def put_aside!
|
203
211
|
# called when end of Rails controller action
|
204
212
|
end
|
205
213
|
|
206
|
-
def recovery
|
214
|
+
def recovery?
|
207
215
|
# called when raise exception to access slave server
|
216
|
+
# retry to access when this method return true
|
208
217
|
end
|
209
218
|
end
|
210
219
|
```
|
@@ -227,13 +236,11 @@ How to setup your test environment.
|
|
227
236
|
First of all, you setting the config of the test mysql server in ```spec/database.yml```
|
228
237
|
|
229
238
|
```bash
|
230
|
-
|
231
|
-
bundle exec appraisal install
|
239
|
+
./bin/setup
|
232
240
|
```
|
233
241
|
|
234
242
|
This command run the spec suite for all rails versions supported.
|
235
243
|
|
236
244
|
```bash
|
237
|
-
|
245
|
+
./bin/test
|
238
246
|
```
|
239
|
-
|
data/Rakefile
CHANGED
@@ -1,10 +1,22 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require 'rake/testtask'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
desc 'Run mysql2 and postgresql test'
|
5
|
+
task :test do
|
6
|
+
Rake::Task["test:mysql2"].invoke
|
7
|
+
Rake::Task["test:postgresql"].invoke
|
8
|
+
end
|
9
|
+
|
10
|
+
namespace :test do
|
11
|
+
%w(mysql2 postgresql).each do |db|
|
12
|
+
Rake::TestTask.new(db) do |t|
|
13
|
+
t.libs << "test"
|
14
|
+
t.libs << "lib"
|
15
|
+
t.test_files = FileList["test/config/prepare_#{db}", 'test/**/*_test.rb']
|
16
|
+
t.verbose = false
|
17
|
+
t.warning = false
|
18
|
+
end
|
19
|
+
end
|
8
20
|
end
|
9
21
|
|
10
22
|
task :default => :test
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "fresh_connection"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/bin/test
ADDED
data/fresh_connection.gemspec
CHANGED
@@ -21,10 +21,11 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.required_ruby_version = '>= 2.0'
|
23
23
|
|
24
|
-
spec.add_dependency 'activerecord', '>=
|
25
|
-
spec.add_dependency '
|
26
|
-
spec.add_dependency 'mysql2', '~> 0.3.18'
|
24
|
+
spec.add_dependency 'activerecord', '>= 4.0.0', '< 5.0'
|
25
|
+
spec.add_dependency 'concurrent-ruby', '~> 1.0.0'
|
27
26
|
|
27
|
+
spec.add_development_dependency 'mysql2', '>= 0.3.13', '< 0.5'
|
28
|
+
spec.add_development_dependency 'pg', '~> 0.11'
|
28
29
|
spec.add_development_dependency "bundler", ">= 1.3.0", "< 2.0"
|
29
30
|
spec.add_development_dependency "rake", ">= 0.8.7"
|
30
31
|
spec.add_development_dependency 'appraisal'
|
data/gemfiles/rails40.gemfile
CHANGED
data/gemfiles/rails41.gemfile
CHANGED
data/gemfiles/rails42.gemfile
CHANGED
@@ -1,17 +1,8 @@
|
|
1
1
|
module FreshConnection
|
2
2
|
class AbstractConnectionManager
|
3
|
-
EXCEPTION_MESSAGE_WHEN_SLAVE_SERVER_DOWN = [
|
4
|
-
"MySQL server has gone away",
|
5
|
-
"closed MySQL connection",
|
6
|
-
"Can't connect to local MySQL server"
|
7
|
-
].map{|msg| Regexp.escape(msg)}.join("|")
|
8
|
-
|
9
|
-
private_constant :EXCEPTION_MESSAGE_WHEN_SLAVE_SERVER_DOWN
|
10
|
-
|
11
3
|
attr_reader :slave_group
|
12
4
|
|
13
5
|
def initialize(slave_group = "slave")
|
14
|
-
@mutex = Mutex.new
|
15
6
|
@slave_group = slave_group.to_s
|
16
7
|
@slave_group = "slave" if @slave_group.empty?
|
17
8
|
end
|
@@ -28,23 +19,9 @@ module FreshConnection
|
|
28
19
|
end
|
29
20
|
undef_method :put_aside!
|
30
21
|
|
31
|
-
def recovery
|
32
|
-
end
|
33
|
-
undef_method :recovery
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def synchronize
|
38
|
-
@mutex.synchronize{ yield }
|
39
|
-
end
|
40
|
-
|
41
|
-
def current_thread_id
|
42
|
-
Thread.current.object_id
|
43
|
-
end
|
44
|
-
|
45
|
-
def slave_down_message?(message)
|
46
|
-
/#{EXCEPTION_MESSAGE_WHEN_SLAVE_SERVER_DOWN}/o === message
|
22
|
+
def recovery?
|
47
23
|
end
|
24
|
+
undef_method :recovery?
|
48
25
|
end
|
49
26
|
end
|
50
27
|
|
@@ -18,6 +18,14 @@ module FreshConnection
|
|
18
18
|
access_db == :slave
|
19
19
|
end
|
20
20
|
|
21
|
+
def catch_exceptions
|
22
|
+
return @catch_exceptions if defined?(@catch_exceptions)
|
23
|
+
@catch_exceptions = [ActiveRecord::StatementInvalid]
|
24
|
+
@catch_exceptions << ::Mysql2::Error if defined?(::Mysql2)
|
25
|
+
@catch_exceptions += [::PG::Error, ::PGError] if defined?(::PG)
|
26
|
+
@catch_exceptions
|
27
|
+
end
|
28
|
+
|
21
29
|
private
|
22
30
|
|
23
31
|
def switch_to(new_db)
|
@@ -1,10 +1,8 @@
|
|
1
|
-
require 'active_support/core_ext/hash/keys'
|
2
|
-
|
3
1
|
module FreshConnection
|
4
2
|
class ConnectionFactory
|
5
3
|
def initialize(group, modify_spec = {})
|
6
4
|
@group = group.to_sym
|
7
|
-
@modify_spec = modify_spec
|
5
|
+
@modify_spec = modify_spec
|
8
6
|
end
|
9
7
|
|
10
8
|
def new_connection
|
@@ -22,9 +20,8 @@ module FreshConnection
|
|
22
20
|
end
|
23
21
|
|
24
22
|
def build_spec
|
25
|
-
config = ar_spec.config
|
26
|
-
|
27
|
-
config.merge(group_config).merge(@modify_spec)
|
23
|
+
config = ar_spec.config
|
24
|
+
config.merge(config[@group] || {}).merge(@modify_spec)
|
28
25
|
end
|
29
26
|
|
30
27
|
def ar_spec
|
@@ -1,43 +1,44 @@
|
|
1
|
-
require '
|
2
|
-
require 'fresh_connection/connection_factory'
|
1
|
+
require 'concurrent'
|
3
2
|
|
4
3
|
module FreshConnection
|
5
4
|
class ConnectionManager < AbstractConnectionManager
|
5
|
+
def initialize(*args)
|
6
|
+
super
|
7
|
+
@connections = Concurrent::Map.new
|
8
|
+
end
|
9
|
+
|
6
10
|
def slave_connection
|
7
|
-
|
8
|
-
|
11
|
+
@connections.fetch_or_store(current_thread_id) do |_|
|
12
|
+
connection_factory.new_connection
|
9
13
|
end
|
10
14
|
end
|
11
15
|
|
12
16
|
def put_aside!
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
17
|
+
conn = @connections.delete(current_thread_id)
|
18
|
+
return unless conn
|
19
|
+
conn && conn.disconnect! rescue nil
|
18
20
|
end
|
19
21
|
|
20
22
|
def clear_all_connections!
|
21
|
-
|
22
|
-
|
23
|
-
@slave_connections.clear
|
23
|
+
@connections.each_value do |conn|
|
24
|
+
conn.disconnect! rescue nil
|
24
25
|
end
|
26
|
+
@connections.clear
|
25
27
|
end
|
26
28
|
|
27
|
-
def recovery
|
28
|
-
|
29
|
-
|
30
|
-
do_recovery
|
29
|
+
def recovery?
|
30
|
+
put_aside!
|
31
|
+
true
|
31
32
|
end
|
32
33
|
|
33
34
|
private
|
34
35
|
|
35
|
-
def slave_connections
|
36
|
-
@slave_connections ||= {}
|
37
|
-
end
|
38
|
-
|
39
36
|
def connection_factory
|
40
37
|
@connection_factory ||= ConnectionFactory.new(@slave_group)
|
41
38
|
end
|
39
|
+
|
40
|
+
def current_thread_id
|
41
|
+
Thread.current.object_id
|
42
|
+
end
|
42
43
|
end
|
43
44
|
end
|
@@ -1,32 +1,24 @@
|
|
1
|
-
require 'active_support/core_ext/class/attribute'
|
2
|
-
require 'fresh_connection/slave_connection_handler'
|
3
|
-
require 'fresh_connection/access_control'
|
4
|
-
|
5
1
|
module FreshConnection
|
6
2
|
module Extend
|
7
3
|
module ArBase
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
delegate :read_master, to: :all
|
16
|
-
when 3
|
17
|
-
delegate :read_master, to: :scoped
|
18
|
-
end
|
19
|
-
|
20
|
-
def manage_access(slave_access, &block)
|
21
|
-
if master_db_only?
|
22
|
-
FreshConnection::AccessControl.force_master_access(&block)
|
4
|
+
def connection
|
5
|
+
if FreshConnection::AccessControl.slave_access?
|
6
|
+
if logger && logger.debug?
|
7
|
+
slave_connection.tap{|c| c.slave_group = slave_group }
|
8
|
+
else
|
9
|
+
slave_connection
|
10
|
+
end
|
23
11
|
else
|
24
|
-
|
12
|
+
super
|
25
13
|
end
|
26
14
|
end
|
27
15
|
|
16
|
+
def read_master
|
17
|
+
all.read_master
|
18
|
+
end
|
19
|
+
|
28
20
|
def with_master(&block)
|
29
|
-
manage_access(false, &block)
|
21
|
+
all.manage_access(false, &block)
|
30
22
|
end
|
31
23
|
|
32
24
|
def establish_fresh_connection(slave_group = nil)
|
@@ -54,13 +46,19 @@ module FreshConnection
|
|
54
46
|
slave_connection_handler.put_aside!
|
55
47
|
end
|
56
48
|
|
57
|
-
def
|
58
|
-
slave_connection_handler.recovery(self
|
49
|
+
def slave_connection_recovery?
|
50
|
+
slave_connection_handler.recovery?(self)
|
59
51
|
end
|
60
52
|
|
61
53
|
def slave_group
|
62
54
|
slave_connection_handler.slave_group(self)
|
63
55
|
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def slave_connection_handler
|
60
|
+
@@slave_connection_handler ||= FreshConnection::SlaveConnectionHandler.new
|
61
|
+
end
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
@@ -1,18 +1,46 @@
|
|
1
|
-
require "fresh_connection/extend/ar_relation/for_rails#{ActiveRecord::VERSION::MAJOR}"
|
2
|
-
|
3
1
|
module FreshConnection
|
4
2
|
module Extend
|
5
3
|
module ArRelation
|
6
|
-
|
7
|
-
|
4
|
+
RETRY_LIMIT = 3
|
5
|
+
private_constant :RETRY_LIMIT
|
6
|
+
|
7
|
+
def manage_access(slave_access = enable_slave_access, &block)
|
8
|
+
if @klass.master_db_only?
|
9
|
+
FreshConnection::AccessControl.force_master_access(&block)
|
10
|
+
else
|
11
|
+
retry_count = 0
|
12
|
+
begin
|
13
|
+
FreshConnection::AccessControl.access(slave_access, &block)
|
14
|
+
rescue *FreshConnection::AccessControl.catch_exceptions
|
15
|
+
if @klass.slave_connection_recovery?
|
16
|
+
retry_count += 1
|
17
|
+
retry if retry_count < RETRY_LIMIT
|
18
|
+
end
|
19
|
+
|
20
|
+
raise
|
21
|
+
end
|
22
|
+
end
|
8
23
|
end
|
9
24
|
|
10
|
-
def calculate(
|
11
|
-
|
25
|
+
def calculate(*args)
|
26
|
+
manage_access { super }
|
12
27
|
end
|
13
28
|
|
14
29
|
def exists?(*args)
|
15
|
-
|
30
|
+
manage_access { super }
|
31
|
+
end
|
32
|
+
|
33
|
+
def pluck(*args)
|
34
|
+
manage_access { super }
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_master
|
38
|
+
spawn.read_master!
|
39
|
+
end
|
40
|
+
|
41
|
+
def read_master!
|
42
|
+
@read_from_master = true
|
43
|
+
self
|
16
44
|
end
|
17
45
|
|
18
46
|
def enable_slave_access
|
@@ -22,11 +50,7 @@ module FreshConnection
|
|
22
50
|
private
|
23
51
|
|
24
52
|
def exec_queries
|
25
|
-
|
26
|
-
|
27
|
-
@klass.manage_access(enable_slave_access) do
|
28
|
-
super
|
29
|
-
end
|
53
|
+
manage_access { super }
|
30
54
|
end
|
31
55
|
end
|
32
56
|
end
|
@@ -1,27 +1,18 @@
|
|
1
|
-
require '
|
1
|
+
require 'active_record'
|
2
|
+
require 'fresh_connection/extend/ar_base'
|
3
|
+
require 'fresh_connection/extend/ar_relation'
|
4
|
+
require 'fresh_connection/extend/ar_abstract_adapter'
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
require 'fresh_connection/extend/connection_handler'
|
7
|
-
require 'fresh_connection/extend/mysql2_adapter'
|
8
|
-
require 'active_record/connection_adapters/mysql2_adapter'
|
6
|
+
module ActiveRecord
|
7
|
+
Base.extend FreshConnection::Extend::ArBase
|
8
|
+
Relation.send :prepend, FreshConnection::Extend::ArRelation
|
9
9
|
|
10
|
-
|
11
|
-
ActiveRecord::Relation.__send__(:prepend, FreshConnection::Extend::ArRelation)
|
12
|
-
|
13
|
-
ActiveRecord::ConnectionAdapters::ConnectionHandler.__send__(
|
14
|
-
:prepend, FreshConnection::Extend::ConnectionHandler
|
15
|
-
)
|
16
|
-
|
17
|
-
ActiveRecord::ConnectionAdapters::Mysql2Adapter.__send__(
|
18
|
-
:prepend, FreshConnection::Extend::Mysql2Adapter
|
19
|
-
)
|
20
|
-
|
21
|
-
if defined?(ActiveRecord::StatementCache)
|
10
|
+
if defined?(StatementCache)
|
22
11
|
require 'fresh_connection/extend/ar_statement_cache'
|
23
|
-
|
12
|
+
StatementCache.send :prepend, FreshConnection::Extend::ArStatementCache
|
24
13
|
end
|
25
14
|
|
26
|
-
|
15
|
+
ConnectionAdapters::AbstractAdapter.send :prepend, FreshConnection::Extend::ArAbstractAdapter
|
16
|
+
|
17
|
+
Base.establish_fresh_connection
|
27
18
|
end
|
@@ -1,15 +1,17 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
1
3
|
module FreshConnection
|
2
4
|
class SlaveConnectionHandler
|
3
5
|
def initialize
|
4
|
-
@class_to_pool =
|
6
|
+
@class_to_pool = Concurrent::Map.new
|
5
7
|
end
|
6
8
|
|
7
9
|
def establish_connection(name, slave_group)
|
8
|
-
if cm =
|
10
|
+
if cm = class_to_pool[name]
|
9
11
|
cm.put_aside!
|
10
12
|
end
|
11
13
|
|
12
|
-
|
14
|
+
class_to_pool[name] = FreshConnection.connection_manager.new(slave_group)
|
13
15
|
end
|
14
16
|
|
15
17
|
def connection(klass)
|
@@ -22,16 +24,16 @@ module FreshConnection
|
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
27
|
+
def recovery?(klass)
|
28
|
+
detect_connection_manager(klass).recovery?
|
29
|
+
end
|
30
|
+
|
25
31
|
def put_aside!
|
26
32
|
all_connection_managers do |connection_manager|
|
27
33
|
connection_manager.put_aside!
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
31
|
-
def recovery(klass, failure_connection, exception)
|
32
|
-
detect_connection_manager(klass).recovery(failure_connection, exception)
|
33
|
-
end
|
34
|
-
|
35
37
|
def slave_group(klass)
|
36
38
|
detect_connection_manager(klass).slave_group
|
37
39
|
end
|
@@ -39,17 +41,21 @@ module FreshConnection
|
|
39
41
|
private
|
40
42
|
|
41
43
|
def all_connection_managers
|
42
|
-
|
44
|
+
class_to_pool.values.each do |connection_manager|
|
43
45
|
yield(connection_manager)
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
49
|
def detect_connection_manager(klass)
|
48
|
-
c =
|
50
|
+
c = class_to_pool[klass.name]
|
49
51
|
return c if c
|
50
52
|
return nil if ActiveRecord::Base == klass
|
51
53
|
detect_connection_manager(klass.superclass)
|
52
54
|
end
|
55
|
+
|
56
|
+
def class_to_pool
|
57
|
+
@class_to_pool
|
58
|
+
end
|
53
59
|
end
|
54
60
|
end
|
55
61
|
|
data/lib/fresh_connection.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
require 'fresh_connection/connection_manager'
|
2
|
-
|
3
1
|
module FreshConnection
|
2
|
+
autoload :ConnectionManager, 'fresh_connection/connection_manager'
|
3
|
+
autoload :AbstractConnectionManager, 'fresh_connection/abstract_connection_manager'
|
4
|
+
autoload :ConnectionManager, 'fresh_connection/connection_manager'
|
5
|
+
autoload :ConnectionFactory, 'fresh_connection/connection_factory'
|
6
|
+
autoload :SlaveConnectionHandler, 'fresh_connection/slave_connection_handler'
|
7
|
+
autoload :AccessControl, 'fresh_connection/access_control'
|
8
|
+
|
4
9
|
class << self
|
5
10
|
attr_writer :connection_manager
|
6
11
|
|
@@ -11,4 +16,4 @@ module FreshConnection
|
|
11
16
|
end
|
12
17
|
|
13
18
|
require 'fresh_connection/extend'
|
14
|
-
require
|
19
|
+
require 'fresh_connection/railtie' if defined?(Rails)
|
metadata
CHANGED
@@ -1,143 +1,157 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fresh_connection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.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: 2016-
|
11
|
+
date: 2016-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
- - <
|
19
|
+
version: 4.0.0
|
20
|
+
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '5.0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
- - <
|
29
|
+
version: 4.0.0
|
30
|
+
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '5.0'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: concurrent-ruby
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- -
|
37
|
+
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version:
|
40
|
-
- - <
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: '5.0'
|
39
|
+
version: 1.0.0
|
43
40
|
type: :runtime
|
44
41
|
prerelease: false
|
45
42
|
version_requirements: !ruby/object:Gem::Requirement
|
46
43
|
requirements:
|
47
|
-
- -
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: 3.2.0
|
50
|
-
- - <
|
44
|
+
- - "~>"
|
51
45
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
46
|
+
version: 1.0.0
|
53
47
|
- !ruby/object:Gem::Dependency
|
54
48
|
name: mysql2
|
55
49
|
requirement: !ruby/object:Gem::Requirement
|
56
50
|
requirements:
|
57
|
-
- -
|
51
|
+
- - ">="
|
58
52
|
- !ruby/object:Gem::Version
|
59
|
-
version: 0.3.
|
60
|
-
|
53
|
+
version: 0.3.13
|
54
|
+
- - "<"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0.5'
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.3.13
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0.5'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: pg
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0.11'
|
74
|
+
type: :development
|
61
75
|
prerelease: false
|
62
76
|
version_requirements: !ruby/object:Gem::Requirement
|
63
77
|
requirements:
|
64
|
-
- - ~>
|
78
|
+
- - "~>"
|
65
79
|
- !ruby/object:Gem::Version
|
66
|
-
version: 0.
|
80
|
+
version: '0.11'
|
67
81
|
- !ruby/object:Gem::Dependency
|
68
82
|
name: bundler
|
69
83
|
requirement: !ruby/object:Gem::Requirement
|
70
84
|
requirements:
|
71
|
-
- -
|
85
|
+
- - ">="
|
72
86
|
- !ruby/object:Gem::Version
|
73
87
|
version: 1.3.0
|
74
|
-
- - <
|
88
|
+
- - "<"
|
75
89
|
- !ruby/object:Gem::Version
|
76
90
|
version: '2.0'
|
77
91
|
type: :development
|
78
92
|
prerelease: false
|
79
93
|
version_requirements: !ruby/object:Gem::Requirement
|
80
94
|
requirements:
|
81
|
-
- -
|
95
|
+
- - ">="
|
82
96
|
- !ruby/object:Gem::Version
|
83
97
|
version: 1.3.0
|
84
|
-
- - <
|
98
|
+
- - "<"
|
85
99
|
- !ruby/object:Gem::Version
|
86
100
|
version: '2.0'
|
87
101
|
- !ruby/object:Gem::Dependency
|
88
102
|
name: rake
|
89
103
|
requirement: !ruby/object:Gem::Requirement
|
90
104
|
requirements:
|
91
|
-
- -
|
105
|
+
- - ">="
|
92
106
|
- !ruby/object:Gem::Version
|
93
107
|
version: 0.8.7
|
94
108
|
type: :development
|
95
109
|
prerelease: false
|
96
110
|
version_requirements: !ruby/object:Gem::Requirement
|
97
111
|
requirements:
|
98
|
-
- -
|
112
|
+
- - ">="
|
99
113
|
- !ruby/object:Gem::Version
|
100
114
|
version: 0.8.7
|
101
115
|
- !ruby/object:Gem::Dependency
|
102
116
|
name: appraisal
|
103
117
|
requirement: !ruby/object:Gem::Requirement
|
104
118
|
requirements:
|
105
|
-
- -
|
119
|
+
- - ">="
|
106
120
|
- !ruby/object:Gem::Version
|
107
121
|
version: '0'
|
108
122
|
type: :development
|
109
123
|
prerelease: false
|
110
124
|
version_requirements: !ruby/object:Gem::Requirement
|
111
125
|
requirements:
|
112
|
-
- -
|
126
|
+
- - ">="
|
113
127
|
- !ruby/object:Gem::Version
|
114
128
|
version: '0'
|
115
129
|
- !ruby/object:Gem::Dependency
|
116
130
|
name: minitest
|
117
131
|
requirement: !ruby/object:Gem::Requirement
|
118
132
|
requirements:
|
119
|
-
- -
|
133
|
+
- - ">="
|
120
134
|
- !ruby/object:Gem::Version
|
121
135
|
version: '0'
|
122
136
|
type: :development
|
123
137
|
prerelease: false
|
124
138
|
version_requirements: !ruby/object:Gem::Requirement
|
125
139
|
requirements:
|
126
|
-
- -
|
140
|
+
- - ">="
|
127
141
|
- !ruby/object:Gem::Version
|
128
142
|
version: '0'
|
129
143
|
- !ruby/object:Gem::Dependency
|
130
144
|
name: minitest-reporters
|
131
145
|
requirement: !ruby/object:Gem::Requirement
|
132
146
|
requirements:
|
133
|
-
- -
|
147
|
+
- - ">="
|
134
148
|
- !ruby/object:Gem::Version
|
135
149
|
version: '0'
|
136
150
|
type: :development
|
137
151
|
prerelease: false
|
138
152
|
version_requirements: !ruby/object:Gem::Requirement
|
139
153
|
requirements:
|
140
|
-
- -
|
154
|
+
- - ">="
|
141
155
|
- !ruby/object:Gem::Version
|
142
156
|
version: '0'
|
143
157
|
description: https://github.com/tsukasaoishi/fresh_connection
|
@@ -147,32 +161,32 @@ executables: []
|
|
147
161
|
extensions: []
|
148
162
|
extra_rdoc_files: []
|
149
163
|
files:
|
150
|
-
- .gitignore
|
151
|
-
- .travis.yml
|
164
|
+
- ".gitignore"
|
165
|
+
- ".travis.yml"
|
152
166
|
- Appraisals
|
153
167
|
- CODE_OF_CONDUCT.md
|
154
168
|
- Gemfile
|
155
169
|
- LICENSE.txt
|
156
170
|
- README.md
|
157
171
|
- Rakefile
|
172
|
+
- bin/console
|
173
|
+
- bin/setup
|
174
|
+
- bin/test
|
158
175
|
- fresh_connection.gemspec
|
159
|
-
- gemfiles/rails3.gemfile
|
160
176
|
- gemfiles/rails40.gemfile
|
161
177
|
- gemfiles/rails41.gemfile
|
162
178
|
- gemfiles/rails42.gemfile
|
179
|
+
- gemfiles/rails50.gemfile
|
163
180
|
- lib/fresh_connection.rb
|
164
181
|
- lib/fresh_connection/abstract_connection_manager.rb
|
165
182
|
- lib/fresh_connection/access_control.rb
|
166
183
|
- lib/fresh_connection/connection_factory.rb
|
167
184
|
- lib/fresh_connection/connection_manager.rb
|
168
185
|
- lib/fresh_connection/extend.rb
|
186
|
+
- lib/fresh_connection/extend/ar_abstract_adapter.rb
|
169
187
|
- lib/fresh_connection/extend/ar_base.rb
|
170
188
|
- lib/fresh_connection/extend/ar_relation.rb
|
171
|
-
- lib/fresh_connection/extend/ar_relation/for_rails3.rb
|
172
|
-
- lib/fresh_connection/extend/ar_relation/for_rails4.rb
|
173
189
|
- lib/fresh_connection/extend/ar_statement_cache.rb
|
174
|
-
- lib/fresh_connection/extend/connection_handler.rb
|
175
|
-
- lib/fresh_connection/extend/mysql2_adapter.rb
|
176
190
|
- lib/fresh_connection/rack/connection_management.rb
|
177
191
|
- lib/fresh_connection/railtie.rb
|
178
192
|
- lib/fresh_connection/slave_connection_handler.rb
|
@@ -188,17 +202,17 @@ require_paths:
|
|
188
202
|
- lib
|
189
203
|
required_ruby_version: !ruby/object:Gem::Requirement
|
190
204
|
requirements:
|
191
|
-
- -
|
205
|
+
- - ">="
|
192
206
|
- !ruby/object:Gem::Version
|
193
207
|
version: '2.0'
|
194
208
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
209
|
requirements:
|
196
|
-
- -
|
210
|
+
- - ">="
|
197
211
|
- !ruby/object:Gem::Version
|
198
212
|
version: '0'
|
199
213
|
requirements: []
|
200
214
|
rubyforge_project:
|
201
|
-
rubygems_version: 2.
|
215
|
+
rubygems_version: 2.5.1
|
202
216
|
signing_key:
|
203
217
|
specification_version: 4
|
204
218
|
summary: FreshConnection supports to connect with Mysql slave servers via Load Balancers.
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module FreshConnection
|
2
|
-
module Extend
|
3
|
-
module ArRelation
|
4
|
-
module ForRails
|
5
|
-
def pluck(column_name)
|
6
|
-
if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
|
7
|
-
column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
|
8
|
-
end
|
9
|
-
|
10
|
-
result = @klass.manage_access(enable_slave_access) do
|
11
|
-
klass.connection.select_all(select(column_name).arel, nil)
|
12
|
-
end
|
13
|
-
|
14
|
-
return result if result.nil? || result.empty?
|
15
|
-
|
16
|
-
last_columns = result.last.keys.last
|
17
|
-
|
18
|
-
result.map do |attributes|
|
19
|
-
klass.type_cast_attribute(last_columns, klass.initialize_attributes(attributes))
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def read_master
|
24
|
-
relation = clone
|
25
|
-
relation.instance_variable_set("@read_from_master", true)
|
26
|
-
relation
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module FreshConnection
|
2
|
-
module Extend
|
3
|
-
module ArRelation
|
4
|
-
module ForRails
|
5
|
-
def pluck(*args)
|
6
|
-
@klass.manage_access(enable_slave_access) { super }
|
7
|
-
end
|
8
|
-
|
9
|
-
def read_master
|
10
|
-
spawn.read_master!
|
11
|
-
end
|
12
|
-
|
13
|
-
def read_master!
|
14
|
-
@read_from_master = true
|
15
|
-
self
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'fresh_connection/access_control'
|
2
|
-
|
3
|
-
module FreshConnection
|
4
|
-
module Extend
|
5
|
-
module Mysql2Adapter
|
6
|
-
RETRY_LIMIT = 3
|
7
|
-
private_constant :RETRY_LIMIT
|
8
|
-
|
9
|
-
def self.prepended(base)
|
10
|
-
base.__send__(:attr_writer, :model_class)
|
11
|
-
end
|
12
|
-
|
13
|
-
def select_all(arel, name = nil, binds = [])
|
14
|
-
if FreshConnection::AccessControl.slave_access?
|
15
|
-
change_connection do
|
16
|
-
super(arel, "[#{@model_class.slave_group}] #{name}", binds)
|
17
|
-
end
|
18
|
-
else
|
19
|
-
super
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def change_connection
|
26
|
-
retry_count = 0
|
27
|
-
master_connection = @connection
|
28
|
-
begin
|
29
|
-
slave_connection = @model_class.slave_connection
|
30
|
-
@connection = slave_connection.raw_connection
|
31
|
-
yield
|
32
|
-
rescue ActiveRecord::StatementInvalid => exception
|
33
|
-
if @model_class.recovery(slave_connection, exception)
|
34
|
-
retry_count += 1
|
35
|
-
retry if retry_count < RETRY_LIMIT
|
36
|
-
end
|
37
|
-
|
38
|
-
raise
|
39
|
-
end
|
40
|
-
ensure
|
41
|
-
@connection = master_connection
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|