seamless_database_pool 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +2 -0
- data/Rakefile +3 -0
- data/lib/active_record/connection_adapters/seamless_database_pool_adapter.rb +64 -45
- data/lib/seamless_database_pool.rb +20 -5
- data/spec/connection_adapters_spec.rb +26 -25
- data/spec/test_adapter/active_record/connection_adapters/read_only_adapter.rb +18 -5
- data/spec/test_model.rb +3 -3
- metadata +45 -28
    
        data/README.rdoc
    CHANGED
    
    | @@ -66,6 +66,8 @@ In this configuration, the master connection will be a mysql connection to maste | |
| 66 66 |  | 
| 67 67 | 
             
            The read pool will use three mysql connections to master-db, read-db-1, and read-db-2. The master connection will use a different port, username, password for the connection. The read connections will use the same values. Further, the connection read-db-1 will get half the traffic as the other two connections, so presumably it's on a more powerful box.
         | 
| 68 68 |  | 
| 69 | 
            +
            You must use compatible database adapters for both the master and the read connections. For example, you cannot use an Oracle server as your master and PostgreSQL servers as you read slaves.
         | 
| 70 | 
            +
             | 
| 69 71 | 
             
            = Using the read pool
         | 
| 70 72 |  | 
| 71 73 | 
             
            By default, the master connection will be used for everything. This is not terribly useful, so you should really specify a method of using the read pool for the actions that need it. Read connections will only be used for select statements against the database.
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -75,6 +75,9 @@ begin | |
| 75 75 | 
             
                gem.add_dependency('activerecord', '>= 2.2.2')
         | 
| 76 76 | 
             
                gem.add_development_dependency('rspec', '>= 2.0')
         | 
| 77 77 | 
             
                gem.add_development_dependency('jeweler')
         | 
| 78 | 
            +
                gem.add_development_dependency('sqlite3')
         | 
| 79 | 
            +
                gem.add_development_dependency('mysql')
         | 
| 80 | 
            +
                gem.add_development_dependency('pg')
         | 
| 78 81 | 
             
              end
         | 
| 79 82 |  | 
| 80 83 | 
             
              Jeweler::GemcutterTasks.new
         | 
| @@ -16,7 +16,7 @@ module ActiveRecord | |
| 16 16 | 
             
                    master_connection.class.send(:include, SeamlessDatabasePool::ConnectTimeout) unless master_connection.class.include?(SeamlessDatabasePool::ConnectTimeout)
         | 
| 17 17 | 
             
                    master_connection.connect_timeout = master_config[:connect_timeout]
         | 
| 18 18 | 
             
                    pool_weights[master_connection] = master_config[:pool_weight].to_i if master_config[:pool_weight].to_i > 0
         | 
| 19 | 
            -
             | 
| 19 | 
            +
                    
         | 
| 20 20 | 
             
                    read_connections = []
         | 
| 21 21 | 
             
                    config[:read_pool].each do |read_config|
         | 
| 22 22 | 
             
                      read_config = default_config.merge(read_config).with_indifferent_access
         | 
| @@ -30,6 +30,7 @@ module ActiveRecord | |
| 30 30 | 
             
                          read_connections << conn
         | 
| 31 31 | 
             
                          pool_weights[conn] = read_config[:pool_weight]
         | 
| 32 32 | 
             
                        rescue Exception => e
         | 
| 33 | 
            +
                          raise e # TODO remove
         | 
| 33 34 | 
             
                          if logger
         | 
| 34 35 | 
             
                            logger.error("Error connecting to read connection #{read_config.inspect}")
         | 
| 35 36 | 
             
                            logger.error(e)
         | 
| @@ -48,7 +49,7 @@ module ActiveRecord | |
| 48 49 | 
             
                    return klass.new(nil, logger, master_connection, read_connections, pool_weights)
         | 
| 49 50 | 
             
                  end
         | 
| 50 51 |  | 
| 51 | 
            -
                  def establish_adapter | 
| 52 | 
            +
                  def establish_adapter(adapter)
         | 
| 52 53 | 
             
                    raise AdapterNotSpecified.new("database configuration does not specify adapter") unless adapter
         | 
| 53 54 | 
             
                    raise AdapterNotFound.new("database pool must specify adapters") if adapter == 'seamless_database_pool'
         | 
| 54 55 |  | 
| @@ -77,7 +78,7 @@ module ActiveRecord | |
| 77 78 | 
             
                  end
         | 
| 78 79 |  | 
| 79 80 | 
             
                  # Force reload to use the master connection since it's probably being called for a reason.
         | 
| 80 | 
            -
                  def reload_with_seamless_database_pool | 
| 81 | 
            +
                  def reload_with_seamless_database_pool(*args)
         | 
| 81 82 | 
             
                    SeamlessDatabasePool.use_master_connection do
         | 
