いくつかのワーカープロセスを備えたRails 6アプリケーションがあります。このアプリは DB として PostgreSQL を使用します。場合によっては、DB が再起動し (マイナー バージョンのアップグレードなど)、ワーカーの接続が失われます。自動的に再接続されることを期待していましたが、それは起こりません。
私はdatabase.ymlでreconnect: trueフラグを使用しようとしました。同じ話です。まだ「PG::UnableToSend: サーバーへの接続がありません」というメッセージが表示されます。このオプションは PostgresqlAdapter でも使用できません。 MySQLアダプターのオプションのみだと思います。
ワーカーは Rails Runner で実行する単純なサービス クラスです
何ができるでしょうか?答えは簡単なはずだと私は信じています。
PG自動再接続用のActiveRecordパッチを作成しました。
例外の処理は最適化できますが、PG::UnableToSend、PG::ConnectionBad、PG::Error が奇妙に混在していたので、代わりに例外名を比較します。
module PostgreSQLAdapterAutoReconnectPatch
QUERY_EXCEPTIONS = [
"SSL connection has been closed unexpectedly",
"server closed the connection unexpectedly",
"no connection to the server",
].freeze
CONNECTION_EXCEPTIONS = [
"could not connect to server",
"the database system is starting up",
].freeze
def exec_query(*args)
super(*args)
rescue ActiveRecord::StatementInvalid => e
raise unless recoverable_query?(e.message)
in_transaction = transaction_manager.current_transaction.open?
try_reconnect
in_transaction ? raise : retry
end
private
def recoverable_query?(error_message)
QUERY_EXCEPTIONS.any? { |e| error_message.include?(e) }
end
def recoverable_connection?(error_message)
CONNECTION_EXCEPTIONS.any? { |e| error_message.include?(e) }
end
def try_reconnect
sleep_times = [0.1, 0.5, 1, 2, 4, 8, 16, 32]
begin
reconnect!
rescue PG::Error => e
sleep_time = sleep_times.shift
if sleep_time && recoverable_connection?(e.message)
logger.warn("DB Server timed out, retrying in #{sleep_time} sec")
sleep sleep_time
retry
else
logger.error(e)
raise
end
end
end
end
require "active_record/connection_adapters/postgresql_adapter"
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(PostgreSQLAdapterAutoReconnectPatch)
に触発された