Re: BUG #17255: Server crashes in index_delete_sort_cmp() due to race condition with vacuum
Andres Freund <andres@anarazel.de>
From: Andres Freund <andres@anarazel.de>
To: Peter Geoghegan <pg@bowt.ie>
Cc: Dmitry Dolgov <9erthalion6@gmail.com>, Alexander Lakhin <exclusion@gmail.com>, Matthias van de Meent <boekewurm+postgres@gmail.com>, PostgreSQL mailing lists <pgsql-bugs@lists.postgresql.org>
Date: 2022-03-10T00:46:37Z
Lists: pgsql-bugs
Hi,
On 2022-03-03 19:31:32 -0800, Peter Geoghegan wrote:
> Attached is a new revision of my fix. This is more or less a
> combination of my v4 fix from November 12 [1] and Andres'
> already-committed fix (commit 18b87b20), rebased on top of HEAD. This
> patch was originally a bugfix, but now it's technically just
> refactoring/hardening of the logic in pruneheap.c. It hasn't changed
> all that much, though.
Perhaps worth discussing outside of -bugs?
> We're still doing an up-front scan of the heap page to precalculate
> HTSV for each tuple (no change from commit 18b87b20), but we no longer
> do that on correctness grounds. It is purely a performance thing now.
> Note that I am making a working assumption that that still makes sense
> for now (I think that it probably does, but I haven't yet verified it
> for myself).
I'd be surprised if not.
> We now do "3 passes" over the page. The first is the aforementioned
> "precalculate HTSV" pass, the second is for determining the extent of
> HOT chains, and the third is for any remaining disconnected/orphaned
> HOT chains. I suppose that that's okay, since the amount of work has
> hardly increased in proportion to this "extra pass over the page". Not
> 100% sure about everything myself right now, though. I guess that "3
> passes" might be considered excessive.
We should be able to optimize away the third pass in the majority of cases, by
keeping track of the number of tuples visited or such. That seems like it
might be worth doing?
> + /*
> + * Start from the root item. Mark it as valid up front, since root items
> + * are always processed here (not as disconnected tuples in third pass
> + * over page).
> + */
> + prstate->visited[rootoffnum] = true;
> offnum = rootoffnum;
> + nchain = 0;
I wonder if it'd be clearer if we dealt with redirects outside of the
loop. Would make it easier to assert that the target of a redirect may not be
unused / !heap-only?
> +
> + /*
> + * Remember the offnum of the last DEAD tuple in this HOT
> + * chain. To keep things simple, don't treat heap-only tuples
> + * from a HOT chain as DEAD unless they're only preceded by
> + * other DEAD tuples (in addition to actually being DEAD).
s/actually/themselves/?
> + * Remaining tuples that appear DEAD (but don't get treated as
> + * such by us) are from concurrently aborting updaters.
I don't understand this bit. A DEAD tuple following a RECENTLY_DEAD one won't
be removed now, and doesn't need to involve a concurrent abort? Are you
thinking of "remaining" as the tuples not referenced in the previous sentences?
> + * VACUUM will ask us to prune the heap page again when it
> + * sees that there is a DEAD tuple left behind, but that would
> + * be necessary regardless of our approach here.
> + */
Only as long as we do another set of HTSV calls...
> case HEAPTUPLE_LIVE:
> case HEAPTUPLE_INSERT_IN_PROGRESS:
> + pastlatestdead = true; /* no further DEAD tuples in CHAIN */
If we don't do anything to the following tuples, why don't we just abort here?
I assume it is because we'd then treat them as disconnected? That should
probably be called out...
> - }
> - else if (nchain < 2 && ItemIdIsRedirected(rootlp))
> - {
> - /*
> - * We found a redirect item that doesn't point to a valid follow-on
> - * item. This can happen if the loop in heap_page_prune caused us to
> - * visit the dead successor of a redirect item before visiting the
> - * redirect item. We can clean up by setting the redirect item to
> - * DEAD state.
> - */
> - heap_prune_record_dead(prstate, rootoffnum);
> +
> + return ndeleted;
> }
Could there be such tuples from before a pg_upgrade? Do we need to deal with
them somehow?
Greetings,
Andres Freund
Commits
-
vacuumlazy.c: Standardize rel_pages terminology.
- e370f100f05d 15.0 landed
-
vacuumlazy.c: document vistest and OldestXmin.
- 73f6ec3d3c8d 15.0 landed
-
heap pruning: Only call BufferGetBlockNumber() once.
- c702d656a283 15.0 landed
-
Fix possible HOT corruption when RECENTLY_DEAD changes to DEAD while pruning.
- dad1539aec28 14.2 landed
- 18b87b201f73 15.0 landed
-
Assert redirect pointers are sensible after heap_page_prune().
- bb42bfb5ccb5 15.0 landed
-
Remove tupgone special case from vacuumlazy.c.
- 8523492d4e34 14.0 cited
-
Recycle nbtree pages deleted during same VACUUM.
- 9dd963ae2534 14.0 cited
-
snapshot scalability: Don't compute global horizons while building snapshots.
- dc7420c2c927 14.0 cited
-
Refactor heap_page_prune so that instead of changing item states on-the-fly,
- 6f10eb21118f 8.4.0 cited