Re: index prefetching

From: Yuhang Qiu <iamqyh(at)gmail(dot)com>
To: Peter Geoghegan <pg(at)bowt(dot)ie>
Cc: Rui Zhao <zhaorui126(at)gmail(dot)com>, Tomas Vondra <tomas(at)vondra(dot)me>, Andres Freund <andres(at)anarazel(dot)de>, Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>, Thomas Munro <thomas(dot)munro(at)gmail(dot)com>, Nazir Bilal Yavuz <byavuz81(at)gmail(dot)com>, Robert Haas <robertmhaas(at)gmail(dot)com>, Melanie Plageman <melanieplageman(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, Georgios <gkokolatos(at)protonmail(dot)com>, Konstantin Knizhnik <knizhnik(at)garret(dot)ru>, Dilip Kumar <dilipbalaut(at)gmail(dot)com>
Subject: Re: index prefetching
Date: 2026-09-23 06:43:53
Message-ID: ECCA330B-75BB-43BF-96E9-9049BF213019@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Peter,

This is a cool feature, and I like it. I spent some time catching up
on the thread and reviewing the code.

The core prefetch logic LGTM. I found a few issues elsewhere.

The GiST cleanup-lock fix appears to be incomplete on standbys.
gistvacuumpage() already accounts for the root changing from a leaf page
to an internal page while a scan holds a pin, but it only emits VACUUM
WAL when it actually deletes index tuples from a leaf page.

After a root split, a standby cursor may still hold a batch copied from
the old root leaf, along with a pin on the root buffer. VACUUM acquires
a cleanup lock on the now-internal root on the primary, but there is no
corresponding WAL record to enforce that interlock on the standby. Heap
cleanup and visibility map updates can therefore be replayed while the
cursor still holds the old batch. When the cursor resumes and checks
visibility for the remaining items, it can see the new all-visible bits,
skip the heap checks, and return entries for deleted tuples. The
necessary cleanup-lock barriers need to be WAL-logged even when there
are no tuple deletions.

There is also an inconsistency in the batch layout initialization
comments: relscan.h says the dynamic opaque area size is set in
ambeginscan, whereas spgrescan() explains that it must wait until
xs_want_itup is known.

While reviewing the code, I also found an unrelated issue in the
baseline's cmp_orderbyvals():
```c
else if (anulls[i] && bnulls[i])
return 0;
```

When both values for the current key are NULL, this ignores the
remaining sort keys. In a lossy KNN scan, that can make the executor
incorrectly conclude that the index-provided distances are exact and
return tuples in the wrong order. I suggest changing `return 0` to
`continue` so that the remaining keys are compared.

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Chao Li 2026-09-23 07:06:29 Re: [PATCH] pg_combinebackup: make the OID range check in parse_oid() effective
Previous Message Chao Li 2026-09-23 06:32:28 Re: [PATCH] Misleading error message for REPACK USING INDEX on shared catalogs