« November 2009 | Main | January 2010 »

December 2009

December 15, 2009

[ANN] CGI::Application::Emulate::PSGI

From: Kazuho Oku
To: cgiapp-ml

Hi,

I have just uploaded CGI::Application::Emulate::PSGI onto CPAN.

It's a yet-another adapter for running CGI::Application on top of PSGI. The differences from the older one: CGI::App::PSGI is that CGI::App::Emulate::PSGI is more friendly to existing code in two aspects. From the POD,

uses CGI.pm instead of CGI::PSGI
CGI::Application::PSGI (that uses CGI::PSGI) does not support programs calling CGI.pm in func-style (like CGI::virtual_host()). CGI::Application::Emulate::PSGI sets up environment variables so that codes using CGI.pm will work.
compatible with CGI::Application::Dispatch
The interface of CGI::Application::Emulate::PSGI is different from CGI::Application::PSGI, and is compatible with CGI::Application::Dispatch.

Thanks to MARKSTOS and miyagawa for answering my question (https://rt.cpan.org/Public/Bug/Display.html?id=52756), I agree that the ideal way would be to run CGI::App::Dispatch would be to make it somehow run on CGI::Application::PSGI (since it would be cleaner in the fact that it doesn't modify %ENV for each HTTP request), CGI::App::Emulate::PSGI would be a good solution IMO for running existing CGI::App-based applications on top of PSGI.

December 14, 2009

Comparing InnoDB performance on HDD, SSD, in-memory

The chart shows benchmark results taken using sysbench.  Rough understanding would be that (for this scenario) the performance ratio is HDD:SSD:in-memory = 1:10:50.

transactions/sec. read/write reqs./sec.
buffer_pool=8M, HDD 19.93 378.59
buffer_pool=8M, SSD (Intel X25-M) 207.70 3946.29
buffer_pool=2048M, HDD 998.82 18977.51

Details:

The benchmark was taken using MySQL 5.1.41 using innodb_plugin running on linux 2.6.31/x86_64 (Ubuntu 9.10 server).  Options passed to sysbench were: --test=oltp --db-driver=mysql --mysql-table-engine=innodb --oltp-table-size=1000000 --mysql-user=root --mysql-db=test --oltp-table-name=test_t --num-threads=20 .  My.cnf was set as follows.

max_allowed_packet=16777216
query_cache_size=0
default-storage-engine=INNODB
innodb_buffer_pool_size=2048M
innodb_read_io_threads=4           # set to 10 for X25-M
innodb_log_file_size=64M
innodb_flush_method=O_DIRECT
innodb_file_per_table
innodb_flush_log_at_trx_commit=2   # sufficient for our task
key_buffer_size=64M
myisam_sort_buffer_size=32M
slow-query-log
long_query_time=1

December 09, 2009

Hot-deploying Plack applications using Server::Starter

It's time to eat my own dog food.

During YAPC::Asia 2009, I wrote a tiny module called Server::Starter, a superdaemon for hot-deploying TCP servers (see Writing Hot-deployable servers (introduction of Server::Starter) for the benefits of using the module).  On the other hand, I have been involved in developing the http servers in Plack since then.

And time has come.  Having decided to use the two in my new web application server, I wrote a module to glue them together, Kazuho Oku / Plack-Server-Standalone-Prefork-Server-Starter - search.cpan.org.

Although the name is pretty long, it is easy to use :-p

// start server
# start_server --port=80 -- plackup -s Standalone::Prefork::Server::Starter myapp.psgi

// or use setuidgid to drop privileges
# start_server --port=80 -- setuidgid www plackup -s Standalone::Prefork::Server::Starter myapp.psgi

// graceful update
# killall -HUP start_server

Or use svc -h if you are going to manage Server::Starter using daemontools.

December 03, 2009

MySQLをロックしてほげほげするツール「mysqllock」を書いた

 MySQLを使っていると書込みロックをかけてバックアップを取る、ってのは一般的だと思います。実際、標準添付の mysqlhotcopy や Xtrabackup もそういうことをやっています。

 しかし、これらはいずれもロックの管理とバックアップ処理が密結合になっている(ですよね?)のが玉に瑕。

 特にボリュームレベルのスナップショット機能を使ってバックアップを取る場合、スナップショットを取るためのコマンドは環境(LVM とか XenServer とか VMware ESXi とか...)によって異なるので、ロック管理とバックアップコマンドは疎結合にしておきたい。と思ったので、書くことにしました。というか、疎結合なのを探すより書いたほうが早かった。コードはこちら↓。

kazuho's blockdiff at master - GitHub

 使い方は、以下のとおり。setlock 風のインターフェイスなので、慣れている人には簡単だと思います。

% mysqllock --help
Usage: ./mysqllock [options] cmd
Options: --user=db_user    username (default: current user)
         --password=pass   password (default: none)
         --host=db_host    db host (default: 127.0.0.1)
         --port=db_port    db port (default: 3306)
         --db=db_name      database name (default: "mysql")
         --before=stmt     statement to execute before running the command
                           (default: "FLUSH TABLES WITH READ LOCK")
         --after=stmt      statement to execute after running the command
                           (default: "UNLOCK TABLES")

 自作のバックアップツール(Kazuho@Cybozu Labs: リモートからXenのDomUとかLVMやファイルを差分バックアップするスクリプトを書いた)も、1行パッチをあてるだけで、テーブルロックをした状態でスナップショットを取ってから、バックアップを取れるようになりました。こんな感じで。

% SNAPSHOT_SIZE=5G BLOCKSIZE=65536 LVCREATE_PREFIX='bin/mysqllock --host=mysqld.local --user=root --password=XXXXXXXX'  bin/blockdiff_backup backup-name bin/ssh_lvm_dump -z root@mysqld.local /dev/XXXXXXXX/XXXXXXXX
creating snapshot...
issuing lock statement: FLUSH TABLES WITH READ LOCK
  Logical volume "lvm_dump" created
issuing unlock statement: UNLOCK TABLES
running: bin/ssh_blockdiff_dump -z "root@mysqld.local" "/dev/XXXXXXXX/lvm_dump"...
removing snapshot /dev/XXXXXXXX/lvm_dump...
  Logical volume "lvm_dump" successfully removed
backup completed successfully

 結論:djb++

参考: Kazuho@Cybozu Labs: setlock を使って cron をぶんまわす方法

December 01, 2009

高度に進化した分散データストアは RDBMS と見分けがつかない? (shibuya.pm #12 スライド)

 昨日開催された shibuya.pm #12 - NoSQL特集で使用したスライドを slideshare にアップロードしました。

 開発しているシャーディングミドルウェアである Incline と Pacific については YAPC::Asia 2009 を始めいろいろな所で話をする機会をいただいてきたので、今回は、なぜ RDBMS ベースのアプローチを採用したのかという背景を中心に説明させていただきました。概念的な話が多くて分かりにくかったと思います(すみません)が、細かな点についてはパフォーマンスとスケーラビリティのためのデータベースアーキテクチャ (BPStudy#25発表資料)を参照いただければと思います。

 また、中で出てきた「実体化ビュー」については、Materialized view - Wikipedia, the free encyclopediaが良くまとまっているかと思います。Incline は一言でいうと、RDBで構成されるshard群の上で read-only かつ eventually consistent な materialized view を実現するためのツールです。

TCP通信ではデータの送信をまとめて行うべき、もうひとつの理由(& サーバのベンチマーク手法の話)

 TCP通信をするプログラムを書く際に「データの送信はまとめて1回で」行うべき、というのは鉄則と言っていい、と思います。その理由としては、パケット数を最小限に抑えることでオーバーヘッドを少なくするためだと一般に説明されますが、自分はもうひとつポイントがあると考えています。次のグラフを見てください。

Write_size_and_speed

 グラフは、一定量のデータを転送するのにかかる時間と使用するブロックサイズ(1回のwrite(2)で書き込むサイズ)の関係を表したものです注1

 ホスト間のTCP通信を行っている場合は、TCPのバッファが有効に機能するので、ブロックサイズ(=パケット数の逆数)による速度の変化は、ほぼありません。一方、同一ホスト上で通信を行うと、ブロックサイズと反比例して所要時間が反比例の関係にあることがわかります。

 原因は、同一ホスト上の通信では、送信プロセスがwrite(2)を呼ぶたびにコンテクストスイッチが発生し、受信プロセスが起き上がってread(2)でデータを受信するところにあります。そのオーバーヘッドが大きいため、ブロックサイズを小さくするのに反比例して速度が遅くなるのです。この傾向は、送受信プロセスが交互に起動するユニプロセッサのシステムだけでなく、別個のCPUで実行されるマルチコアの環境でも同様です。

 以上が、TCP通信をするプログラムではデータの送信はまとめて1回で行うべき、もうひとつの理由です。アプリケーションプログラマの視点から見ると、ホスト間のTCP通信はスループット重視だが、同一ホスト間の通信はレイテンシ重視だ、と理解しておけばいいのかもしれません。少なくとも、高速なサーバプログラムを開発する際には、ローカルとリモート両方でのベンチマークをとった上でチューニングを行うべきなのは確かだと思います。

 自分が昨日の Shibuya.pm #12 で「サーバプログラムはリモートホストからベンチマークすべき注2」と主張したのは、以上に基づき、不要なコンテクストスイッチを避けるべき、という考えからです。今、手元にいいデータがないのですが、サーバとベンチマークプログラムの両者を同一ホスト上で動かした場合のみ、コンテクストスイッチが頻発しパフォーマンスが低下するケースがあったと記憶しています注3

 サーバの作りは製品によって異なりますから、最大スループットを比較するようなベンチマークテストを行う場合は、リモートホストから測定するのが公平なのではないでしょうか注4

注1: 確か linux 2.6.18 で測定。TCP_NODELAY はセットしています
注2: 「実際の利用形態がリモートホストからのアクセスになるのであれば」とすべきかも
注3: 例えばマルチスレッドなサーバとマルチスレッドなクライアントだと速度が落ちると思います
注4: 最大スループットを必要とするような大規模の運用では、通常リモートホストからサーバを利用すると考えられるため