Fix WAL redo of FSM truncation. We can't call smgrtruncate() during WAL
authorHeikki Linnakangas
Wed, 1 Oct 2008 08:12:14 +0000 (08:12 +0000)
committerHeikki Linnakangas
Wed, 1 Oct 2008 08:12:14 +0000 (08:12 +0000)
replay, because it tries to XLogInsert().

src/backend/storage/freespace/freespace.c

index d6296156cfc460c312de733a8817a98a66bfb85e..3e065eab97bed0a5fc18bd4e6c8df0faed4ab8cb 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.62 2008/09/30 14:15:58 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.63 2008/10/01 08:12:14 heikki Exp $
  *
  *
  * NOTES:
@@ -123,6 +123,8 @@ static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot,
 static BlockNumber fsm_search(Relation rel, uint8 min_cat);
 static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof);
 
+static void fsm_redo_truncate(xl_fsm_truncate *xlrec);
+
 
 /******** Public API ********/
 
@@ -281,7 +283,7 @@ FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks)
     * record, but that's not enough to zero out the last remaining FSM page.
     * (if we didn't need to zero out anything above, we can skip this)
     */
-   if (!rel->rd_istemp && !InRecovery && first_removed_slot != 0)
+   if (!rel->rd_istemp && first_removed_slot != 0)
    {
        xl_fsm_truncate xlrec;
        XLogRecData     rdata;
@@ -310,8 +312,8 @@ FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks)
     * Need to invalidate the relcache entry, because rd_fsm_nblocks_cache
     * seen by other backends is no longer valid.
     */
-   if (!InRecovery)
-       CacheInvalidateRelcache(rel);
+   CacheInvalidateRelcache(rel);
+
    rel->rd_fsm_nblocks_cache = new_nfsmblocks;
 }
 
@@ -762,6 +764,43 @@ fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof_p)
 
 /****** WAL-logging ******/
 
+static void
+fsm_redo_truncate(xl_fsm_truncate *xlrec)
+{
+   FSMAddress  first_removed_address;
+   uint16      first_removed_slot;
+   BlockNumber fsmblk;
+   Buffer      buf;
+
+   /* Get the location in the FSM of the first removed heap block */
+   first_removed_address = fsm_get_location(xlrec->nheapblocks,
+                                            &first_removed_slot);
+   fsmblk = fsm_logical_to_physical(first_removed_address);
+
+   /*
+    * Zero out the tail of the last remaining FSM page. We rely on the
+    * replay of the smgr truncation record to remove completely unused
+    * pages.
+    */
+   buf = XLogReadBufferWithFork(xlrec->node, FSM_FORKNUM, fsmblk, false);
+   if (BufferIsValid(buf))
+   {
+       fsm_truncate_avail(BufferGetPage(buf), first_removed_slot);
+       MarkBufferDirty(buf);
+       UnlockReleaseBuffer(buf);
+   }
+   else
+   {
+       /*
+        * The page doesn't exist. Because FSM extensions are not WAL-logged,
+        * it's normal to have a truncation record for a page that doesn't
+        * exist. Tell xlogutils.c not to PANIC at the end of recovery
+        * because of the missing page
+        */
+       XLogTruncateRelation(xlrec->node, FSM_FORKNUM, fsmblk);
+   }
+}
+
 void
 fsm_redo(XLogRecPtr lsn, XLogRecord *record)
 {
@@ -770,15 +809,7 @@ fsm_redo(XLogRecPtr lsn, XLogRecord *record)
    switch (info)
    {
        case XLOG_FSM_TRUNCATE:
-           {
-               xl_fsm_truncate *xlrec;
-               Relation rel;
-
-               xlrec = (xl_fsm_truncate *) XLogRecGetData(record);
-               rel = CreateFakeRelcacheEntry(xlrec->node);
-               FreeSpaceMapTruncateRel(rel, xlrec->nheapblocks);
-               FreeFakeRelcacheEntry(rel);
-           }
+           fsm_redo_truncate((xl_fsm_truncate *) XLogRecGetData(record));
            break;
        default:
            elog(PANIC, "fsm_redo: unknown op code %u", info);