diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 5016181fd7..9fb68cd7b3 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8047,7 +8047,7 @@ heap_xlog_cleanup_info(XLogReaderState *record) } /* - * Handles HEAP2_CLEAN record type + * Handles XLOG_HEAP2_CLEAN record type */ static void heap_xlog_clean(XLogReaderState *record) @@ -8055,7 +8055,6 @@ heap_xlog_clean(XLogReaderState *record) XLogRecPtr lsn = record->EndRecPtr; xl_heap_clean *xlrec = (xl_heap_clean *) XLogRecGetData(record); Buffer buffer; - Size freespace = 0; RelFileNode rnode; BlockNumber blkno; XLogRedoAction action; @@ -8107,8 +8106,6 @@ heap_xlog_clean(XLogReaderState *record) nowdead, ndead, nowunused, nunused); - freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */ - /* * Note: we don't worry about updating the page's prunability hints. * At worst this will cause an extra prune cycle to occur soon. @@ -8117,18 +8114,24 @@ heap_xlog_clean(XLogReaderState *record) PageSetLSN(page, lsn); MarkBufferDirty(buffer); } + if (BufferIsValid(buffer)) + { + Size freespace = PageGetHeapFreeSpace(BufferGetPage(buffer)); + UnlockReleaseBuffer(buffer); - /* - * Update the FSM as well. - * - * XXX: Don't do this if the page was restored from full page image. We - * don't bother to update the FSM in that case, it doesn't need to be - * totally accurate anyway. - */ - if (action == BLK_NEEDS_REDO) + /* + * After cleaning records from a page, it's useful to update the FSM + * about it, as it may cause the page become target for insertions + * later even if vacuum decides not to visit it (which is possible if + * gets marked all-visible.) + * + * Do this regardless of a full-page image being applied, since the + * FSM data is not in the page anyway. + */ XLogRecordPageWithFreeSpace(rnode, blkno, freespace); + } } /* @@ -8201,9 +8204,34 @@ heap_xlog_visible(XLogReaderState *record) * wal_log_hints enabled.) */ } + if (BufferIsValid(buffer)) + { + Size space = PageGetFreeSpace(BufferGetPage(buffer)); + UnlockReleaseBuffer(buffer); + /* + * Since FSM is not WAL-logged and only updated heuristically, it + * easily becomes stale in standbys. If the standby is later promoted + * and runs VACUUM, it will skip updating individual free space + * figures for pages that became all-visible (or all-frozen, depending + * on the vacuum mode,) which is troublesome when FreeSpaceMapVacuum + * propagates too optimistic free space values to upper FSM layers; + * later inserters try to use such pages only to find out that they + * are unusable. This can cause long stalls when there are many such + * pages. + * + * Forestall those problems by updating FSM's idea about a page that + * is becoming all-visible or all-frozen. + * + * Do this regardless of a full-page image being applied, since the + * FSM data is not in the page anyway. + */ + if (xlrec->flags & VISIBILITYMAP_VALID_BITS) + XLogRecordPageWithFreeSpace(rnode, blkno, space); + } + /* * Even if we skipped the heap page update due to the LSN interlock, it's * still safe to update the visibility map. Any WAL record that clears