Permalink
Browse files

More specs, connection test can now be disabled, connections can be e…

…ager loaded, master and slave can now use different database adapters, docs update
  • Loading branch information...
1 parent e986083 commit 249d9a66be6efb6844a919d39fa46ad73716b229 @mauricio committed Aug 23, 2009
Showing with 167 additions and 50 deletions.
  1. +1 −0 .gitignore
  2. +6 −1 README
  3. +1 −1 lib/master_slave_adapter/active_record_extensions.rb
  4. +25 −4 lib/master_slave_adapter/adapter.rb
  5. +134 −44 specs/specs.rb
View
@@ -0,0 +1 @@
+nbproject
View
7 README
@@ -31,7 +31,12 @@ development:
adapter: master_slave # the adapter must be set to "master_slave"
host: 10.21.34.80
master_slave_adapter: mysql # here's where you'll place the real database adapter name
+ disable_connection_test: true # this will disable the connection test before use,
+ # can possibly improve the performance but you could also hit stale connections, default is false
+ eager_load_connections: true # connections are lazy loaded by default, you can load gem eagerly setting this to true
master: # and here's where you'll add the master database configuration
database: talkies_development # you shouldn't specify an "adapter" here, the
username: root # value at "master_slave_adapter" is going to be used
- host: 10.21.34.82
+ host: 10.21.34.82
+ adapter: postgresql # you can use another adapter for the master connection if needed
+ # if you don't set it the "master_slave_adapter" property will be used
@@ -27,7 +27,7 @@ def with_master
def master_slave_connection( config )
config = config.symbolize_keys
- raise "You must provide a configuration for the master database" if config[:master].blank?
+ raise "You must provide a configuration for the master database - #{config.inspect}" if config[:master].blank?
raise "You must provide a 'master_slave_adapter' value at your database config file" if config[:master_slave_adapter].blank?
unless self.respond_to?( "#{config[:master_slave_adapter]}_connection" )
@@ -12,7 +12,9 @@ class MasterSlaveAdapter
checkout :test_connections
attr_accessor :connections
- attr_accessor :database_config
+ attr_accessor :master_config
+ attr_accessor :slave_config
+ attr_accessor :disable_connection_test
delegate :select_all, :select_one, :select_rows, :select_value, :select_values, :to => :slave_connection
@@ -21,8 +23,16 @@ def initialize( config )
if config[:master].blank?
raise "There is no :master config in the database configuration provided -> #{config.inspect} "
end
- self.database_config = config
+ self.slave_config = config.symbolize_keys
+ self.master_config = self.slave_config.delete(:master).symbolize_keys
+ self.slave_config[:adapter] = self.slave_config.delete(:master_slave_adapter)
+ self.master_config[ :adapter ] ||= self.slave_config[:adapter]
+ self.disable_connection_test = self.slave_config.delete( :disable_connection_test ) == 'true'
self.connections = []
+ if self.slave_config.delete( :eager_load_connections ) == 'true'
+ connect_to_master
+ connect_to_slave
+ end
end
def slave_connection
@@ -31,7 +41,7 @@ def slave_connection
elsif @master_connection && @master_connection.open_transactions > 0
master_connection
else
- @slave_connection ||= ActiveRecord::Base.send( "#{self.database_config[:master_slave_adapter]}_connection", self.database_config.symbolize_keys )
+ connect_to_slave
end
end
@@ -54,14 +64,15 @@ def method_missing( name, *args, &block )
end
def master_connection
- @master_connection ||= ActiveRecord::Base.send( "#{self.database_config[:master_slave_adapter]}_connection", self.database_config[:master].symbolize_keys )
+ connect_to_master
end
def connections
[ @master_connection, @slave_connection ].compact
end
def test_connections
+ return if self.disable_connection_test
self.connections.each do |c|
begin
c.select_value( 'SELECT 1', 'test select' )
@@ -96,6 +107,16 @@ def disable_master
end
+ private
+
+ def connect_to_master
+ @master_connection ||= ActiveRecord::Base.send( "#{self.master_config[:adapter]}_connection", self.master_config )
+ end
+
+ def connect_to_slave
+ @slave_connection ||= ActiveRecord::Base.send( "#{self.slave_config[:adapter]}_connection", self.slave_config)
+ end
+
end
end
View
@@ -9,32 +9,30 @@
ActiveRecord::Base.instance_eval do
def test_connection( config )
- config = config.symbolize_keys
-
- config[:master_slave_adapter] ? _slave : _master
+ config[:database] == 'slave' ? _slave : _master
end
def _master=( new_master )
- @master = new_master
+ @_master = new_master
end
def _master
- @master
+ @_master
end
def _slave=( new_slave )
- @slave = new_slave
+ @_slave = new_slave
end
def _slave
- @slave
+ @_slave
end
end
describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
- before(:all) do
+ before do
@mocked_methods = { :verify! => true, :reconnect! => true, :run_callbacks => true, :disconnect! => true }
@@ -44,65 +42,157 @@ def _slave
@master_connection = ActiveRecord::Base._master
@slave_connection = ActiveRecord::Base._slave
- @database_setup = {
- :adapter => 'master_slave',
- :username => 'root',
- :database => 'master_slave_test',
- :master_slave_adapter => 'test',
- :master => { :username => 'root', :database => 'master_slave_test' }
- }
-
- ActiveRecord::Base.establish_connection( @database_setup )
+ end
+ after do
+ ActiveRecord::Base.connection_handler.clear_all_connections!
end
- ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::SELECT_METHODS.each do |method|
+ describe 'with common configuration' do
+
+
+ before do
+
+ @database_setup = {
+ :adapter => 'master_slave',
+ :username => 'root',
+ :database => 'slave',
+ :master_slave_adapter => 'test',
+ :master => { :username => 'root', :database => 'master' }
+ }
+
+ ActiveRecord::Base.establish_connection( @database_setup )
+
+ [ @master_connection, @slave_connection ].each do |c|
+ c.stub!( :select_value ).with( "SELECT 1", "test select" ).and_return( true )
+ end
- it "Should send the method '#{method}' to the slave connection" do
- @master_connection.stub!( :open_transactions ).and_return( 0 )
- @slave_connection.should_receive( method ).and_return( true )
- ActiveRecord::Base.connection.send( method )
end
- it "Should send the method '#{method}' to the master connection if with_master was specified" do
- @master_connection.should_receive( method ).and_return( true )
- ActiveRecord::Base.with_master do
+ ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::SELECT_METHODS.each do |method|
+
+ it "Should send the method '#{method}' to the slave connection" do
+ @master_connection.stub!( :open_transactions ).and_return( 0 )
+ @slave_connection.should_receive( method ).with('testing').and_return( true )
+ ActiveRecord::Base.connection.send( method, 'testing' )
+ end
+
+ it "Should send the method '#{method}' to the master connection if with_master was specified" do
+ @master_connection.should_receive( method ).with('testing').and_return( true )
+ ActiveRecord::Base.with_master do
+ ActiveRecord::Base.connection.send( method, 'testing' )
+ end
+ end
+
+ it "Should send the method '#{method}' to the master connection if there are open transactions" do
+ @master_connection.stub!( :open_transactions ).and_return( 1 )
+ @master_connection.should_receive( method ).with('testing').and_return( true )
+ ActiveRecord::Base.with_master do
+ ActiveRecord::Base.connection.send( method, 'testing' )
+ end
+ end
+
+ end
+
+ ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods.map(&:to_sym).each do |method|
+
+ it "Should send the method '#{method}' from ActiveRecord::ConnectionAdapters::SchemaStatements to the master" do
+ @master_connection.should_receive( method ).and_return( true )
ActiveRecord::Base.connection.send( method )
end
+
end
- end
+ (ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods.map(&:to_sym) - ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::SELECT_METHODS).each do |method|
- ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods.map(&:to_sym).each do |method|
+ it "Should send the method '#{method}' from ActiveRecord::ConnectionAdapters::DatabaseStatements to the master" do
+ @master_connection.should_receive( method ).and_return( true )
+ ActiveRecord::Base.connection.send( method )
+ end
- it "Should send the method '#{method}' from ActiveRecord::ConnectionAdapters::SchemaStatements to the master" do
- @master_connection.should_receive( method ).and_return( true )
- ActiveRecord::Base.connection.send( method )
end
- end
+ it 'Should be a master slave connection' do
+ ActiveRecord::Base.connection.class.should == ActiveRecord::ConnectionAdapters::MasterSlaveAdapter
+ end
- (ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods.map(&:to_sym) - ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::SELECT_METHODS).each do |method|
+ it 'Should have a master connection' do
+ ActiveRecord::Base.connection.master_connection.should == @master_connection
+ end
- it "Should send the method '#{method}' from ActiveRecord::ConnectionAdapters::DatabaseStatements to the master" do
- @master_connection.should_receive( method ).and_return( true )
- ActiveRecord::Base.connection.send( method )
+ it 'Should have a slave connection' do
+ @master_connection.stub!( :open_transactions ).and_return( 0 )
+ ActiveRecord::Base.connection.slave_connection.should == @slave_connection
end
end
- it 'Should be a master slave connection' do
- ActiveRecord::Base.connection.class.should == ActiveRecord::ConnectionAdapters::MasterSlaveAdapter
- end
+ describe 'with connection testing disabled' do
- it 'Should have a master connection' do
- ActiveRecord::Base.connection.master_connection.should == @master_connection
- end
+ before do
+ @database_setup = {
+ :adapter => 'master_slave',
+ :username => 'root',
+ :database => 'slave',
+ :disable_connection_test => 'true',
+ :master_slave_adapter => 'test',
+ :master => { :username => 'root', :database => 'master' }
+ }
+
+ ActiveRecord::Base.establish_connection( @database_setup )
+
+ end
+
+ ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods.map(&:to_sym).each do |method|
+
+ it "Should not perform the testing select on the master if #{method} is called" do
+ @master_connection.should_not_receive( :select_value ).with( "SELECT 1", "test select" )
+ @master_connection.should_receive( method ).with('testing').and_return(true)
+ ActiveRecord::Base.connection.send(method, 'testing')
+ end
+
+ end
+
+ ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::SELECT_METHODS.each do |method|
+
+ it "Should not perform the testing select on the slave if #{method} is called" do
+ @slave_connection.should_not_receive( :select_value ).with( "SELECT 1", "test select" )
+ @slave_connection.should_receive( method ).with('testing').and_return(true)
+ ActiveRecord::Base.connection.send(method, 'testing')
+ end
+
+ end
- it 'Should have a slave connection' do
- @master_connection.stub!( :open_transactions ).and_return( 0 )
- ActiveRecord::Base.connection.slave_connection.should == @slave_connection
end
+ describe 'with connection eager loading enabled' do
+
+ before do
+ @database_setup = {
+ :adapter => 'master_slave',
+ :username => 'root',
+ :database => 'slave',
+ :eager_load_connections => 'true',
+ :master_slave_adapter => 'test',
+ :master => { :username => 'root', :database => 'master' }
+ }
+
+ ActiveRecord::Base.establish_connection( @database_setup )
+ [ @master_connection, @slave_connection ].each do |c|
+ c.should_receive( :select_value ).with( "SELECT 1", "test select" ).and_return( true )
+ end
+
+ end
+
+ it 'should load the master connection before any method call' do
+ ActiveRecord::Base.connection.instance_variable_get(:@master_connection).should == @master_connection
+ end
+
+ it 'should load the slave connection before any method call' do
+ ActiveRecord::Base.connection.instance_variable_get(:@slave_connection).should == @slave_connection
+ end
+
+ end
+
end

0 comments on commit 249d9a6

Please sign in to comment.