Update minimum recovery point on truncation.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 10 Dec 2012 13:54:42 +0000 (15:54 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 10 Dec 2012 14:56:54 +0000 (16:56 +0200)
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
src/backend/catalog/storage.c

index 33431585d865c0345a458018c72fa2ae9c820e73..dfa836fd2f5ffdb85aac3635d649d0721291f7e1 100644 (file)
@@ -4489,25 +4489,46 @@ xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid, XLogRecPtr lsn)
        }
 
        /* 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);
        }
 
        /*
         * We issue an XLogFlush() for the same reason we emit ForceSyncCommit()
-        * in normal operation. For example, in DROP DATABASE, we delete all the
-        * files belonging to the database, and then commit the transaction. If we
-        * crash after all the files have been deleted but before the commit, you
-        * have an entry in pg_database without any files. To minimize the window
+        * in normal operation. For example, in CREATE DATABASE, we copy all files
+        * from the template database, and then commit the transaction. If we
+        * crash after all the files have been copied but before the commit, you
+        * have files in the data directory without an entry in pg_database. To
+        * minimize the window
         * for that, we use ForceSyncCommit() to rush the commit record to disk as
         * quick as possible. We have the same window during recovery, and forcing
         * an XLogFlush() (which updates minRecoveryPoint during recovery) helps
index 0f99516a6948631528194bce97f978a651d5a935..de55047e419a42ffad6d09b87199610e59c84345 100644 (file)
@@ -474,6 +474,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 */