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が解放されるのを待ちます。
 デッドロック状態に陥ります。

お試しあれ。

シソーラス辞書 その2

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


シソーラス辞書を探しています。
もちろん、オープンソースの日本語シソーラス辞書を望みます。


http://www.gengokk.co.jp/thesaurus/
言語工学研究所。有償。


荻野綱男の研究室
日本大学文理学部の荻野さん。
無償で公開しているようですが、文面を見る限り、
オープンソースとは相いれないようです。


無料総合オフィスソフトウェア - Apache OpenOffice 日本語プロジェクト
有名なOpenOfficeLGPL
シソーラス辞書が含まれているので、そこだけ流用する?
未調査。


おすすめのシソーラス辞書ありましたら、教えて下さい。

Ludia1.5.0

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


そろそろリリースのLudia1.5.0の変更履歴は以下となります。
下記以外にも不具合改修は含まれますので、バージョンアップをおすすめします。

追加機能

正規化関数pgs2normの追加

PG83関連

@@演算子から%%演算子に変更。(2/27の日記)
postgresql.conf対応。
シーケンシャルスキャンのコストを設定する。(2/25の日記)
シーケンシャルスキャンの不具合解消。

Senna1.1.1, 1.1.2関連

pgs2indexinfo関数、4Gbyte以上のファイル使用可能。
シーケンシャルスキャンで+検索の不具合修正。

仕様改善

MeCabをアンインストールせずとも、Ludiaを使用可能。(windows)
pgs2snippet1関数、hitしない場合にNULL返却。(Ludia-users 175)
pgs2snippet1関数、全角空白に対応。

不具合改修

pgs2snippet1関数、文字列連結の不具合修正。(Ludia-users 170)
max_n_index_cacheで指定した値より1小さい数の
インデックスしか使用できないパターンを修正。(Ludia-users 164)
FULL VACUUMの不具合改修。(2/8の日記のコメント)
配列型インデックスで配列の要素でNULL対応。(11/8の日記のコメント)
Mac(OS X)上のビルド不具合改修
シーケンシャルスキャンでludiaオプションが解釈されないパターンを改修

正規化関数pgs2norm

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


senna-1.1.2がリリースされましたね。
Ludia1.5.0もさっさとリリースしちゃいます。
Ludia1.5.0では、正規化関数pgs2normを追加します。

=# SELECT pgs2norm('ABcdEFg hijk%&& 123456', 1);
       pgs2norm
----------------------
 abcdefghijk%&&123456
(1 row)
=# SELECT pgs2norm('ABcdEFg hijk%&& 123456', 0);
       pgs2norm
----------------------
 abcdefg hijk%&& 123456
(1 row)

英数字記号の全角半角、英字の大文字小文字を正規化します。
二つ目の引数を1とするとスペースを削除します。
二つ目の引数を0とするとスペースを削除せず、
全角スペースを半角スペースに変換します。


さて、リリースに向けてがんばるぞっと。

pgs2destroyと分散チェックポイント

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


Ludiaのインデックスは、
1234567, 1234567.SEN, 1234567.SEN.l, 1234567.SEN.i, 1234567.SEN.c
の5つのファイルから構成されます。
DROP INDEXにより、1234567という名前のファイルが削除されると、
残り4つのファイルが残ってしまいます。
そこで、pgs2destroy関数を使って、4つのファイルを削除しましょう。
というのが、今までのLudiaでした。

=# DROP INDEX idx;
=# SELECT pgs2destroy();

PostgreSQL8.3からは、少し変わります。
PostgreSQL8.3では、分散チェックポイントが導入されました。
http://journal.mycom.co.jp/special/2007/postgresql/009.html
分散チェックポイントの機能により、コマンド直後にファイルが削除されません。
定期的に走るチェックポイント処理後に、ようやくファイルが削除されます。
このため、DROP INDEXをした直後は、1234567という名前のファイルが残ってしまいます。
残念。pgs2destroy関数が正常に動作しません。
時間を置いてからpgs2destroy関数を実行しましょう。
チェックポイントが走るのを待ってられないよ、という人は、
手動でチェックポイントを走らせましょう。

=# DROP INDEX idx;
=# CHECKPOINT;
=# SELECT pgs2destroy();

現在、pgs2destroy関数が必要となるのは、DROP INDEXのみ。
(Ludia1.3以降では、REINDEX, CLUSTER, TRUNCATE後にpgs2destroy関数を必要としません。)
この事象で困る場面は少ないと思いますが、ご注意を。


# pgs2destroy関数の改善案を思いついた人、コメントください。
# PostgreSQL8.3対応のLudia1.5.0リリースまで、あと少しです。少々お待ちを。