4
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?

OrioleDB の PITR

Last updated at Posted at 2025-12-07

この記事は SRA Advent Calendar 2025 12月 8日の記事です。

これまでの OrioleDB に関する記事は OrioleDBタグで検索してください。また、そもそも OrioleDB とは何ぞや、と言う方は、OrioleDB を触ってみるやこちらのスライド資料を参照ください。

OrioleDB PITR はどうやる?

データベースの PITR(Point in Time Recovery)とは、障害発生直前を含む、指定した時刻に戻すリカバリであり、それを可能にするバックアップ方式を意味します。PostgreSQL であれば、pg_basebackup コマンドで取得したオンライン物理バックアップと、データ変更差分が書かれた WALファイルのアーカイブを蓄積していくことで、PITR を実現しています。ドキュメントにも詳しい説明があります

一方、OrioleDB のドキュメントには PITR の説明がありません。アーキテクチャの章で独自の WAL とリカバリの説明はあるのだけれど、WAL の仕組みを変えたとして、PITR はどうなるの? ということが分かりません。

先に結論を言うと、OrioleDB では PostgreSQL と同じように PITR が使えます。以下に検証してみたことを報告します。

OrioleDB の PITR を検証する

こちらの記事の手順で作った環境を使用します。
まず、どの時刻までリカバリできたか分かりやすいテーブルを用意します。

odb1=# CREATE TABLE t_pitrtest1 (id serial PRIMARY KEY, ts timestamp DEFAULT now(), dat text);
odb1=# INSERT INTO t_pitrtest1 (dat) VALUES ('start');
odb1=# SELECT * FROM t_pitrtest1;
 id |             ts             |  dat
----+----------------------------+-------
  1 | 2025-11-30 20:37:57.067097 | start
(1 row)

順次データを投入する pgbenchスクリプトを用意して、pgbenchで実行できるようにします。

$ cat insert_t_pitrtest1.sql
INSERT INTO t_pitrtest1 (dat) VALUES ('dat:' || random()::text);

$ pgbench -R 1 -f insert_t_pitrtest1.sql -c 1 -T 10 -n odb1

上記の引数ですと -R 1 (実行レート 1 TPS)、-c 1 (同時接続数 1)ですので、こんな風にデータが投入されます。

odb1=# SELECT * FROM t_pitrtest1;
 id |             ts             |           dat
----+----------------------------+-------------------------
  1 | 2025-11-30 20:37:57.067097 | start
  2 | 2025-11-30 20:41:29.81414  | dat:0.03360275779345856
  3 | 2025-11-30 20:41:30.953703 | dat:0.29123590557641754
  4 | 2025-11-30 20:41:31.166052 | dat:0.693811003445401
  5 | 2025-11-30 20:41:31.968601 | dat:0.10015774776845565
  6 | 2025-11-30 20:41:32.192674 | dat:0.5571895583180564
  7 | 2025-11-30 20:41:32.818466 | dat:0.9104035803366943
  8 | 2025-11-30 20:41:34.362157 | dat:0.07976987531966118
  9 | 2025-11-30 20:41:36.083216 | dat:0.5679965428816829
 10 | 2025-11-30 20:41:38.682022 | dat:0.688073851012398
(10 rows)

OrioleDB の WAL

OrioleDB の WAL は、専用のディレクトリ・ファイル群ではなく、PostgreSQL の WAL と同じく、pg_wal 以下の WALファイル内の WALレコードとして出力されます。pg_waldump で先ほど INSERT を繰り返したときのWAL内容を確認すると次のようになります。

$ ls -l $PGDATA/pg_wal
total 16384
-rw-------. 1 postgres postgres 16777216 Nov 30 20:45 000000010000000000000001
drwx------. 2 postgres postgres        6 Nov 30 20:27 archive_status
drwx------. 2 postgres postgres        6 Nov 30 20:27 summaries

