}" end

Наследуем от этого класса тот класс, который будем хранить в другой БД:

    class Message < SecondDb
    end

Не забываем, что миграции тоже должны использовать SecondDb:

    SecondDb.connection.create_table :auto_buy_stats do |t|
      t.timestamps
    end

Но сразу возникает проблема с тестами. Дело в том, что после теста откатывается (ROLLBACK) только основная БД. Для того, чтобы и на второй БД транзакции откатывались, можно вот так пропатчить spec/spec_helper.rb (rspec 1.1.1)

    module Test #:nodoc:
      module Unit #:nodoc:
        class TestCase #:nodoc:
          remove_const('DATABASES') if defined?(DATABASES)
          DATABASES = [ActiveRecord::Base, SecondDb]

          def setup_preloaded_fixtures
            return if @fixtures_setup
            @fixtures_setup = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            if pre_loaded_fixtures && !use_transactional_fixtures
              raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
            end

            @fixture_cache = {}

            # Load fixtures once and begin transaction.
            if use_transactional_fixtures?
              if @@already_loaded_fixtures[self.class]
                @loaded_fixtures = @@already_loaded_fixtures[self.class]
              else
                load_fixtures
                @@already_loaded_fixtures[self.class] = @loaded_fixtures
              end
              DATABASES.each do |db|
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  db.send :increment_open_transactions
                else
                  # rails >= 2.2
                  db.connection.send :increment_open_transactions
                end
                db.connection.begin_db_transaction
              end
              # Load fixtures for every test.
            else
              Fixtures.reset_cache
              @@already_loaded_fixtures[self.class] = nil
              load_fixtures
            end

            # Instantiate fixtures for every test if requested.
            instantiate_fixtures if use_instantiated_fixtures
          end

          setup_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('setup_fixtures') ? :setup_fixtures : :setup_with_fixtures
          alias_method setup_fixtures_method_name, :setup_preloaded_fixtures
          alias_method :setup, setup_fixtures_method_name

          def teardown_preloaded_fixtures
            return if @fixtures_teardown
            @fixtures_teardown = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            unless use_transactional_fixtures?
              Fixtures.reset_cache
            end

            # Rollback changes if a transaction is active.
            if use_transactional_fixtures?
              DATABASES.each do |db|
                db.connection.rollback_db_transaction
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  Thread.current['open_transactions'] = 0
                else
                  # rails >= 2.2
                  db.connection.instance_variable_set('@open_transactions',0)
                end
              end
            end
            ActiveRecord::Base.verify_active_connections!
          end

          teardown_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('teardown_fixtures') ? :teardown_fixtures : :teardown_with_fixtures
          alias_method teardown_fixtures_method_name, :teardown_preloaded_fixtures
          alias_method :teardown, teardown_fixtures_method_name
        end
      end
    end

Вуаля!

' name='description'> }" end

Наследуем от этого класса тот класс, который будем хранить в другой БД:

    class Message < SecondDb
    end

Не забываем, что миграции тоже должны использовать SecondDb:

    SecondDb.connection.create_table :auto_buy_stats do |t|
      t.timestamps
    end

