接続状態を確認し、必要に応じて再接続するための「ベストプラクティス」の方法はありますか?#
はい、少なくとも私の見解では、競合状態のないアプローチは1つしかなく、エラーが発生した場合にエラーを処理する再試行ループでクエリを実行するためです。
それ以外の場合はまだあります:
PREPARE
SELECT 1;
またはあなたのテストステートメントが何であれ
- ネットワークがドロップアウトし、バックエンドがクラッシュし、管理者がサーバーを再起動します。
EXECUTE
- 感嘆符。
正しい動作には、擬似コードのようなものが必要です。
while not succeeded:
try:
execute_statement()
succeeded = True
except some_database_exception:
if transaction_is_valid():
// a `SELECT 1` or `select 1 from pg_prepared_statements where name = 'blah'
// succeeded in transaction_is_valid(), so the issue was probably
// transient. Retry, possibly with a retry counter that resets the
// connection if more than a certain number of retries.
// It can also be useful to examine the exception or error state to
// see if the error is recoverable so you don't do things like retry
// repeatedly for a transaction that's in the error state.
else if test_connection_usable_after_rollback():
// Connection is OK but transaction is invalid. You might determine
// this from the exception state or by seeing if sending a `ROLLBACK`
// succeeds. In this case you don't have to re-prepare, just open
// a new transaction. This case is not needed if you're using autocommit.
else:
// If you tried a SELECT 1; and a ROLLBACK and neither succeeded, or
// the exception state suggests the connection is dead. Re-establish
// it, re-prepare, and restart the last transaction from the beginning.
reset_connection_and_re_prepare()
冗長で迷惑ですか?はい、しかし通常はヘルパーやライブラリに簡単にラップされます。他のすべてはまだレースの対象です。
最も重要なことは、アプリケーションが複数のことを実行するトランザクションを発行している場合、トランザクションがコミットするまで実行したすべてのことを記憶し、エラーが発生した場合にトランザクション全体を再試行できるようにする必要があります。または、ユーザーに「おっと、データを食べました。もう一度入力して、もう一度やり直してください」と伝えます。
レースを気にせず、定期的なチェックで明らかにデッドな接続を処理したい場合は、最後のクエリの時刻を変数に格納するだけです。クエリを発行するときは、タイムスタンプが数分以上前のものであるかどうかを確認し、古い場合は、SELECT 1;
またはクエリを発行pg_prepared_statements
して、準備されたステートメントを確認します。ユーザーにエラーを報告する準備をするか、とにかくすべてを適切なエラー処理でラップする必要があります...この場合、時間のチェックとテストに煩わされることはまったくありません。