PostgreSQLのトランザクションID周回問題を強制的に発生させる
こんにちは!
PostgreSQLのトランザクションID(XID)の周回により、
以前は見ていた古いデータが見えなくなる事象(XID周回問題)を強制的に発生させたいと思います。
通常は自動Vacuumが古いXIDの整理を自動実行している、かつ
XID周回問題が発生する前に以下のようなメッセージが出力され、
XID周回問題(データが見えなくなる)は発生しません。
XID周回問題の発生まで、残り1,000万トランザクションで以下の警告メッセージが出力されます。
WARNING: database "mydb" must be vacuumed within 177009986 transactions HINT: To avoid a database shutdown, execute a database-wide VACUUM in "mydb".
残り100万トランザクションでエラーになります。(XID周回問題の防止)
エラーが発生した場合には、メッセージのHINTに出力されている通り、
シングルユーザモードでVACUUM(FREEZE)を実行し、復旧させる必要があます。
ERROR: database is not accepting commands to avoid wraparound data loss in database "mydb" HINT: Stop the postmaster and vacuum that database in single-user mode.
参考にした情報
https://www.postgresql.jp/document/9.6/html/routine-vacuuming.html
https://www.slideshare.net/iakio/jpug-ezo20110809
今回はXID周回が発生した場合の動作を確認するために、XID周回を強制的に発生させました。
※ データが見えなくなりますので、壊れてよいデータベースクラスタを用意して実行しています。
0. 環境情報
# cat /etc/redhat-release Red Hat Enterprise Linux Server release 7.3 (Maipo)
$ psql -c "SELECT version()" version --------------------------------------------------------------------------------------------------------- PostgreSQL 10.0 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11), 64-bit (1 row)
1. テスト用テーブル作成
$ psql =# CREATE TABLE test(id int); =# INSERT INTO test VALUES (1); =# INSERT INTO test VALUES (2); =# INSERT INTO test VALUES (3); =# SELECT xmin,xmax,id FROM test; xmin | xmax | id ------+------+---- 556 | 0 | 1 557 | 0 | 2 558 | 0 | 3 (3 rows)
2. データベース停止
$ pg_ctl stop waiting for server to shut down.... done server stopped
3. xidを2の31乗進める
$ pg_resetwal -x 0x80000000 data Write-ahead log reset
4. シングルユーザモードで起動
$ postgres --single postgres PostgreSQL stand-alone backend 10.0
5. frozenxidを書き換える
=# UPDATE pg_database SET datfrozenxid = '2147483648' -- 2の31乗 =# UPDATE pg_class SET relfrozenxid= '2147483648' -- 2の31乗 =# SELECT datname,datfrozenxid from pg_database; -- datfrozenxidが"2147483648" =# SELECT relname,relfrozenxid from pg_class; -- relfrozenxidが"2147483648" Ctrl + D
6. データベース起動
$ pg_ctl start
7. XIDを消費
for i in `seq 0 545` do psql -c "SELECT txid_current();" done
7. 確認
$ psql =# SELECT txid_current(); txid_current -------------- 2147484201 (1 row) =# SELECT xmin,xmax,id FROM test; xmin | xmax | id ------+------+---- 556 | 0 | 1 557 | 0 | 2 558 | 0 | 3 (3 rows) ※ 上記の2SQLを繰り返すると、xmin=556のデータから見えなくなる xmin | xmax | id ------+------+---- (0 rows)
6. VACUUM FREEZEの実行
=# VACUUM FREEZE; =# SELECT xmin,xmax,id FROM test; ERROR: catalog is missing 1 attribute(s) for relid 16384 LINE 1: SELECT xmin,xmax,id FROM test; =# SELECT * FROM pg_attribute WHERE attrelid = ( SELECT oid FROM pg_class WHERE relname = 'test' ); (0 rows)
システムカタログpg_attributeのデータが見えなくなってしまったようです。
何らかの特殊要因(システムカタログを書き換えたなど(ないと思いますが))で、周回問題が発生しうるか否かを確認する際には、
frozenidと該当テーブルのxminの値を比較してみるとよいかもしれません。
以上です。