![Travis status](https://travis-ci.org/dalibor/octoshark.png) # Octoshark 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. ## Installation Add this line to your application's Gemfile: ``` gem 'octoshark' ``` And then execute: ``` $ bundle ``` Or install it yourself as: ``` $ gem install octoshark ``` ## Usage Specify the connections for Octoshark to manage. This is usually done in an app initializer. ```ruby Octoshark.configure({ 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. ```ruby class Post < ActiveRecord::Base def self.connection Octoshark.current_connection end end ``` To use a specific database connection, do: ```ruby Octoshark.with_connection(:db1) do # work with db1 Post.first end ``` Octoshark connection is changed for the duration of the block and then reversed back to the previous connection. `Octoshark.current_connection` returns the active connection while in `with_connection` block, and outside it raises `Octoshark::NoCurrentConnectionError` error. Multiple connection switch blocks can be nested: ```ruby Octoshark.with_connection(:db1) do # work with db1 Octoshark.with_connection(:db2) do # work with db2 end # work with db1 end ``` ## 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. We specify the connection for the sharded models based on the shard key (User) 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) 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. ## 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. ```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 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. ## Octoshark.reload! While Octoshark tries hard to avoid any **monkey-patching** of `ActiveRecord`, there is a single patch it applies so that end-user does not have to do it manually. Basically, whenever ActiveRecord::Base establishes a new database connection, `Octoshark.reload!` needs to be called. This is necessary for Octoshark to disconnect old connection pools and set new ones, otherwise `ActiveRecord::ConnectionNotEstablished` will be raised. Few examples where database connections are re-established: * 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: ```ruby config.before(:suite) do clean_database_with(:truncation) end config.before(:each) do DatabaseCleaner.start end config.after(:each) do clean_database_with(:transaction) end def clean_database_with # default ActiveRecord::Base connection pool DatabaseCleaner[:active_record, {connection: ActiveRecord::Base.connection_pool}] # Octoshark connection pool for the connection Octoshark.connection_pools.each_pair do |name, connection_pool| DatabaseCleaner[:active_record, {connection: connection_pool}] end DatabaseCleaner.clean_with(strategy) end ``` ## Contributing 1. Fork it ( http://github.com/dalibor/octoshark/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request