2011年11月28日月曜日

PostgreSQL 死活監視のタイムアウト

お疲れぎみです、サイオス 那賀です。

スクリプト等から PostgreSQL のプロセスが正しく動作しているかどうかを監視するための方法としては、pg_ctl コマンドを使ってのチェックと、実際にポートにアクセスしてのチェックがあると思います。

まずはプロセスの確認です。pg_ctl コマンドの "status" サブコマンドで、ローカルで動作している PostgreSQL のプロセスの状態を取ることができます。

$ /usr/local/pgsql/bin/pg_ctl -D ~/pgdata-main/ status
pg_ctl: postmaster is running (PID: 10532)
Command line was:
/usr/lib/postgresql/8.4/bin/postgres "-D" "/home/knaka/pgdata-main"

"-D" オプションで指定したデータディレクトリ下には "postmaster.pid" ファイルがあり、内容は、外部からのコネクションを受け付けている Postmaster プロセスのプロセス番号 (PID) です。この PID に対応するプロセスが動作しているどうかの結果を返します。

このコマンドがブロックしてしまうことはないのでしょうか? 少しソースも見てみましょう。Postmaster の死活を見ているのは、下記の部分です。src/bin/pg_ctl/pg_ctl.c です。

static bool
postmaster_is_alive(pid_t pid)
{
  ...
  if (kill(pid, 0) == 0)
    return true;
  ...
}

kill(2) にシグナル番号 0 を投げるというのはどういう意味でしたっけ? man には以下のようにあります。

If sig is 0, then no signal is sent, but error checking is still per-formed; this can be used to check for the existence of a process ID orprocess group ID.

単にプロセスが存在しているかどうかしか見ていませんが、これがブロックすることはなさそうなので、DB の状態に拠らず安心して呼べますね。しかしこれでは、プロセスはあるけれど実際にはサービスが行われていなかった場合を検出できません。

そこで、ネットワークのポート (TCP/IP なりドメインソケットなりの) に接続して死活監視を行います (パスワードを訊かれないよう、パスワードファイルなりサービスファイルなりは事前に設定しておいてください)。

$ psql -h localhost -p 5433 -U admin template1 -l
                          List of databases
   Name    | Owner | Encoding | Collation | Ctype | Access privileges
-----------+-------+----------+-----------+-------+-------------------
 eucdb     | admin | EUC_JP   | C         | C     |
 main      | admin | UTF8     | C         | C     |
 postgres  | knaka | UTF8     | C         | C     |
 template0 | knaka | UTF8     | C         | C     | =c/knaka
(中略)
$ echo $?
0 

# なおここで、"postgres" データベースに接続してチェックするのはやめましょう。世の中には "postgres" データベースを持たない PostgreSQL データベースがあります。"template1" が無いことはありません。

しかしながら、もしポートを開いたまま死んでいる PostgreSQL へ不用意に接続して状態を確認してしまうと、いつまでも待ってしまいます。netcat コマンドで listen ポートを開いて試してみましょう。

$ nc -l 15432
(待ち...)

別のコンソールから。

$ psql -h localhost -p 15432 -U postgres postgres
(待ち...)

はい、返ってきません。もし一定時間ごとに死活監視のコマンドを発行するようにしていたら大変です。プロセスは増え続け、いずれリソースを食い尽くします。

そんな時、たとえば SSH でリモートのコマンドを叩くような状態監視であれば、タイムアウトのパラメータを渡すことで対処します。

$ nc -l 10022

$ ssh -p 10022 localhost
これだと戻りませんが。
$ ssh -o "ConnectTimeout=5" -p 10022 localhost
(5 秒経過…)
Connection timed out during banner exchange
$ echo $?
255
$ 

"ConnectionTimeout" パラメータを渡すことで、ちゃんとエラーを返すようになりました。

一方、psql コマンドのドキュメントにはそういうオプションが見当たりません。しかし psql コマンドは、実際のデータベースへの接続には libpq ライブラリを用いており、タイムアウトの設定はそちらにあります。libpq が理解する環境変数を見てみますと、"PGCONNECT_TIMEOUT=~" というのがありますね。試してみましょう。

$ PGCONNECT_TIMEOUT=5 psql -h localhost -p 15432 -U postgres postgres
(5 秒経過…)
psql: timeout expired
$ echo $?
2

めでたくタイムアウトし、エラーのリターンコードを返すようになりました。

では

0 件のコメント:

コメントを投稿