「Perlメモ」の排他制御ロジックをPHPに移植してみた
PHPにおける排他制御のベストプラクティスが分からなかったので、定評のありそうな「Perlメモ」の「排他制御(ファイルロック)をする」を移植してみました。誤りがあるかもしれませんので、お気づきの方はご指摘いただけると助かります。
<?php function my_flock($options = array()) { $lfh = array_merge(array('dir' => './lockdir/', 'basename' => 'lockfile', 'timeout' => 60, 'trytime' => 10), $options); $lfh['path'] = $lfh['dir'] . $lfh['basename']; for ($i = 0; $i < $lfh['trytime']; $i++, sleep(1)) { if (rename($lfh['path'], $lfh['current'] = $lfh['path'] . time())) { return $lfh; } } $filelist = scandir($lfh['dir']); foreach ($filelist as $file) { if (preg_match('/^' . $lfh['basename'] . '(\d+)/', $file, $matches)) { if (time() - $matches[1] > $lfh['timeout'] and rename($lfh['dir'] . $matches[0], $lfh['current'] = $lfh['path'] . time()) ) { return $lfh; } break; } } return false; } function my_funlock($lfh) { rename($lfh['current'], $lfh['path']); } # ロックする(タイムアウトあり) $lfh = my_flock() or die('Busy!'); # アンロックする my_funlock($lfh);
排他制御の目的ですが、WebサービスのAPIを呼び出す際のインターバル管理に使うつもりです。たとえば、Amazon Associates Web ServiceのAPIには、1秒1コールまでという制限があります。これを遵守するためには、最終呼び出し時刻を保持しつつ、それをアトミックに更新する仕組みが必要になるでしょう。
ちなみに、拙作のリリースチェッカーにもそのような仕組みはあるのですが、Perlメモのロジックに比べると中途半端な感じなので、改善したいと思っているわけです。