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

2009/08/10(月)adiary C76 Special Edition

adiary C76 Special Editionがコミックマーケット76にて配布されます。

8/15(土) 「東2ホール Q-02b」

詳細はadiaryユーザ会のC76ページをご覧下さい。

  • C76限定テーマはこちらを見てね。
  • 絵師さま多忙につき、レーベルをでっちあげるので(レーベルは)期待しないでください。同じ理由によりポストカードも用意できるか微妙。
  • 万が一、委託物で混雑していてもadiary C76 Special Editionの配布を優先するので気軽に声をおかけください。

頒布物の修正情報

http://adiary.jp/C76 を参照ください。

2009/08/09(日)PostgreSQLトランザクションとDBD::Pgの謎の挙動

PostgreSQLのトランザクションの仕様

CREATE TABLE test(x INT UNIQUE);
INSERT INTO test (x) VALUES (1);
INSERT INTO test (x) VALUES (2);

としておきます。

ここで、

=> BEGIN;
=> INSERT INTO test (x) VALUES (3);
INSERT 0 1
=> INSERT INTO test (x) VALUES (1);
ERROR:  duplicate key value violates unique constraint "test_x_key"

とするとエラーになります。PostgreSQLはトランザクション中にエラーが起こると、以後何をしてもエラーになり受け付けなくなります。ためしに続けて色々発行してみても、

=> INSERT INTO test (x) VALUES (10);
ERROR:  current transaction is aborted, commands ignored until end of transaction block
=> INSERT INTO test (x) VALUES (20);
ERROR:  current transaction is aborted, commands ignored until end of transaction block

こんな感じです。COMMITすると

=> COMMIT;
ROLLBACK

このようにROLLBACKされます。これがPostgreSQLの仕様です。

DBD::Pgの謎

  • 確認環境
    • PostgreSQL 8.3.7
    • DBD::Pg Ver1.49

DBD::Pgの場合

01: $dbh->begin_work;
02: $dbh->do('INSERT INTO test (x) VALUES (10)');
03: $dbh->do('INSERT INTO test (x) VALUES (1);');
04: $dbh->do('INSERT INTO test (x) VALUES (20);');
05: $dbh->commit;

とすると、3行目でエラーが起こり、4行目の実行でも"ERROR: current transaction is aborted, commands ignored until end of transaction block"といわれます。

これを、DBIのprepareを使用して

01: $dbh->begin_work;
02: $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(10);
03: $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(1);
04: $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(20);
05: $dbh->commit;

とすると、3行目でエラーが起っても、4行目の実行が反映されてしまいます。謎の挙動です。

謎の解析

このようにソースを改変してログを取ってみました。

open(my $fh, ">pg_log.txt");
$dbh->pg_server_trace($fh);
$dbh->begin_work;
$dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(10);
$dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(1);
$dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(20);
$dbh->pg_server_untrace();
close($fh);

このときサーバに対して次のようなコマンドが発行されていました。

=> BEGIN;
=> INSERT INTO test (x) VALUES (10);
=> INSERT INTO test (x) VALUES (1);
エラー : duplicate key value violates unique constraint "test_x_key"
=> ROLLBACK;
=> BEGIN;
=> INSERT INTO test (x) VALUES (20);
=> COMMIT;

どうりで最後のCOMMITが成功するはずです。取得した生ログも置いておきます

これを、

01: $dbh->begin_work;
02: $dbh->prepare('INSERT INTO test (x) VALUES (10)')->execute();
03: $dbh->prepare('INSERT INTO test (x) VALUES ( 1)')->execute();
04: $dbh->prepare('INSERT INTO test (x) VALUES (20)')->execute();
05: $dbh->commit;

とすると再現しません。prepare中にUNIQUE制約をチェックして、勝手にrollbackしているようですが、原因がPostgreSQL側なのかDBD::Pg側なのかは絞り込めませんでした。

追記

DBD::Pgの実装仕様みたいです。トラックバックを参考にしてください(iakioさんに感謝)。

DBD::Pgを直すとすれば、プレースホルダが破棄されてもトランザクションが終了するまでDEALLOCATEするのを待つ、くらいでしょうが。

明示的なトランザクション内でのエラーとDEALLOCATE

ROLLBACKしていいから、DEALLOCATE後に空のトランザクションを begin して、失敗させてくれればそれで十分な予感。

メモ

  • どちらの場合も $dbh->commit(); の戻り値は "1" で成功している。

2009/07/09(木)動的ページと Last-Modified の検討

adairyの基盤システムであるフレームワーク(Satsuki-system)にあちこち手を入れてるのですが*1、Last-modifiedをどう扱うかで悩んでしまいまいした。

あまり知られていないことですが、adiaryは正しいLast-modifiedヘッダを返します。ネットワーク負荷削減のためには大切なことなのですが、これを正しく返すため関連するすべてのファイルの更新日時を取得し、データベース情報にもテーブル単位で同じものを入れ管理しているためプログラム負荷(実行時間)が増えているという現実があります。