Но сразу возникает проблема с тестами. Дело в том, что после теста откатывается (ROLLBACK) только основная БД. Для того, чтобы и на второй БД транзакции откатывались, можно вот так пропатчить spec/spec_helper.rb (rspec 1.1.1)

    module Test #:nodoc:
      module Unit #:nodoc:
        class TestCase #:nodoc:
          remove_const('DATABASES') if defined?(DATABASES)
          DATABASES = [ActiveRecord::Base, SecondDb]

          def setup_preloaded_fixtures
            return if @fixtures_setup
            @fixtures_setup = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            if pre_loaded_fixtures && !use_transactional_fixtures
              raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
            end

            @fixture_cache = {}

            # Load fixtures once and begin transaction.
            if use_transactional_fixtures?
              if @@already_loaded_fixtures[self.class]
                @loaded_fixtures = @@already_loaded_fixtures[self.class]
              else
                load_fixtures
                @@already_loaded_fixtures[self.class] = @loaded_fixtures
              end
              DATABASES.each do |db|
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  db.send :increment_open_transactions
                else
                  # rails >= 2.2
                  db.connection.send :increment_open_transactions
                end
                db.connection.begin_db_transaction
              end
              # Load fixtures for every test.
            else
              Fixtures.reset_cache
              @@already_loaded_fixtures[self.class] = nil
              load_fixtures
            end

            # Instantiate fixtures for every test if requested.
            instantiate_fixtures if use_instantiated_fixtures
          end

          setup_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('setup_fixtures') ? :setup_fixtures : :setup_with_fixtures
          alias_method setup_fixtures_method_name, :setup_preloaded_fixtures
          alias_method :setup, setup_fixtures_method_name

          def teardown_preloaded_fixtures
            return if @fixtures_teardown
            @fixtures_teardown = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            unless use_transactional_fixtures?
              Fixtures.reset_cache
            end

            # Rollback changes if a transaction is active.
            if use_transactional_fixtures?
              DATABASES.each do |db|
                db.connection.rollback_db_transaction
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  Thread.current['open_transactions'] = 0
                else
                  # rails >= 2.2
                  db.connection.instance_variable_set('@open_transactions',0)
                end
              end
            end
            ActiveRecord::Base.verify_active_connections!
          end

          teardown_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('teardown_fixtures') ? :teardown_fixtures : :teardown_with_fixtures
          alias_method teardown_fixtures_method_name, :teardown_preloaded_fixtures
          alias_method :teardown, teardown_fixtures_method_name
        end
      end
    end

Вуаля!

' property='og:description'> }" end

Наследуем от этого класса тот класс, который будем хранить в другой БД:

    class Message < SecondDb
    end

Не забываем, что миграции тоже должны использовать SecondDb:

    SecondDb.connection.create_table :auto_buy_stats do |t|
      t.timestamps
    end

Но сразу возникает проблема с тестами. Дело в том, что после теста откатывается (ROLLBACK) только основная БД. Для того, чтобы и на второй БД транзакции откатывались, можно вот так пропатчить spec/spec_helper.rb (rspec 1.1.1)

    module Test #:nodoc:
      module Unit #:nodoc:
        class TestCase #:nodoc:
          remove_const('DATABASES') if defined?(DATABASES)
          DATABASES = [ActiveRecord::Base, SecondDb]

          def setup_preloaded_fixtures
            return if @fixtures_setup
            @fixtures_setup = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            if pre_loaded_fixtures && !use_transactional_fixtures
              raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
            end

            @fixture_cache = {}

            # Load fixtures once and begin transaction.
            if use_transactional_fixtures?
              if @@already_loaded_fixtures[self.class]
                @loaded_fixtures = @@already_loaded_fixtures[self.class]
              else
                load_fixtures
                @@already_loaded_fixtures[self.class] = @loaded_fixtures
              end
              DATABASES.each do |db|
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  db.send :increment_open_transactions
                else
                  # rails >= 2.2
                  db.connection.send :increment_open_transactions
                end
                db.connection.begin_db_transaction
              end
              # Load fixtures for every test.
            else
              Fixtures.reset_cache
              @@already_loaded_fixtures[self.class] = nil
              load_fixtures
            end

            # Instantiate fixtures for every test if requested.
            instantiate_fixtures if use_instantiated_fixtures
          end

          setup_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('setup_fixtures') ? :setup_fixtures : :setup_with_fixtures
          alias_method setup_fixtures_method_name, :setup_preloaded_fixtures
          alias_method :setup, setup_fixtures_method_name

          def teardown_preloaded_fixtures
            return if @fixtures_teardown
            @fixtures_teardown = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            unless use_transactional_fixtures?
              Fixtures.reset_cache
            end

            # Rollback changes if a transaction is active.
            if use_transactional_fixtures?
              DATABASES.each do |db|
                db.connection.rollback_db_transaction
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  Thread.current['open_transactions'] = 0
                else
                  # rails >= 2.2
                  db.connection.instance_variable_set('@open_transactions',0)
                end
              end
            end
            ActiveRecord::Base.verify_active_connections!
          end

          teardown_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('teardown_fixtures') ? :teardown_fixtures : :teardown_with_fixtures
          alias_method teardown_fixtures_method_name, :teardown_preloaded_fixtures
          alias_method :teardown, teardown_fixtures_method_name
        end
      end
    end

