Skip to content

Commit 46c450c

Browse files
committed
Do all-visible handling in lazy_vacuum_page() outside its critical section.
Since fdf9e21 lazy_vacuum_page() rechecks the all-visible status of pages in the second pass over the heap. It does so inside a critical section, but both visibilitymap_test() and heap_page_is_all_visible() perform operations that should not happen inside one. The former potentially performs IO and both potentially do memory allocations. To fix, simply move all the all-visible handling outside the critical section. Doing so means that the PD_ALL_VISIBLE on the page won't be included in the full page image of the HEAP2_CLEAN record anymore. But that's fine, the flag will be set by the HEAP2_VISIBLE logged later. Backpatch to 9.3 where the problem was introduced. The bug only came to light due to the assertion added in 4a170ee and isn't likely to cause problems in production scenarios. The worst outcome is a avoidable PANIC restart. This also gets rid of the difference in the order of operations between master and standby mentioned in 2a8e1ac. Per reports from David Leverton and Keith Fiske in bug #10533.
1 parent c1f8fb9 commit 46c450c

File tree

1 file changed

+17
-9
lines changed

1 file changed

+17
-9
lines changed

src/backend/commands/vacuumlazy.c

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,13 +1206,6 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
12061206

12071207
PageRepairFragmentation(page);
12081208

1209-
/*
1210-
* Now that we have removed the dead tuples from the page, once again
1211-
* check if the page has become all-visible.
1212-
*/
1213-
if (heap_page_is_all_visible(buffer, &visibility_cutoff_xid))
1214-
PageSetAllVisible(page);
1215-
12161209
/*
12171210
* Mark buffer dirty before we write WAL.
12181211
*/
@@ -1230,6 +1223,23 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
12301223
PageSetLSN(page, recptr);
12311224
}
12321225

1226+
/*
1227+
* End critical section, so we safely can do visibility tests (which
1228+
* possibly need to perform IO and allocate memory!). If we crash now the
1229+
* page (including the corresponding vm bit) might not be marked all
1230+
* visible, but that's fine. A later vacuum will fix that.
1231+
*/
1232+
END_CRIT_SECTION();
1233+
1234+
/*
1235+
* Now that we have removed the dead tuples from the page, once again
1236+
* check if the page has become all-visible. The page is already marked
1237+
* dirty, exclusively locked, and, if needed, a full page image has been
1238+
* emitted in the log_heap_clean() above.
1239+
*/
1240+
if (heap_page_is_all_visible(buffer, &visibility_cutoff_xid))
1241+
PageSetAllVisible(page);
1242+
12331243
/*
12341244
* All the changes to the heap page have been done. If the all-visible
12351245
* flag is now set, also set the VM bit.
@@ -1242,8 +1252,6 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
12421252
visibility_cutoff_xid);
12431253
}
12441254

1245-
END_CRIT_SECTION();
1246-
12471255
return tupindex;
12481256
}
12491257

0 commit comments

Comments
 (0)