まだ重たいCMSをお使いですか?
毎秒1000リクエスト を捌く超高速CMS「adiary

2007/05/08(火)adiary Version 1.31リリース情報

ダウンロードはこちらから

本リリースは、おもに Version1.30 のバグフィクスです。テストしているので大丈夫だとは思いますが、心配な方は数日様子を見てから導入してください。

Ver1.311 (2007/05/09)
カレンダーが表示されなくなる不具合を修正。カテゴリのページ送りの問題を修正。

Ver1.30→Ver1.31の変更点

  • 日付部のリンクを復活させ、日記モードならば日付リンクを、blogモードならば記事IDリンクを生成するようにしました。
  • タイトルの無い記事の扱いを変更し、タイトルのない記事では日記表示の際にタイトル欄が表示されないようにしました。*1
  • 最初の記事のフッタ(後部)のみに表示する埋め込みテキスト <@s.bodyend_1st> を作りました。(Thanks to ASANO)
  • カテゴリ検索などのQuery形式を若干変更しました。
  • メール投稿の pop_mode においてエラー原因をログに記録するようにしました。
  • Perlink が分かりにくいので、日付をリンクしないようにしました(日記帳の設定でリンクさせることもできます)。
  • 管理者ユーザーのみトラストモードに設定できるようになりました(Thanks to なゆた)。
  • 【メール投稿】popモード時、書き込み時刻を DATE ヘッダから生成するようにしました。([ml:users:55:Thanks to hitoxu])
  • 【fix】Microsoft-IIS + perl.exe 環境において、redirect+Cookieかうまく行かないIISのバグに対策しました(READMEを参照ください)。
  • 【fix】Microsoft-IIS環境において64KB以上のファイルをインポートまたはアップロードすると、CGIが応答しなくなる問題を解消しました。
  • 以下は浅野さんよりパッチ付きでバグ報告頂きました。ありがとうございます。
    • 【fix】日記データをインポート時、トラックバックURLから"-"が除去されてしまう問題を修正しました。
    • 【fix】Google AdSense や Amazon Associate 用モジュールタグの不具合を修正しました。
    • 【fix】カレンダーで「みどりの日」「海の日」「文化の日」が正しく表示されない等の問題を修正しました。
    • 【fix】「一覧(複数)表示のとき」の 「開閉(スイッチ)」の設定が反映されない問題を修正しました。
  • 【fix】任意のIDで作成した日記帳にメール投稿できない不具合を修正しました。日記帳の詳細設定→携帯設定を確認ください。([ml:users:49:Thanks to hitoxu])
  • 【fix(1.30-)】メール投稿時、書き込みパスワードが dummy に化けてしまうバグを修正しました。([ml:users:52:Thanks to すがぬま])

<@s.bodyend_1st> を追加した関係と日付をリンクオフにする機能の関係で _main, _main_onelog が変更されています。スケルトンを直接書き換えている方でそれらの機能を利用する場合は追従してください。

*1 : タイトルがあることが前提のテーマでは表示が崩れることがあります

Version1.30β以降(1.29以降)からの乗り換え

そのまま上書きしてください。

  • テーマは一切変更はないのでテーマなし版で十分です。
  • カテゴリ検索などのQueryが変更されているので任意の日記を保存するか、複数日記帳がある場合は管理者でログイン後「システム管理→付属情報の再生成」を行ってください。
  • 検索も変更されているため、日記本文内で検索を利用している方(はてな式カテゴリを利用の方)は、すべての日記を再構築してください。

Version1.21~1.22からの乗り換え

上書き後、管理者ユーザーでログインし「管理」→「システム管理」→「管理者メニュー」→「Version 1.22以前 → Version 1.30以降へのアップグレード」を行ってください。

  • popup記法や,Amazon記法の画像popupをご利用の方は、該当の日記帳で「日記帳の再構築」を行ってください。

Version1.20以前からの乗り換え

本リリースを上書き後、先にVersion1.21リリース情報の該当乗り換え処理を行ってください。

2007/05/03(木)adiary wiki?

adiaryの記法に慣れすぎて、HTML書くのが面倒になってきたのでwiki的なものを作ろうかと検討してます。

とりあえず、PukiWikiYukiWikiを軽く触ってみたんですが、なんとまーシンプルな実装で(褒め言葉)。Wikiはもっと凝った作りだと勝手に思い込んでいたのですが、基本的には ?wikiname に対応するファイル(文字列のHEX展開.txt)が置かれているだけです。1データ1ファイル対応。*1