大手サイトはどうしてるのかなと思いまして少し調べてみました。

  • Last-modified を返さない
    • Yahoo、Yahooブログ
    • mixi
    • livedoorブログ
  • Last-modified を返す
    • はてなダイアリー
    • アメーバブログ(トップページのみ)

あまり返さないところが多いようです。画像ファイルとかならともかく、今時テキストファイルコンテンツはあまり気にしなくて良いみたいですね。

  • Last-modified を返すメリット
    • ネットワーク負荷が減る
    • うまくするとリクエストを受けた時点で、処理せずに出力内容を決定できる。トップページにおいて特に大きな効力がある。
  • Last-modified を返さないメリット
    • 動的ページを同一出力する条件をいちいち探索しないため、構造が単純になる。
    • 動的ページ内で柔軟な処理が可能になる。例えばカウンタ機能を実装する等。

ちなみにRFC的には

HTTP/1.1 サーバは、可能であればいつでも Last-Modified を送るべきである。

RFC2616

です。それで結構苦労して後から*2実装したのですが、Satsuki-systemを扱う上で難しくしている要因でもあるので消そうかなあ。

*1 : 仕様の汚い場所を思想を持って直してる。

*2 : と言ってもVer1.00よりずっと昔のβ時代

結局

消しました。実装がとてもすっきりしました。

2009/07/07(火)ライセンスは難しい

(現在配布されている)MySQLのライセンスはGPLv2であるので、クライアントライブラリとリンクするソフトウェアはGPLv2と互換性がなくてはいけないと思っていたのですが、FOSS ライセンス除外規定というものがあって、この中のライセンスなら除外規定の適用を受けて何ら問題なく使用出来るらしい。

しかし、なぜかこの中にはGPLv3が含まれない。でもって、世の中にはGPLv3でMySQLというWebアプリがいくつも存在する*1

もうひとつ気になっているのがAGPLv3。AGPLv3はGPLv3とリンクさせる限り自由であるので、AGPLv3のアプリに対してGPLv3のソースを書いて拡張すると、AGPLv3のAGPLv3たる意味がほとんど無力化するような気がする。

*1 : adiaryの場合はMySQL利用時はGPLv2 or laterでライセンスされたものを使ってくださいとなってますが、他も一緒なんでしょうか。

mod_perlのライセンス

そういえばmod_perlってのはApacheとリンクして動的に操作する仕組みであるので、Apache Licenseの適用を受けるんだけど、これはGPLv2とは互換性がなくて、GPLv3とApache License 2.0は互換性がある。MySQLでmod_perlなMovable TypeのGPLv2版とかどうしてるんだろう?

注意

この記事を信用してなにか失敗しても何ら責任は負いかねます。ライセンスの確認は必ず各自で行いましょう。

2009/07/01(水)adiary Ver2.09 リリース情報

Ver2.08に対し、バグ修正とメール解析ルーチンを改良したものです。

  • 7/15 リリースしました(RC5と同一)。

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

Ver2.08→Ver2.09の変更点

  • rel="canonical" に対応しました。はてブ対策
  • 【セキュリティfix】第3者レンタル時のセキュリティホールを修正しました
  • 【fix】トラストモード時も、タグ中JavaScriptがエスケープされる問題を修正しました。(Thanks to tmz
  • 【fix】2.04から実装された、メール投稿時のファイル名を日時で書き換える機能が配布物にまだ完全に含まれてなかったのできちんと収録しました。(Thanks to nasano
  • 【fix】(RC3)記法ヘルパーの見出し記法が(少なくともFxで)クリックできない問題を修正しました。
  • 【メール投稿】RFC2231形式やUTF-8のMIME等にきちんと対応しました。詳細情報。([ml:users:00343:Thanks to ひとぅ])
  • 【メール投稿】Content-DispositionとContent-Typeの2つにファイル名が設定されるとき、Content-Dispositionのファイル名を優先するようにしました。iPhone対策ですが問題がある場合はお知らせください。
  • 【メール投稿】メール投稿時のファイル名を日時で書き換える機能の設定に関わらず、同一ファイル名があるときファイル名を日時にリネームする機能を付けました。
  • 【メール投稿】(RC3)メールのパースをRFC3676に準拠しました。([ml:users:0351:Thanks to ひとぅ])

Version2.00(β含む)以降からの乗り換え

  • そのまま上書きしてください。
  • Ver2.05以前の場合、uploader.conf.cgi をサンプルから再生成してください。

なおVer2.06よりアルバムシステム関連のJavaScriptの置き場が変更になっていますので、紛らわしい場合は theme/*.js を消してから上書きしてください。

Version1.44以前(C73/2.00α含む)以前からの乗り換え

Version2.00への移行処理を先に行ってください。