メモリの中に住むキャッシュ
このサービスが描くカウンター画像のうち、五分の一近くはもう何も変えません。前の世代のマウス追跡スクリプトが動くたびに画像を読み直し、そしてそれらのリクエストが何も増やさなくなって以来、続けて二回で同じ画像がバイト単位で同じように出てきます。
キャッシュの明らかな候補です。面白かったのは、どこに置くか、そして鍵を何に基づかせるかという二つの判断でした。
ディスクには置かない
これは Raspberry Pi の上で動いていて、ディスクは SD カードです。描いた画像をそこにキャッシュするのは、一日およそ 70 MB の書き込みを意味し、その見返りは実測で一日 1.6 分の計算時間でした。
割に合いません。SD カードは書き込みで死にますし、買えるものは、計算時間に縛られていない機械の上での丸め誤差です。ですからキャッシュは /dev/shm に住んでいます。tmpfs、つまり RAM です。そこでの実測:読み 0.027 ms、書き 0.036 ms、空き 1.9 GB。
再起動ですべて消えます。キャッシュにとってそれは損失ではなく、通常の姿です。
同じ Pi の上では Redis も動いています。使いませんでした。理由は技術とは無関係です。別のアプリケーションのもので、そのためにパスワードが掛かっています。一つのインスタンスを共有すれば無関係な二つのサービスが結び付き、片方の悪い一日がもう片方の悪い一日になります。
鍵が数字を含む
ふつうのキャッシュ鍵は同一性と期限であり、期限とは賭けです。この五分間に重要なことは何も変わらなかった、という賭け。カウンターでは、変わるものがまさに表示されているものです。
そこで表示される値は鍵の中に入ります。数字が変われば別の鍵になり、画像は描き直されます。賭けは慎重に張られるのではなく、消えます。数えられた訪問は少なくとも累計を一つ増やすので、数えられた訪問が古い画像を受け取ることはありえません。残る期限は、鍵に入っていない値 — 週・月・年の数字 — の上限にすぎません。
先に確かめる必要があったこと
キャッシュが安全なのは、しまうものが鍵の関数であるときだけです。それはすべてのデザインについての主張であり、仮定ではなく確認されました。
- どのデザインも
rand()、mt_rand()、shuffle()、uniqid()を使っていない - どのデザインも時計を時間単位より細かく読まない
- 旧スクリプトの追跡パラメータはツリーのどこでも読まれていない
最後の一点が、この作業全体でいちばん美しい失敗を生みました。それらのパラメータがまだ鍵の一部だったころ、リクエストごとにマウス座標がわずかに違い、したがってどの鍵も一度きりでした。キャッシュは丸一日動いて、13 件のヒットを記録しました。完璧に働き、何もしていない — 気づくのがいちばん難しい種類の壊れ方です。
そしてここで何かが失敗したら — 読めないディレクトリ、いっぱいのディスク、壊れたエントリ — 画像はふつうに描かれます。キャッシュがカウンターの出ない理由になることは、決してあってはなりません。