README.rdoc in db-charmer-1.4.0 vs README.rdoc in db-charmer-1.4.1
- old
+ new
@@ -2,25 +2,24 @@
+DbCharmer+ is a simple yet powerful plugin for ActiveRecord that does a few things:
1. Allows you to easily manage AR models' connections (+switch_connection_to+ method)
2. Allows you to switch AR models' default connections to a separate servers/databases
-3. Allows you to easily choose where your query should go (<tt>Model.on_db</tt> methods)
+3. Allows you to easily choose where your query should go (<tt>Model.on_*</tt> methods family)
4. Allows you to automatically send read queries to your slaves while masters would handle all the updates.
5. Adds multiple databases migrations to ActiveRecord
== Installation
There are two options when approaching db-charmer installation:
-* using gem (recommended)
+* using the gem (recommended)
* install as a Rails plugin
To install as a gem, add this to your environment.rb:
- config.gem 'kovyrin-db-charmer', :lib => 'db_charmer',
- :source => 'http://gems.github.com'
+ config.gem 'db-charmer', :lib => 'db_charmer', :source => 'http://gemcutter.org'
And then run the command:
sudo rake gems:install
@@ -29,47 +28,48 @@
script/plugin install git://github.com/kovyrin/db-charmer.git
== Easy ActiveRecord Connection Management
-As a part of this plugin we've added +switch_connection_to+ method that accepts many different kinds
-of db connections and uses them on a model. We support:
+As a part of this plugin we've added +switch_connection_to+ method that accepts many different kinds
+of db connections specifications and uses them on a model. We support:
1. Strings and symbols as the names of connection configuration blocks in database.yml.
2. ActiveRecord models (we'd use connection currently set up on a model).
3. Database connections (<tt>Model.connection</tt>)
4. Nil values to reset model to default connection.
Sample code:
class Foo < ActiveRecord::Model; end
-
+
Foo.switch_connection_to(:blah)
Foo.switch_connection_to('foo')
Foo.switch_connection_to(Bar)
Foo.switch_connection_to(Baz.connection)
Foo.switch_connection_to(nil)
-The +switch_connection_to+ method has an optional second parameter +should_exist+ which is true
-by default. This parameter is used when the method is called with a string or a symbol connection
-name and there is no such connection configuration in the database.yml file. If this parameter
-is true, an exception would be raised, if it is false, the error would be ignored and no connection
-change would happen. This is really useful when in development mode or in tests you do not want to
-create many different databases on your local machine and just want to put all your tables in one
-database.
+The +switch_connection_to+ method has an optional second parameter +should_exist+ which is true
+by default. This parameter is used when the method is called with a string or a symbol connection
+name and there is no such connection configuration in the database.yml file. If this parameter
+is +true+, an exception would be raised, otherwise, the error would be ignored and no connection
+change would happen.
-Warning: All the connection switching calls would switch connection *only* for those classes the
-method called on. You can't call the +switch_connection_to+ method and switch connection for a
-base class in some hierarchy (for example, you can't switch AR::Base connection and see all your
-models switched to the new connection, use classic +establish_connection+ instead).
+This is really useful when in development mode or in a tests you do not want to create many different
+databases on your local machine and just want to put all your tables in a single database.
+*Warning*: All the connection switching calls would switch connection *only* for those classes the
+method called on. You can't call the +switch_connection_to+ method and switch connection for a
+base class in some hierarchy (for example, you can't switch AR::Base connection and see all your
+models switched to the new connection, use the classic +establish_connection+ instead).
+
== Multiple DB Migrations
-In every application that works with many databases, there is need in convenient schema migrations mechanism.
+In every application that works with many databases, there is need in a convenient schema migrations mechanism.
-All Rails users already have this mechanism - rails migrations. So in +DbCharmer+, we've made it possible
+All Rails users already have this mechanism - rails migrations. So in +DbCharmer+, we've made it possible
to seamlessly use multiple databases in Rails migrations.
There are two methods available in migrations to operate on more than one database:
1. Global connection change method - used to switch whole migration to a non-default database.
@@ -77,18 +77,18 @@
Migration class example (global connection rewrite):
class MultiDbTest < ActiveRecord::Migration
db_magic :connection => :second_db
-
+
def self.up
create_table :test_table, :force => true do |t|
t.string :test_string
t.timestamps
end
end
-
+
def self.down
drop_table :test_table
end
end
@@ -101,43 +101,44 @@
t.string :test_string
t.timestamps
end
end
end
-
+
def self.down
on_db :second_db { drop_table :test_table }
end
end
-By default in development and test environments you could skip this <tt>:second_db</tt>
-connection from your database.yml files, but in production you'd specify it and
-get the table created on a separate server and/or in a separate database.
+By default in development and test environments you could skip this <tt>:second_db</tt>
+connection from your database.yml files and rails would create the tables in your single database,
+but in production you'd specify it and get the table created on a separate server and/or in a
+separate database.
-This behaviour is controlled by the <tt>DbCharmer.migration_connections_should_exist</tt>
+This behaviour is controlled by the <tt>DbCharmer.migration_connections_should_exist</tt>
configuration attribute which could be set from a rails initializer.
== Using Models in Master-Slave Environments
-Master-slave replication is the most popular scale-out technique in medium and large
-database applications today. There are some rails plugins out there that help rails
-developers to use slave servers in their models but none of there were flexible enough
+Master-slave replication is the most popular scale-out technique in a medium-sized and
+large database-centric applications today. There are some rails plugins out there that help
+developers to use slave servers in their models but none of them were flexible enough
for us to start using them in a huge application we work on.
-So, we've been using ActsAsReadonlyable plugin for a long time and have developed a
-lots of additions to its code over that time. Since that plugin has been abandoned
-by its authors, we've decided to collect all of our master-slave code in one plugin
+So, we've been using ActsAsReadonlyable plugin for a long time and have made tons
+of changes in its code over the time. But since that plugin has been abandoned
+by its authors, we've decided to collect all of our master-slave code in one plugin
and release it for rails 2.2+. +DbCharmer+ adds the following features to Rails models:
-=== Auto-Switching all Reads to Slave(s)
+=== Auto-Switching all Reads to the Slave(s)
-When you create a model, you could use <tt>db_magic :slave => :blah</tt> or
-<tt>db_magic :slaves => [ :foo, :bar ]</tt> commands in your model to set up reads
-redirection mode when all your find/count/exist/etc methods will be reading data
+When you create a model, you could use <tt>db_magic :slave => :blah</tt> or
+<tt>db_magic :slaves => [ :foo, :bar ]</tt> commands in your model to set up reads
+redirection mode when all your find/count/exist/etc methods will be reading data
from your slave (or a bunch of slaves in a round-robin manner). Here is an example:
class Foo < ActiveRecord::Base
db_magic :slave => :slave01
end
@@ -147,72 +148,72 @@
end
=== Default Connection Switching
-If you have more than one master-slave cluster (or simply more than one database)
-in your database environment, then you might want to change the default database
-connection of some of your models. You could do that by using
+If you have more than one master-slave cluster (or simply more than one database)
+in your database environment, then you might want to change the default database
+connection of some of your models. You could do that by using
<tt>db_magic :connection => :foo</tt> call from your models. Example:
class Foo < ActiveRecord::Base
db_magic :connection => :foo
end
-Sample model on a separate master-slave cluster (so, separate main connection +
+Sample model on a separate master-slave cluster (so, separate main connection +
a slave connection):
class Bar < ActiveRecord::Base
db_magic :connection => :bar, :slave => :bar_slave
end
=== Per-Query Connection Management
-Sometimes you have some select queries that you know you want to run on the master.
-This could happen for example when you have just added some data and need to read
-it back and not sure if it made it all the way to the slave yet or no. For this
-situation an few others there are a few methods we've added to ActiveRecord models:
+Sometimes you have select queries that you know you want to run on the master.
+This could happen for example when you have just added some data and need to read
+it back and not sure if it made it all the way to the slave yet or no. For this
+situation and a few others there is a set of methods we've added to ActiveRecord models:
-1) +on_master+ - this method could be used in two forms: block form and proxy form.
+1) +on_master+ - this method could be used in two forms: block form and proxy form.
In the block form you could force connection switch for a block of code:
User.on_master do
user = User.find_by_login('foo')
user.update_attributes!(:activated => true)
end
-In the proxy form this method could be used to force one query to be performed on
+In the proxy form this method could be used to force one query to be performed on
the master database server:
Comment.on_master.last(:limit => 5)
User.on_master.find_by_activation_code(code)
User.on_master.exists?(:login => login, :password => password)
-2) +on_slave+ - this method is used to force a query to be run on a slave even in
-situations when it's been previously forced to use the master. If there is more
-than one slave, one would be selected randomly. Tis method has two forms as
+2) +on_slave+ - this method is used to force a query to be run on a slave even in
+situations when it's been previously forced to use the master. If there is more
+than one slave, one would be selected randomly. Tis method has two forms as
well: block and proxy.
-3) <tt>on_db(connection)</tt> - this method is what makes two previous methods possible.
-It is used to switch a model's connection to some db for a short block of code
-or even for one statement (two forms). It accepts the same range of values as
-the +switch_connection_to+ method does. Example:
+3) <tt>on_db(connection)</tt> - this method is what makes two previous methods
+possible. It is used to switch a model's connection to some db for a short block
+of code or even for one statement (two forms). It accepts the same range of values
+as the +switch_connection_to+ method does. Example:
Comment.on_db(:olap).count
Post.on_db(:foo).find(:first)
=== Associations Connection Management
-ActiveRecord models can have associations and with their own connections and it becomes
-pretty hard to manage connections in chained calls like <tt>User.posts.count</tt>. With
-class-only connection switching methods this call would look like the following if we'd
-want to count posts on a separate database:
+ActiveRecord models can have an associations with each other and since every model has its
+own database connections, it becomes pretty hard to manage connections in a chained calls
+like <tt>User.posts.count</tt>. With a class-only connection switching methods this call
+would look like the following if we'd want to count posts on a separate database:
Post.on_db(:olap) { User.posts.count }
-Apparently this is not the best way to write the code and we've implemented <tt>on_*</tt>
+Apparently this is not the best way to write the code and we've implemented an <tt>on_*</tt>
methods on associations as well so you could do things like this:
@user.posts.on_db(:olap).count
@user.posts.on_slave.find(:title => 'Hello, world!')
@@ -222,21 +223,46 @@
@post.user.on_slave - would return post's author
@photo.owner.on_slave - would return photo's owner
+Starting with +DbCharmer+ release 1.4 it is possible to use prefix notation for has_many
+and HABTM associations connection switching:
+
+ @user.on_db(:foo).posts
+ @user.on_slave.posts
+
+
+=== Named Scopes Support
+
+To make it easier for +DbCharmer+ users to use connections switching methods with named scopes,
+we've added <tt>on_*</tt> methods support on the scopes as well. All the following scope chains
+would do exactly the same way (the query would be executed on the :foo database connection):
+
+ Post.on_db(:foo).published.with_comments.spam_marked.count
+ Post.published.on_db(:foo).with_comments.spam_marked.count
+ Post.published.with_comments.on_db(:foo).spam_marked.count
+ Post.published.with_comments.spam_marked.on_db(:foo).count
+
+And now, add this feature to our associations support and here is what we could do:
+
+ @user.on_db(:archive).posts.published.all
+ @user.posts.on_db(:olap).published.count
+ @user.posts.published.on_db(:foo).first
+
+
== Documentation
For more information on the plugin internals, please check out the source code. All the plugin's
-code is covered with tests that were placed in a separate staging rails project located at
-http://github.com/kovyrin/db-charmer-sandbox. The project has unit tests for all or at least the
+code is ~100% covered with a tests that were placed in a separate staging rails project located
+at http://github.com/kovyrin/db-charmer-sandbox. The project has unit tests for all or at least the
most of the parts of plugin's code.
== What Ruby and Rails implementations does it work for?
-We've tested the plugin on MRI 1.8.6 with Rails 2.2 and 2.3. We use it in production on Scribd.com
-with MRI 1.8.6 and Rails 2.2.
+We have a continuous integration setups for this plugin on MRI 1.8.6 with Rails 2.2 and 2.3.
+We use the plugin in production on Scribd.com with MRI (rubyee) 1.8.6 and Rails 2.2.
== Who are the authors?
This plugin has been created in Scribd.com for our internal use and then the sources were opened for