それとフロントページやインデックス、最近の更新なんかを別ファイルとして管理してるだけで……まあたしかに考えてみればそれで済むのか。

*1 : DB対応wikiだとまた違うでしょうが、今回はお手軽に

データ構造に悩み中

9. 賢いデータ構造と間抜けなコードのほうが、その逆よりずっとまし。

伽藍とバザール

とあるとおりデータ構造に非常に悩んでおります。

2007/04/30(月)adiary Version 1.30リリース情報

ダウンロードはこちらから

一応テストもしていますしβ公開して数日、大丈夫だと思いますが、Ver1.22が長らく安定であったため、安定を最重視する方はしばらく様子を見てからにしましょう。

Ver1.22→Ver1.30の変更点

  • プライベート日記モード(強制非公開モード)を強化し、日記帳一覧などにも表示されないようにしました。
  • 画像リンク、http記法リンクに rel 属性を設定できるようになりました(日記帳の詳細設定から行ってください)。
  • マルチユーザーモード時に、blog名を表示できるようにしました。
  • はてな新テーマのために若干の修正を行いました。
  • 非公開日記のカテゴリも扱えるように仕様変更しました。
  • 多重ログインができるようになりました*1
  • 記事ID(0123などの記事番号)を変えずにインポートする機能が付きました。
  • はてなライクな見出しカテゴリに対応しました。
  • WordPress XML形式のインポートに対応しました
  • MT/JUGEM形式でエクスポート時に投稿日時を日付設定から生成するよう指定できるようになりました。(Thanks to ひとぅ)
  • サイドバーに月別記事数リストを表示出来るようになりました。日記帳の設定から行ってください。(Thanks to ひとぅ)*2
  • 【記法】uncタグができました。
  • 【仕様変更】a target=_blank といった、裸の属性値がキャンセルされる仕様を変更しました。*3
  • 【仕様変更】</p > のような余計に空白を含む閉じタグを許可するようにしました。
  • 【内部処理】utf8 から他のエンコードへの変換が3倍ぐらい速くなりました
  • 【内部処理】日記の書き込み処理を高速化しました。*4
  • 【fix】Windows環境(IIS/AN HTTPD, Apache除く)において動作しないバグを修正しました。(Thanks to ひとぅ)
  • 【fix】画像の下部に1pxの空間ができる CSS の問題を修正しました。
  • 【fix】popupがIEでうまく動作しないバグを修正しました。*5
  • 【fix】URL自動リンクが ~ に対して無効だったので修正しました。
  • 【fix】空白を含むカテゴリ名を編集できないバグを修正しました。
  • 【fix】インポート画面で、はてな形式の取り込みオプションが表示されない不具合を修正しました。*6
  • その他細々とした修正。

変更点について

ユーザー承認関係を変更した関係で、アップグレード用にファイルを差し替えると、ログイン中のセッションがすべて無効になります。ご了承ください。(ログインしなおしてください)

マルチユーザーモードのtopにblog名を表示出来るようになりました。blog名を一覧するタイプのトップページを表示するには、adiary.conf.cgi の次の箇所を修正してください(表示確認は移行処理を行ってからお願いします)。

<$v.multiuser_top_skelton = "_multiuser_top">
↓
<$v.multiuser_top_skelton = "_multiuser_top2">

他にもマルチユーザモード関連の設定項目が増えておりますので、必要ならば新しいサンプルを参照ください。

はてなライクな見出しカテゴリに対応しました。

*[test][カテゴリ]見出し

システム上カテゴリとしては認識してないのですが*7、検索への自動リンクを張りますので実用上はあまり問題ないと思います。

月別リストをサイドバーに表示する際、詳細デザインを利用している方は、

<module name="month_list">

を表示したい位置に追加の上で、日記帳の詳細設定→サイドバー設定から「月別記事数リスト」をチェックしてください。

*1 : 標準ではオフです。セキュリティの観点からあまりおすすめはしません。

*2 : この関係で _sidebar.html が一部加筆されています。スケルトンを直接カスタマイズしている方は必要に応じてマージしてください。

*3 : HTMLの仕様としてはよろしくないのですが……。

*4 : 安全重視処理のため、5回ぐらいデータベースや設定ファイルに更新(update/write)をかけていたのを、まとめて1回にしました。

*5 : popup用ブロック <div id="_popup"> のID が HTML4.0違反だったことが原因

*6 : アップデートしてもでなかったらスーパーリロードしてください。

*7 : あくまでカテゴリは記事1つに対して付くので

Version1.30β(1.29表記)からの乗り換え

そのまま上書きしてください。

