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

2007/09/26(水)PerlInterpMax の示すもの

daily dayflower などの情報をみて worker MPM な Apache を利用しても、同時に利用可能な Perlインタプリタ(mod_perlプロセス) は PerlInterpMax に制限されるように思っていました。デフォルトではこの値は 5 であり、一見少なく感じられます。

サーバDoS事件

ご存じのとおり本 blog.abk.nu サーバは、以前まで「データベースに接続できないエラー」を吐いてブログが表示されないことが多々発生していました*1。以前の設定値は次のとおりです。

mod_perl : PerlInterpMax 5
PostgreSQL : max_connections = 15

これでも、PostgreSQLへのコネクションが足りなくなりエラーとなっていました。PostgreSQLへのコネクションを保持するデーモンは adiary の他にありません。一体何が起こっていたのでしょうか。

*1 : それどころか、少し前にシステムリソースを食いつぶしサーバ機能を停止させる事件まで発生してました。

PerlInterpMax の示すもの

ここで実験です。worker MPM(スレッド動作)で動く Apache で"PerlInterpMax 3" に設定し、TCPデーモンとTCP接続をするだけのモジュールを用意して、PostgreSQL への接続と同様にコネクションを(切断することなく)プールさせてみました。この状態で

~$ ab -c 10 -n 100 http://blog.yyy.xx/

として、接続負荷をかけてみます。このときのTCPデーモンの表示は次のようになりました。

[05] Connection from 127.0.0.1
[06] Connection from 127.0.0.1
[07] Connection from 127.0.0.1
[08] Connection from 127.0.0.1
[09] Connection from 127.0.0.1
[10] Connection from 127.0.0.1
[11] Connection from 127.0.0.1
[12] Connection from 127.0.0.1

8本の同時接続です。PerlInterpMax 3 であるのにです。不思議です。ここでもう一度よく PerlInterpMax の説明を読んでみます。

PerlInterpreter を各 httpd スレッド毎に一対一対応させる代わりに,mod_perl では設定自在なインタプリタプールを管理しています。この方式により,必要最小限な数のインタプリタを用意することでメモリ使用量を押さえることができます。

daily dayflower より

どうやらスレッドに対する設定項目であることが読みとれます。そもそもよく考えてみれば、プロセス間でPerlインタプリタを共有することなんて不可能です。この仮説を検証するため、Apache のプロセス数を2つに制限してみました。

<IfModule mpm_worker_module>
    StartServers          2
    ServerLimit           2
</IfModule>

さてもう一度実験です。

[05] Connection from 127.0.0.1
[06] Connection from 127.0.0.1
[07] Connection from 127.0.0.1
[08] Connection from 127.0.0.1
[09] Connection from 127.0.0.1
[10] Connection from 127.0.0.1

今度はきちんと6本になりました。PerlInterpMax(3) * ServerLimit(2) = 6 ですから、計算に合っています。

PerlInterpMax の示すもの

mod_perl はスレッド動作時に 1プロセスごとに Perlインタプリタ をプールして保管します。1プロセスごとにその保管数まで mod_perl スクリプトを実行できます。プールされる実体は Perl 5.6 以降の ithreads そのものです。*2

この1プロセスごとのスレッドプール最大数を決めるのが PerlInterpMax です。worker MPM におけるプロセス数は ServerLimit で制限されます。つまり最大 PerlInterpMax × ServerLimit 個のmod_perl環境が同時に実行/保存されることになります。

それぞれデフォルト値では5、16ですから、80になります。この状況でデータベースへの接続をプールすると、あっと言う間にサーバ資源を食いつぶすことになるので注意が必要です。

注意

PerlInterpMax を小さくするときは、同時に PerlInterpStart も小さくします。PerlInterpStart より小さな値を PerlInterpMax に設定しても無効です

*2 : ですから、threads::shared によりスレッド間(同一プロセス)に限って変数を共有することもできます。