Apache2.2のmod_cacheを使って,Wiki(CGI)の高速化を試みる

Apache 2.2.3を使用した.

1.基礎知識

まずキャッシュをやるなら,

Caching Guide
http://httpd.apache.org/docs/2.2/en/caching.html

を熟読すべき. 英語だからといって敬遠して俺のように斜め読みしていると,つまらないところではまる.

それで結局のところ,パラメータ付きのCGIをキャッシュするには,CGIからの出力に

  • Last-Modified
  • Expires

のヘッダをつけるべきだということがわかる.

2.キャッシュ更新タイミングの考察

要件

キャッシュの更新タイミングとは,
  • ページの更新がキャッシュに反映されてwebに公開されるまでのタイムラグ

となる.その観点で考えると,常識的にはせいぜい1日とかになってしまうが,1日だけキャッシュできて嬉しいだろうか? 毎日1回目のリクエストではCGIが実行されてしまうのである.

毎日同じページを更新するような,ページ上書き型更新の場合はそれでも良さそうだが, うちの場合は数日に1回新しいページを作り,そのページは半永久的に更新しないというページ追記型更新であり, この追記したページはそれこそ半永久的にキャッシュして,もう二度とページ生成のCGIが動かないというのが理想である.

つまり,

  • 必要なタイミング(ごく稀)にキャッシュを手動更新(CGIを実行)して,それ以外ではキャッシュの更新はかからない(CGIは実行されない)

という状態にしておきたい.

ただし,Wikiの場合は,ヘッダ,フッタ,メニューという全ページ共通的な要素があり, それらをページ自体とは無関係に更新する可能性があるため, 厳密に半永久だとは言い切れないところもある.

実現方法

Apacheがキャッシュを更新するタイミングは,
  1. 保持しているキャッシュが有効期限切れになった時 (Expires切れ)
  2. 保持しているキャッシュよりLast-modifiedが新しくなったページに対して,no-cacheリクエストを受けた時

である *1. 2.について補足すると,このLast-modifiedの条件があるため, Last-modifiedが新しくならない限りはいくらno-cacheのリクエストが来ても,古いページを送り続けてしまうのである. (もちろんmod_cacheのCacheIgnoreCacheControlの設定がデフォルトのoffであっても)

それで今回の要件を考えたとき,1.は事前に手動更新するタイミングを予測してExpiresを設定することは不可能なので無理. なので,2.を使い,ブラウザからキャッシュを更新したいページに対して

 Cache-Control: no-cache
というヘッダがついたリクエストを投げることによって手動更新を実現することを考える. (例えばIEなら,CTRL+F5押下でできる)

そして,このときに対象ページでLast-modifiedが新しくなっていることを保証するために, Last-modifiedには,リクエストを受けた時点のシステム日時を設定することとする.

...普通に考えると,

  • Last-modifiedにはそのページの更新日時を設定すべきでは?

となるのだが, Wikiでは他ページを参照するようなプラグイン(#ls系など)を使っていると, ページ自体は更新されていない(=更新日時が繰り上がっていない)のに表示内容が変わるということがよくあり, その場合でもキャッシュを更新できる必要はある,という考えで,ページの更新日時は使わない.

Expiresには,まぁ半永久的な期間を設定すれば良いのだが,とりあえず10日程度としておく. (根拠はない.様子を見てもっと延ばすかも)

3.キャッシュすべきでないコンテンツ

ここまでで,
  • no-cacheのリクエスト(例えばIEならCTRL+F5)を使えばキャッシュを更新でき,それがなければキャッシュは10日間使われる

という状態が実現できたのだが, Wikiの性質として不特定多数がページを編集することを考えると,それら不特定多数の人々に

  • ページ編集後は必ずCTRL+F5を押すこと

を徹底できるとは思えない. しかしもしこれが守られなかった場合,自分が編集しようとして見ているページが実はキャッシュが更新されていない関係で古い内容だという状況が発生し, 混乱状態になると思われる.

なので,不特定多数が編集可能なページをキャッシュするのは無理な気がしている.

カモランドでは,そのような(=凍結状態でない)ページはキャッシュ対象外として

  • Last-Modified
  • Expires

ヘッダを出力しないようにWikiのCGIで制御している. 「凍結状態」で管理者(=俺)しか編集できないページのみを,キャッシュ対象にしている.

あと,RSSも更新頻度によるがキャッシュ対象外になりうる.

4.まとめ

WikiのCGI

凍結状態のページへのリクエストに対して,
 Last-Modified: 現在日時
 Expires: 現在日時+10日後(864000秒)

のヘッダを出力するようにする.

Apache (mod_cache)

 <IfModule mod_cache.c>
 <IfModule mod_disk_cache.c>
 CacheDirLevels 2
 CacheDirLength 1
 CacheRoot "/var/cache/apache2/disk_cache/vh1"
 CacheMaxExpire 864000
 CacheEnable disk /
 CacheDisable /rss/
 CacheDisable /blog/index.rdf
 </IfModule>
 </IfModule>

5.余談

リダイレクトとの関係

Caching Guideには冒頭にちゃんと書いてあったのだが,

ページに対してRedirectなど別の設定をしても, もしそのページのキャッシュが残っていればそっちが使われてレスポンスを返されてしまう.

おかげで,

  • Redirectを設定しているのに全然効かねぇ

と長時間ハマった.

結局,キャッシュをディレクトリごと直接消しして解決したのだが,要注意...

mod_deflateとの関係

なぜだかよくわからんが,mod_cacheをmod_deflateと併用すると,異様に遅くなった. mod_cacheを使わない場合とほぼ同等の遅さである.

そのため,mod_deflateの使用をあきらめる羽目になった.

FireFoxの問題 (2007.04.30追加)

普段使わないFireFoxを使った時に気づいたのだが,FireFoxだと普通にリロードしてもno-cacheが効いてしまう.

そのため,キャッシュを更新したい時に管理者がno-cacheリクエストを行う(=低頻度)という想定なのに, 現実にはApacheキャッシュの更新が頻発する=CGIの呼び出しがたくさん発生してしまうことが判った.

つまり今回の方法だと,FireFoxユーザに対してはあまり意味がないと.

そこで仕方がないので,Wiki側でもキャッシュをするという対策を行った. → PyukiWikiにキャッシュ機能をつける


*1 別解として,キャッシュファイルを直接消してしまうという方法が考えられる.しかし,htcachecleanにはURLを指定して消せるような機能はなさそうだ


© 2024 KMIソフトウェア