README.md in octoshark-0.0.9 vs README.md in octoshark-0.1.0

- old
+ new

@@ -1,10 +1,10 @@ ![Octoshark logo](https://dl.dropboxusercontent.com/u/3230730/github/octoshark.png) ![Travis status](https://travis-ci.org/dalibor/octoshark.png) -Octoshark is a simple ActiveRecord connection switcher. It provides a general purpose connection switching mechanism that can be used in sharding and master-slave multi-database environments. It's up to you to specify how ActiveRecord models will use the Octoshark connections, see below for example scenarios. +Octoshark is a simple ActiveRecord connection manager. It provides a connection switching mechanism that can be used in various scenarios like: sharding, master-slave, multi-tenancy, etc. You have control over how connections are configured and used, see below for examples. ## Installation Add this line to your application's Gemfile: @@ -26,110 +26,135 @@ ``` ## Usage -Specify the connections for Octoshark to manage. This is usually done in an app initializer. +Create a new connection manager with connection 2 pools for Octoshark to manage. This usually goes in the app initializer. ```ruby -Octoshark.configure({ +CONN_MANAGER = Octoshark::ConnectionManager.new({ db1: { adapter: "sqlite3", database: "db/db1.sqlite" }, db2: { adapter: "sqlite3", database: "db/db2.sqlite" } }) ``` -Configure which ActiveRecord models will use the Octoshark connection by overriding the Model.connection method. If there are many models, extract it in a module and include it. +Configure which ActiveRecord model will use the Octoshark connection by overriding the `Model.connection` method. ```ruby class Post < ActiveRecord::Base def self.connection - Octoshark.current_connection + CONN_MANAGER.current_connection end end ``` -To use a specific database connection, do: +Alternatively, extract it as a module and include in models. ```ruby -Octoshark.with_connection(:db1) do - # work with db1 - Post.first +module ShardingModel + extend ActiveSupport::Concern + + module ClassMethods + def connection + OCTOSHARK.current_connection + end + end end ``` -Octoshark connection is changed for the duration of the block and then reversed back to the previous connection. +To use a specific database connection: -`Octoshark.current_connection` returns the active connection while in `with_connection` block, and outside it raises `Octoshark::NoCurrentConnectionError` error. +```ruby +CONN_MANAGER.with_connection(:db1) do + # run queries on db1 + Post.first +end +``` -Multiple connection switch blocks can be nested: +Multiple `with_connection` blocks can be nested: ```ruby -Octoshark.with_connection(:db1) do - # work with db1 +CONN_MANAGER.with_connection(:db1) do + # run queries on db1 - Octoshark.with_connection(:db2) do - # work with db2 + CONN_MANAGER.with_connection(:db2) do + # run queries on db2 end - # work with db1 + # run queries on db1 end ``` +`CONN_MANAGER.current_connection` returns the active connection while in the `with_connection` block or raises `Octoshark::Error::NoCurrentConnection` otherwise. -## Sharding Example -For example, let's say we have few models that are in the default Rails database (User, Account, etc) and few models that we want to shard (Blog, Post, Comment, etc). For all models in the default database, we can use the default ActiveRecord connection, and for all sharded models we need to the Octoshark connection. +## Sharding example -We specify the connection for the sharded models based on the shard key (User) in a controller with an around filter: +Some models are in the core DB, and others in shard DBs. Shard is selected based on a user attribute. For core models use the default ActiveRecord connection and for sharded models define and use Octoshark connections. +Switch the connection in a controller with an around filter: + ```ruby -# before_filter :find_user around_filter :select_shard def select_shard(&block) - Octoshark.with_connection(current_user.shard, &block) + CONN_MANAGER.with_connection(current_user.shard, &block) end ``` -Similarly, in all other application entry-points that start with the default ActiveRecord connection (background jobs for an example), we need to switch the shard connection and then proceed. +Similar approach applies to other application entry-points like background jobs. -## Master-Slave Example +## Master-Slave example -When we want to do something in the slave database with all ActiveRecord models, then we need to add Octoshark's current or default connection to all models, either by overriding `ActiveRecord:Base.connection` or using a module that we include in all models. +All models are in master and slave databases. For master models use the default ActiveRecord connection and for slave models define and use Octoshark connections. ```ruby class ActiveRecord::Base def self.connection - # Some rake tasks like `rake db:create` does not load initializers, - # and because we're overriding ActiveRecord::Base.connection, - # we need to make sure Octoshark is configured before using it. - Octoshark.configure(configs) unless Octoshark.configured? - # Return the current connection (from with_connection block) or default one - Octoshark.current_or_default_connection + CONN_MANAGER.current_or_default_connection end end ``` -Here we use `Octoshark.current_or_default_connection` method which returns the current connection while in `with_connection` block and fallback to the default connection when outside. +`CONN_MANAGER.current_or_default_connection` method returns the current connection while in `with_connection` block or the default ActiveRecord connection when outside. -## Octoshark.reload! +## Multi-tenant example -Whenever ActiveRecord::Base establishes a new database connection, `Octoshark.reload!` is called. This is necessary for Octoshark to disconnect old connection pools and set new ones, otherwise `ActiveRecord::ConnectionNotEstablished` will be raised. +Some models are in the core DB, and others in user's own dedicated database. For core models use the default ActiveRecord connection and for tenant models can use Octoshark's mechanism to create new temporary connection. -Few examples where database connections are re-established: +Switch the connection in a controller with an around filter: +```ruby +# in initializer +CONN_MANAGER = Octoshark::ConnectionManager.new + +# in controller +around_filter :select_shard + +def select_shard(&block) + CONN_MANAGER.with_new_connection(name, config, reusable: false, &block) +end +``` + +`CONN_MANAGER.with_new_connection` method creates a temporary connection that will automatically disconnect. If you want to reuse it in subsequent connection switches, set `reusable: true` and it will be added to the connection manager and reused with the next calls. Depends on the use-case and what's preferable. In test environment usually you would want to set it to `reusable` so that database cleaner can clean data with transaction strategy. + + +## Octoshark.reset_connection_managers! + +Whenever ActiveRecord::Base calls `establish_connection` (usually by an ancestor process that must have subsequently forked), `Octoshark.reset_connection_managers!` is automatically called to re-establish the Octoshark connections. It prevents `ActiveRecord::ConnectionNotEstablished` in the scenarios like: + * Unicorn before/after fork * Spring prefork/serve * Some rake tasks like `rake db:test:prepare` ## Database Cleaner -Here's an example how to clean default and shard databases using both default connection and Octoshark connections: +RSpec example on how to clean default and Octoshark data with Database Cleaner: ```ruby config.before(:suite) do setup_database_cleaner DatabaseCleaner.clean_with(:truncation) @@ -144,18 +169,23 @@ setup_database_cleaner DatabaseCleaner.clean_with(:transaction) end def setup_database_cleaner - Octoshark.connection_pools.each_pair do |connection_name, connection_pool| - DatabaseCleaner[:active_record, {connection: connection_pool}] + DatabaseCleaner[:active_record, {connection: ActiveRecord::Base.connection_pool}] + Octoshark.connection_managers.each do |manager| + manager.connection_pools.each_pair do |connection_name, connection_pool| + DatabaseCleaner[:active_record, {connection: connection_pool}] + end end end ``` + ## Logo Thanks to [@saschamt](https://github.com/saschamt) for Octoshark logo design. :) + ## Contributing 1. Fork it ( http://github.com/dalibor/octoshark/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`)