Version1.21~1.22以降からの乗り換え

上書き後、管理者ユーザーでログインし「管理」→「システム管理」→「管理者メニュー」→「Version 1.22以前 → Version 1.30以降へのアップグレード」を行ってください。

  • popup記法や,Amazon記法の画像popupをご利用の方は、該当の日記帳で「日記帳の再構築」を行ってください。

Version1.20以前からの乗り換え

本リリースを上書き後、先にVersion1.21リリース情報の該当乗り換え処理を行ってください。

2007/04/30(月)Microsoft-IIS がダメダメな件(303 See Otherを無視する件)

●対象 Microsoft-IIS/5.0(Windows 2000系付属)

IE で数々のダメを露呈している MS ですが、案の定IISもダメダメでした。adiaryは、IISサーバ + perlis.dll でも動作することになっているのですが、IISサーバ + perl.exe で動かないという報告がありまして、対応外なのですが調査してみました。どうも IIS の Cookie 処理が怪しいようです。

perlis.dll だと Image::Magick を使えない*1のだそうで。試しに動作させたらメモリを食う暴走して危険でした(汗)

*1 : IISの仕様でShellのパイプが使えない

303 See Otherとは何であるか

原因を説明する前に、adiaryのログイン時の動きを確認しておきましょう。

(1)adiary/?loginlogin フォーム
(2)adiary/パスワード認証をして set-cookie
(3)adiary/?login_authcookieがきちんとブラウザに保存されているか確認
(4)adiary/ログイン後の画面

IISだと (3) の時点で「Cookieが有効になっていません」とエラーになります。

HTTPヘッダには 303 See Other というものがありまして、これはHTTP/1.1対応ならば使えます。HTTP/1.1対応かどうかというのは、HTTPクライアントとサーバが両方対応しているときに環境変数 SERVER_PROTOCOL=HTTP/1.1 が設定されます。

adiary(というより Satsuki-system)ではリクエストを redirect するとき、SERVER_PROTOCOL を参照して適切な HTTP ヘッダを返します。というのも、ログインのような POST をリダイレクトするとき、Location ヘッダを出せばいいと思っている cgi 作者もたくさんいらっしゃるようですが、事はそう単純ではありません。

Location ヘッダを同時に出せる(出力してもいい) HTTP/1.1ヘッダ には3種類あります。

ヘッダ移動後のメソッド意味
301 Moved Permanentlyそのままコンテンツが永久的に移動した
302 Foundそのままコンテンツの一時的な移動
303 See OtherGETに変更コンテンツの一時的な移動

HTTP/1.1 においては、302で redirect してしまうと、フォームのデータを再度送信されてしまい永久にログインし続ける恐れがあります。クライアントの実装がマチマチですので難しいところですが、HTTP/1.1対応を名乗るクライアントはこのように実装されているハズです。

303 See Otherを無視するIIS

論より証拠、Microsoft-IIS/5.0 の実装を確認してみましょう。HTTP/1.0クライアント。

[console~]$ telnet 192.168.1.1 80
POST /adiary-1.30/adiary.cgi HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

action=login&id=test&pass=test
--------------------------------------------------
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Mon, 30 Apr 2007 06:17:02 GMT
Set-Cookie: session=%00%02%00sid%00aJXNNh2FJGd3%00id%00test; path=/adiary-1.30/;
Content-Type: text/html; charset=EUC-JP;

Locationヘッダは出ていません。これは正しい実装です。続いて HTTP/1.1 の接続です。この場合 SERVER_PROTOCOL=HTTP/1.1 が設定されていますので、cgiは303 See Other を返します

[console~]$ telnet 192.168.1.1 80
POST /adiary-1.30/adiary.cgi HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Host: 192.168.1.1

action=login&id=test&pass=test
--------------------------------------------------
HTTP/1.1 100 Continue
Server: Microsoft-IIS/5.0
Date: Mon, 30 Apr 2007 06:18:47 GMT

HTTP/1.1 302 Object Moved
Location: http://192.168.17.104/adiary-1.30/adiary.cgi/nabe?login_auth
Server: Microsoft-IIS/5.0
Content-Type: text/html
Connection: close
Content-Length: 183
  • 303 をなぜか 302 に書き換えている。
  • Locationヘッダはきちんと出力されている。
  • Set-Cookieヘッダを出力しない(cgi側ではきちんと出力している)

解決策は?

Microsoft-IIS で cgi 動作させるときは、SERVER_PROTOCOL を HTTP/1.0 と見せかけるしかないでしょう。*2

