#author("2019-01-14T02:07:25+00:00","default:haikikyou","haikikyou")
#author("2019-01-14T08:33:56+00:00","default:haikikyou","haikikyou")
[[PostgreSQL/解析]]

* pg_rewind [#g8c14678]

タイムライン分岐したデータベースサーバを同期させるツール。例えば、レプリケーションでプライマリ、スタンバイとあって、プライマリが何らかの障害等で停止し、スタンバイがプライマリに昇格した時、スタンバイは新しいタイムラインでシステムを継続する。この後、プライマリを新しいスタンバイとして組み込みたい場合、pg_rewindコマンドを使用すると、新しいタイムラインのスタンバイ(新プライマリ)に同期させることができる。リカバリの方法としてpg_basebackupコマンドがあるが、こちらはデータベースディレクトリを丸々複製するのに対し、pg_rewindは差分データのみ同期させることで、データ転送量を抑えることができ、復旧時間の短縮化することができる。(更新内容にもよるが、データベースクラスタサイズが大きいシステムではpg_rewindがリカバリ時間の短縮をすることができるだろう)。


#geshi(bash){{{
pg_rewind --debug -D primary/pgdata -P --source-server="host=localhost dbname=postgres port=5433"

# source-serverとは、複製元となるサーバのことである。
# つまり新しく組み込もうとしているサーバを同期させるサーバである。
}}}

pg_rewindは、差分ファイル転送と巻き戻しを行うだけであり、コマンド実行以降は起動時リカバリ処理でソースサーバに追従する。復旧までに時間がかかり、ソースサーバ上のWALが既にアーカイブされて退避されていた場合、起動時リカバリ処理に失敗してしまうため、起動前にアーカイブWALをターゲットサーバに転送しておく必要がある。

''例:ソースサーバ上のWALがアーカイブされてしまい、ターゲットサーバの起動時リカバリ処理で失敗してしまう''

#geshi{{{
LOCATION:  libpqrcv_receive, libpqwalreceiver.c:757
LOG:  00000: started streaming WAL from primary at 0/4000000 on timeline 2
LOCATION:  WalReceiverMain, walreceiver.c:409
FATAL:  XX000: could not receive data from WAL stream: ERROR:  requested WAL segment 000000020000000000000004 has already been removed
}}}

* pg_rewindの実行条件 [#l6c574c4]

pg_rewindを実行するには、正常停止している必要がある。~
よって障害等でプロセスが突然ダウンしてしまった場合、一度インスタンスを起動し正常停止させる。

* pg_rewindの動作概要 [#d0d09003]

- コピー元のソース一覧を取得し、コピー元にないファイルがコピー先にある場合、コピー先のファイルを削除する。
- pg_rewindは、fork前の最後のチェックポイント以降のWALレコードを辿り、更新されたブロック情報を取得する。
- コピー元から、更新ブロックされたブロック、ファイルなどを取得する。
- forkの箇所まで巻き戻した後は、インスタンス起動し、rewindで取得したWAL(コピー元になり場合はアーカイブからリストアすることもある)を再生する。

#ref(./pg_rewind_overview.png,80%)

#ref(./pg_rewind_image_block_actions.png,80%)


* ソースサーバ上のファイル一覧取得 [#tc6121fb]

ソースサーバのファイル一覧を知るために、ソースサーバ上で以下のSQLが実行される。

#geshi(sql){{{
WITH RECURSIVE files (path, filename, size, isdir) AS (
  SELECT '' AS path, filename, size, isdir FROM
  (SELECT pg_ls_dir('.', true, false) AS filename) AS fn,
        pg_stat_file(fn.filename, true) AS this
  UNION ALL\n"
  SELECT parent.path || parent.filename || '/' AS path,
         fn, this.size, this.isdir\n"
  FROM files AS parent,\n"
       pg_ls_dir(parent.path || parent.filename, true, false) AS fn,
       pg_stat_file(parent.path || parent.filename || '/' || fn, true) AS this
       WHERE parent.isdir = 't'
)
SELECT path || filename, size, isdir,
       pg_tablespace_location(pg_tablespace.oid) AS link_target
FROM files
LEFT OUTER JOIN pg_tablespace ON files.path = 'pg_tblspc/'
                             AND oid::text = files.filename;
}}}

ただし、以下のリソースは対象外である。

#geshi{{{
# 対象外のディレクトリ
pg_stat_tmp
pg_replslot
pg_dynshmem
pg_notify
pg_serial
pg_snapshots
pg_subtrans

# 対象外のファイル
postgresql.auto.conf.tmp
current_logfiles.tmp
backup_label
tablespace_map
postmaster.pid
postmaster.opts
}}}

