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.
Add this line to your application's Gemfile:
gem 'octoshark'
And then execute:
$ bundle
Or install it yourself as:
$ gem install octoshark
Specify the connections for Octoshark to manage. This is usually done in an app initializer.
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.
class Post < ActiveRecord::Base
def self.connection
Octoshark.current_connection
end
endTo use a specific database connection, do:
Octoshark.with_connection(:db1) do
# work with db1
Post.first
endOctoshark connection is changed for the duration of the block and then reversed back to the previous connecti 8000 on.
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:
Octoshark.with_connection(:db1) do
# work with db1
Octoshark.with_connection(:db2) do
# work with db2
end
# work with db1
endFor 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:
# before_filter :find_user
around_filter :select_shard
def select_shard(&block)
Octoshark.with_connection(current_user.shard, &block)
endSimilarly, 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.
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.
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
endHere 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.
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.
Few examples where database connections are re-established:
- Unicorn before/after fork
- Spring prefork/serve
- Some rake tasks like
rake db:test:prepare
Here's an example how to clean default and shard databases using both default connection and Octoshark connections:
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)
endThanks to @saschamt for Octoshark logo design. :)
- Fork it ( http://github.com/dalibor/octoshark/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 new Pull Request