Вуаля!

' property='twitter:description'>

Технические заметки одного Евтуховича

Рассказ о серых трудовых буднях инженера программных систем

Несколько баз данных

| Комментарии

В некоторых ситуациях необходимо использовать более чем одну базу данных в проекте. Например, статистику и большие таблицы хранить на другом сервере, у которого большой и медленный диск, а данные, которые нужны постоянно, хранить на основном сервере с маленьким и быстрым диском.

В нашем случае хотелось унести большие таблицы на другой сервер, чтобы в дисковом кэше (а у главного сервера БД 32 Гб оперативной памяти) хранились все рабочие таблицы и никогда оттуда не убегали.

Делаем абстракный класс SecondDb, в котором указываем, с каким сервером БД соединяться.

    class SecondDb < ActiveRecord::Base
      self.abstract_class = true
      establish_connection "linkfeed2_#{RAILS_ENV || 'development'}"
    end

Наследуем от этого класса тот класс, который будем хранить в другой БД:

    class Message < SecondDb
    end

Не забываем, что миграции тоже должны использовать SecondDb:

    SecondDb.connection.create_table :auto_buy_stats do |t|
      t.timestamps
    end

Но сразу возникает проблема с тестами. Дело в том, что после теста откатывается (ROLLBACK) только основная БД. Для того, чтобы и на второй БД транзакции откатывались, можно вот так пропатчить spec/spec_helper.rb (rspec 1.1.1)

    module Test #:nodoc:
      module Unit #:nodoc:
        class TestCase #:nodoc:
          remove_const('DATABASES') if defined?(DATABASES)
          DATABASES = [ActiveRecord::Base, SecondDb]

          def setup_preloaded_fixtures
            return if @fixtures_setup
            @fixtures_setup = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            if pre_loaded_fixtures && !use_transactional_fixtures
              raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
            end

            @fixture_cache = {}

            # Load fixtures once and begin transaction.
            if use_transactional_fixtures?
              if @@already_loaded_fixtures[self.class]
                @loaded_fixtures = @@already_loaded_fixtures[self.class]
              else
                load_fixtures
                @@already_loaded_fixtures[self.class] = @loaded_fixtures
              end
              DATABASES.each do |db|
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  db.send :increment_open_transactions
                else
                  # rails >= 2.2
                  db.connection.send :increment_open_transactions
                end
                db.connection.begin_db_transaction
              end
              # Load fixtures for every test.
            else
              Fixtures.reset_cache
              @@already_loaded_fixtures[self.class] = nil
              load_fixtures
            end

            # Instantiate fixtures for every test if requested.
            instantiate_fixtures if use_instantiated_fixtures
          end

          setup_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('setup_fixtures') ? :setup_fixtures : :setup_with_fixtures
          alias_method setup_fixtures_method_name, :setup_preloaded_fixtures
          alias_method :setup, setup_fixtures_method_name

          def teardown_preloaded_fixtures
            return if @fixtures_teardown
            @fixtures_teardown = true
            return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?

            unless use_transactional_fixtures?
              Fixtures.reset_cache
            end

            # Rollback changes if a transaction is active.
            if use_transactional_fixtures?
              DATABASES.each do |db|
                db.connection.rollback_db_transaction
                if db.respond_to?(:increment_open_transactions, true)
                  # rails < 2.2
                  Thread.current['open_transactions'] = 0
                else
                  # rails >= 2.2
                  db.connection.instance_variable_set('@open_transactions',0)
                end
              end
            end
            ActiveRecord::Base.verify_active_connections!
          end

          teardown_fixtures_method_name = Test::Unit::TestCase.instance_methods.include?('teardown_fixtures') ? :teardown_fixtures : :teardown_with_fixtures
          alias_method teardown_fixtures_method_name, :teardown_preloaded_fixtures
          alias_method :teardown, teardown_fixtures_method_name
        end
      end
    end

Вуаля!

Комментарии