Skip to content

Commit 151c9fb

Browse files
ChangAo Chenhackorum
authored andcommitted
pg_rewind: use flush lsn to set minRecoveryPoint.
Currently pg_rewind uses pg_current_wal_insert_lsn() to set minRecoveryPoint. The minRecoveryPoint is expected to be a lsn points to a xlog record end lsn + 1, but the return value of pg_current_wal_insert_lsn() is the current wal insert lsn. They are different when at a xlog page boundary, e.g., when the former points to the begin of a xlog page, the latter will skip the xlog page header. This can lead to a standby cannot reach consistent when the primary is idle. To fix it, use pg_current_wal_flush_lsn() instead. It's safe because we query flush lsn at the end of rewind, so all copied pages should have lsn <= flush lsn. Using the flush lsn is also crash-safe with respect to the source: the insert lsn lives only in shared memory and can be lost on a source crash, leaving the target's minRecoveryPoint ahead of any lsn the source can subsequently reach.
1 parent 1f3b9bb commit 151c9fb

4 files changed

Lines changed: 20 additions & 11 deletions

File tree

src/bin/pg_rewind/libpq_source.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static void libpq_queue_fetch_range(rewind_source *source, const char *path,
6868
static void libpq_finish_fetch(rewind_source *source);
6969
static char *libpq_fetch_file(rewind_source *source, const char *path,
7070
size_t *filesize);
71-
static XLogRecPtr libpq_get_current_wal_insert_lsn(rewind_source *source);
71+
static XLogRecPtr libpq_get_current_wal_flush_lsn(rewind_source *source);
7272
static void libpq_destroy(rewind_source *source);
7373

7474
/*
@@ -91,7 +91,7 @@ init_libpq_source(PGconn *conn)
9191
src->common.queue_fetch_file = libpq_queue_fetch_file;
9292
src->common.queue_fetch_range = libpq_queue_fetch_range;
9393
src->common.finish_fetch = libpq_finish_fetch;
94-
src->common.get_current_wal_insert_lsn = libpq_get_current_wal_insert_lsn;
94+
src->common.get_current_wal_flush_lsn = libpq_get_current_wal_flush_lsn;
9595
src->common.destroy = libpq_destroy;
9696

9797
src->conn = conn;
@@ -202,21 +202,21 @@ run_simple_command(PGconn *conn, const char *sql)
202202
}
203203

204204
/*
205-
* Call the pg_current_wal_insert_lsn() function in the remote system.
205+
* Call the pg_current_wal_flush_lsn() function in the remote system.
206206
*/
207207
static XLogRecPtr
208-
libpq_get_current_wal_insert_lsn(rewind_source *source)
208+
libpq_get_current_wal_flush_lsn(rewind_source *source)
209209
{
210210
PGconn *conn = ((libpq_source *) source)->conn;
211211
XLogRecPtr result;
212212
uint32 hi;
213213
uint32 lo;
214214
char *val;
215215

216-
val = run_simple_query(conn, "SELECT pg_current_wal_insert_lsn()");
216+
val = run_simple_query(conn, "SELECT pg_current_wal_flush_lsn()");
217217

218218
if (sscanf(val, "%X/%08X", &hi, &lo) != 2)
219-
pg_fatal("unrecognized result \"%s\" for current WAL insert location", val);
219+
pg_fatal("unrecognized result \"%s\" for current WAL flush location", val);
220220

221221
result = ((uint64) hi) << 32 | lo;
222222

src/bin/pg_rewind/local_source.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ init_local_source(const char *datadir)
4646
src->common.queue_fetch_file = local_queue_fetch_file;
4747
src->common.queue_fetch_range = local_queue_fetch_range;
4848
src->common.finish_fetch = local_finish_fetch;
49-
src->common.get_current_wal_insert_lsn = NULL;
49+
src->common.get_current_wal_flush_lsn = NULL;
5050
src->common.destroy = local_destroy;
5151

5252
src->datadir = datadir;

src/bin/pg_rewind/pg_rewind.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -714,12 +714,20 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
714714
{
715715
/*
716716
* Source is a production, non-standby, server. We must replay to
717-
* the last WAL insert location.
717+
* the last WAL flush location.
718+
*
719+
* Use the source's flush LSN as the target's minRecoveryPoint: every
720+
* WAL-logged page we copied has page-LSN <= source's flush LSN at
721+
* copy time (WAL-before-data), and flush LSN is monotonic. We avoid
722+
* using the insert LSN because it can sit one page-header past a
723+
* record's end at page boundaries (where no record will end), and
724+
* it is not durable, a source crash can leave flush LSN behind an
725+
* insert LSN we already pinned.
718726
*/
719727
if (ControlFile_source_after.state != DB_IN_PRODUCTION)
720728
pg_fatal("source system was in unexpected state at end of rewind");
721729

722-
endrec = source->get_current_wal_insert_lsn(source);
730+
endrec = source->get_current_wal_flush_lsn(source);
723731
endtli = Max(ControlFile_source_after.checkPointCopy.ThisTimeLineID,
724732
ControlFile_source_after.minRecoveryPointTLI);
725733
}

src/bin/pg_rewind/rewind_source.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,10 @@ typedef struct rewind_source
6666
void (*finish_fetch) (struct rewind_source *);
6767

6868
/*
69-
* Get the current WAL insert position in the source system.
69+
* Get the current WAL flush position in the source system. To use
70+
* this callback, the source must be a live primary server.
7071
*/
71-
XLogRecPtr (*get_current_wal_insert_lsn) (struct rewind_source *);
72+
XLogRecPtr (*get_current_wal_flush_lsn) (struct rewind_source *);
7273

7374
/*
7475
* Free this rewind_source object.

0 commit comments

Comments
 (0)