SwitchConnection
Switching database connection between slave one and writable one. Fork from switch_point gem.
Original Version: https://github.com/eagletmt/switch_point.
Installation
Add this line to your application's Gemfile:
gem 'switch_connection'
And then execute:
$ bundle
Or install it yourself as:
$ gem install switch_connection
Usage
Suppose you have 4 databases: db-blog-master, db-blog-slave, db-comment-master and db-comment-slave. Article model and Category model are stored in db-blog-master,slave and Comment model is stored in db-comment-master,slave.
Configuration
In database.yml:
production_blog_master:
adapter: mysql2
username: blog_writable
host: db-blog-master
production_blog_slave:
adapter: mysql2
username: blog_slave
host: db-blog-slave
production_comment_master:
...
In initializer:
SwitchConnection.configure do |config|
config.define_switch_point :blog,
slaves: [:"#{Rails.env}_blog_slave1",:"#{Rails.env}_blog_slave2"]
master: :"#{Rails.env}_blog_master"
config.define_switch_point :comment,
slaves: [:"#{Rails.env}_comment_slave"]
master: :"#{Rails.env}_comment_master"
end
In models:
class Article < ActiveRecord::Base
use_switch_point :blog
end
class Category < ActiveRecord::Base
use_switch_point :blog
end
class Comment < ActiveRecord::Base
use_switch_point :comment
end
Switching connections
Article.with_slave { Article.first } # Read from db-blog-slave
Category.with_slave { Category.first } # Also read from db-blog-slave
Comment.with_slave { Comment.first } # Read from db-comment-slave
Article.with_slave do
article = Article.first # Read from db-blog-slave
article.title = 'new title'
Article.with_master do
article.save! # Write to db-blog-master
article.reload # Read from db-blog-master
Category.first # Read from db-blog-master
end
end
- with_switch_point
ruby Book.with_switch_point(:main) { Book.count }
Note that Article and Category shares their connections.
Notes
auto_master
auto_master by default.
When auto_master is enabled, destructive queries is sent to writable connection even in slave mode.
But it does NOT work well on transactions.
Suppose after_save callback is set to User model. When User.create is called, it proceeds as follows.
- BEGIN TRANSACTION is sent to READONLY connection.
- switch_point switches the connection to WRITABLE.
- INSERT statement is sent to WRITABLE connection.
- switch_point reset the connection to READONLY.
- after_save callback is called.
- At this point, the connection is READONLY and in a transaction.
- COMMIT TRANSACTION is sent to READONLY connection.
connection-related methods of model
Model has several connection-related methods: connection_handler, connection_pool, connected? and so on.
Since only connection method is monkey-patched, other connection-related methods doesn't work properly.
If you'd like to use those methods, send it to Model.switch_point_proxy.model_for_connection.
Internals
There's a proxy which holds two connections: slave one and writable one. A proxy has a thread-local state indicating the current mode: slave or writable.
Each ActiveRecord model refers to a proxy.
ActiveRecord::Base.connection is hooked and delegated to the referred proxy.
When the writable connection is requested to execute destructive query, the slave connection clears its query cache.
Special case: ActiveRecord::Base.connection
Basically, each connection managed by a proxy isn't shared between proxies. But there's one exception: ActiveRecord::Base.
Contributing
- Fork it ( https://github.com/phamvanmhung2e123/switch_point/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request