「記事リスト」をPerl版ダイナミックパブリッシング化にしている場合に、キャッシュをクリアするタイミングを記事投稿時に切り替える方法

MovableTypeの標準機能の動的生成させる方法にダイナミックパブリッシングってのがありますが、phpで動いているので、結構な数のプラグインが動かない。
そこで、Perl版ダイナミックパブリッシングという、ほとんどのプラグインが問題なく動作してくれるありがたいプラグインがあります。Perl版ダイナミックパブリッシングについては、以下からインストールしてください。

【本体】
 ・Perl版ダイナミックパブリッシング(MT5以降用・その1)
 ・Perl版ダイナミックパブリッシング(MT5用・その2)
【ページ分割プラグインDivPages
 ・ページ分割プラグイン(Perl版ダイナミック・パブリッシング専用)・その1
 ・ページ分割プラグイン(Perl版ダイナミック・パブリッシング専用)・その2

MovableTypeといえば、静的生成で多量のアクセスがあってもサーバーに負荷をかけないことが最大のメリット。
しかしながら、記事数が多くなってくると、再構築の時間が多くかかったり最悪サーバーの処理限界を超えて500エラーになったりします。
そういったMTのデメリット部分を払拭してくれたのが、Perl版ダイナミックパブリッシング。
僕の場合は、特に記事リストの再構築で限界がきていたので、導入しました。Perl版ダイナミックパブリッシングとページ分割プラグイン「DivPages」を使うようになってから、リストページを並び替えて表示させたりしてコンテンツの幅が広げることができたり、再構築する必要もなくなったので管理面でも非常に助かっています。
さらにPerl版ダイナミックパブリッシングで嬉しい機能が、動的生成だけど、ちゃんと生成したファイルはキャッシュしておいて、次に同じページを読みにいった人にはキャッシュをみせてくれるようになっています。このことにより、動的生成でありながらサーバーの負荷は抑えられてページの読み込みも速いという、良いことずくめのプラグイン。

このような、素晴らしいPerl版ダイナミックパブリッシングだけど、一点だけキャッシュをクリアするタイミングが僕のサイトには合わなかったので、少し改造しました。
当たり前だけど、生成されたキャッシュをクリアするというのは重要で、いつまでも古い情報をキャッシュしていてそれを変わらず見せたままにしていたら意味がない。新しい記事を投稿したり、コメント投稿されたり、なんらかのアクションがあればキャッシュをクリアして新たに生成させなければならない。

それはそうなんだけど、Perl版ダイナミックパブリッシングでのデフォルトでのキャッシュクリアのタイミングが、「MovableTypeのシステム全体でなんらかのアクションがあった時」になっている。SQLで言えば「mt_blog」テーブルの「blog_children_modified_on」カラムの時刻を見に行っている。
ここの時刻って、管理画面にログインするだけで更新されたりする場所なので、そりゃもう頻繁にせっかく生成されたキャッシュがすべてクリアされてしまうので、キャッシュしている意味がないような状況になってしまった。

こりゃいかんっていうことで、キャッシュクリアのタイミングを、該当するカテゴリ内で記事の新規投稿や更新があった時に、そのカテゴリの記事リストだけをキャッシュをクリアするように改造しました。

■Perl版ダイナミックパブリッシングのカスタマイズ(キャッシュクリアのタイミング変更)

変えたいこと ⇒ キャッシュクリアのタイミングが通常だと、ブログ全体の更新時刻で判定しており、すぐにキャッシュがクリアされてしまうので、新規記事投稿や更新があったカテゴリリストのみをクリアするように変更する(ページ分割DivPagesプラグインによるクエリ付きURLにも対応)
さらにブラウザのスーパーリロード(Ctrl + F5)でも、キャッシュをクリアしてページを更新できるようにする。
※あくまでこの改造は「記事リスト」だけにPerl版ダイナミックパブリッシングを使っている場合です。

1. /plugins/PerlDynamic/PerlDynamic.pl を修正する

