.NETのトランザクションスコープなど、分散トランザクションを利用したアプリケーションの運用時に、サーバがダウンするなどのシステム異常が発生すると、トランザクションがインダウト状態になることがあります。このとき、トランザクションで占有した資源がロックされ、他のトランザクションから当該資源へのアクセスがブロックされて利用不可になります。
以降にインダウトトランザクションの確認方法と解決方法を説明します。
インダウトトランザクションの確認方法
分散トランザクションを利用したアプリケーションの動作中に、サーバまたはクライアントがダウンした場合、インダウトトランザクションが発生している可能性があります。
確認方法を以下に示します。
サーバの再起動時のログに、以下のようなメッセージが出力されていると、インダウトトランザクションが発生していると判断できます。
LOG: 準備されたトランザクション2103を復旧しています
システムビューpg_prepared_xactsを参照して、準備されたトランザクションに関する情報を取得します。
準備されたトランザクションの一覧にあるトランザクション識別子(pg_prepared_xacts のtransactionカラム)が、再起動時のログから取得したインダウトトランザクションのトランザクション識別子と一致している場合、その行がインダウトトランザクションに関する情報です。
postgres=# select * from pg_prepared_xacts; transaction | gid | prepared | owner | database -------------+-----------+------------+----------+---------- 2103 | 374cc221-f6dc-4b73-9d62-d4fec9b430cd | 2015-05-06 16:28:48.471+08 | postgres | postgres (1 row)
transactionカラムが2103の行に、インダウトトランザクションに関する情報が出力されています。
クライアントが1つも接続していないときに、pg_prepared_xacts に準備されたトランザクションが存在した場合、そのトランザクションはインダウト状態であると判断できます。
クライアントが1つ以上接続しているときに、pg_prepared_xactsに準備されたトランザクションが存在しても、インダウト状態かどうか判断できません。この場合、下記のクエリで、取得したデータベース名、ユーザー名、PREPARE TRANSACTION を実行した時刻とアクセス先のテーブル名の情報から、インダウトトランザクションを特定してください。
select gid,x.database,owner,prepared,l.relation::regclass as relation from pg_prepared_xacts x left join pg_locks l on l.virtualtransaction = '-1/'||x.transaction and l.locktype='relation';
これだけでは特定できない場合は、十分に時間が経過した後に、再度pg_prepared_xacts を調べてください。
前回調べたときから継続しているトランザクションがあれば、そのトランザクションがインダウト状態である可能性が高いと判断できます。
ポイント
ここで説明したように、確実にインダウトトランザクションを特定する汎用的な方法は存在しません。
これまでに述べた方法で特定できるように、何らかの補助的な情報の採取(例:クライアント側でのロギング)や、運用方法(例:業務毎にデータベースユーザーを割り当てる)を検討してください。
インダウトトランザクションの解決方法
前述のシステムビューpg_prepared_xactsからインダウトトランザクションのグローバルトランザクション識別子(pg_prepared_xacts のgidカラム)を取得し、ROLLBACK PREPARED文またはCOMMIT PREPARED文を発行することで、インダウトトランザクションを解決します。
例
インダウトトランザクションをロールバックする場合
postgres=# rollback prepared '374cc221-f6dc-4b73-9d62-d4fec9b430cd';
ROLLBACK PREPARED
インダウトトランザクションをコミットする場合
postgres=# commit prepared '374cc221-f6dc-4b73-9d62-d4fec9b430cd';
COMMIT PREPARED