ほんとIEといい、IISといい、まったくデタラメなソフトばかりです。AN HTTPDの方が幸せなになるんじゃないかなぁ。perlis.dll でも普通に ImageMagick 使えましたし。

*2 : やだなぁ……。だいたい perlis.dll より .exe を使う方が遅くなるんですよね。

2007/04/28(土)ページ送りのためのデータベース検索

adiaryにGoogleのようなページ送りを実装できないかというお話がメーリングリストでありました。

google_pages.jpg

実は速度面で不利なので実装してなかったんですよね。どれくらい不利なのか調べてなかったので検討してみました。

その前にクイズ

adiaryではご覧の通り「次のページ」と「前のページ」は(存在すれば)常に表示されるようになっていますが、実は全件のデータを取得することなく表示しています。どうやってるか分かりますか?

分かってしまえばかなり単純な仕組みですが、ご覧の通り十分実用的な動作をしています。

実行速度の検証

データ数が少ないときは気にするほどの差はないので、とりあえず810件、1MBほどのデータをインポートしてテストしてみました。

条件pseudo(cgi)pseudo(cache)Pg(8.1.8)MySQL(5.0.27)
今の実装(トップ)131.0 ms110.9 ms92.9 ms50.2 ms
件数取得(トップ)130.7 ms111.7 ms98.2 ms51.7 ms
今の実装(検索)135.3 ms121.6 ms171.5 ms86.1 ms
件数取得(検索)1642.6 ms397.5 ms269.4 ms88.3 ms

検索は「あ」一文字の全文検索という、多量にデータが引っかかるような検索を行いました。マシンは相変わらず C3 500MHz(笑)

擬似データベース(pseudo)

件数取得の全文検索以外は大したことないですね。擬似データベースはインデックスデータを常にメモリに展開していますので、全文検索しない限りは個別のデータファイルを参照にいきません。たかだか数件しか取得しないのならば、数件発見した時点で個別データのロードを終了するので、通常のトップ表示と比べせいぜい10ms程度しか違わないわけです。

  • 件数取得の検索は、810個の個別のデータファイルをすべてロードすることになり、多量の時間がかかっている。
  • cache と付いているのは、cgiがキャッシュされている場合です*1。この場合、一度でも読み出したファイルはメモリにキャッシュされますので810件のファイルを開く必要がないので速く済んでいます。
  • それ以外は、結局メモリに展開されている(1ファイルになっている)インデックスデータをループで回すだけなので、時間的にほとんど変わらない。

PostgreSQL (Pg)

総じて擬似データベースより少し良いパフォーマンスという感じですね。件数取得に時間がかかっていますが、PostgreSQLでは、件数取得を指示された場合、そのためだけに1回クエリを投げる実装になっています(Satsuki-systemの実装の話です)。

SELECT count(pkey) FROM $table WHERE ~

件数取得をすると時間が増えてしまうのは、単純に検索範囲が増えた結果なのか……難しいところです。

MySQL 5.0.27

MySQL (MyISAM) がこんな速いとは思いませんでした。単純 Query で約倍も速い。MySQLはありがたいことに、検索時にヒット件数を取得する書式があり、ほとんどオーバーヘッドなく該当件数を取得出来ます。

SELECT SQL_CALC_FOUND_ROWS $cols FROM $table WHERE ~

*1 : SpeedyCGI, FastCGI, mod_perl等

考察

微妙なところですねぇ~。気にしなくていいと言えば気にしなくていいような、気にすると言えば気にするような。検索のことを考えなければ、どのデータベースでも件数取得しても問題ないのですか、全文検索、しかも多くのユーザーが使っている cgi 動作を考えると、ちょっとなぁ……。

その他、擬似データベースは結構チューニングしてあるのですが、その甲斐あってか思ったより高パフォーマンスでびっくりました。1000件ぐらいなら余裕で使えますね。これ見てるadiary利用者の方々は「MySQL速いんだ、ぜひとも使おう」とか思いませんように。

adiary を cgi として動かす限り、MySQLの速さの恩恵にあずかるどころかMySQLのモジュールロード時間で恩恵どころか遅くなってしまいます(PostgreSQLも同様)。速く動作させたかったら、まず cgi 以外の方法で動作させてからにしましょう。

件数取得せずにページ送りを出す方法

クイズの答え。例えば1ぺージ5件表示だとしたら、検索するときに6件のデータを取得します。検索結果として6件のデータが得られたら次のページが存在すると分かります。前のページがあるかどうかは、検索開始位置が1件目であれば前のページがない、2より大きければ前のページがあると判別します。

単純でしょ?