さじろーどっとねっと
<TOP?LOCK方式>

Perl DATABASEでは、ファイルロックにTOP?LOCK方式を採用しています。この章ではTOP?LOCK方式の概要を説明します。

ファイルのロックの方法は、flockを使用するケースが多くありますが、flockを使用する場合、特定のライブラリを必要とします。PERLDBではOPEN環境での稼動を目的としておりますので、特定ライブラリの仕様は規定外になります。

その問題を解決し、どの環境でも動作する、確実いロックする方法でTOP?LOCK方式を採用しました。以下に概要を説明します。

TOP?LOCK方式では特定ファイルへロック要求を出します。そして、そのファイルを読み込んだ時に自分の書き込みが一番上(先頭)にあれば使用する権利を得たことになります。

順を追って説明しましょう。

1.ロックの要求を出します。
open(LOCK,">>test.lock");
print LOCK "myname\n";
close(LOCK);

2.ロックできたか見ます。
open(LOCK," @lock=;
close(LOCK);
if($lock[0] eq "myname"){
print "ロック成功!";
}else{
print "ロック失敗!";
}

これでロックの正否が解ります。しかし、これでは同一実行ファイルがロックをかけてしまった場合に「myname」がだれなのかわかりません。自分自身を認識する必要があります。そこでランダムな数字を使います。(Sajiro DATABASEでは乱数値+プロセスIDを使用しています)

open(LOCK,">>test.lock");
$id=rand(100);
print LOCK "$id\n";
close(LOCK);

open(LOCK," @lock=;
close(LOCK);
if($lock[0] eq "$id"){
print "ロック成功!";
}else{
print "ロック失敗!";
}

しかし、WEBアプリケーションの場合、ロックをかけたプロセスが異常終了する場合があります。その時はいつまで経っても自分のIDは先頭にくることはなく結果として他プロセスがロックした状態になります。それを防ぐために時間も必要になります。

open(LOCK,">>test.lock");
$id=rand(100);
$tm=time;
print LOCK "$id<>$tm<>\n";
close(LOCK);

ロックした時間を見て古いデータであれば削除しても良いとします。

open(LOCK," @lock=;
close(LOCK);
($wid,$wtm)=split(/<>/,$lock[0]);
if($wid eq "$id"){
print "ロック成功!";
}elsif($wtm < time - 600){
print "ロックファイル削除!";
unlink("test.lock");
}else{
print "ロック失敗!";
}

しかし、削除してしまうとほかの処理が実はロックしている場合があります。その時にロックが重複してしまいます。それを防ぐために再度、データ反映時にロックの確認をします。

# ファイルのロック
open(LOCK,">>test.lock");
$id=rand(100);
$tm=time;
print LOCK "$id<>$tm<>";
close(LOCK);

# ファイルのロック確認
open(LOCK," @lock=;
close(LOCK);
($wid,$wtm)=split(/<>/,$lock[0]);
if($wid eq "$id"){
print "ロック成功!";
}elsif($wtm < time - 600){
print "ロックファイル削除!";
unlink("test.lock");
exit;
}else{
print "ロック失敗!";
exit;
}

# ファイルの更新
open(DATA,"workdata.tmp");
print DATA "01<>更新データを追加します<>");
close(DATA);

# 再度ファイルのロック確認
open(LOCK," @lock=;
close(LOCK);
($wid,$wtm)=split(/<>/,$lock[0]);
if($wid eq "$id"){
print "ロック成功!";
}elsif($wtm < time - 600){
print "ロックファイル削除!";
unlink("test.lock");
exit;
}else{
print "ロック失敗!";
exit;
}

# ロック成功のときだけ処理を本番ファイルにします。
rename("workdata.tmp","workdata.dat");

全てのロック情報は、ロック要求から始まり、ロック確認、データの書き込み、ロック確認、本番ファイルへの反映となります。

実際には、IDが0?100では同じ番号が出る場合がありますので、30桁以上の数値を使用しています。プロセスIDを使用するとWinでうまく行かないので乱数を使っています。

ブックマークに追加する