2015年4月26日日曜日

キャッシュをフル活用

久しぶりの投稿になってしまいました。
キャッシュと言っても、お金の話ではありません。

hitoisでは、レスポンス速度(サーバーにアクセスして、結果が返ってくるまでの速度)が遅いです。いや、遅かったのです。最初、DBアクセスが遅いと思っていたのですが、計測してみると、各ページのテンプレートからのレンダリングが遅い。これには驚きました。

DBアクセスについてはDBキャッシュというものを使えます。hitoisで使っているGoogle App Engineは、2年ほど前にndbという新しいDBAPIが提供され、これを利用することでDBアクセスが早くなりました。これは、DBに同じクエリを投げ、同じ結果が得られる場合には、DBにアクセスせずに、キャッシュを使うことで、速度改善されるというものです。hitoisですぐに採用しましたね。

しかし、レンダリングが遅いので、DBをいくら早くしても対して差がありません。そこで考えたのが、サーバーキャッシュ。Google App Engineに元からある機能ではありませんが、自作しました。これは、レンダリングが終わって出力されるHTMLを丸ごとキャッシュするというもので、同じURLにアクセスされた場合、DBの更新日時と、キャッシュの保存日時を比較して、キャッシュのほうが新しい場合は、キャッシュを送り返す、つまりレンダリングしないというものです。

これは効果てきめんでしたね。相当早くなりました。
でも、DBから読み出す量は多いので、従量課金のGoogle App Engineでは少し辛い。そこで次に取り組んだのがブラウザキャッシュです。しかし、ブラウザキャッシュは動的サイトには向いていません。ブラウザから、保持しているキャッシュの最終保存日時を送信してもらい、その日時と、サーバーが持っているコンテンツの最終保存日時を比較して、サーバーの方が新しければ200 OKで、コンテンツを返すのですが、キャッシュの最終日時がサーバーの最終保存日時より新しい場合は、304 Not Modifiedを返して、ブラウザのキャッシュを表示してもらいます。

これを可能にするには、ブラウザがコンテンツにアクセスした際、レスポンスヘッダに「Cache-Control」というものを付け、値に「must-revalidate」を指定します。これをすることにより、ブラウザはキャッシュを行い、自分のキャッシュが新しいかどうかをサーバーに対して「If-Modified-Since」というキャッシュの保存日時を送って、サーバーの方が新しければ200 OKを、古ければ304 Not Modifiedを受け取るようになります。

これにより、特に早くなったのが画像ですね。HTMLは頻繁に更新されるので、あまりキャッシュは有効ではなかったのですが、画像はそんなに更新されることはないので、画像の表示速度は格段に上がりました。

しかし、落とし穴がありました。Firefoxは、「Cache-Control」の値を「must-revalidate」にしても、「If-Modified-Since」を送らないことがあるのです。他のブラウザではそんなことはありません。Firefoxだけがおかしいのです。やむを得ず、HTMLがキャッシュを採用された場合、Ajaxでサーバに本当に最新かどうかを問合せて、古ければJavaScriptでリロードするという仕組みを採用しました。Firefoxのためだけにです。

なにはともあれ、今のところは、DBキャッシュ、サーバーキャッシュ、ブラウザキャッシュ共に上手く行っています。