241行目あたりに、#clear cache のエリアがあるので、
そこから「if ($force || $blog->children_modified_on > $last_clear) {」をコメントアウトしておいて、ハイライト部分にある記述に追記する。

# clear cache
sub _clear_cache {
  my ($plg, $blog, $force) = @_;

  return if (!$plugin->get_config_value('dp_use_cache', 'blog:' . $blog->id));

  my $last_clear = $plugin->get_config_value('dp_last_clear', 'blog:' . $blog->id);
   # if ($force || $blog->children_modified_on > $last_clear) {
   if ($force) {
     # clear database
     eval {
       my @objs = PerlDynamic::DPCache->load({ blog_id => $blog->id });
       for my $obj (@objs) {
         $obj->remove;
       }
     };
     # clear file
     _clear_all_file_in_directory(File::Spec->catdir($blog->site_path, 'phpcache'));
     $plugin->set_config_value('dp_last_clear', epoch2ts($blog, time), 'blog:' . $blog->id);
   }
}

2. /plugins/PerlDynamic/tmpl/mt-dynamic.tmpl を修正する。
※すでにPerl版ダイナミックパブリッシングをインストールしているなら、インデックステンプレートの「mt-dynamic.php」も合わせて修正して再構築すること。

61行目あたりを探して、
「$sql = "select blog_children_modified_on from mt_blog where blog_id = " . $blog_id;」をコメントアウトし、ハイライト部分を追記する。

if ($is_use_cache) {
  // Blogの最終更新日時を取得
  // $sql = "select blog_children_modified_on from mt_blog where blog_id = " . $blog_id;
  $query_pattern = '/\?page=\d+/';
  $replace_url = preg_replace($query_pattern, "", $base_uri);
  $sql  = "SELECT entry_modified_on as blog_children_modified_on FROM `mt_entry` INNER JOIN `mt_placement` ON entry_id = placement_entry_id WHERE placement_category_id = (SELECT fileinfo_category_id FROM `mt_fileinfo` WHERE fileinfo_url = '" . $replace_url . "'";
  $sql .= "LIMIT 1) ORDER BY entry_modified_on desc LIMIT 1";
  $result = @sql_query($con, $sql) or exit(show_error_message("キャッシュの検索に失敗しました sql = $sql"));
  $data = sql_fetch_array($result);
  $children_modified_on = $data["blog_children_modified_on"];
  
  // キャッシュをチェック
  $sql = "select * from mt_dpcache where dpcache_uri = '" . $base_uri . "' and dpcache_blog_id = " . $blog_id;
  $result = @sql_query($con, $sql) or exit(show_error_message("キャッシュの検索に失敗しました sql = $sql"));
  $data = sql_fetch_array($result);
  $is_exist = sql_num_rows($result);
}

76行目あたりを探して、
「if (!$is_use_cache || !$is_exist || $data["dpcache_modified_on"] < $children_modified_on)」と「$req .= "User-Agent: " . $_SERVER['HTTP_USER_AGENT'] . "\r\n\r\n";」の行をコメントアウトして、ハイライトの行を追記する。

// ページをキャッシュしないか、ページがキャッシュにないか、Blogの最終更新時刻よりも古いなら、再構築する
// if (!$is_use_cache || !$is_exist || $data["dpcache_modified_on"] < $children_modified_on)
if (!$is_use_cache || !$is_exist || $data["dpcache_modified_on"] < $children_modified_on || $_SERVER["HTTP_CACHE_CONTROL"])
{
    // ページを再構築する
    $req = "<$MTCGIRelativeURL$>plugins/PerlDynamic/mt-perl-dynamic.<$MTGetSystemConfig name="dp_cgi_ext"$>";
    $req .= "?uri=" . urlencode($uri);
    $req .= "&blog_id=" . <$MTBlogID$>;
    foreach ($_GET as $key => $value) {
        $req .= "&$key=" . urlencode($value);
    }
    $req = "GET " . $req . " HTTP/1.0\r\n";
    $req .= "Host: <$MTGetSystemConfig name="dp_hostname"$>\r\n";
    // $req .= "User-Agent: " . $_SERVER['HTTP_USER_AGENT'] . "\r\n\r\n";
    $req .= "User-Agent: " . $_SERVER['HTTP_USER_AGENT'] . "\r\n";
    $req .= "X-DP-Remote-IP: " . $_SERVER['REMOTE_ADDR'] . "\r\n\r\n";
    $_SERVER['X_DP_REMOTE_IP'] = $_SERVER['REMOTE_ADDR'];
    $port = <$MTGetSystemConfig name="dp_hostport"$>;
    if ($port == '') {
        $port = 80;
    }
    $fp = fsockopen('<$MTCGIHost$>', $port) or exit("communication error");

以上が終わったら、再構築してみて、実際にブラウザで記事リストページにアクセスして、スーパーリロードしたら更新されるか、記事内容が更新されない限りキャッシュが残っているかどうか確認してみてください。