Skip to content

Commit 172d067

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 0dfbb64 commit 172d067

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
@@ -4489,25 +4489,46 @@ xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid, XLogRecPtr lsn)
44894489
}
44904490

44914491
/* Make sure files supposed to be dropped are dropped */
4492-
for (i = 0; i < xlrec->nrels; i++)
4492+
if (xlrec->nrels > 0)
44934493
{
4494-
SMgrRelation srel = smgropen(xlrec->xnodes[i]);
4495-
ForkNumber fork;
4494+
/*
4495+
* First update minimum recovery point to cover this WAL record. Once
4496+
* a relation is deleted, there's no going back. The buffer manager
4497+
* enforces the WAL-first rule for normal updates to relation files,
4498+
* so that the minimum recovery point is always updated before the
4499+
* corresponding change in the data file is flushed to disk, but we
4500+
* have to do the same here since we're bypassing the buffer manager.
4501+
*
4502+
* Doing this before deleting the files means that if a deletion fails
4503+
* for some reason, you cannot start up the system even after restart,
4504+
* until you fix the underlying situation so that the deletion will
4505+
* succeed. Alternatively, we could update the minimum recovery point
4506+
* after deletion, but that would leave a small window where the
4507+
* WAL-first rule would be violated.
4508+
*/
4509+
XLogFlush(lsn);
44964510

4497-
for (fork = 0; fork <= MAX_FORKNUM; fork++)
4511+
for (i = 0; i < xlrec->nrels; i++)
44984512
{
4499-
XLogDropRelation(xlrec->xnodes[i], fork);
4500-
smgrdounlink(srel, fork, false, true);
4513+
SMgrRelation srel = smgropen(xlrec->xnodes[i]);
4514+
ForkNumber fork;
4515+
4516+
for (fork = 0; fork <= MAX_FORKNUM; fork++)
4517+
{
4518+
XLogDropRelation(xlrec->xnodes[i], fork);
4519+
smgrdounlink(srel, fork, false, true);
4520+
}
4521+
smgrclose(srel);
45014522
}
4502-
smgrclose(srel);
45034523
}
45044524

45054525
/*
45064526
* We issue an XLogFlush() for the same reason we emit ForceSyncCommit()
4507-
* in normal operation. For example, in DROP DATABASE, we delete all the
4508-
* files belonging to the database, and then commit the transaction. If we
4509-
* crash after all the files have been deleted but before the commit, you
4510-
* have an entry in pg_database without any files. To minimize the window
4527+
* in normal operation. For example, in CREATE DATABASE, we copy all files
4528+
* from the template database, and then commit the transaction. If we
4529+
* crash after all the files have been copied but before the commit, you
4530+
* have files in the data directory without an entry in pg_database. To
4531+
* minimize the window
45114532
* for that, we use ForceSyncCommit() to rush the commit record to disk as
45124533
* quick as possible. We have the same window during recovery, and forcing
45134534
* 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
@@ -474,6 +474,24 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
474474
*/
475475
smgrcreate(reln, MAIN_FORKNUM, true);
476476

477+
/*
478+
* Before we perform the truncation, update minimum recovery point
479+
* to cover this WAL record. Once the relation is truncated, there's
480+
* no going back. The buffer manager enforces the WAL-first rule
481+
* for normal updates to relation files, so that the minimum recovery
482+
* point is always updated before the corresponding change in the
483+
* data file is flushed to disk. We have to do the same manually
484+
* here.
485+
*
486+
* Doing this before the truncation means that if the truncation fails
487+
* for some reason, you cannot start up the system even after restart,
488+
* until you fix the underlying situation so that the truncation will
489+
* succeed. Alternatively, we could update the minimum recovery point
490+
* after truncation, but that would leave a small window where the
491+
* WAL-first rule could be violated.
492+
*/
493+
XLogFlush(lsn);
494+
477495
smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno, false);
478496

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

0 commit comments

Comments
 (0)