PyukiWiki 0.1.5にページキャッシュ機能をつけて高速化を図る 1.はじめにこのカモランドはPyukiWikiで稼働しているのだが,Wikiと言ってもページを作るのは作者なのでほとんどのページは内容が固定している.しかしページを表示するために毎回CGIが動作してページ内容を生成しているというのが,何とももったいない. これを何とかしようということで,Apacheのmod_cacheを試したりしたが,ブラウザから強制リロードをやられると,対処できない.*1 そこで,PyukiWiki側にもキャッシュ機能を実装することにした. 前提は以下の通り.
2.キャッシュ機能の仕様
説明図
更新A(Apacheキャッシュの更新)の発生条件以下のどちらか
更新B(PyukiWikiキャッシュの更新)の発生条件以下のどちらか
なおApacheのmod_cacheを使っていない場合は,上述の(1)や「更新A」は無視してください.
3.プログラムの変更内容PyukiWiki(0.1.5)のwiki.cgiについて,以下のようにdo_read()関数を改造する.変更前 sub do_read { &skinex($::form{mypage}, &text_to_html($::database{$::form{mypage}}), 1); } 変更後 sub do_read { my ($page) = $::form{mypage}; my($dbm_lastmod) = (stat($::data_dir . "/" . &dbmname($page) . ".txt"))[9]; my($cachefile) = $::cache_dir . "/" . &dbmname($page) . ".cac"; my($cache_lastmod) = (stat($cachefile))[9]; if ($dbm_lastmod > $cache_lastmod || $cache_lastmod == 0) { # キャッシュファイルを更新する open(CACHE, ">$cachefile") or die "Open failed: $!\n"; flock(CACHE, 2) or die "Exclusive lock failed: $!\n"; my($tempfile) = $::cache_dir . "/" . &dbmname($page) . ".tmp"; open (STDOUT, "|tee $tempfile") or die "Tee failed: $!\n"; &skinex($::form{mypage}, &text_to_html($::database{$::form{mypage}}), 1); close(STDOUT) or die "Close failed: $!\n"; # 一時ファイルからキャッシュファイルに転送する open(CACHE_TEMP, "$tempfile") or die "Open failed: $!\n"; while (<CACHE_TEMP>) { print CACHE $_; } close(CACHE); unlink($tempfile) or die "Unlink failed: $!\n"; } else { # キャッシュファイルの内容をSTDOUTに転送する open(CACHE, "$cachefile") or die "Open failed: $!\n"; flock(CACHE, 1) or die "Share lock failed: $!\n"; while (<CACHE>) { print $_; } close(CACHE); } } この状態でアクセスすれば,$::cache_dir 配下にどんどんキャッシュファイルが作られてゆくでしょう.
mod_cacheと併用する場合について (2007.06.16 追記)なお,mod_cacheでWiki(CGI)の高速化で行っているように,スキンなどで
のヘッダを出力することで,Apacheのキャッシュ(mod_cache)を有効にしている場合は,上記の内容では足りない. 上記の内容だと,初回のページ作成時に付与されたヘッダ(Last-Modified,Expires)をそのまま返してしまうため,それ以降Apacheのキャッシュが効かなくなってしまう.(常に有効期限切れの扱いになる) そのため,これらのヘッダについては,PyukiWikiのキャッシュファイルの内容そのままでなく,実行時に差し替えたものを返してやる必要があります. カモランドでは,以下のように差し替え処理を入れています.上記のsub do_read修正の,else以下の箇所です.(なお,これを使うためには,事前にPyukiWikiのdate関数を修正しておく必要があります.) } else { # キャッシュファイルの内容をSTDOUTに転送する my($jst_diff) = ((localtime(time))[2] + (localtime(time))[3] * 24) - ((gmtime(time))[2] + (gmtime(time))[3] * 24); $jst_diff *= 3600; my($expire_period) = 86400 * 10; # 全ページ有効期限10日にしている my($http_lastmod) = &date("D, d M Y H:i:s", time - $jst_diff); my($http_expires) = &date("D, d M Y H:i:s", time - $jst_diff + $expire_period); my($in_httpheader) = 1; open(CACHE, "$cachefile") or die "Open failed: $!\n"; flock(CACHE, 1) or die "Share lock failed: $!\n"; while (<CACHE>) { if (/^$/) { $in_httpheader = 0; } if ($in_httpheader && /^Last-Modified:/) { print "Last-Modified: $http_lastmod GMT\n"; } elsif ($in_httpheader && /^Expires:/) { print "Expires: $http_expires GMT\n"; } else { print $_; } } close(CACHE); }
4.キャッシュの強制更新について更新Bの発生条件で述べたように,PyukiWikiのキャッシュは対象ページが更新されて dbmファイルのタイムスタンプが新しくなったタイミングで更新される.しかしWikiでは,他のページが更新されただけでも自ページが影響を受けて表示内容が変わることがよくある. (例えば,ls系プラグインを使っている場合とか) そういうときにキャッシュを更新する方法は,現在のところ
という,いまいちなものしかありません. 今後の課題だが,「キャッシュ管理画面」というようなのがあって, そこで指定したページのキャッシュを更新できるというのが理想だ. →管理画面を作成しました. PyukiWikiにキャッシュ機能をつける#2 |