25.4. 動的追跡

PostgreSQLは、データベースサーバの動的追跡をサポートする機能を提供します。 これにより、外部ユーティリティをコードの特定のポイントで呼び出すことができ、これにより追跡を行うことができるようになります。 現時点では、コードに関する精通度を多く必要としますので、この機能は主にデータベース開発者向けを意図しています。

多くの追跡ポイント(よくプローブとよばれます)はソースコード内部に存在します。 デフォルトでは、これらのプローブは無効です。 ユーザは明示的にconfigureスクリプトで、PostgreSQLでプローブを有効にするように設定しなければなりません。

現在、Solaris ExpressおよびSolaris 10+でのみ利用可能なDTraceユーティリティのみがサポートされています。 DTraceは将来FreeBSDやMac OS Xで利用できるようになることが予定されています。 他の動的追跡ユーティリティのサポートは、src/include/pg_trace.h内のPG_TRACEマクロ定義を変更することで、理論上は可能です。

25.4.1. 動的追跡のためのコンパイル

デフォルトでは、トレースポイントは無効です。 そのため、PostgreSQLでプローブが利用できるようにするためにconfigureスクリプトで明示的に設定しなければなりません。 DTraceサポートを含めるには、configureに--enable-dtraceを指定します。 詳細は項14.5を参照してください。

25.4.2. 組み込み済みの追跡ポイント

標準的な数個の追跡ポイントがソースコード内で提供されています。 (当然、特定の問題に必要なポイントをもっと追加することは可能です。) これらを表25-3に示します。

表 25-3. 組み込み済みの追跡ポイント

名前パラメータ概要
transaction__start(int transactionId)新しいトランザクションの開始
transaction__commit(int transactionId)トランザクションの正常終了
transaction__abort(int transactionId)トランザクションの異常終了
lwlock__acquire(int lockid, int mode)軽量ロックの獲得
lwlock__release(int lockid, int mode)軽量ロックの解放
lwlock__startwait(int lockid, int mode)軽量ロックがすぐに利用できず、バックエンドが利用できるようになるまでロックを待機
lwlock__endwait(int lockid, int mode)バックエンドは軽量ロックの待機を解放
lwlock__condacquire(int lockid, int mode)呼び出し元が待機しないことを指定した時に、軽量ロックの獲得に成功
lwlock__condacquire__fail(int lockid, int mode)呼び出し元が待機しないことを指定した時に、軽量ロックの獲得に失敗
lock__startwait(int locktag_field2, int lockmode)ロックが利用できないため、通常のロック(lmgrロック)要求が待機を開始
lock__endwait(int locktag_field2, int lockmode)通常のロック(lmgrロック)要求が待機を終了(つまりロックを獲得した)

25.4.3. 追跡ポイントの利用

以下の例では、システムにおけるトランザクション数を解析するDTraceスクリプトを示します。 性能試験前後でpg_stat_databaseのスナップショットを取ることで代替可能です。

#!/usr/sbin/dtrace -qs 

postgresql$1:::transaction-start
{
      @start["Start"] = count();
      self->ts  = timestamp;
}

postgresql$1:::transaction-abort
{
      @abort["Abort"] = count();
}

postgresql$1:::transaction-commit
/self->ts/
{
      @commit["Commit"] = count();
      @time["Total time (ns)"] = sum(timestamp - self->ts);
      self->ts=0;
}

追跡ポイント内の二重のアンダーラインをDスクリプトを使用するときにハイフンに置き換える必要があることに注意してください。 実行すると、例のDスクリプトは以下のような出力をします。

# ./txn_count.d `pgrep -n postgres`
^C

Start                                          71
Commit                                         70
Total time (ns)                        2312105013

追跡プログラムの作成には注意が必要であり、使用前にデバッグが必要であることは忘れないでください。 さもないと、収集される追跡情報の意味がなくなるかもしれません。 ほとんどの場合、見つかる問題はシステムではなく使用方法の間違いです。 動的追跡を使用して見つかった情報に関して議論を行う際には、スクリプトの検査や議論もできるようにスクリプトも含めるようにしてください。

25.4.4. 追跡ポイントの定義

開発者が望めばコード内に新しく追跡ポイントを定義することができます。 しかし、これには再コンパイルが必要です。

追跡ポイントは追跡マクロの1つを使用して追加することができます。 その追跡ポイントの検査で利用可能とする変数の数に応じた選択肢があります。 イベント発生を追跡することは、追跡ポイント命のみを使用して一行で実現できます。

PG_TRACE (my__new__trace__point);

より複雑な追跡ポイントでは、PG_TRACEnマクロを使用して、動的追跡ユーティリティによる検査に複数の変数を提供することができます。 ここで、追跡ポイント名の後には対応するパラメータ数を記載します。

PG_TRACE3 (my__complex__event, varX, varY, varZ);

transaction__start追跡ポイントの定義を以下に示します。

static void
StartTransaction(void)
{
    ...

    /*
     * generate a new transaction id
     */
    s->transactionId = GetNewTransactionId(false);

    XactLockTableInsert(s->transactionId);

    PG_TRACE1(transaction__start, s->transactionId);

    ...
}    

トランザクションIDがどのようにして動的追跡ユーティリティから利用できるようになったかに注目してください。

動的追跡ユーティリティは、追跡ポイントの更なる定義を要求する可能性があります。 例えば、DTraceは以下に示すような新しいプローブをsrc/backend/utils/probes.dに追加することを要求します。

provider postgresql {
      ...
      probe transaction__start(int);
      ...
 };

プローブの引数に指定したデータ型がPG_TRACEマクロで使用される変数のデータ型と一致することに注意しなければなりません。 これはコンパイル時に検査されません。 新しく追加した追跡ポイントが再コンパイルにより利用できることを検査することはできます。 その後に新しいバイナリを起動し、rootとしtげ、以下のようなDTraceコマンドを実行してください。

dtrace -l -n transaction-start