🚄

PostgreSQLで複数のSET LOCAL効率よく実行する

2025/03/11に公開

SETコマンドは実行時設定パラメータを変更するためのコマンドです[1]。影響範囲をトランザクション内に限定するときはLOCALを指定し、何も指定しないかSESSIONを指定すると、影響範囲はセッション全体になります。

ただし、使用するクライアントライブラリやプロトコルの制約で複数のSQL文をまとめて一度に送信できない場合は、それぞれのSETごとにネットワーク上でのやり取りが発生し、ネットワークの往復時間(RTT: Round Trip Time)が増えてしまいます。例えば以下のように個別にクエリを実行すると、SET LOCALだけで3回のRTTが発生します。

tx, err := db.BeginTx(ctx, nil)
if err != nil {
	// ...
}

// 個別にSET LOCALを実行
if _, err := tx.ExecContext(ctx, `SET LOCAL statement_timeout = '30s'`); err != nil {
	// ...
}
if _, err := tx.ExecContext(ctx, `SET LOCAL lock_timeout = '5s'`); err != nil {
	// ...
}
if _, err := tx.ExecContext(ctx, fmt.Sprintf(`SET LOCAL app.tenant_id = '%s'`, validatedTenantID)); err != nil {
	// ...
}
// ...

PostgreSQLでは、SETコマンドと等価な機能をset_config関数として提供しています[2]。この関数を使えば、1つのSELECTクエリ内で複数の設定をまとめて実行できます。第3引数の真偽値で影響範囲をトランザクション内かセッション内か指定します。

つまり、SET LOCALSET SESSIONはそれぞれ以下のように書き換えられます。

-- SET LOCAL some_setting = 'value'; と等価
set_config('some_setting', 'value', true);

-- SET SESSION some_setting = 'value'; と等価
set_config('some_setting', 'value', false);

これを利用すると、先ほどの例は以下のように書けます。

tx, err := db.BeginTx(ctx, nil)
if err != nil {
	// ...
}

// set_configを使い、1回のクエリ内で設定をまとめて実行
_, err := tx.ExecContext(ctx, fmt.Sprintf(
	`SELECT
		set_config('statement_timeout', '5s', true),
		set_config('lock_timeout', '2s', true),
		set_config('app.tenant_id', '%s', true)`,
	validatedTenantID,
))
if err != nil {
	// ...
}
脚注
  1. https://www.postgresql.jp/document/16/html/sql-set.html ↩︎

  2. https://www.postgresql.jp/document/16/html/functions-admin.html#FUNCTIONS-ADMIN-SET ↩︎

Discussion