$ pg_waldump $PGDATA/pg_wal/000000010000000000000001 | tail -15
rmgr: Transaction len (rec/tot):     34/    34, tx:        755, lsn: 0/01998BE8, prev 0/01998B68, desc: COMMIT 2025-11-30 20:41:29.817364 JST
rmgr: Standby     len (rec/tot):     58/    58, tx:          0, lsn: 0/01998C10, prev 0/01998BE8, desc: RUNNING_XACTS nextXid 756 latestCompletedXid 755 oldestRunningXid 756
rmgr: custom129   len (rec/tot):    122/   122, tx:          0, lsn: 0/01998C50, prev 0/01998C10, desc: UNKNOWN (0) rmid: 129
rmgr: custom129   len (rec/tot):    120/   120, tx:          0, lsn: 0/01998CD0, prev 0/01998C50, desc: UNKNOWN (0) rmid: 129
rmgr: custom129   len (rec/tot):    122/   122, tx:          0, lsn: 0/01998D48, prev 0/01998CD0, desc: UNKNOWN (0) rmid: 129
rmgr: custom129   len (rec/tot):    121/   121, tx:          0, lsn: 0/01998DC8, prev 0/01998D48, desc: UNKNOWN (0) rmid: 129
rmgr: custom129   len (rec/tot):    121/   121, tx:          0, lsn: 0/01998E48, prev 0/01998DC8, desc: UNKNOWN (0) rmid: 129
rmgr: custom129   len (rec/tot):    122/   122, tx:          0, lsn: 0/01998EC8, prev 0/01998E48, desc: UNKNOWN (0) rmid: 129
rmgr: custom129   len (rec/tot):    121/   121, tx:          0, lsn: 0/01998F48, prev 0/01998EC8, desc: UNKNOWN (0) rmid: 129
rmgr: custom129   len (rec/tot):    120/   120, tx:          0, lsn: 0/01998FC8, prev 0/01998F48, desc: UNKNOWN (0) rmid: 129
rmgr: Standby     len (rec/tot):     58/    58, tx:          0, lsn: 0/01999040, prev 0/01998FC8, desc: RUNNING_XACTS nextXid 756 latestCompletedXid 755 oldestRunningXid 756
rmgr: XLOG        len (rec/tot):     30/    30, tx:          0, lsn: 0/01999080, prev 0/01999040, desc: CHECKPOINT_REDO wal_level replica
rmgr: Standby     len (rec/tot):     58/    58, tx:          0, lsn: 0/019990A0, prev 0/01999080, desc: RUNNING_XACTS nextXid 756 latestCompletedXid 755 oldestRunningXid 756
rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 0/019990E0, prev 0/019990A0, desc: CHECKPOINT_ONLINE redo 0/1999080; tli 1; prev tli 1; fpw true; wal_level replica; xid 0:756; oid 24576; multi 1; offset 0; oldest xid 730 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 756; online
rmgr: Standby     len (rec/tot):     58/    58, tx:          0, lsn: 0/01999158, prev 0/019990E0, desc: RUNNING_XACTS nextXid 756 latestCompletedXid 755 oldestRunningXid 756

custom129 という見慣れない種類の WALがたくさん出ています。これが OrioleDB のテーブルに対応したカスタムWALレコードです。注目すべきはトランザクションのコミットの WALレコードは、「rmgr: Transaction ..中略.. desc: COMMIT 2025-11-30 20:41:29.817364 JST」となっているものしか出ていないことです。今回の pgbench は1行ずつ INSERTするので、それぞれが 1トランザクションであるはずですが、それに対応したコミットの WALレコードは見当たりません。

PostgreSQLはコミットのログにタイムスタンプが付随していて、それで PITR の時刻指定に対応しています。果たして、OrileDB のテーブルで PITR はできるのでしょうか?

時刻指定リカバリをやってみる

ひとまず、PostgreSQL と同じように PITR をセットアップしてみます。
ディレクトリを用意して、archive_mode、archive_command を設定して、WALアーカイブを有効にします。

$ mkdir 17/odata_arc
$ mkdir 17/odata_bkup

$ vi $PGDATA/postgresql.conf
   archive_mode = on
   archive_command = 'cp %p /var/lib/pgsql/17/odata_arc/%f'

$ pg_ctl restart

先ほどの pgbench をもっとたくさんINSERTするようにパラメータを与えて実行すると、そのうちに WALアーカイブが出力されます。