* デバッグ出力の例 [#h458a543]

#geshi{{{
connected to server
fetched file "global/pg_control", length 8192
fetched file "pg_wal/00000002.history", length 41
Source timeline history:
Target timeline history:
1: 0/0 - 0/0
servers diverged at WAL location 0/301F968 on timeline 1
rewinding from last common checkpoint at 0/301F8F8 on timeline 1
reading source file list
entry "postmaster.opts" excluded from source file list
entry "postmaster.pid" excluded from source file list
entry "global/pg_internal.init" excluded from source file list
entry "pg_notify/0000" excluded from source file list
entry "pg_subtrans/0000" excluded from source file list
entry "base/12630/pg_internal.init" excluded from source file list
reading target file list
entry "pg_notify/0000" excluded from target file list
entry "pg_subtrans/0000" excluded from target file list
entry "postmaster.opts" excluded from target file list
reading WAL in target
backup_label.old (COPY)
base/1/12465_fsm (COPY)
base/1/12465_vm (COPY)
base/1/12470_fsm (COPY)
...
pg_stat/global.stat (REMOVE)
need to copy 51 MB (total source directory size is 70 MB)
getting file chunks
received chunk for file "backup_label.old", offset 0, size 224
    0/53164 kB (0%) copied
received chunk for file "base/1/12465_fsm", offset 0, size 24576
received chunk for file "base/1/12465_vm", offset 0, size 8192
received chunk for file "base/1/12470_fsm", offset 0, size 24576
received chunk for file "base/1/12470_vm", offset 0, size 8192
...
received chunk for file "base/12630/16384", offset 50008192, size 1000000
...
received chunk for file "pg_wal/000000020000000000000003", offset 16000000, size 777216
received chunk for file "pg_xact/0000", offset 0, size 8192
received chunk for file "postgresql.auto.conf", offset 0, size 88
received chunk for file "postgresql.conf", offset 0, size 23809
received chunk for file "recovery.done", offset 0, size 274
received chunk for file "base/12630/16384", offset 8192, size 49152
53164/53164 kB (100%) copied
creating backup label and updating control file
syncing target data directory
}}}

転送されたテーブルのOIDからテーブル名を知りたい場合はoid2nameを使えば良い。

#geshi(bash){{{
# contribモジュールにある
$ cd contrib/oid2name
$ make install

# pgenv2を使っている場合
$ pgenv prefix --source "cd contrib/oid2name && make install"
}}}

データベースpostgresのoidとテーブル名の対応を見る例は以下の通り。

#geshi(bash){{{
$ oid2name -d postgres -p 24312
$ oid2name -d postgres
From database "postgres":
  Filenode  Table Name
----------------------
     16384           t
}}}

&label(warn){参考};

一度に転送される最大のデータサイズは、size 1000000となっている。ざっと1MB程度である。
- [[src/bin/pg_rewind/libpq_fetch.c>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/bin/pg_rewind/libpq_fetch.c;h=9a085ea258f583e11092f13724a6b433bedf5049;hb=refs/heads/REL_11_STABLE#l38]] - &size(11){&color(gray){on https://git.postgresql.org/gitweb/?p=postgresql.git};};
- [[oid2name>https://www.postgresql.jp/document/10/html/oid2name.html]] - &size(11){&color(gray){on https://www.postgresql.jp/document/10/html/oid2name.html};};
* ファイル転送の内部動作 [#p583814f]

+ pg_rewindで転送するファイルに関する情報を一時テーブルでソースサーバ上に作成する。
#geshi(sql){{{
CREATE TEMPORARY TABLE fetchchunks(path text, begin int8, len int4);
}}}
+ 続いて、COPYコマンドで、path、begin、lenの情報を各転送対象ソースに対して求める。
#geshi(sql){{{
COPY fetchchunks FROM STDIN;
}}}
+ 最後は、SELECT分を発行し、実データを取得する。
#geshi(sql){{{
SELECT path, begin, pg_read_binary_file(path, begin, len, true) AS chunk FROM fetchchunks;
}}}
* pg_rewindの動作確認シェル [#mc4558d4]

pg_rewindの動作を順を追って確認できるようなシェルを準備した。
pgenv2が必要である。

&ref(./rewind-study.sh);

https://github.com/moritetu/pgenv2

#geshi(bash){{{
bash rewind-study.sh
}}}

各ステップごとにプログラムを停止するようにしているので、状態確認しながら動作理解することができる。

#geshi{{{
...

# [Step-5] 2019年 1月12日 土曜日 22時57分33秒 JST
# Down primary, go?
[ENTER(yes)|n(no)|s(stop)]: 

waiting for server to shut down.... done
server stopped

# [Step-6] 2019年 1月12日 土曜日 22時57分37秒 JST
# Promote standby, go?
[ENTER(yes)|n(no)|s(stop)]: 

...
}}}

* 参考リンク [#j995d3c4]

- [[pg_rewind>https://www.postgresql.jp/document/10/html/app-pgrewind.html]] - &size(11){&color(gray){on https://www.postgresql.jp/document/10/html/app-pgrewind.html};};
- [[pg_rewindソース>https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;f=src/bin/pg_rewind;hb=refs/heads/REL_11_STABLE]] - &size(11){&color(gray){on https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;f=src/bin/pg_rewind;hb=refs/heads/REL_11_STABLE};};



PR

トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP