Skip to content

Commit 9ba0361

Browse files
committed
Update minimum recovery point on truncation.
If a file is truncated, we must update minRecoveryPoint. Once a file is truncated, there's no going back; it would not be safe to stop recovery at a point earlier than that anymore. Per report from Kyotaro HORIGUCHI. Backpatch to 8.4. Before that, minRecoveryPoint was not updated during recovery at all.
1 parent 8bc8f70 commit 9ba0361

File tree

2 files changed

+50
-11
lines changed

2 files changed

+50
-11
lines changed

src/backend/access/transam/xact.c

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4544,25 +4544,46 @@ xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid, XLogRecPtr lsn)
45444544
}
45454545

45464546
/* Make sure files supposed to be dropped are dropped */
4547-
for (i = 0; i < xlrec->nrels; i++)
4547+
if (xlrec->nrels > 0)
45484548
{
4549-
SMgrRelation srel = smgropen(xlrec->xnodes[i], InvalidBackendId);
4550-
ForkNumber fork;
4549+
/*
4550+
* First update minimum recovery point to cover this WAL record. Once
4551+
* a relation is deleted, there's no going back. The buffer manager
4552+
* enforces the WAL-first rule for normal updates to relation files,
4553+
* so that the minimum recovery point is always updated before the
4554+
* corresponding change in the data file is flushed to disk, but we
4555+
* have to do the same here since we're bypassing the buffer manager.
4556+
*
4557+
* Doing this before deleting the files means that if a deletion fails
4558+
* for some reason, you cannot start up the system even after restart,
4559+
* until you fix the underlying situation so that the deletion will
4560+
* succeed. Alternatively, we could update the minimum recovery point
4561+
* after deletion, but that would leave a small window where the
4562+
* WAL-first rule would be violated.
4563+
*/
4564+
XLogFlush(lsn);
45514565

4552-
for (fork = 0; fork <= MAX_FORKNUM; fork++)
4566+
for (i = 0; i < xlrec->nrels; i++)
45534567
{
4554-
XLogDropRelation(xlrec->xnodes[i], fork);
4555-
smgrdounlink(srel, fork, true);
4568+
SMgrRelation srel = smgropen(xlrec->xnodes[i], InvalidBackendId);
4569+
ForkNumber fork;
4570+
4571+
for (fork = 0; fork <= MAX_FORKNUM; fork++)
4572+
{
4573+
XLogDropRelation(xlrec->xnodes[i], fork);
4574+
smgrdounlink(srel, fork, true);
4575+
}
4576+
smgrclose(srel);
45564577
}
4557-
smgrclose(srel);
45584578
}
45594579

45604580
/*
45614581
* We issue an XLogFlush() for the same reason we emit ForceSyncCommit()
4562-
* in normal operation. For example, in DROP DATABASE, we delete all the
4563-
* files belonging to the database, and then commit the transaction. If we
4564-
* crash after all the files have been deleted but before the commit, you
4565-
* have an entry in pg_database without any files. To minimize the window
4582+
* in normal operation. For example, in CREATE DATABASE, we copy all files
4583+
* from the template database, and then commit the transaction. If we
4584+
* crash after all the files have been copied but before the commit, you
4585+
* have files in the data directory without an entry in pg_database. To
4586+
* minimize the window
45664587
* for that, we use ForceSyncCommit() to rush the commit record to disk as
45674588
* quick as possible. We have the same window during recovery, and forcing
45684589
* an XLogFlush() (which updates minRecoveryPoint during recovery) helps

src/backend/catalog/storage.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,24 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
508508
*/
509509
smgrcreate(reln, MAIN_FORKNUM, true);
510510

511+
/*
512+
* Before we perform the truncation, update minimum recovery point
513+
* to cover this WAL record. Once the relation is truncated, there's
514+
* no going back. The buffer manager enforces the WAL-first rule
515+
* for normal updates to relation files, so that the minimum recovery
516+
* point is always updated before the corresponding change in the
517+
* data file is flushed to disk. We have to do the same manually
518+
* here.
519+
*
520+
* Doing this before the truncation means that if the truncation fails
521+
* for some reason, you cannot start up the system even after restart,
522+
* until you fix the underlying situation so that the truncation will
523+
* succeed. Alternatively, we could update the minimum recovery point
524+
* after truncation, but that would leave a small window where the
525+
* WAL-first rule could be violated.
526+
*/
527+
XLogFlush(lsn);
528+
511529
smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno);
512530

513531
/* Also tell xlogutils.c about it */

0 commit comments

Comments
 (0)