Home » July 2006 » MT 3.31のMTEntryTagsの性能バグ

MT 3.31のMTEntryTagsの性能バグ

Movable Type 3.3日本語版がリリースされてから2週間ほど経ち、3.31が出てしまったがまだ移行できないでいる。なぜできないかと言えば再構築がとても遅いからなのである。Apache 2.2.2 + mod_fcgidの組み合わせですべての個別エントリーアーカイブを再構築したとき、3.2では要する時間は40秒程度。ところが3.3では3分強かかる。ありていに言って4~5倍。これでは話にならない。

少し追っかけてみて…分かった。

実は3.3で導入された「タグ」を使わなければこの問題は表面化しない。なので最初、TagSupplementals Pluginmt-keywords2tagsのせいだと思っていたのだが、まったくそうではなかった。

そうではなくて、MTEntryTagsコンテナのレンダリング時の処理に問題がある。

まず、MTEntryTagsコンテナを含むテンプレートの再構築のたびに、該当ブログの全タグ情報をデータベースから読み込む。実際にはこの全タグ情報はキャッシュされるので、CGIの場合には「再構築のリクエスト回数」だけ同じ処理が行われると考えればよい。例えば、エントリー総数1000で、EntriesPerRebuildの設定が40のとき、25回全タグをデータベースから読み込むことになる。ただし、FastCGI環境の場合には継続中のアプリケーションインスタンスが再利用されることで1~数回で済む公算がある。

次に、読み込んだ(あるいはキャッシュにある)全タグ情報に対して、各タグを利用しているエントリーの個数を数え上げている。この処理はMTEntryTagsをレンダリングするたびに行われるので、CGIでもFastCGIでもエントリー総数1000の場合には1000回この処理を行うことになる。インデックステンプレートなどでMTEntryTagsを繰り返し呼び出す場合も悲惨。

総合的に考えると、タグの総数はエントリーの総数に比例するのでO(n2)の時間がかかるはず、ということになる。

この処理はそもそも全タグ情報をキャッシュしたり、全タグのランク(MTTagRank)を取得したりする目的で行われている。例えばMTEntryTagsコンテナを利用した後、MTTagsコンテナを利用した場合などには前処理を幾分省略できる。だが、MTTagsコンテナの処理でも同様にキャッシュを行っているし、エントリーで全タグ情報を参照することは、MTEntryTagsの中でMTTagRankを使う場合を除いてはない。だから安直に考えれば処理そのものをなくせば大幅に高速化される。実際、以下の変更を加えれば3.2 + Tagwire Pluginと遜色ない速度で動作する。

--- MT-3.31-ja/lib/MT/Template/ContextHandlers.pm.bak	Fri Jul  7 02:11:02 2006
+++ MT-3.31-ja/lib/MT/Template/ContextHandlers.pm	Thu Jul 13 01:50:05 2006
@@ -559,34 +559,10 @@
     
     require MT::ObjectTag;
     require MT::Entry;
-    my $type = MT::Entry->datasource;
     my $entry = $ctx->stash('entry');
     return '' unless $entry;
     my $glue = $args->{glue} || '';
 
-    my $blog_id = $ctx->stash('blog_id');
-    my $tags = _tags_for_blog($blog_id, $type);
-    my $min = 0;
-    my $max = 0;
-    foreach my $tag (@$tags) {
-        my $count = MT::Entry->count({
-            status => MT::Entry::RELEASE()
-        }, { join => [ 'MT::ObjectTag', 'object_id', {
-                       tag_id => $tag->id,
-                       blog_id => $blog_id,
-                       object_datasource => $type
-                     } ],
-             unique => 1
-        });
-        $tag->{__entry_count} = $count;
-        $min = $count if ($count && ($count < $min)) || $min == 0;
-        $max = $count if $count && ($count > $max);
-    }
-    @$tags = grep { $_->{__entry_count} } @$tags;
-
-    local $ctx->{__stash}{tag_max_count} = $max;
-    local $ctx->{__stash}{tag_min_count} = $min;
-    
     my $iter = MT::Tag->load_iter(undef, { 'sort' => 'name',
         join => ['MT::ObjectTag', 'tag_id',
         { object_id => $entry->id, blog_id => $entry->blog_id, object_datasource => MT::Entry->datasource }, { unique => 1 } ]});

上の変更では、MTEntryTagsの中でMTTagRankが使えなくなってしまうので本当は良くない。tag_max_count, tag_min_countなどの計算をMTTagRankの最初のレンダリング時まで遅延すれば、タグの振る舞いを変えることなくMTTagRankを含まないMTEntryTagsを高速化できる。この修正はここに掲載するには少し長いので以下で参照のこと。

3.31-ja UO Patch - Ogawa Code

2006-08-06追記: 3.32のbranchにマージされたっぽい。これなら安心して使えるかな。

