[CakePHPのバグ]キャッシュ処理でunlinkエラーが発生する

CakePHP バージョン1系・バージョン2系でごくまれに次のようなエラーが出ることがあります。
ごくまれにしか発生しない現象で、気づきにくくリロードすると解消されるため調査・検証がしづらいです。

[参考記事] キャッシュ処理でunserializeエラーが発生する

Warning (2): unlink(/……/app/tmp/cache/models/cake_model_default_xxx) [http://php.net/function.unlink]: No such file or directory [CORE/cake/libs/file.php, line 298]
Code
unlink - [internal], line ??
File::delete() - CORE/cake/libs/file.php, line 298
FileEngine::clear() - CORE/cake/libs/cache/file.php, line 246
FileEngine::gc() - CORE/cake/libs/cache/file.php, line 104
Cache::_buildEngine() - CORE/cake/libs/cache.php, line 168
Cache::config() - CORE/cake/libs/cache.php, line 141
Configure::__loadBootstrap() - CORE/cake/libs/configure.php, line 436
Configure::getInstance() - CORE/cake/libs/configure.php, line 52
include - CORE/cake/bootstrap.php, line 38
[main] - …….php, line xxx

これはTMP cache models以下のデータベース用キャッシュファイルが同時アクセスでたまたま別アクセスで削除されたファイルを削除しようとした場合に発生します。

エラーが発生している箇所をたどっていくと次のようなコードになっています。

エラーの該当箇所付近
CORE/cake/libs/file.php, line 298

/**
 * Deletes the File.
 *
 * @return boolean Success
 * @access public
 */
	function delete() {
		clearstatcache();
		if (is_resource($this->handle)) {
			fclose($this->handle);
			$this->handle = null;
		}
		if ($this->exists()) {
			return unlink($this->path);
		}
		return false;
	}

clearstatcache()でファイルキャッシュを削除していますが、unlink()までに複数の処理があり同時にアクセスされた時にエラーが発生します。

次のように変更すると解消されます。

		if ($this->exists()) {
			return unlink($this->path);
		}
		   ↓
		if ($this->exists()) {
			return @unlink($this->path);
		}

ただし削除がされているにも関わらずfalseが戻ることがあるのでコードによっては障害となることがあります。

キャッシュ削除部分のコード
CORE/cake/libs/cache/file.php, line 246

/**
 * Delete all values from the cache
 *
 * @param boolean $check Optional - only delete expired cache items
 * @return boolean True if the cache was succesfully cleared, false otherwise
 * @access public
 */
	function clear($check) {
		if (!$this->_init) {
			return false;
		}
		$dir = dir($this->settings['path']);
		if ($check) {
			$now = time();
			$threshold = $now - $this->settings['duration'];
		}
		$prefixLength = strlen($this->settings['prefix']);
		while (($entry = $dir->read()) !== false) {
			if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) {
				continue;
			}
			if ($this->_setKey($entry) === false) {
				continue;
			}
			if ($check) {
				$mtime = $this->_File->lastChange();

				if ($mtime === false || $mtime > $threshold) {
					continue;
				}

				$expires = $this->_File->read(11);
				$this->_File->close();

				if ($expires > $now) {
					continue;
				}
			}
			$this->_File->delete();
		}
		$dir->close();
		return true;
	}

delete()の戻り値を見ていないのでキャッシュ削除では問題となりません。

CakePHPではアクセス数が多い場合の考慮がされていない部分がいくつかあります。
[参考記事] キャッシュ処理でunserializeエラーが発生する

関連記事

スポンサーリンク

PHPでfacebook投稿時に公開範囲を指定する方法

ホームページ製作・web系アプリ系の製作案件募集中です。

上に戻る