Protect against torn pages when deleting GIN list pages.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 8 May 2014 11:43:04 +0000 (14:43 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 8 May 2014 11:43:04 +0000 (14:43 +0300)
To-be-deleted list pages contain no useful information, as they are being
deleted, but we must still protect the writes from being torn by a crash
after a partial write. To do that, re-initialize the pages on WAL replay.

Jeff Janes caught this with a test program to test partial writes.
Backpatch to all supported versions.

src/backend/access/gin/ginxlog.c

index f43ac50115bb7fbeae185288f01ab253f212fe72..92b483b3fc32bb53dc20ff5609caf7ed87633301 100644 (file)
@@ -682,25 +682,25 @@ ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
     * cannot get past a reader that is on, or due to visit, any page we are
     * going to delete.  New incoming readers will block behind our metapage
     * lock and then see a fully updated page list.
+    *
+    * No full-page images are taken of the deleted pages. Instead, they are
+    * re-initialized as empty, deleted pages. Their right-links don't need to
+    * be preserved, because no new readers can see the pages, as explained
+    * above.
     */
    for (i = 0; i < data->ndeleted; i++)
    {
-       Buffer      buffer = XLogReadBuffer(data->node, data->toDelete[i], false);
+       Buffer      buffer;
+       Page        page;
 
-       if (BufferIsValid(buffer))
-       {
-           Page        page = BufferGetPage(buffer);
-
-           if (lsn > PageGetLSN(page))
-           {
-               GinPageGetOpaque(page)->flags = GIN_DELETED;
+       buffer = XLogReadBuffer(data->node, data->toDelete[i], true);
+       page = BufferGetPage(buffer);
+       GinInitBuffer(buffer, GIN_DELETED);
 
-               PageSetLSN(page, lsn);
-               MarkBufferDirty(buffer);
-           }
+       PageSetLSN(page, lsn);
+       MarkBufferDirty(buffer);
 
-           UnlockReleaseBuffer(buffer);
-       }
+       UnlockReleaseBuffer(buffer);
    }
    UnlockReleaseBuffer(metabuffer);
 }