Как получить 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.
Кроме того, вам понадобятся данные мониторинга, такие как память вашего сервера, использование процессора и диска, загрузка сервера, состояние базы данных, набор материалов. Еще одно слегка настроенное правило монитора.