Perlでファイルを壊れにくくする排他制御を考察

サーバー関連
この記事は約5分で読めます。

この記事は、13年前に書かれました。

はじめに

Perlでファイルを読み書きする際、ファイル破損することがあります。
なぜこのようなことが発生するかというと、一番の原因は同時書き込みが発生した場合でしょう。
破損の仕方は様々ですが、中身が空になるという事態が最も怖いです。
そこで、ファイルロック処理が必要となります。れを排他制御といいます。より確実な排他制御を考察します。

ファイルロックの種類

Perlでファイルロックを行う方法は何種類かあります。

  • flock関数を使用
  • ロックディレクトリを生成する方法
  • alermを併用する方法
  • その他の対処方法

flcokを使用する方法

flockが最もポピュラーなファイルロック方法になります。
openで開いたファイルを、flock関数で排他ロックを掛けます。ロックが掛かっている間に書き込み処理をおこないます。closeをおこなうと自動でロック解除になりますので、明示的にflock(8)は必要ありません。

	open(FILE, "+< file.txt") || die('file open error');
	flock(FILE, 2) or die "Can't flock   : $!";
	truncate(FILE, 0);
	seek(FILE, 0, 0);
	print FILE $newdata;
	close(FILE);
1共有ロック(ブロックモード)
2排他ロック(ブロックモード)
5共有ロック(非ブロックモード)
6排他ロック(非ブロックモード)
8ロックを解除

ロックディレクトリを作成する方法

ロックディレクトリで排他制御をおこなう方法です。

#--------------#
#  ロック処理  #
#--------------#
sub lock {
	my($retry,$mtime,$lockfile);
	$lockfile = shift;

	if(!$lockfile){ die ("lock error"); }

	# 1分以上古いロックは削除する
	if (-e $lockfile) {
		($mtime) = (stat($lockfile))[9];
		if ($mtime < time - 60) { unlock($lockfile); }
	}

	$retry = 5;
	while (!symlink(".", $lockfile)) {
		if (--$retry <= 0) {  die("現在混み合っています。しばらくしてから操作してください。"); }
		sleep(1);
	}
	my $lockflag=1;
}

#--------------#
#  ロック解除  #
#--------------#
sub unlock {
	my $lockfile = shift;

	if(!$lockfile){ die ("lock error"); }

	unlink($lockfile);
	#rmdir($lockfile);

	my $lockflag=0;
}

alermを併用する方法

alermを併用する方法です。

	eval{
		local $SIG{ALRM} = sub { die "timeout" };
		alarm(10);
		if(!open(FILE, "file.txt")){ die "fileopen"; }
		while(! flock(FILE,6)){
			sleep(1);
		}
		my $count = <FILE>;
		$count++;
		truncate(FILE, 0);
		seek(FILE, 0, 0);
		print FILE $count;
		close(FILE);
		alarm(0);
	};
	alarm(0);
	close(out);

	if($@ =~ /timeout/){
		&show_error("ファイル書き込みがタイムアウトしました。戻って再度アクセスをおこなってください。");
	}elsif($@ =~ /fileopen/){
		&show_error("ファイルを開けませんでした。戻って再度アクセスをおこなってください。");
	}elsif($@){
		&show_error("その他のエラーです。[".$@."]。戻って再度アクセスをおこなってください。");
	}

以下の記事を参考にさせていただきました。

その他の対処方方法

ファイルロックで完全にファイル破損を防ぐことはできません。あくまで破損率を下げるのみです。
排他制御ではありませんが、ファイルが壊れてしまったも問題ないような仕組みを入れておくこともあります。私が実務で実装した仕組みは以下のようなものです。

  • 書き込み前にファイルをコピー退避しておき、ファイル書き込み完了後のファイル容量(byte)がゼロになっていた場合は、退避しておいたファイルで自動的にリカバリをおこなう
  • 毎日最初のファイルアクセスがあった場合に、別ディレクトリにファイルを退避しておく。自動バックアップの仕組み。万一破損した場合は手動でリカバリをおこなう。

実際に、上記の仕組みを入れていたことにより、何度かデータを復帰させることが出来ました。
ファイルロック自体は完全にファイル破損を防ぐことはできません。

最後に

ファイルロックは完全ではありません。確実にデータを守りたい場合はデータベースを使用しましょう。しかし、クライアントのサーバーでデータベースが使用できない場合があります。
その場合は排他制御を確実におこない、リカバリの仕組みも取り入れてデータを防御しましょう。