From 7e0d8d8457d922a63292c39862738969cac6c418 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 10 Dec 2012 15:54:42 +0200 Subject: [PATCH] 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. --- src/backend/access/transam/xact.c | 40 +++++++++++++++++++++++-------- src/backend/catalog/storage.c | 18 ++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index e33e5a912c8..89d30f94ec4 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -4255,7 +4255,7 @@ xactGetCommittedChildren(TransactionId **ptr) */ static void -xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid) +xact_redo_commit(XLogRecPtr lsn, xl_xact_commit *xlrec, TransactionId xid) { TransactionId *sub_xids; TransactionId max_xid; @@ -4280,17 +4280,37 @@ xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid) } /* Make sure files supposed to be dropped are dropped */ - for (i = 0; i < xlrec->nrels; i++) + if (xlrec->nrels > 0) { - SMgrRelation srel = smgropen(xlrec->xnodes[i]); - ForkNumber fork; + /* + * First update minimum recovery point to cover this WAL record. Once + * a relation is deleted, there's no going back. The buffer manager + * enforces the WAL-first rule for normal updates to relation files, + * so that the minimum recovery point is always updated before the + * corresponding change in the data file is flushed to disk, but we + * have to do the same here since we're bypassing the buffer manager. + * + * Doing this before deleting the files means that if a deletion fails + * for some reason, you cannot start up the system even after restart, + * until you fix the underlying situation so that the deletion will + * succeed. Alternatively, we could update the minimum recovery point + * after deletion, but that would leave a small window where the + * WAL-first rule would be violated. + */ + XLogFlush(lsn); - for (fork = 0; fork <= MAX_FORKNUM; fork++) + for (i = 0; i < xlrec->nrels; i++) { - XLogDropRelation(xlrec->xnodes[i], fork); - smgrdounlink(srel, fork, false, true); + SMgrRelation srel = smgropen(xlrec->xnodes[i]); + ForkNumber fork; + + for (fork = 0; fork <= MAX_FORKNUM; fork++) + { + XLogDropRelation(xlrec->xnodes[i], fork); + smgrdounlink(srel, fork, false, true); + } + smgrclose(srel); } - smgrclose(srel); } } @@ -4346,7 +4366,7 @@ xact_redo(XLogRecPtr lsn, XLogRecord *record) { xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record); - xact_redo_commit(xlrec, record->xl_xid); + xact_redo_commit(lsn, xlrec, record->xl_xid); } else if (info == XLOG_XACT_ABORT) { @@ -4364,7 +4384,7 @@ xact_redo(XLogRecPtr lsn, XLogRecord *record) { xl_xact_commit_prepared *xlrec = (xl_xact_commit_prepared *) XLogRecGetData(record); - xact_redo_commit(&xlrec->crec, xlrec->xid); + xact_redo_commit(lsn, &xlrec->crec, xlrec->xid); RemoveTwoPhaseFile(xlrec->xid, false); } else if (info == XLOG_XACT_ABORT_PREPARED) diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index 326e76f283e..2e279c7ed85 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -426,6 +426,24 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record) */ smgrcreate(reln, MAIN_FORKNUM, true); + /* + * Before we perform the truncation, update minimum recovery point + * to cover this WAL record. Once the relation is truncated, there's + * no going back. The buffer manager enforces the WAL-first rule + * for normal updates to relation files, so that the minimum recovery + * point is always updated before the corresponding change in the + * data file is flushed to disk. We have to do the same manually + * here. + * + * Doing this before the truncation means that if the truncation fails + * for some reason, you cannot start up the system even after restart, + * until you fix the underlying situation so that the truncation will + * succeed. Alternatively, we could update the minimum recovery point + * after truncation, but that would leave a small window where the + * WAL-first rule could be violated. + */ + XLogFlush(lsn); + smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno, false); /* Also tell xlogutils.c about it */ -- 2.47.2