Как получить Rails для автоматического восстановления соединений с базой данных после простоя базы данных

После простоя базы данных Rails сначала выбросит эту ошибку один раз:

ActiveRecord:: StatementInvalid: NativeException: org.postgresql.util.PSQLException: соединение отклонено. Убедитесь, что имя хоста и порт правильны и что почтмейстер принимает TCP/IP.

С этого момента каждый вызов базы данных имеет следующую ошибку, даже после резервного копирования базы данных:

ActiveRecord:: StatementInvalid: ActiveRecord:: JDBCError: Это соединение было закрыто.

Чтобы запустить сервер снова, я должен перезапустить сервер rails. Это не идеально для нас, так как наши инженеры-разработчики хотели бы выполнять техническое обслуживание наших баз данных, не прибегая также к восстановлению всех служб, которые зависят от базы данных. Итак, мне интересно - есть ли способ автоматически получить Rails, чтобы попытаться восстановить соединение с базой данных или рекомендованный способ получить это поведение?

Вещи, которые я пробовал:

Я уже пробовал настройку reconnect to true в моих настройках базы данных, и с этим я могу убить отдельные соединения с базой данных, а рельсы восстановят соединения. Однако после сбоя базы данных это не произойдет. Я обнаружил, что из командной консоли я могу восстановить соединение, вызвав

ActiveRecord:: Base:: establish_connection

Так что, может быть, найти чистое место для рельсов, чтобы вызвать вышеприведенную команду, это сработает? Любые предложения?

Ответы

Ответ 1

Это действительно уродливое решение, но я думаю, что он должен работать.

  • Установите повторно на true, как и раньше.

  • Отредактируйте файл activerecord-X.Y.Z/lib/active_record/connection_adapters/postgresql_adapter.rb и измените метод reconnect!, чтобы сказать

    def reconnect!
      clear_cache!
      ActiveRecord::Base.establish_connection
    end
    

Требуются дополнительные исследования

  • Проверьте, действительно ли он работает
  • Убедитесь, что он не вызывает connect_connection несколько раз одновременно (в этом случае вам понадобится блокировка)
  • Проверьте, есть ли место для ввода этого кода. Ruby позволяет вам переопределить любой метод во время выполнения, но вам нужны загруженные символы. Другими словами, вам нужен класс PostgreSQLAdapter. Ближайший я пришел к тому, что этот символ загружен в config/environment.rb после initialize!, но он все еще недостаточно глубоко в стеке, чтобы загрузить этот символ.

Если вы находите место вне кода ActiveRecord, у которого уже загружен символ, и вы можете редактировать его методы, тогда вставьте в него следующий код:

class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::StatementPool
  def reconnect!
    clear_cache!
    ActiveRecord::Base.establish_connection
  end
end

Что еще, это то, что это немного перебор, чтобы на самом деле вызвать establish_connection. Возможно, можно вызвать важный материал внутри этого метода, чтобы избежать некоторых накладных расходов.

Сообщите мне, помогло ли это, и если вы достигли какого-либо прогресса.

Ответ 2

У меня была такая же проблема с Mysql2Adapter. Я повторил ошибку, выполнив очень длинный запрос: User.find_all_by_id((1..1000000).to_a); с этого момента все запросы ActiveRecord терпят неудачу (сбой User.first)

Вот как я его решил:

Проблема очень проста: всякий раз, когда мы получаем исключение выше, мы хотим восстановить соединение и повторить попытку. Мы решим проблему, накладывая метод execute, обертывая его с началом спасения и восстанавливая соединение db при спасении.

Для Mysql код находится в Mysql2Adapter, а ниже - исправление:

Поместите этот код в config/initializers/active_record.rb

module ActiveRecord
  module ConnectionAdapters
    class Mysql2Adapter < AbstractMysqlAdapter
      alias_method :old_execute, :execute

      def execute(sql, name=nil)
        begin
          old_execute(sql, name)
        rescue ActiveRecord::StatementInvalid
          # you can do some logging here

          ActiveRecord::Base.establish_connection

          old_execute(sql, name)
        end
      end
    end
  end
end

Вам нужно сделать то же самое для адаптера postgres.

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb

Я думаю, что поскольку select является наиболее часто используемым запросом, вы можете выбрать псевдоним и как только он будет терпеть неудачу, соединение будет восстановлено.

Вы хотите создать инициализатор (config/initializers/active_record.rb) и псевдоним select_rows (это может быть что-то еще, просто найти правильный метод и исправить его. Возможно, это async_exec или выполнить, я не выглядел много в адаптер Postgres):

module ConnectionAdapters::PostgreSQLAdapter
      module DatabaseStatements
      end
end

Ответ 3

Вставьте спасение где-нибудь

rescue ActiveRecord::StatementInvalid: ActiveRecord::JDBCError
  ActiveRecord::Base::establish_connection
  retry

Но где? Я не знаю

Также вы можете использовать функцию rescue из ApplicationController. Но это не приведет к повторению действия, которое не удалось, поэтому вы, вероятно, должны также отобразить некоторый шаблон ошибки

rescue_from ActiveRecord::StatementInvalid: ActiveRecord::JDBCError do
  ActiveRecord::Base::establish_connection
  render 'errors/error', :status => 500
end

Ответ 4

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

Разработайте набор рецептов capistrano (или других скриптов), которые ваши инженеры-разработчики могут использовать для остановки и запуска всех приложений. Для обычного Rails-приложения все, что вам действительно нужно сделать, это поместить страницу обслуживания, чтобы nginx или apache обслуживали эту страницу вместо отправки запросов в экземпляры рельсов. В идеале, рабочие-рельсы перестают получать запросы, db падает, db появляется, затем страница обслуживания сбрасывается, и рабочие снова получают запросы, никогда не понимая, что база данных ушла на некоторое время.

В случае фоновых работников они могут быть фактически остановлены и запущены с помощью script, если их очередь не пуста и остается пустой. Любые запланированные рейк-задачи или другие запланированные задания, вероятно, будут терпеть неудачу, если они будут зависеть от базы данных и запускаться во время ее работы, поэтому вы захотите попытаться запланировать их запуск за пределами окна, когда вы обычно выполняете обслуживание db.

Если ваши инженеры-разработчики не любят запускать скрипты (!), вы, вероятно, можете настроить хороший веб-интерфейс, чтобы облегчить им работу. Вероятно, это окажется полезным не только для устранения ошибок подключения к базе данных, так как это даст больше возможностей для людей в вашей организации позаботиться об основных вещах, таких как остановка и запуск ваших приложений.

Ответ 5

Я бы рекомендовал использовать внешние инструменты/скрипты для наблюдения за такими событиями. Повторное подключение Activerecord работает, когда базы данных убивают соединение после определенного времени простоя, и он откажется после определенного времени сбоя. Так что это не поможет в вашем случае.

Думаю, вы должны написать свой собственный script для контроля состояния вашей базы данных. Если он вернется через какое-то время, просто перезапустите приложение rails.

Кроме того, вам понадобятся данные мониторинга, такие как память вашего сервера, использование процессора и диска, загрузка сервера, состояние базы данных, набор материалов. Еще одно слегка настроенное правило монитора.