[CakePHP] memcached を使っている場合のキャッシュクリア方法
久々登場、みなさんこんにちは nakamura です。今年こそはベイスターズの優勝を信じて疑いません!ぼちぼちマジック点灯しちゃいますよ!
さてさて今日は CakePHP のちょっとだけ深いお話し。キャッシュの保存先に memcached を使っている場合の問題と対処法についてです。
CakePHP を使っているとどうしてもキャッシュをクリアしたい場面が出てくると思いますが、そんな時みなさんはどうしていますか?ファイルにキャッシュしているのであれば直接削除する人が多いでしょうか 。memcached の場合はどうでしょう?Cake の Cache クラスには clear というメソッドがあるのでこれを使ってシェルを書くのが良いかもしれません。もしくはちょっと乱暴ですが、memcached を再起動しちゃうなんてのもあるかもですね。
大抵の場合これで問題はなさそうですが、実はキャッシュの保存先に memcached を使っていてなおかつセッションの保存先としても使っている場合、上記の方法だとけっこうクリティカルな問題が起きてしまいます。
何が問題?
察しのいい人はお分かりと思いますが、Cache::clear にしろ memcached の再起動にしろ一緒にセッションデータまで消えてしまいます。memcached 再起動はまぁ当然でしょ、な感じはしますが、Cache::clear までそんな仕様になってるの??と最初はガクゼンとしたものです、、、Cake のソースを追ってみると一目瞭然です。
cake/libs/cache/memcache.php
/**
* Delete all keys from the cache
*
* @return boolean True if the cache was succesfully cleared, false otherwise
* @access public
*/
function clear() {
return $this->__Memcache->flush();
}
Memcache::flush を使っていますね。Memcache::flush は memcached 内の全ての項目を削除してしまいます。
対処法
てなわけで、対処法としてはキャッシュクリア用のシェルに一工夫こらしてセッション以外の項目だけを削除するようなプログラムを書きました。参考にしたのは以下の記事です。
ソースはこんな感じ。
class ClearCacheShell extends Shell {
function initialize() {
Configure::write("Cache.disable", true);
parent::initialize();
}
function main() {
$memcache = new Memcache();
$memcache->addServer("memcached.exmaple.com");
$slabs = $memcache->getExtendedStats("slabs");
foreach (array_keys($slabs["memcached.example.com:11211"]) as $memcache_id) {
if ($memcache_id !== "active_slabs" && $memcache_id !== "total_malloced") {
$key_array = $memcache->getStats("cachedump", $memcache_id, 10000);
if (!empty($key_array)) {
foreach (array_keys($key_array) as $key) {
// core.php の Cache::config["prefix"] にマッチしたら削除
$app = Inflector::slug(APP_DIR);
if (preg_match("/^{$app}_/", $key)) {
$memcache->delete($key);
}
}
}
}
}
}
}
これを app/vendors/shells/clear_cache.php として保存して、以下のコマンドを叩けばセッションを消すことなくキャッシュのみを削除可能です。
cd PATH_TO_ROOT
cake/console/cake -app app/ clear_cache
CakePHP-2.0 でも今のところ仕様変わってなさそうなのよね~、、、