接続は長時間維持しますが、トランザクションはできるだけ短くします。
データベースの検索方法
SELECT current_database()
postgresql 情報関数を参照してください
耐障害性を備えた長い接続
接続を頻繁に開いたり閉じたりしないでください。それらに固執しますが、それらがあなたの下から消えることを覚悟してください。例えば:
- データベースまたはそれが存在するサーバーが再起動された場合
- あなたとサーバーの間にステートフルなファイアウォール/ルーターがあり、しばらくするとアイドル状態の接続追跡の関連付けを忘れる可能性がある場合
- データベース管理者が何らかの理由で意図的に接続を閉じた場合
アプリは、接続が使用できなくなることに適切に対処し、新しい接続を確立した後に操作を再試行できるように準備する必要があります。再試行回数を制限して、永遠にループしないようにしてください。同じ障害がデータベース サーバーに何度も発生します。
接続はいつでも無効になる可能性があるため、データベース操作が失敗したときに言語が例外をスローできない場合を除き、すべてのデータベース操作のエラー コードを常に確認する必要があります。例外を使用できる場合は、一連のデータベース操作全体を 1 回の try/catch でラップするだけで済みます。
アプリが非常に単純で、ユーザー フレンドリーである必要がない場合は、接続が失われたことをユーザーに伝え、アプリケーションを再起動するように依頼するだけでかまいません。個人的には、このアプローチを選択すると後悔することになると思いますが、エラー処理がはるかに簡単になります。
アプリで一度に複数の接続が必要な場合は、接続プールを使用するか再設計して、単一の接続で操作をキューに入れます。
耐障害性と再試行を伴う短いトランザクション
必要に応じて何年にもわたって接続を維持できますが、トランザクションはできるだけ短くする必要があります。長時間実行されるトランザクションは、PostgreSQL を含む一部のデータベース システムで問題を引き起こします。これにより、効率が低下し、ロックの問題のリスクが高まる可能性があります。可能であれば、ユーザーの「思考時間」、つまりユーザーの操作のために一時停止するまでトランザクションを開いたままにしないでください。 . トランザクションを開いたままにしておく必要がある場合は、ユーザーがフォームへの入力の途中で休暇を取ることを決定した場合に、セッションの有効期限が切れて最初に戻るようにタイムアウトを設定してみてください。冗談だと思うかもしれませんが、本番アプリの保守にまだ十分な時間を費やしていません。
アプリは、データベース操作が失敗して再試行できるように準備する必要があります。データベース ステートメントが正常に実行された後、タスクに関するアプリの知識を捨てないでください。トランザクション全体が正常にコミットされるまで保持します。そうすれば、シリアライゼーションに失敗した場合や、tx がサーバーによってキャンセルされた場合などに、トランザクション全体を再発行できます。
次のような典型的な線形データベース アクセス コード (疑似コード、Visual Basic は話せません) を書いたとします。
BEGIN;
value = get_value1_from_user();
UPDATE something SET field = :value;
value = get_value2_from_user();
UPDATE somethingelse SET otherfield = :value;
COMMIT;
UPDATE
2 番目が失敗した場合に何が起こるか想像してみてください。これCOMMIT
は起こりません-実行しようとすると、ROLLBACK
代わりに実行されます-ユーザーが入力したものがわからないため、get_value1_from_user()
再試行できません。
通常、次のように書く方が賢明です。
value1 = get_value1_from_user();
value2 = get_value2_from_user();
committed = false;
retries = 3;
do:
try:
BEGIN;
UPDATE something SET field = :value1;
UPDATE somethingelse SET otherfield = :value2;
COMMIT;
committed = true;
catch ...:
retries = retries - 1;
log "Failed to commit (sometask) because of (error message from database), "+retries+" left"
while not committed and retries > 0;
if not committed:
print "Tell the user I couldn't save their work"
end if;
もちろん、再試行について賢くなりたいと思うでしょう。失敗した後、接続がまだ存在することを確認し、存在しない場合は再試行する前に再確立します。ステートメントが失敗した理由も確認してください。構文エラーのあるステートメントを再試行しても意味がありません。これはSQLSTATE
、メッセージ テキストを調べることなく、さまざまな種類のエラーを区別するためのものです。
メッセージは翻訳され、バージョンごとに変更される可能性があるため、決定を下す方法としてエラー メッセージ テキストを調べないでください。
「検証済み」接続などというものはありません
データベース開発の初心者である開発者は、操作を続行する前にデータベース接続を確認することで、多くの手間を省くことができると考えています。または何かを発行し、接続が使用可能であり、次の操作が試行されたときに失敗しないことにSELECT 1
基づいて結論付けます。
これは偽物です。「検証」と次の操作の間には固有の競合状態があります。それだけでなく、単純な検証操作が成功したからといって、次の重要なステートメントが失敗しないわけではありません。アプリは、トランザクションがコミットされるまで、トランザクション中に行われたすべての変更を追跡し、トランザクションが失敗した場合は再試行できる必要があります。
別の方法は、ユーザーに「おっと、失敗しました。すべての変更を再入力して、もう一度やり直してください」と伝えることです。場合によっては、楽観的ロック戦略が使用されている場合、実行中の 2 つの変更が競合し、それらをプログラムで安全にマージできない場合など、実際にはそれが正しいこともあります。ただし、通常は舞台裏で再試行することをお勧めします。