| 82 83 | 
             
                      reload_without_seamless_database_pool(*args)
         | 
| 83 84 | 
             
                    end
         | 
| @@ -92,50 +93,60 @@ module ActiveRecord | |
| 92 93 |  | 
| 93 94 | 
             
                  attr_reader :read_connections, :master_connection
         | 
| 94 95 |  | 
| 95 | 
            -
                   | 
| 96 | 
            -
             | 
| 97 | 
            -
                     | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
                       | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
                       | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 96 | 
            +
                  class << self
         | 
| 97 | 
            +
                    # Create an anonymous class that extends this one and proxies methods to the pool connections.
         | 
| 98 | 
            +
                    def adapter_class(master_connection)
         | 
| 99 | 
            +
                      # Define methods to proxy to the appropriate pool
         | 
| 100 | 
            +
                      read_only_methods = [:select_one, :select_all, :select_value, :select_values, :select, :select_rows, :execute, :tables, :columns]
         | 
| 101 | 
            +
                      master_methods = []
         | 
| 102 | 
            +
                      master_connection_classes = [AbstractAdapter, Quoting, DatabaseStatements, SchemaStatements]
         | 
| 103 | 
            +
                      master_connection_classes << DatabaseLimits if const_defined?(:DatabaseLimits)
         | 
| 104 | 
            +
                      master_connection_class = master_connection.class
         | 
| 105 | 
            +
                      while ![Object, AbstractAdapter].include?(master_connection_class) do
         | 
| 106 | 
            +
                        master_connection_classes << master_connection_class
         | 
| 107 | 
            +
                        master_connection_class = master_connection_class.superclass
         | 
| 108 | 
            +
                      end
         | 
| 109 | 
            +
                      master_connection_classes.each do |connection_class|
         | 
| 110 | 
            +
                        master_methods.concat(connection_class.public_instance_methods(false))
         | 
| 111 | 
            +
                        master_methods.concat(connection_class.protected_instance_methods(false))
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                      master_methods.uniq!
         | 
| 114 | 
            +
                      master_methods -= public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false)
         | 
| 115 | 
            +
                      master_methods = master_methods.collect{|m| m.to_sym}
         | 
| 116 | 
            +
                      master_methods -= read_only_methods
         | 
| 116 117 |  | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 118 | 
            +
                      klass = Class.new(self)
         | 
| 119 | 
            +
                      master_methods.each do |method_name|
         | 
| 120 | 
            +
                        klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
         | 
| 121 | 
            +
                          def #{method_name}(*args, &block)
         | 
| 122 | 
            +
                            use_master_connection do
         | 
| 123 | 
            +
                              return proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block)
         | 
| 124 | 
            +
                            end
         | 
| 123 125 | 
             
                          end
         | 
| 124 | 
            -
                         | 
| 125 | 
            -
                       | 
| 126 | 
            -
                    end
         | 
| 126 | 
            +
                        EOS
         | 
| 127 | 
            +
                      end
         | 
| 127 128 |  | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 129 | 
            +
                      read_only_methods.each do |method_name|
         | 
| 130 | 
            +
                        klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
         | 
| 131 | 
            +
                          def #{method_name}(*args, &block)
         | 
| 132 | 
            +
                            connection = @use_master ? master_connection : current_read_connection
         | 
