DMM.comラボエンジニアブログ

DMM.comラボのエンジニアブログです。DMM.comを支える技術について書いています。

phpのapcuはmemchachedみたいに使えなかった話

こんにちは、DMMのシステム本部プラットフォーム開発部所属、金沢の高身長エンジニアことリョウタです^^

今回はPHPでテストコードを書く際、memchachedの代わりにAPCuを利用して有効期限内のAPIアクセストークンをキャッシュして使い回すつもりが、うまくいかなかった話をお伝えします!


判明したこと

ざっくりいうと、phpのcli(コマンドライン)から、APCuはmemchachedっぽく使うことができません!

もう少し細かく言うと、キャッシュするようにデータを入れたり、出したりできますが、有効期限(ttl)は意味をなさないし、格納したキャッシュはコマンドラインの実行プロセスが終了すると全部破棄されるのでmemchachedのように使うことはできませんでした。


経緯

PHPにAPC(現在はAPCu)を入れるとmemchachedみたいにメモリにデータ入れられると聞いたことからです。
php5.5からコードキャッシュはAPCからZend OpcacheとAPCuに分割されており、ユーザ定義データをキャッシュするにはAPCuが必要です。
cf. PHP - APCがAPCuとZend OPcacheに取って代わられたワケ - Qiita

cf. ZendOpcacheとAPCuではじめるハイパフォーマンスPHP - pixiv inside



やろうとしたこと

PHPでテストを行う際に、有効期限内のあるトークンをキャッシュして使いまわそうと思っていました。
PHPマニュアルを見る限りできそうです、ひとまず意図通り使えるのかサンプルコードを書いてみました。

<?php
if (!apc_exists('hoge')) {
    echo 'create new token.', PHP_EOL;
    // サンプルなのでttl(有効期限)を3秒に指定
    apc_store('hoge', 'token'.date('YmdHis'), 3);
}
// token20151021144256と出力される、OK
echo apc_fetch('hoge');
sleep(5);
// token20151021144256と出力される…ってなんでやねん!!!
echo apc_fetch('hoge');




有効期限きれない!!!!!
もちろん、ちゃんと理由がありました。
APCuでは、どういう風にキャッシュしているかを知ると答えが見えます。


有効期限が切れても参照できるわけ



その答えは、キャッシュが削除されてないからです!!!

本家PHPマニュアルには

有効期間。
var は、キャッシュに ttl 秒間だけ格納されます。
ttl が経過すると、格納されている変数は (次のリクエスト時に)キャッシュから削除されます。
ttl が指定されていない(あるいは ttl が 0 の場合)は、 キャッシュから手動で削除される・あるいはキャッシュに存在できなくなる (clear, restart など)まで値が持続します。

と書かれています。

ミソは「(次のリクエスト時に)キャッシュから削除されます。」です。

次ってなんでしょうか?


2回目のapc_fetchはリクエストちゃうんかい...
実は違うんですね、リクエスト時なんでphpが再度実行された時、なんですね。

ちなみにOpchacheは、その名の通りPHPの内部的なコンパイル結果(オペコード)をメモリにキャッシュして、また同じPHPファイルを内部的にコンパイルする時はAPCのキャッシュを使っているようです。
一方、APCuは、メモリに変数を格納しておける機能なので、オペコードは関係ないようです。




では、気になるもう1つ検証を。


有効期限を伸ばしPHPを再実行することで、キャッシュから値がとれるか?




答えは取れません。

APCのキャッシュはCLIだと実行が終わるたびにキャッシュが毎回破棄されちゃうんです!そもそもcliでのAPCuはデフォルトオフ扱いになっているくらいですからね。

同士がいないか探してみたところ…

私もそう思います、はい。
グローバル変数に毛が生えた感じ?

atijusts.hatenablog.com



APCのキャッシュはプロセスごとに独立しているのでCLIで使えたところで特に意味はない。スクリプトの実行が終わるたびにキャッシュは破棄されるので性能上の恩恵はない(ハズ)。

githubでもこんなやりとりがありました(抜粋)github.com

APC's memory space is tied to the PHP runtime process, so by design php cli with always create a new apc space for the duration of the script and then destroy it after the script is over and the php cli runtime environment stops. So it can store data but initially it will always be empty.
Also, and this might be what you are having issues with, PHP CLI APC uses a separate memory space than the APC memory space that php-fpm uses. So you cannot run a PHP CLI script and access the APC data that php-fpm has and vice versa because they are executed in two different php runtime environments.
Unless you dump it out to a file like @sergeyklay's link shows, but that'd easily get out of sync and overly complicated in my opinion. I would personally use memcached only if you need a CLI script to be able to access data that a webapp is updating.
I think apc cli is really just for testing/debugging. Like making a quick script to test out how code works with an empty apc cache, populate and retrieve information tests, etc.



結論

cliのphpでAPCuのキャッシュにデータをストアすることはできますが、有効期限が切れても残ってるし、スクリプトが終了すれば破棄される制約があります!
素直にmemchachedとかredisを使うのが賢明でしょう!