switch_connection 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +26 -0
- data/.hound.yml +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +63 -0
- data/.rubocop_todo.yml +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +22 -0
- data/Appraisals +66 -0
- data/CHANGELOG.md +73 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +143 -0
- data/Rakefile +16 -0
- data/assets/switch_point.svg +2 -0
- data/benchmark/proxy.rb +71 -0
- data/gemfiles/rails_3.2.gemfile +8 -0
- data/gemfiles/rails_4.0.gemfile +8 -0
- data/gemfiles/rails_4.1.gemfile +8 -0
- data/gemfiles/rails_4.2.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +8 -0
- data/gemfiles/rails_5.2.gemfile +9 -0
- data/gemfiles/rails_edge.gemfile +9 -0
- data/lib/switch_connection.rb +66 -0
- data/lib/switch_connection/config.rb +94 -0
- data/lib/switch_connection/error.rb +12 -0
- data/lib/switch_connection/model.rb +144 -0
- data/lib/switch_connection/proxy.rb +165 -0
- data/lib/switch_connection/proxy_repository.rb +30 -0
- data/lib/switch_connection/version.rb +5 -0
- data/spec/models.rb +141 -0
- data/spec/spec_helper.rb +91 -0
- data/spec/switch_connection/model_spec.rb +402 -0
- data/spec/switch_connection_spec.rb +108 -0
- data/switch_connection.gemspec +36 -0
- metadata +265 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a610610ba7b802579bd70c8b25df8f8bba50299
|
4
|
+
data.tar.gz: c796e8855c8f3472629390723cd8c0c6c00559ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b3a4ad59a79b047914a09d125bae342993ab7b619cd48c23a2d7b0c53ade3be1d1d74948ae011a91741247b10729a611d5f1494e538945a641f196c9f4476958
|
7
|
+
data.tar.gz: 3d2fa748e38c0dc9536d9d41d0699868d387556df4d7730ba19e621a79a69e05d593f7b4810ed8e5f9d6dbdeb933fb4d4a0f765cb94b2025ceeaccc67543aca6
|
data/.gitignore
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
vendor
|
24
|
+
/gemfiles/*.lock
|
25
|
+
.idea
|
26
|
+
.ruby-version
|
data/.hound.yml
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
TargetRubyVersion: 2.3
|
5
|
+
DisplayCopNames: true
|
6
|
+
Exclude:
|
7
|
+
- "gemfiles/**/*"
|
8
|
+
- "vendor/**/*"
|
9
|
+
|
10
|
+
Performance/RedundantBlockCall:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Layout/AlignParameters:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
LineLength:
|
17
|
+
Max: 100
|
18
|
+
|
19
|
+
Metrics:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Naming/FileName:
|
23
|
+
Exclude:
|
24
|
+
- Appraisals
|
25
|
+
|
26
|
+
Metrics/BlockLength:
|
27
|
+
Exclude:
|
28
|
+
- 'spec/**/*'
|
29
|
+
|
30
|
+
Style/AccessModifierDeclarations:
|
31
|
+
Enabled: false
|
32
|
+
Style/Alias:
|
33
|
+
EnforcedStyle: prefer_alias_method
|
34
|
+
Style/BlockDelimiters:
|
35
|
+
Enabled: false
|
36
|
+
Style/GuardClause:
|
37
|
+
Enabled: false
|
38
|
+
Style/HashSyntax:
|
39
|
+
Exclude:
|
40
|
+
- Rakefile
|
41
|
+
Style/IfUnlessModifier:
|
42
|
+
Enabled: false
|
43
|
+
Style/Lambda:
|
44
|
+
Enabled: false
|
45
|
+
Style/Next:
|
46
|
+
Enabled: false
|
47
|
+
Style/PercentLiteralDelimiters:
|
48
|
+
PreferredDelimiters:
|
49
|
+
'%w': '[]'
|
50
|
+
Style/RaiseArgs:
|
51
|
+
EnforcedStyle: compact
|
52
|
+
Style/SignalException:
|
53
|
+
Enabled: false
|
54
|
+
Style/TrailingCommaInArguments:
|
55
|
+
Enabled: false
|
56
|
+
Style/TrailingCommaInArrayLiteral:
|
57
|
+
Enabled: false
|
58
|
+
Style/TrailingCommaInHashLiteral:
|
59
|
+
Enabled: false
|
60
|
+
Style/FrozenStringLiteralComment:
|
61
|
+
Exclude:
|
62
|
+
- "gemfiles/*.gemfile"
|
63
|
+
- "Appraisals"
|
data/.rubocop_todo.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.6
|
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.3.7
|
4
|
+
- 2.4.4
|
5
|
+
- 2.5.1
|
6
|
+
- ruby-head
|
7
|
+
gemfile:
|
8
|
+
- gemfiles/rails_3.2.gemfile
|
9
|
+
- gemfiles/rails_4.0.gemfile
|
10
|
+
- gemfiles/rails_4.1.gemfile
|
11
|
+
- gemfiles/rails_4.2.gemfile
|
12
|
+
- gemfiles/rails_5.0.gemfile
|
13
|
+
- gemfiles/rails_5.1.gemfile
|
14
|
+
- gemfiles/rails_5.2.gemfile
|
15
|
+
- gemfiles/rails_edge.gemfile
|
16
|
+
sudo: false
|
17
|
+
after_script:
|
18
|
+
- bundle exec rake benchmark
|
19
|
+
matrix:
|
20
|
+
allow_failures:
|
21
|
+
- rvm: ruby-head
|
22
|
+
- gemfile: gemfiles/rails_edge.gemfile
|
data/Appraisals
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
appraise 'rails-3.2' do
|
2
|
+
gem 'activerecord', '~> 3.2.0'
|
3
|
+
|
4
|
+
platforms :ruby do
|
5
|
+
gem 'sqlite3'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
appraise 'rails-4.0' do
|
10
|
+
gem 'activerecord', '~> 4.0.0'
|
11
|
+
|
12
|
+
platforms :ruby do
|
13
|
+
gem 'sqlite3'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
appraise 'rails-4.1' do
|
18
|
+
gem 'activerecord', '~> 4.1.0'
|
19
|
+
|
20
|
+
platforms :ruby do
|
21
|
+
gem 'sqlite3'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
appraise 'rails-4.2' do
|
26
|
+
gem 'activerecord', '~> 4.2.0'
|
27
|
+
|
28
|
+
platforms :ruby do
|
29
|
+
gem 'sqlite3'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
appraise 'rails-5.0' do
|
34
|
+
gem 'activerecord', '~> 5.0.0'
|
35
|
+
|
36
|
+
platforms :ruby do
|
37
|
+
gem 'sqlite3'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
appraise 'rails-5.1' do
|
42
|
+
gem 'activerecord', '~> 5.1.0'
|
43
|
+
|
44
|
+
platforms :ruby do
|
45
|
+
gem 'sqlite3'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
appraise 'rails-5.2' do
|
50
|
+
gem 'activerecord', '~> 5.2.0'
|
51
|
+
|
52
|
+
platforms :ruby do
|
53
|
+
gem 'sqlite3'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
appraise 'rails-edge' do
|
58
|
+
gem 'activerecord', git: 'https://github.com/rails/rails'
|
59
|
+
gem 'arel', git: 'https://github.com/rails/arel'
|
60
|
+
|
61
|
+
platforms :ruby do
|
62
|
+
gem 'sqlite3'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# vim: set ft=ruby:
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
## 1.0.0 (2019-07-31)
|
2
|
+
- Multi slaves support
|
3
|
+
- Connect to master by default
|
4
|
+
- Change switch point (thread safe)
|
5
|
+
- Log connection name
|
6
|
+
|
7
|
+
## 0.8.0 (2016-06-06)
|
8
|
+
- Drop Ruby 2.0.0 and 2.1 support
|
9
|
+
- Add `AR::Base#with_readonly` and `AR::Base#with_writable`
|
10
|
+
- short-hand for `AR::Base.with_readonly` and `AR::Base.with_writable`
|
11
|
+
- Add `AR::Base#transaction_with`
|
12
|
+
- short-hand for `AR::Base.transaction_with`
|
13
|
+
- Fix warnings for Rails 5.0
|
14
|
+
|
15
|
+
## 0.7.0 (2015-10-16)
|
16
|
+
- `Model.with_readonly` and `Model.with_writable` now raises error when the Model doesn't use switch_point
|
17
|
+
|
18
|
+
## 0.6.0 (2015-04-14)
|
19
|
+
- Add `SwitchConnection::QueryCache` middleware
|
20
|
+
- `Model.cache` and `Model.uncached` is now hooked by switch_point
|
21
|
+
- `Model.cache` enables query cache for both readonly and writable.
|
22
|
+
- `Model.uncached` disables query cache for both readonly and writable.
|
23
|
+
- Add `SwitchConnection.with_readonly_all` and `SwitchConnection.with_writable_all` as shorthand
|
24
|
+
|
25
|
+
## 0.5.0 (2014-11-05)
|
26
|
+
- Rename `SwitchConnection.with_connection` to `SwitchConnection.with_mode`
|
27
|
+
- To avoid confusion with `ActiveRecord::ConnectionPool#with_connection`
|
28
|
+
- Inherit superclass' switch_point configuration
|
29
|
+
|
30
|
+
## 0.4.4 (2014-07-14)
|
31
|
+
- Memorize switch_point config to ConnectionSpecification#config instead of ConnectionPool
|
32
|
+
- To support multi-threaded environment since Rails 4.0.
|
33
|
+
|
34
|
+
## 0.4.3 (2014-06-24)
|
35
|
+
- Add Model.transaction_with method (#2, @ryopeko)
|
36
|
+
|
37
|
+
## 0.4.2 (2014-06-19)
|
38
|
+
- Establish connection lazily
|
39
|
+
- Just like ActiveRecord::Base, real connection isn't created until `.connection` is called
|
40
|
+
|
41
|
+
## 0.4.1 (2014-06-19)
|
42
|
+
- Support :writable only configuration
|
43
|
+
|
44
|
+
## 0.4.0 (2014-06-17)
|
45
|
+
- auto_writable is disabled by default
|
46
|
+
- To restore the previous behavior, set `config.auto_writable = true`.
|
47
|
+
- Add shorthand methods `SwitchConnection.with_readonly`, `SwitchConnection.with_writable`
|
48
|
+
|
49
|
+
## 0.3.1 (2014-06-04)
|
50
|
+
- Support defaulting to writable ActiveRecord::Base connection
|
51
|
+
- When `:writable` key is omitted, ActiveRecord::Base is used for the writable connection.
|
52
|
+
|
53
|
+
## 0.3.0 (2014-06-04)
|
54
|
+
- Improve thread safety
|
55
|
+
- Raise appropriate error if unknown mode is given to with_connection
|
56
|
+
|
57
|
+
## 0.2.3 (2014-06-02)
|
58
|
+
- Support specifying the same database name within different switch_point
|
59
|
+
- Add Proxy#readonly? and Proxy#writable? predicate
|
60
|
+
|
61
|
+
## 0.2.2 (2014-05-30)
|
62
|
+
- Fix nil error on with_{readonly,writable} from non-switch_point model
|
63
|
+
|
64
|
+
## 0.2.1 (2014-05-29)
|
65
|
+
- Add Proxy#switch_name to switch proxy configuration
|
66
|
+
- Fix weird nil error when Config#define_switch_point isn't called yet
|
67
|
+
|
68
|
+
## 0.2.0 (2014-05-29)
|
69
|
+
- Always send destructive operations to writable connection
|
70
|
+
- Fix bug on pooled connections
|
71
|
+
|
72
|
+
## 0.1.0 (2014-05-28)
|
73
|
+
- Initial release
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Kohei Suzuki
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# SwitchConnection
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/switch_point.svg)](http://badge.fury.io/rb/switch_point)
|
3
|
+
[![Build Status](https://travis-ci.org/phamvanhung2e123/switch_point.svg?branch=master)](https://travis-ci.org/phamvannhung22123/switch_point)
|
4
|
+
[![Coverage Status](https://img.shields.io/coveralls/phamvanhung2e123/switch_point.svg?branch=master)](https://coveralls.io/r/phamvannhung2e123/switch_point?branch=master)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/phamvanhung2e123/switch_point/badges/gpa.svg)](https://codeclimate.com/github/phamvannhung2e123/switch_point)
|
6
|
+
|
7
|
+
Switching database connection between slave one and writable one. Fork from `switch_point` gem.
|
8
|
+
Original Version: https://github.com/eagletmt/switch_point.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'switch_connection'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install switch_connection
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
Suppose you have 4 databases: db-blog-master, db-blog-slave, db-comment-master and db-comment-slave.
|
26
|
+
Article model and Category model are stored in db-blog-{master,slave} and Comment model is stored in db-comment-{master,slave}.
|
27
|
+
|
28
|
+
### Configuration
|
29
|
+
In database.yml:
|
30
|
+
|
31
|
+
```yaml
|
32
|
+
production_blog_master:
|
33
|
+
adapter: mysql2
|
34
|
+
username: blog_writable
|
35
|
+
host: db-blog-master
|
36
|
+
production_blog_slave:
|
37
|
+
adapter: mysql2
|
38
|
+
username: blog_slave
|
39
|
+
host: db-blog-slave
|
40
|
+
production_comment_master:
|
41
|
+
...
|
42
|
+
```
|
43
|
+
|
44
|
+
In initializer:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
SwitchConnection.configure do |config|
|
48
|
+
config.define_switch_point :blog,
|
49
|
+
slaves: [:"#{Rails.env}_blog_slave1",:"#{Rails.env}_blog_slave2"]
|
50
|
+
master: :"#{Rails.env}_blog_master"
|
51
|
+
config.define_switch_point :comment,
|
52
|
+
slaves: [:"#{Rails.env}_comment_slave"]
|
53
|
+
master: :"#{Rails.env}_comment_master"
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
In models:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class Article < ActiveRecord::Base
|
61
|
+
use_switch_point :blog
|
62
|
+
end
|
63
|
+
|
64
|
+
class Category < ActiveRecord::Base
|
65
|
+
use_switch_point :blog
|
66
|
+
end
|
67
|
+
|
68
|
+
class Comment < ActiveRecord::Base
|
69
|
+
use_switch_point :comment
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
### Switching connections
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
Article.with_slave { Article.first } # Read from db-blog-slave
|
77
|
+
Category.with_slave { Category.first } # Also read from db-blog-slave
|
78
|
+
Comment.with_slave { Comment.first } # Read from db-comment-slave
|
79
|
+
|
80
|
+
Article.with_slave do
|
81
|
+
article = Article.first # Read from db-blog-slave
|
82
|
+
article.title = 'new title'
|
83
|
+
Article.with_master do
|
84
|
+
article.save! # Write to db-blog-master
|
85
|
+
article.reload # Read from db-blog-master
|
86
|
+
Category.first # Read from db-blog-master
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
- with_switch_point
|
92
|
+
```ruby
|
93
|
+
Book.with_switch_point(:main) { Book.count }
|
94
|
+
```
|
95
|
+
|
96
|
+
Note that Article and Category shares their connections.
|
97
|
+
|
98
|
+
## Notes
|
99
|
+
|
100
|
+
### auto_master
|
101
|
+
`auto_master` by default.
|
102
|
+
|
103
|
+
When `auto_master` is enabled, destructive queries is sent to writable connection even in slave mode.
|
104
|
+
But it does NOT work well on transactions.
|
105
|
+
|
106
|
+
Suppose `after_save` callback is set to User model. When `User.create` is called, it proceeds as follows.
|
107
|
+
|
108
|
+
1. BEGIN TRANSACTION is sent to READONLY connection.
|
109
|
+
2. switch_point switches the connection to WRITABLE.
|
110
|
+
3. INSERT statement is sent to WRITABLE connection.
|
111
|
+
4. switch_point reset the connection to READONLY.
|
112
|
+
5. after_save callback is called.
|
113
|
+
- At this point, the connection is READONLY and in a transaction.
|
114
|
+
6. COMMIT TRANSACTION is sent to READONLY connection.
|
115
|
+
|
116
|
+
### connection-related methods of model
|
117
|
+
Model has several connection-related methods: `connection_handler`, `connection_pool`, `connected?` and so on.
|
118
|
+
Since only `connection` method is monkey-patched, other connection-related methods doesn't work properly.
|
119
|
+
If you'd like to use those methods, send it to `Model.switch_point_proxy.model_for_connection`.
|
120
|
+
|
121
|
+
## Internals
|
122
|
+
There's a proxy which holds two connections: slave one and writable one.
|
123
|
+
A proxy has a thread-local state indicating the current mode: slave or writable.
|
124
|
+
|
125
|
+
Each ActiveRecord model refers to a proxy.
|
126
|
+
`ActiveRecord::Base.connection` is hooked and delegated to the referred proxy.
|
127
|
+
|
128
|
+
When the writable connection is requested to execute destructive query, the slave connection clears its query cache.
|
129
|
+
|
130
|
+
![switch_point](https://gyazo.wanko.cc/switch_point.svg)
|
131
|
+
|
132
|
+
### Special case: ActiveRecord::Base.connection
|
133
|
+
Basically, each connection managed by a proxy isn't shared between proxies.
|
134
|
+
But there's one exception: ActiveRecord::Base.
|
135
|
+
|
136
|
+
|
137
|
+
## Contributing
|
138
|
+
|
139
|
+
1. Fork it ( https://github.com/phamvanmhung2e123/switch_point/fork )
|
140
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
141
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
142
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
143
|
+
5. Create a new Pull Request
|