| 133 | 
            +
                            proxy_connection_method(connection, :#{method_name}, :read, *args, &block)
         | 
| 134 | 
            +
                          end
         | 
| 135 | 
            +
                        EOS
         | 
| 136 | 
            +
                      end
         | 
| 137 | 
            +
                      klass.send :protected, :select
         | 
| 137 138 |  | 
| 138 | 
            -
             | 
| 139 | 
            +
                      return klass
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
                  
         | 
| 142 | 
            +
                    # Set the arel visitor on the connections.
         | 
| 143 | 
            +
                    def visitor_for(pool)
         | 
| 144 | 
            +
                      # This is ugly, but then again, so is the code in ActiveRecord for setting the arel
         | 
| 145 | 
            +
                      # visitor. There is a note in the code indicating the method signatures should be updated.
         | 
| 146 | 
            +
                      config = pool.spec.config.with_indifferent_access
         | 
| 147 | 
            +
                      adapter = config[:master][:adapter] || config[:pool_adapter]
         | 
| 148 | 
            +
                      SeamlessDatabasePool.adapter_class_for(adapter).visitor_for(pool)
         | 
| 149 | 
            +
                    end
         | 
| 139 150 | 
             
                  end
         | 
| 140 151 |  | 
| 141 152 | 
             
                  def initialize(connection, logger, master_connection, read_connections, pool_weights)
         | 
| @@ -161,7 +172,7 @@ module ActiveRecord | |
| 161 172 | 
             
                  end
         | 
| 162 173 |  | 
| 163 174 | 
             
                  # Get the pool weight of a connection
         | 
| 164 | 
            -
                  def pool_weight | 
| 175 | 
            +
                  def pool_weight(connection)
         | 
| 165 176 | 
             
                    return @weighted_read_connections.select{|conn| conn == connection}.size
         | 
| 166 177 | 
             
                  end
         | 
| 167 178 |  | 
| @@ -169,6 +180,14 @@ module ActiveRecord | |
| 169 180 | 
             
                    false
         | 
| 170 181 | 
             
                  end
         | 
| 171 182 |  | 
| 183 | 
            +
                  def visitor=(visitor)
         | 
| 184 | 
            +
                    all_connections.each{|conn| conn.visitor = visitor}
         | 
| 185 | 
            +
                  end
         | 
| 186 | 
            +
                  
         | 
| 187 | 
            +
                  def visitor
         | 
| 188 | 
            +
                    master_connection.visitor
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
                  
         | 
| 172 191 | 
             
                  def active?
         | 
| 173 192 | 
             
                    active = true
         | 
| 174 193 | 
             
                    do_to_connections {|conn| active &= conn.active?}
         | 
| @@ -237,7 +256,7 @@ module ActiveRecord | |
| 237 256 | 
             
                    attr_reader :connections, :failed_connection
         | 
| 238 257 | 
             
                    attr_writer :expires
         | 
| 239 258 |  | 
| 240 | 
            -
                    def initialize | 
| 259 | 
            +
                    def initialize(connections, failed_connection = nil, expires = nil)
         | 
| 241 260 | 
             
                      @connections = connections
         | 
| 242 261 | 
             
                      @failed_connection = failed_connection
         | 
| 243 262 | 
             
                      @expires = expires
         | 
| @@ -1,7 +1,7 @@ | |
| 1 | 
            -
            require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connect_timeout')
         | 
| 2 | 
            -
            require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connection_statistics')
         | 
| 3 | 
            -
            require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'controller_filter')
         | 
| 4 | 
            -
            require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'seamless_database_pool_adapter')
         | 
| 1 | 
            +
            require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connect_timeout.rb')
         | 
| 2 | 
            +
            require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connection_statistics.rb')
         | 
| 3 | 
            +
            require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'controller_filter.rb')
         | 
| 4 | 
            +
            require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'seamless_database_pool_adapter.rb')
         | 
| 5 5 | 
             
            $LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__FILE__))
         | 
| 6 6 |  | 
| 7 7 | 
             
            # This module allows setting the read pool connection type. Generally you will use one of
         | 
| @@ -15,7 +15,12 @@ $LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__F | |
| 15 15 | 
             
            # read connection type. If none is ever called, the read connection type will be :master.
         | 
| 16 16 |  | 
| 17 17 | 
             
            module SeamlessDatabasePool
         | 
| 18 | 
            -
             | 
| 18 | 
            +
                
         | 
| 19 | 
            +
              # Adapter name to class name map. This exists because there isn't an obvious way to translate things like
         | 
| 20 | 
            +
              # sqlite3 to SQLite3. The adapters that ship with ActiveRecord are defined here. If you use
         | 
| 21 | 
            +
              # an adapter that doesn't translate directly to camel case, then add the mapping here in an initializer.
         | 
| 22 | 
            +
              ADAPTER_TO_CLASS_NAME_MAP = {"sqlite" => "SQLite", "sqlite3" => "SQLite3", "postgresql" => "PostgreSQL"}
         | 
| 23 | 
            +
              
         | 
| 19 24 | 
             
              READ_CONNECTION_METHODS = [:master, :persistent, :random]
         | 
| 20 25 |  | 
| 21 26 | 
             
              class << self
         | 
| @@ -111,6 +116,16 @@ module SeamlessDatabasePool | |
| 111 116 | 
             
                def clear_read_only_connection
         | 
| 112 117 | 
             
                  Thread.current[:read_only_connection] = nil
         | 
| 113 118 | 
             
                end
         | 
| 119 | 
            +
                
         | 
| 120 | 
            +
                # Get the connection adapter class for an adapter name. The class will be loaded from
         | 
| 121 | 
            +
                # ActiveRecord::ConnectionAdapters::NameAdapter where Name is the camelized version of the name.
         | 
| 122 | 
            +
                # If the adapter class does not fit this pattern (i.e. sqlite3 => SQLite3Adapter), then add
         | 
| 123 | 
            +
                # the mapping to the +ADAPTER_TO_CLASS_NAME_MAP+ Hash.
         | 
| 124 | 
            +
                def adapter_class_for(name)
         | 
| 125 | 
            +
                  name = name.to_s
         | 
| 126 | 
            +
                  class_name = ADAPTER_TO_CLASS_NAME_MAP[name] || name.camelize
         | 
