1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PostgreSQLのスタンバイ側で、「テーブルスペース削除によるリカバリ競合」と「テーブルロック」によりデッドロックを発生させる手順

Posted at

レプリケーション構成のスタンバイ側で、以前の記事では、「バッファ・ピンによるリカバリ競合」と「テーブルロック」によりデッドロックを発生させました。

今回は、「テーブルスペース削除によるリカバリ競合」と「テーブルロック」によりデッドロックを発生させます。このデッドロックは、以下2つの待ちが重なることで発生します。

  • バックエンドプロセスが利用中のテーブルスペースを、startupプロセスが削除しようとして待ち状態になる
  • startupプロセスが取得済のテーブルロックを、バックエンドプロセスが取得しようとして待ち状態になる

デッドロックを発生させる手順

:one:レプリケーション構成のプライマリ側をセットアップする。

$ initdb -D data --no-locale --encoding=UTF8
$ pg_ctl -D data start

:two:テーブルスペースを作成する。

$ mkdir /tmp/hoge1
$ psql -c "CREATE TABLESPACE hoge LOCATION '/tmp/hoge1'"

:three:レプリケーション構成のスタンバイ側をセットアップする。

$ pg_basebackup -D sby1 -R -T /tmp/hoge1=/tmp/hoge2 -X fetch
$ echo "port = 5433" >> sby1/postgresql.conf
$ echo "temp_tablespaces = 'hoge'" >> sby1/postgresql.conf
$ echo "max_standby_streaming_delay = -1" >> sby1/postgresql.conf
$ pg_ctl -D sby1 start
  • 今回は同一ホスト上でプライマリとスタンバイを動かすため、pg_basebackup実行時に-Tオプションでテーブルスペースのディレクトリの対応を指定する。また、プライマリと異なるポート番号をスタンバイ側で設定する。
  • テーブルスペース削除によるリカバリ競合を引き起こすために、:two:で作成したテーブルスペースをtemp_tablespacesに設定する。
  • リカバリと競合するリードオンリー・トランザクションがすぐにキャンセルされないように(すぐにキャンセルされるとデッドロックを観察できないため)、max_standby_streaming_delayを-1に設定する。

:four:プライマリ側で、テーブルを作成する。また、そのテーブルに対してACCESS EXCLUSIVEロックを取得して、ロック取得のWALがすぐにスタンバイに転送されるようにpg_switch_wal()を実行する。

CREATE TABLE t();
BEGIN;
LOCK TABLE t IN ACCESS EXCLUSIVE MODE;
SELECT pg_switch_wal();
  • この手順により、スタンバイ側では、startupプロセスはテーブルに対するACCESS EXCLUSIVEロックを取得する。

:five:スタンバイ側で、ORDER BYのディスクソートにより、temp_tablespacesに設定したテーブルスペース上で一時ファイルを発生させる。また、テーブルをSELECTする。

BEGIN;
SET work_mem TO 64;
DECLARE mycur CURSOR FOR SELECT * FROM generate_series(1, 1000000) n ORDER BY n;
FETCH mycur;
SELECT * FROM t;
  • ディスクソートが発生しやすいように、ORDER BY検索の実行前にwork_memを小さい値に設定しておく。
  • 一時ファイルが残ったままになるように、カーソルを使ってORDER BY検索が途中状態のままになるようにする。
  • テーブルのSELECTにより、バックエンドプロセスはテーブルに対するACCESS SHAREロックを取得しようとして待ち状態になる。

:six:プライマリ側で、:four:とは別のセッションを開始して、テーブルスペースを削除する。

DROP TABLESPACE hoge;
  • この手順により、startupプロセスはスタンバイ側でテーブルスペースを削除しようとするが、バックエンドプロセスがまだそのテーブルスペースを使っている(一時ファイルが残っている)ため、テーブルスペース削除によるリカバリ競合となり、待ち状態になる。

:seven:バックエンドプロセスもstartupプロセスも待ち状態のため、デッドロックとなる。

2024年1月28日現在のPostgreSQLのコードでは、このデッドロックは検出されず自動的な解決もされない。(ように見えるが、嘘だったら申し訳ない。。)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?