PostgreSQLのロック制御

co-sakaです。こんにちは。


Ludiaの開発中に、以下のエラーログに当たりました。

ERROR: deadlock detected
Detail: Process 19300 waits for AccessExclusiveLock on relation 211573 of database 122148; blocked by process 19291.
        Process 19291 waits for AccessExclusiveLock on relation 211573 of database 122148; blocked by process 19300.

簡単に言うと、「二つのプロセスが一つの資源を求めてデッドロックになりました。」というもの。
こんな状態ってありえるのでしょうか?
普通、このような状態はありえないのですが、PostgreSQLのようにロックレベルという概念がある場合、
上記のように、デッドロックに陥る可能性があります。


PostgreSQLのロックは以下の法則で実行されます。
(競合が起きると、後からロック取得を試みたプロセスが待ちます。)

    • ロックレベル低とロックレベル低は競合しない。
    • ロックレベル高とロックレベル低は競合する。
    • ロックレベル高とロックレベル高は競合する。
    • ただし、自分自身とは競合しない。

そこで、今回の事象を見てみると、

プロセス19300
資源211573に対して、ロックレベル低を取得。
資源211573に対して、ロックレベル高を取得。
資源211573に対して、ロックレベル高を解放。
資源211573に対して、ロックレベル低を解放。


プロセス19291
資源211573に対して、ロックレベル低を取得。
資源211573に対して、ロックレベル高を取得。
資源211573に対して、ロックレベル高を解放。
資源211573に対して、ロックレベル低を解放。

この2つのプロセスが走ると、デッドロックが起きる可能性があります。


ちなみに、http://www.postgresql.jp/document/pg826doc/html/explicit-locking.htmlには、

トランザクション内のオブジェクトに対して獲得した最初のロックが、
そのオブジェクトが必要とする最高位のモードであることを確実に保証すべきです。

と書いてあります。
なるほど。

-----------------------------

以下追記(3/26)

本当に一つの資源を求めあって、デッドロックになるの?
信じられない!
という声が聞こえてきたので、以下のSQLで再現してみます。

process1
=# CREATE TABLE hoge(id INT);
=# BEGIN;
=# LOCK hoge IN ACCESS SHARE MODE; --- ロック1

process2
=# BEGIN;
=# LOCK hoge IN ACCESS SHARE MODE; --- ロック2
=# LOCK hoge IN ACCESS EXCLUSIVE MODE; --- ロック3
  ロック2とは競合しませんが、ロック1と競合します。
 ロック1が解放されるのを待ちます。

process1
=# LOCK hoge IN ACCESS EXCLUSIVE MODE; --- ロック4
  ロック1とは競合しませんが、ロック2と競合します。
  ロック2が解放されるのを待ちます。
 デッドロック状態に陥ります。

お試しあれ。