| 127 | 
            +
                  "ActiveRecord::ConnectionAdapters::#{class_name}Adapter".constantize
         | 
| 128 | 
            +
                end
         | 
| 114 129 | 
             
              end
         | 
| 115 130 |  | 
| 116 131 | 
             
            end
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'active_record/connection_adapters/read_only_adapter'
         | 
| 2 3 |  | 
| 3 4 | 
             
            describe "Test connection adapters" do
         | 
| 4 5 | 
             
              if SeamlessDatabasePool::TestModel.database_configs.empty?
         | 
| @@ -41,32 +42,32 @@ describe "Test connection adapters" do | |
| 41 42 | 
             
                    it "should quote table names properly" do
         | 
| 42 43 | 
             
                      connection.quote_table_name("foo").should == master_connection.quote_table_name("foo")
         | 
| 43 44 | 
             
                    end
         | 
| 44 | 
            -
             | 
| 45 | 
            +
                          
         | 
| 45 46 | 
             
                    it "should quote column names properly" do
         | 
| 46 47 | 
             
                      connection.quote_column_name("foo").should == master_connection.quote_column_name("foo")
         | 
| 47 48 | 
             
                    end
         | 
| 48 | 
            -
             | 
| 49 | 
            +
                          
         | 
| 49 50 | 
             
                    it "should quote string properly" do
         | 
| 50 51 | 
             
                      connection.quote_string("foo").should == master_connection.quote_string("foo")
         | 
| 51 52 | 
             
                    end
         | 
| 52 | 
            -
             | 
| 53 | 
            +
                          
         | 
| 53 54 | 
             
                    it "should quote booleans properly" do
         | 
| 54 55 | 
             
                      connection.quoted_true.should == master_connection.quoted_true
         | 
| 55 56 | 
             
                      connection.quoted_false.should == master_connection.quoted_false
         | 
| 56 57 | 
             
                    end
         | 
| 57 | 
            -
             | 
| 58 | 
            +
                          
         | 
| 58 59 | 
             
                    it "should quote dates properly" do
         | 
| 59 60 | 
             
                      date = Date.today
         | 
| 60 61 | 
             
                      time = Time.now
         | 
| 61 62 | 
             
                      connection.quoted_date(date).should == master_connection.quoted_date(date)
         | 
| 62 63 | 
             
                      connection.quoted_date(time).should == master_connection.quoted_date(time)
         | 
| 63 64 | 
             
                    end
         | 
| 64 | 
            -
             | 
| 65 | 
            +
                      
         | 
| 65 66 | 
             
                    it "should query for records" do
         | 
| 66 67 | 
             
                      record = model.find_by_name("test")
         | 
| 67 68 | 
             
                      record.name.should == "test"
         | 
| 68 69 | 
             
                    end
         | 
| 69 | 
            -
             | 
| 70 | 
            +
                      
         | 
| 70 71 | 
             
                    it "should work with query caching" do
         | 
| 71 72 | 
             
                      record_id =  model.first.id
         | 
| 72 73 | 
             
                      model.cache do
         | 
| @@ -76,62 +77,62 @@ describe "Test connection adapters" do | |
| 76 77 | 
             
                        model.find(record_id).name.should == "new value"
         | 
| 77 78 | 
             
                      end
         | 
| 78 79 | 
             
                    end
         | 
| 79 | 
            -
             | 
| 80 | 
            +
                      
         | 
| 80 81 | 
             
                    context "read connection" do
         | 
| 81 82 | 
             
                      let(:sample_sql){"SELECT #{connection.quote_column_name('name')} FROM #{connection.quote_table_name(model.table_name)}"}
         | 
| 82 | 
            -
             | 
| 83 | 
            +
                        
         | 
| 83 84 | 
             
                      it "should not include the master connection in the read pool for these tests" do
         | 
| 84 85 | 
             
                        connection.available_read_connections.should_not include(master_connection)
         | 
| 85 86 | 
             
                        connection.current_read_connection.should_not == master_connection
         | 
| 86 87 | 
             
                      end
         | 
| 87 | 
            -
             | 
| 88 | 
            +
                        
         | 
| 88 89 | 
             
                      it "should send select to the read connection" do
         | 
| 89 90 | 
             
                        results = connection.send(:select, sample_sql)
         | 
| 90 91 | 
             
                        results.should == [{"name" => "test"}]
         | 
| 91 92 | 
             
                        results.should == master_connection.send(:select, sample_sql)
         | 
| 92 93 | 
             
                        results.should be_read_only
         | 
| 93 94 | 
             
                      end
         | 
| 94 | 
            -
             | 
| 95 | 
            +
                      
         | 
| 95 96 | 
             
                      it "should send select_all to the read connection" do
         | 
| 96 97 | 
             
                        results = connection.select_all(sample_sql)
         | 
