@@ -92,17 +92,18 @@ To minimize lock/unlock traffic, an index scan always searches a leaf page
92
92
to identify all the matching items at once, copying their heap tuple IDs
93
93
into backend-local storage. The heap tuple IDs are then processed while
94
94
not holding any page lock within the index. We do continue to hold a pin
95
- on the leaf page, to protect against concurrent deletions (see below).
96
- In this state the scan is effectively stopped "between" pages, either
97
- before or after the page it has pinned. This is safe in the presence of
98
- concurrent insertions and even page splits, because items are never moved
99
- across pre-existing page boundaries --- so the scan cannot miss any items
100
- it should have seen, nor accidentally return the same item twice. The scan
101
- must remember the page's right-link at the time it was scanned, since that
102
- is the page to move right to; if we move right to the current right-link
103
- then we'd re-scan any items moved by a page split. We don't similarly
104
- remember the left-link, since it's best to use the most up-to-date
105
- left-link when trying to move left (see detailed move-left algorithm below).
95
+ on the leaf page in some circumstances, to protect against concurrent
96
+ deletions (see below). In this state the scan is effectively stopped
97
+ "between" pages, either before or after the page it has pinned. This is
98
+ safe in the presence of concurrent insertions and even page splits, because
99
+ items are never moved across pre-existing page boundaries --- so the scan
100
+ cannot miss any items it should have seen, nor accidentally return the same
101
+ item twice. The scan must remember the page's right-link at the time it
102
+ was scanned, since that is the page to move right to; if we move right to
103
+ the current right-link then we'd re-scan any items moved by a page split.
104
+ We don't similarly remember the left-link, since it's best to use the most
105
+ up-to-date left-link when trying to move left (see detailed move-left
106
+ algorithm below).
106
107
107
108
In most cases we release our lock and pin on a page before attempting
108
109
to acquire pin and lock on the page we are moving to. In a few places
@@ -154,25 +155,37 @@ starts. This is not necessary for correctness in terms of the btree index
154
155
operations themselves; as explained above, index scans logically stop
155
156
"between" pages and so can't lose their place. The reason we do it is to
156
157
provide an interlock between non-full VACUUM and indexscans. Since VACUUM
157
- deletes index entries before deleting tuples, the super-exclusive lock
158
- guarantees that VACUUM can't delete any heap tuple that an indexscanning
159
- process might be about to visit. (This guarantee works only for simple
160
- indexscans that visit the heap in sync with the index scan, not for bitmap
161
- scans. We only need the guarantee when using non-MVCC snapshot rules; in
162
- an MVCC snapshot, it wouldn't matter if the heap tuple were replaced with
163
- an unrelated tuple at the same TID, because the new tuple wouldn't be
164
- visible to our scan anyway.)
165
-
166
- Because a page can be split even while someone holds a pin on it, it is
167
- possible that an indexscan will return items that are no longer stored on
168
- the page it has a pin on, but rather somewhere to the right of that page.
169
- To ensure that VACUUM can't prematurely remove such heap tuples, we require
170
- btbulkdelete to obtain super-exclusive lock on every leaf page in the index,
171
- even pages that don't contain any deletable tuples. This guarantees that
172
- the btbulkdelete call cannot return while any indexscan is still holding
173
- a copy of a deleted index tuple. Note that this requirement does not say
174
- that btbulkdelete must visit the pages in any particular order. (See also
175
- on-the-fly deletion, below.)
158
+ deletes index entries before reclaiming heap tuple line pointers, the
159
+ super-exclusive lock guarantees that VACUUM can't reclaim for re-use a
160
+ line pointer that an indexscanning process might be about to visit. This
161
+ guarantee works only for simple indexscans that visit the heap in sync
162
+ with the index scan, not for bitmap scans. We only need the guarantee
163
+ when using non-MVCC snapshot rules; when using an MVCC snapshot, it
164
+ doesn't matter if the heap tuple is replaced with an unrelated tuple at
165
+ the same TID, because the new tuple won't be visible to our scan anyway.
166
+ Therefore, a scan using an MVCC snapshot which has no other confounding
167
+ factors will not hold the pin after the page contents are read. The
168
+ current reasons for exceptions, where a pin is still needed, are if the
169
+ index is not WAL-logged or if the scan is an index-only scan. If later
170
+ work allows the pin to be dropped for all cases we will be able to
171
+ simplify the vacuum code, since the concept of a super-exclusive lock
172
+ for btree indexes will no longer be needed.
173
+
174
+ Because a pin is not always held, and a page can be split even while
175
+ someone does hold a pin on it, it is possible that an indexscan will
176
+ return items that are no longer stored on the page it has a pin on, but
177
+ rather somewhere to the right of that page. To ensure that VACUUM can't
178
+ prematurely remove such heap tuples, we require btbulkdelete to obtain a
179
+ super-exclusive lock on every leaf page in the index, even pages that
180
+ don't contain any deletable tuples. Any scan which could yield incorrect
181
+ results if the tuple at a TID matching the the scan's range and filter
182
+ conditions were replaced by a different tuple while the scan is in
183
+ progress must hold the pin on each index page until all index entries read
184
+ from the page have been processed. This guarantees that the btbulkdelete
185
+ call cannot return while any indexscan is still holding a copy of a
186
+ deleted index tuple if the scan could be confused by that. Note that this
187
+ requirement does not say that btbulkdelete must visit the pages in any
188
+ particular order. (See also on-the-fly deletion, below.)
176
189
177
190
There is no such interlocking for deletion of items in internal pages,
178
191
since backends keep no lock nor pin on a page they have descended past.
@@ -396,8 +409,12 @@ that this breaks the interlock between VACUUM and indexscans, but that is
396
409
not so: as long as an indexscanning process has a pin on the page where
397
410
the index item used to be, VACUUM cannot complete its btbulkdelete scan
398
411
and so cannot remove the heap tuple. This is another reason why
399
- btbulkdelete has to get super-exclusive lock on every leaf page, not only
400
- the ones where it actually sees items to delete.
412
+ btbulkdelete has to get a super-exclusive lock on every leaf page, not
413
+ only the ones where it actually sees items to delete. So that we can
414
+ handle the cases where we attempt LP_DEAD flagging for a page after we
415
+ have released its pin, we remember the LSN of the index page when we read
416
+ the index tuples from it; we do not attempt to flag index tuples as dead
417
+ if the we didn't hold the pin the entire time and the LSN has changed.
401
418
402
419
WAL Considerations
403
420
------------------
@@ -462,7 +479,7 @@ metapage update (of the "fast root" link) is performed and logged as part
462
479
of the insertion into the parent level. When splitting the root page, the
463
480
metapage update is handled as part of the "new root" action.
464
481
465
- Each step in page deletion are logged as separate WAL entries : marking the
482
+ Each step in page deletion is logged as a separate WAL entry : marking the
466
483
leaf as half-dead and removing the downlink is one record, and unlinking a
467
484
page is a second record. If vacuum is interrupted for some reason, or the
468
485
system crashes, the tree is consistent for searches and insertions. The
0 commit comments