実はこの問題が解決しても簡単には乗り換えられないもう一つの理由がある。

それはFastCGI環境で利用した場合のResidentのサイズがかなり大きいということだ。3.2では16MB弱程度なのだが、3.3では20MBに達する。また、再構築などを行うと3.2より早くResidentが巨大化する。例えば、個別エントリーアーカイブの全再構築を2回ほど行うと50MBにも達する。以前のバージョンから常駐環境でリクエストごとに発生するメモリリークは大なり小なりあった問題だが、3.3ではアプリケーション自体の巨大化も相まってFastCGI化を断念せざるを得ない水準になっているというのが正直な感想だ。

このエントリーのトラックバックURL: http://as-is.net/mt/mt-tb.cgi/415

Links referred to this entry

Comments (11)

  1. 3.3を使ったら再構築でへきえきしてしまっていたところで、この記事を発見しました。
    困ったときにたよりになる、Ogawa::Memoranda!ありがとうございます。

    ところで、このパッチはcgiにして実行して↓がでれば成功だと思うのですが、
    Now patching... patching file lib/MT/Template/ContextHandlers.pm Successfully patched.

    再構築の体感速度があまり変わらないようなのです。
    私は3.2をとばしているので、3.2がどれくらいの速さだったのかわかりませんが、
    10件ずつにしてもかなり遅いです。デフォルトの40件ずつですと、すぐにエラー表示になります。
    lolipopのmySQLが遅いのかしら?
    ユーザーにやさしくと思って静的に再構築しているのですが、これではこっちがまいってしまいます(涙)

  2. ご無沙汰しています。

    junkoさんのページではタグを使っていないようなので、もしそうであればパッチを当てても効果はありません。最近のLolipopはかなり重いようなのでテンプレートをスリム化したりしないと難しいかもしれませんね。

  3. こんにちわ^^

    わたしのブログでも、MT3.3を入れてさっそくOgawa::Memorandaさんのmt-keywords2tagsでタグを移行して再構築してみたのですが、やたら重たく、挙句の果てにエラーしてしまったので、当面標準のタグは使わないようにしていました。

    今回のハックを教えていただいたので週末にでも試してみようと思います。
    でも、tagwire+mt-xsearchがMT3.3でも使えているので、このままでもいいかな…とも思っています。今のところ不都合なこともなさそうなので…。

  4. あ、なるほど。Tagwireを3.3で使うことはできますね。

    Tagwireのテンプレートタグ名を3.3互換に拡張すると案外使いでがあるかもしれないなと思いました。

  5. 初めまして。

    MT3.3に移行してから、嬉しさのあまり過去記事にわたってタグを付けまくったら、
    再構築にかかる時間がハンパじゃなく増えてしまい、辟易していたトコロでした。
    なのでパッチの方、喜んで当てさせていただきました。ありがとうございます。

    現在再構築中ですが…微妙に速度が上がったような気が!?

    あと、MySQLよりSQLiteの方が若干速いというウワサを聞いて、試そうかと思ったんですが…
    …タグは「書き出し」してくれないようで、未だに試せていません。

    #ベータレポートとしてSixApartにメールしたら「ごめーん、いつか実装するよー(意訳)」
    #と返事が来ました… orz

    MT-db-convertは3.3に対応していないようですが、される予定はあるんでしょうか?

  6. mt-db-convertは先ほど3.3に対応しました。あまり動作確認していないのでat your own riskでどうぞ。

  7. 早速mt-db-convertをMT3.3で試してみました!

    コンバートは正しく終了したのですが、
    再構築しようとしたら、数分何も変化がなかった後に
    タイムアウトしてしまいました orz

    mt-db-convertの正当性すら検証出来なかったのですが、
    まあこういう例があったというコトで…。

    #ちなみに、試したのはリンク先のサイトです。
    #必要ならば、色々なデータもお出し出来ます。

    どうもありがとうございました。

  8. 私もSQLiteで試した限り、管理画面などは正常に動作するのですが、再構築時にロードが1に張り付いてしまってタイムアウトするという現象を確認しています。

    mt-db-convertの問題なのかMT 3.31自体の問題なのか判断がつきかねます。というのもMT 3.2で使っていたSQLiteデータベースを3.3にアップグレードした場合にも同じ現象が見られるので。

  9. 私も再構築で問題が発生しています。
    ページのあちこちで文字サイズがおかしくなっています。
    h3だったり、h2だったり。
    変!

  10. 日本語でおk

  11. 記事の方参考にさせてもらいました。
    これからTagwireを使ってタグクラウドの方も設置しようかと思っているので、またトラックバックいただきに来るかもしれないです。

    もう少し標準装備できちんと動いてくれると・・・と思ってしまいました。(苦笑)

Post a comment

Remember me?