| 97 98 | 
             
                        results.should == [{"name" => "test"}]
         | 
| 98 99 | 
             
                        results.should == master_connection.select_all(sample_sql)
         | 
| 99 100 | 
             
                        results.should be_read_only
         | 
| 100 101 | 
             
                      end
         | 
| 101 | 
            -
             | 
| 102 | 
            +
                      
         | 
| 102 103 | 
             
                      it "should send select_one to the read connection" do
         | 
| 103 104 | 
             
                        results = connection.select_one(sample_sql)
         | 
| 104 105 | 
             
                        results.should == {"name" => "test"}
         | 
| 105 106 | 
             
                        results.should == master_connection.select_one(sample_sql)
         | 
| 106 107 | 
             
                        results.should be_read_only
         | 
| 107 108 | 
             
                      end
         | 
| 108 | 
            -
             | 
| 109 | 
            +
                      
         | 
| 109 110 | 
             
                      it "should send select_values to the read connection" do
         | 
| 110 111 | 
             
                        results = connection.select_values(sample_sql)
         | 
| 111 112 | 
             
                        results.should == ["test"]
         | 
| 112 113 | 
             
                        results.should == master_connection.select_values(sample_sql)
         | 
| 113 114 | 
             
                        results.should be_read_only
         | 
| 114 115 | 
             
                      end
         | 
| 115 | 
            -
             | 
| 116 | 
            +
                      
         | 
| 116 117 | 
             
                      it "should send select_value to the read connection" do
         | 
| 117 118 | 
             
                        results = connection.select_value(sample_sql)
         | 
| 118 119 | 
             
                        results.should == "test"
         | 
| 119 120 | 
             
                        results.should == master_connection.select_value(sample_sql)
         | 
| 120 121 | 
             
                        results.should be_read_only
         | 
| 121 122 | 
             
                      end
         | 
| 122 | 
            -
             | 
| 123 | 
            +
                      
         | 
| 123 124 | 
             
                      it "should send select_rows to the read connection" do
         | 
| 124 125 | 
             
                        results = connection.select_all(sample_sql)
         | 
| 125 126 | 
             
                        results.should == [{"name" => "test"}]
         | 
| 126 127 | 
             
                        results.should == master_connection.select_all(sample_sql)
         | 
| 127 128 | 
             
                        results.should be_read_only
         | 
| 128 129 | 
             
                      end
         | 
| 129 | 
            -
             | 
| 130 | 
            +
                      
         | 
| 130 131 | 
             
                      it "should send execute to the read connection" do
         | 
| 131 132 | 
             
                        results = connection.execute(sample_sql)
         | 
| 132 133 | 
             
                        results.should be_read_only
         | 
| 133 134 | 
             
                      end
         | 
| 134 | 
            -
             | 
| 135 | 
            +
                      
         | 
| 135 136 | 
             
                      it "should send columns to the read connection" do
         | 
| 136 137 | 
             
                        results = connection.columns(model.table_name)
         | 
| 137 138 | 
             
                        columns = results.collect{|c| c.name}.sort.should
         | 
| @@ -139,14 +140,14 @@ describe "Test connection adapters" do | |
| 139 140 | 
             
                        columns.should == master_connection.columns(model.table_name).collect{|c| c.name}.sort
         | 
| 140 141 | 
             
                        results.should be_read_only
         | 
| 141 142 | 
             
                      end
         | 
| 142 | 
            -
             | 
| 143 | 
            +
                      
         | 
| 143 144 | 
             
                      it "should send tables to the read connection" do
         | 
| 144 145 | 
             
                        results = connection.tables
         | 
| 145 146 | 
             
                        results.should == ["test_models"]
         | 
| 146 147 | 
             
                        results.should == master_connection.tables
         | 
| 147 148 | 
             
                        results.should be_read_only
         | 
| 148 149 | 
             
                      end
         | 
| 149 | 
            -
             | 
| 150 | 
            +
                    
         | 
| 150 151 | 
             
                      it "should reconnect dead connections in the read pool" do
         | 
| 151 152 | 
             
                        read_connection.disconnect!
         | 
| 152 153 | 
             
                        read_connection.should_not be_active
         | 
| @@ -155,12 +156,12 @@ describe "Test connection adapters" do | |
| 155 156 | 
             
                        read_connection.should be_active
         | 
| 156 157 | 
             
                      end
         | 
| 157 158 | 
             
                    end
         | 
| 158 | 
            -
             | 
| 159 | 
            +
                      
         | 
| 159 160 | 
             
                    context "master connection" do
         | 
| 160 161 | 
             
                      let(:insert_sql){ "INSERT INTO #{connection.quote_table_name(model.table_name)} (#{connection.quote_column_name('name')}) VALUES ('new')" }
         | 