$ pgbench -R 100 -f insert_t_pitrtest1.sql -c 10 -T 100 -n odb1

$ ls 17/odata_arc/
000000010000000000000001

ベースバックアップを取って、.backupファイルで完了時刻(STOP TIME)を確認します。

$ pg_basebackup -D 17/odata_bkup/bkup202511302122
$ cat 17/odata_arc/*.backup
START WAL LOCATION: 0/2000028 (file 000000010000000000000002)
STOP WAL LOCATION: 0/2000128 (file 000000010000000000000002)
CHECKPOINT LOCATION: 0/2000088
BACKUP METHOD: streamed
BACKUP FROM: primary
START TIME: 2025-11-30 21:22:58 JST
LABEL: pg_basebackup base backup
START TIMELINE: 1
STOP TIME: 2025-11-30 21:22:59 JST
STOP TIMELINE: 1

このときデータは次のようになっていました。

odb1=# SELECT * FROM t_pitrtest1 ORDER BY id DESC LIMIT 5;
  id   |             ts             |           dat
-------+----------------------------+-------------------------
 42419 | 2025-11-30 21:25:04.506869 | dat:0.1621811199307015
 42418 | 2025-11-30 21:25:04.506737 | dat:0.09407346230024816
 42417 | 2025-11-30 21:25:04.499741 | dat:0.4468218632215093
 42416 | 2025-11-30 21:25:04.490913 | dat:0.9747454347627438
 42415 | 2025-11-30 21:25:04.490789 | dat:0.4814442714289098
(5 rows)

11/30 の 21:23 から 21:25 までの間がリカバリ目標時刻に指定可能ということが分かります。そこで、21:24:00 時点へのリカバリを試すことにします。
やっていることは PostgreSQLドキュメントの「継続的アーカイブによるバックアップを使用した復旧」に書いてある通りのことです。

$ pg_ctl stop
$ mv $PGDATA ${PGDATA}.save
$ cp -R 17/odata_bkup/bkup202511302122 $PGDATA
$ rm -rf $PGDATA/pg_wal/*
$ cp -v ${PGDATA}.save/pg_wal/00000001???????????????? $PGDATA/pg_wal/
'/var/lib/pgsql/17/odata.save/pg_wal/000000010000000000000004' -> '/var/lib/pgsql/17/odata/pg_wal/000000010000000000000004'
'/var/lib/pgsql/17/odata.save/pg_wal/000000010000000000000005' -> '/var/lib/pgsql/17/odata/pg_wal/000000010000000000000005'
'/var/lib/pgsql/17/odata.save/pg_wal/000000010000000000000006' -> '/var/lib/pgsql/17/odata/pg_wal/000000010000000000000006'

$ touch $PGDATA/recovery.signal
$ vi $PGDATA/postgresql.conf
   archive_command = 'true'  #cp %p /var/lib/pgsql/17/odata_arc/%f'
   restore_command = 'cp /var/lib/pgsql/17/odata_arc/%f %p'
   recovery_target_time = '2025-11-30 21:24:00'

$ pg_ctl start

さて、どうでしょう。まずはログを見てみます。

$ cat $PGDATA/log/* | less
2025-11-30 21:44:44.131 JST [2455] LOG:  starting OrioleDB 17.5 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5), 64-bit
《中略》
2025-11-30 21:44:45.350 JST [2459] LOG:  starting backup recovery with redo LSN 0/2000028, checkpoint LSN 0/2000088, on timeline ID 1
2025-11-30 21:44:45.358 JST [2459] LOG:  restored log file "000000010000000000000002" from archive
2025-11-30 21:44:45.370 JST [2459] LOG:  starting point-in-time recovery to 2025-11-30 21:24:00+09
2025-11-30 21:44:45.388 JST [2459] LOG:  orioledb recovery started.
2025-11-30 21:44:45.392 JST [2463] LOG:  orioledb recovery worker 0 started.
2025-11-30 21:44:45.394 JST [2464] LOG:  orioledb recovery worker 1 started.
2025-11-30 21:44:45.397 JST [2465] LOG:  orioledb recovery worker 2 started.
2025-11-30 21:44:45.399 JST [2466] LOG:  orioledb recovery worker 3 started.
2025-11-30 21:44:45.402 JST [2459] LOG:  redo starts at 0/2000028
2025-11-30 21:44:45.410 JST [2469] LOG:  orioledb recovery worker 4 started.
2025-11-30 21:44:45.411 JST [2468] LOG:  orioledb recovery worker 5 started.
2025-11-30 21:44:45.425 JST [2459] LOG:  restored log file "000000010000000000000003" from archive
2025-11-30 21:44:45.451 JST [2459] LOG:  completed backup recovery with redo LSN 0/2000028 and end LSN 0/2000128
2025-11-30 21:44:45.451 JST [2459] LOG:  consistent recovery state reached at 0/2000128
2025-11-30 21:44:45.451 JST [2455] LOG:  database system is ready to accept read-only connections
2025-11-30 21:44:45.566 JST [2459] LOG:  recovery stopping before commit of transaction 1850, time 2025-11-30 21:24:00.253165+09
2025-11-30 21:44:45.568 JST [2459] LOG:  pausing at the end of recovery
2025-11-30 21:44:45.568 JST [2459] HINT:  Execute pg_wal_replay_resume() to promote.

リカバリが行われているようです。「orioledb recovery ...」という独自のログメッセージも出ています。並列に複数の recovery worker が動作するのが OrioleDB の独自性です。また、最後は「LOG: pausing at the end of recovery」になっていて指定時刻でリカバリが止まっているようです。

続いてデータを確認します。

odb1=# SELECT * FROM t_pitrtest1 ORDER BY id DESC LIMIT 5;
  id   |             ts             |           dat
-------+----------------------------+-------------------------
 36038 | 2025-11-30 21:24:00.246746 | dat:0.12077053940038973
 36037 | 2025-11-30 21:24:00.239431 | dat:0.40804441267439406
 36036 | 2025-11-30 21:24:00.225517 | dat:0.42610736088514933
 36035 | 2025-11-30 21:24:00.224812 | dat:0.7284851821205414
 36034 | 2025-11-30 21:24:00.224257 | dat:0.7262674959560826
(5 rows)

odb1=# SELECT pg_wal_replay_resume();
 pg_wal_replay_resume
----------------------

(1 row)

データの最後が 2025-11-30 21:24:00.246746 ですので、指定が効いています。成功していますので、PostgreSQL の作法通り pg_wal_replay_resume() でリカバリを終了させました。
PostgreSQL と同じように PITR ができることが確認できました。

Oriole のコミット時刻はどこに書いてあるの?

ところで各 INSERT のコミット時刻はどこに書いてあるのでしょうか? pg_waldump には現れないだけで、OrioleDB のカスタム WALレコードの中に書いてあるのでしょうか。

OrioleDB の recovery/recovery.c という分かりやすいソースファイルに、カスタムWALレコードから日付時刻を取り出して、リカバリ停止判定をするコードがありました。これが PostgreSQL へのパッチで付加した、OrioleDB専用のhookコードになっているのでしょう。

/*
 * This function is called by the RecoveryStopsHook. It decides whether we want
 * to stop applying WAL records.
 *
 * Returns true if we are stopping, false otherwise.
 */
bool
orioledb_recovery_stops_before_hook(XLogReaderState *record,
                                    TransactionId *recordXid,
                                    TimestampTz *recordXtime)
{
    Pointer     startPtr = XLogRecGetData(record);
    Pointer     ptr = startPtr;
//{中略}
    ptr = wal_container_read_header(ptr, &wal_version, &wal_flags);
//{中略}
    memcpy(&xactTime, ptr, sizeof(xactTime));
    ptr += sizeof(xactTime);
    memcpy(&xid, ptr, sizeof(xid));

    *recordXid = xid;
    *recordXtime = xactTime;

    if (recoveryTargetInclusive)
        return xactTime > recoveryTargetTime;
    else
        return xactTime >= recoveryTargetTime;
}

結論

見てきました通り、OrioleDB は PostgreSQL と同じように PITR が使える、ということでした。

4
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
4
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?