| 161 162 | 
             
                      let(:update_sql){ "UPDATE #{connection.quote_table_name(model.table_name)} SET #{connection.quote_column_name('value')} = 2" }
         | 
| 162 163 | 
             
                      let(:delete_sql){ "DELETE FROM #{connection.quote_table_name(model.table_name)}" }
         | 
| 163 | 
            -
             | 
| 164 | 
            +
                        
         | 
| 164 165 | 
             
                      it "should blow up if a master connection method is sent to the read only connection" do
         | 
| 165 166 | 
             
                        lambda{read_connection.update(update_sql)}.should raise_error(NotImplementedError)
         | 
| 166 167 | 
             
                        lambda{read_connection.update(insert_sql)}.should raise_error(NotImplementedError)
         | 
| @@ -168,29 +169,29 @@ describe "Test connection adapters" do | |
| 168 169 | 
             
                        lambda{read_connection.transaction{}}.should raise_error(NotImplementedError)
         | 
| 169 170 | 
             
                        lambda{read_connection.create_table(:test)}.should raise_error(NotImplementedError)
         | 
| 170 171 | 
             
                      end
         | 
| 171 | 
            -
             | 
| 172 | 
            +
                        
         | 
| 172 173 | 
             
                      it "should send update to the master connection" do
         | 
| 173 174 | 
             
                        connection.update(update_sql)
         | 
| 174 175 | 
             
                        model.first.value.should == 2
         | 
| 175 176 | 
             
                      end
         | 
| 176 | 
            -
             | 
| 177 | 
            +
                      
         | 
| 177 178 | 
             
                      it "should send insert to the master connection" do
         | 
| 178 179 | 
             
                        connection.update(insert_sql)
         | 
| 179 180 | 
             
                        model.find_by_name("new").should_not == nil
         | 
| 180 181 | 
             
                      end
         | 
| 181 | 
            -
             | 
| 182 | 
            +
                      
         | 
| 182 183 | 
             
                      it "should send delete to the master connection" do
         | 
| 183 184 | 
             
                        connection.update(delete_sql)
         | 
| 184 185 | 
             
                        model.first.should == nil
         | 
| 185 186 | 
             
                      end
         | 
| 186 | 
            -
             | 
| 187 | 
            +
                      
         | 
| 187 188 | 
             
                      it "should send transaction to the master connection" do
         | 
| 188 189 | 
             
                        connection.transaction do
         | 
| 189 190 | 
             
                          connection.update(update_sql)
         | 
| 190 191 | 
             
                        end
         | 
| 191 192 | 
             
                        model.first.value.should == 2
         | 
| 192 193 | 
             
                      end
         | 
| 193 | 
            -
             | 
| 194 | 
            +
                      
         | 
| 194 195 | 
             
                      it "should send schema altering statements to the master connection" do
         | 
| 195 196 | 
             
                        SeamlessDatabasePool.use_master_connection do
         | 
| 196 197 | 
             
                          begin
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            module ActiveRecord
         | 
| 2 2 | 
             
              class Base
         | 
| 3 | 
            -
                def self.read_only_connection | 
| 3 | 
            +
                def self.read_only_connection(config)
         | 
| 4 4 | 
             
                  real_adapter = config.delete("real_adapter")
         | 
| 5 5 | 
             
                  connection = send("#{real_adapter}_connection", config.merge("adapter" => real_adapter))
         | 
| 6 6 | 
             
                  ConnectionAdapters::ReadOnlyAdapter.new(connection)
         | 
| @@ -10,7 +10,7 @@ module ActiveRecord | |
| 10 10 | 
             
              module ConnectionAdapters
         | 
| 11 11 | 
             
                class ReadOnlyAdapter < AbstractAdapter
         | 
| 12 12 | 
             
                  %w(select_one select_all select_value select_values select select_rows execute tables columns).each do |read_method|
         | 
| 13 | 
            -
                    class_eval <<-EOS
         | 
| 13 | 
            +
                    class_eval <<-EOS, __FILE__, __LINE__ + 1
         | 
| 14 14 | 
             
                      def #{read_method} (*args, &block)
         | 
| 15 15 | 
             
                        raise "Not Connected" unless @connected
         | 
| 16 16 | 
             
                        result = @connection.send(:#{read_method}, *args, &block)
         | 
| @@ -22,7 +22,7 @@ module ActiveRecord | |
| 22 22 | 
             
                    EOS
         | 
| 23 23 |  | 
| 24 24 | 
             
                    %w(update insert delete reload create_table drop_table add_index remove_index transaction).each do |write_method|
         | 
| 25 | 
            -
                      class_eval <<-EOS
         | 
| 25 | 
            +
                      class_eval <<-EOS, __FILE__, __LINE__ + 1
         | 
| 26 26 | 
             
                        def #{write_method} (*args, &block)
         | 
| 27 27 | 
             
                          raise NotImplementedError.new("Master method '#{write_method}' called on read only connection")
         | 
| 28 28 | 
             
                        end
         | 
| @@ -30,11 +30,24 @@ module ActiveRecord | |
| 30 30 | 
             
                    end
         | 
| 31 31 | 
             
                  end
         | 
| 32 32 |  | 
| 33 | 
            -
                  def initialize | 
| 33 | 
            +
                  def initialize(connection)
         | 
| 34 34 | 
             
                    @connection = connection
         | 
| 35 35 | 
             
                    @connected = true
         | 
| 36 | 
            +
                    super
         | 
| 36 37 | 
             
                  end
         | 
| 37 | 
            -
             | 
| 38 | 
            +
                  
         | 
| 39 | 
            +
                  def test_select
         | 
| 40 | 
            +
                    @connection.select_all('SELECT  "test_models".* FROM "test_models"  LIMIT 1')
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                  
         | 
| 43 | 
            +
                  def visitor
         | 
| 44 | 
            +
                    @connection.visitor
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                  
         | 
| 47 | 
            +
                  def visitor=(v)
         | 
| 48 | 
            +
                    @connection.visitor = v
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                  
         | 
| 38 51 | 
             
                  def reconnect!
         | 
| 39 52 | 
             
                    @connected = true
         | 
| 40 53 | 
             
                  end
         | 
    
        data/spec/test_model.rb
    CHANGED
    
    | @@ -5,16 +5,16 @@ module SeamlessDatabasePool | |
| 5 5 | 
             
                    adapters = ENV['TEST_ADAPTERS'].blank? ? [] : ENV['TEST_ADAPTERS'].split(/\s+/)
         | 
| 6 6 | 
             
                    configs = {}
         | 
| 7 7 | 
             
                    YAML.load_file(File.expand_path("../database.yml", __FILE__)).each do |adapter_name, adapter_config|
         | 
| 8 | 
            -
                      configs[adapter_name] = adapter_config if adapters.include?(adapter_name)
         | 
| 8 | 
            +
                      configs[adapter_name] = adapter_config if adapters.include?(adapter_name.downcase)
         | 
| 9 9 | 
             
                    end
         | 
| 10 10 | 
             
                    configs
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 |  | 
| 13 | 
            -
                  def use_database_connection | 
| 13 | 
            +
                  def use_database_connection(db_name)
         | 
| 14 14 | 
             
                    establish_connection(database_configs[db_name.to_s])
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 | 
            -
                  def db_model | 
| 17 | 
            +
                  def db_model(db_name)
         | 
| 18 18 | 
             
                    model_class_name = db_name.classify
         | 
| 19 19 | 
             
                    unless const_defined?(model_class_name)
         | 
| 20 20 | 
             
                      klass = Class.new(self)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,8 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: seamless_database_pool
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash: 5
         | 
| 5 4 | 
             
              prerelease: 
         | 
| 6 | 
            -
               | 
| 7 | 
            -
              - 1
         | 
| 8 | 
            -
              - 0
         | 
| 9 | 
            -
              - 9
         | 
| 10 | 
            -
              version: 1.0.9
         | 
| 5 | 
            +
              version: 1.0.10
         | 
| 11 6 | 
             
            platform: ruby
         | 
| 12 7 | 
             
            authors: 
         | 
| 13 8 | 
             
            - Brian Durand
         | 
| @@ -15,7 +10,7 @@ autorequire: | |
| 15 10 | 
             
            bindir: bin
         | 
| 16 11 | 
             
            cert_chain: []
         | 
| 17 12 |  | 
| 18 | 
            -
            date: 2011- | 
| 13 | 
            +
            date: 2011-10-06 00:00:00 -05:00
         | 
| 19 14 | 
             
            default_executable: 
         | 
| 20 15 | 
             
            dependencies: 
         | 
| 21 16 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -26,11 +21,6 @@ dependencies: | |
| 26 21 | 
             
                requirements: 
         | 
| 27 22 | 
             
                - - ">="
         | 
| 28 23 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 29 | 
            -
                    hash: 3
         | 
| 30 | 
            -
                    segments: 
         | 
| 31 | 
            -
                    - 2
         | 
| 32 | 
            -
                    - 2
         | 
| 33 | 
            -
                    - 2
         | 
| 34 24 | 
             
                    version: 2.2.2
         | 
| 35 25 | 
             
              type: :runtime
         | 
| 36 26 | 
             
              version_requirements: *id001
         | 
| @@ -42,10 +32,6 @@ dependencies: | |
| 42 32 | 
             
                requirements: 
         | 
| 43 33 | 
             
                - - ">="
         | 
| 44 34 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 45 | 
            -
                    hash: 3
         | 
| 46 | 
            -
                    segments: 
         | 
| 47 | 
            -
                    - 2
         | 
| 48 | 
            -
                    - 0
         | 
| 49 35 | 
             
                    version: "2.0"
         | 
| 50 36 | 
             
              type: :development
         | 
| 51 37 | 
             
              version_requirements: *id002
         | 
| @@ -57,12 +43,42 @@ dependencies: | |
| 57 43 | 
             
                requirements: 
         | 
| 58 44 | 
             
                - - ">="
         | 
| 59 45 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 60 | 
            -
                    hash: 3
         | 
| 61 | 
            -
                    segments: 
         | 
| 62 | 
            -
                    - 0
         | 
| 63 46 | 
             
                    version: "0"
         | 
| 64 47 | 
             
              type: :development
         | 
| 65 48 | 
             
              version_requirements: *id003
         | 
| 49 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 50 | 
            +
              name: sqlite3
         | 
| 51 | 
            +
              prerelease: false
         | 
| 52 | 
            +
              requirement: &id004 !ruby/object:Gem::Requirement 
         | 
| 53 | 
            +
                none: false
         | 
| 54 | 
            +
                requirements: 
         | 
| 55 | 
            +
                - - ">="
         | 
| 56 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 57 | 
            +
                    version: "0"
         | 
| 58 | 
            +
              type: :development
         | 
| 59 | 
            +
              version_requirements: *id004
         | 
| 60 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 61 | 
            +
              name: mysql
         | 
| 62 | 
            +
              prerelease: false
         | 
| 63 | 
            +
              requirement: &id005 !ruby/object:Gem::Requirement 
         | 
| 64 | 
            +
                none: false
         | 
| 65 | 
            +
                requirements: 
         | 
| 66 | 
            +
                - - ">="
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 68 | 
            +
                    version: "0"
         | 
| 69 | 
            +
              type: :development
         | 
| 70 | 
            +
              version_requirements: *id005
         | 
| 71 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 72 | 
            +
              name: pg
         | 
| 73 | 
            +
              prerelease: false
         | 
| 74 | 
            +
              requirement: &id006 !ruby/object:Gem::Requirement 
         | 
| 75 | 
            +
                none: false
         | 
| 76 | 
            +
                requirements: 
         | 
| 77 | 
            +
                - - ">="
         | 
| 78 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 79 | 
            +
                    version: "0"
         | 
| 80 | 
            +
              type: :development
         | 
| 81 | 
            +
              version_requirements: *id006
         | 
| 66 82 | 
             
            description: 
         | 
| 67 83 | 
             
            email: brian@embellishedvisions.com
         | 
| 68 84 | 
             
            executables: []
         | 
| @@ -105,25 +121,26 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 105 121 | 
             
              requirements: 
         | 
| 106 122 | 
             
              - - ">="
         | 
| 107 123 | 
             
                - !ruby/object:Gem::Version 
         | 
| 108 | 
            -
                  hash: 3
         | 
| 109 | 
            -
                  segments: 
         | 
| 110 | 
            -
                  - 0
         | 
| 111 124 | 
             
                  version: "0"
         | 
| 112 125 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 113 126 | 
             
              none: false
         | 
| 114 127 | 
             
              requirements: 
         | 
| 115 128 | 
             
              - - ">="
         | 
| 116 129 | 
             
                - !ruby/object:Gem::Version 
         | 
| 117 | 
            -
                  hash: 3
         | 
| 118 | 
            -
                  segments: 
         | 
| 119 | 
            -
                  - 0
         | 
| 120 130 | 
             
                  version: "0"
         | 
| 121 131 | 
             
            requirements: []
         | 
| 122 132 |  | 
| 123 133 | 
             
            rubyforge_project: 
         | 
| 124 | 
            -
            rubygems_version: 1. | 
| 134 | 
            +
            rubygems_version: 1.6.2
         | 
| 125 135 | 
             
            signing_key: 
         | 
| 126 136 | 
             
            specification_version: 3
         | 
| 127 137 | 
             
            summary: Add support for master/slave database clusters in ActiveRecord to improve performance.
         | 
| 128 | 
            -
            test_files:  | 
| 129 | 
            -
             | 
| 138 | 
            +
            test_files: 
         | 
| 139 | 
            +
            - spec/connection_adapters_spec.rb
         | 
| 140 | 
            +
            - spec/connection_statistics_spec.rb
         | 
| 141 | 
            +
            - spec/controller_filter_spec.rb
         | 
| 142 | 
            +
            - spec/seamless_database_pool_adapter_spec.rb
         | 
| 143 | 
            +
            - spec/seamless_database_pool_spec.rb
         | 
| 144 | 
            +
            - spec/spec_helper.rb
         | 
| 145 | 
            +
            - spec/test_adapter/active_record/connection_adapters/read_only_adapter.rb
         | 
| 146 | 
            +
            - spec/test_model.rb
         |