Re: Fix stale relcache fields for TOAST indexes during REINDEX CONCURRENTLY

From: Chao Li <li(dot)evan(dot)chao(at)gmail(dot)com>
To: Bharath Rupireddy <bharath(dot)rupireddyforpostgres(at)gmail(dot)com>
Cc: PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, saicharh(at)amazon(dot)com, Michael Paquier <michael(at)paquier(dot)xyz>
Subject: Re: Fix stale relcache fields for TOAST indexes during REINDEX CONCURRENTLY
Date: 2026-10-28 00:14:17
Message-ID: D9DAB877-D44A-4B7B-8982-FBA4E6EFF37E@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

> On Oct 28, 2026, at 02:44, Bharath Rupireddy <bharath(dot)rupireddyforpostgres(at)gmail(dot)com> wrote:
>
> Hi,
>
> I received an off-list report of an "ERROR: missing chunk number x for
> toast value xxxx in pg_toast_xxxx" during REINDEX CONCURRENTLY.
>
> After the concurrent index rebuild marked the new index ready for
> inserts, the backend that kept the TOAST indexes (both new and old)
> open for inserts doesn't reload the new TOAST index entry as part of
> the invalidation message processing. This leaves the new index not
> ready for inserts in that backend's relcache, making the chunks miss
> in the new index. This later shows up as the [1] when the value is
> read.
>
> Please find the attached patch that allows the TOAST indexes also to
> re-read the pg_index row and reload the relcache fields in
> RelationReloadIndexInfo(). Note that this change also reloads the
> relcache fields for the TOAST indexes of the logical replication
> conflict log tables, which seems fine IMO.
>
> I think this needs to be back-patched to all the supported branches.
> The check in RelationReloadIndexInfo() was introduced in 8ec943856a4,
> and the issue was uncovered by 5dc92b844e68, which added REINDEX
> CONCURRENTLY.
>
> Thoughts?
>
> [1] ERROR: missing chunk number 0 for toast value 20810 in pg_toast_20801
>
> --
> Bharath Rupireddy
> Amazon Web Services: https://aws.amazon.com
> <v1-0001-Fix-stale-relcache-fields-for-TOAST-indexes-durin.patch>

The fix looks good to me. IsSystemRelation() returns true for system catalogs and all TOAST tables and indexes, including those belonging to user tables, while IsCatalogRelation() returns true only for system catalogs and their associated relations, excluding user-table TOAST indexes. So, with this fix, the TOAST index's indisready field gets refreshed.

I see one issue with the test. The test evaluates:
```
SELECT id, length(val) FROM reind_toast ORDER BY id;
```

text_lengh() has an optimization:
```
static int32
text_length(Datum str)
{
/* fastpath when max encoding length is one */
if (pg_database_encoding_max_length() == 1)
return (toast_raw_datum_size(str) - VARHDRSZ);
```

For single-byte encodings, length(val) won't actually fetch the TOAST values, so the test won't detect missing TOAST chunks. I confirmed this by running:
```
% make -C src/test/modules/injection_points check ISOLATION=reindex_concurrently_toast LANG=C ENCODING=LATIN1
```

The test still passed after I reverted the fix.

To force fetching the TOAST data, instead of evaluating the length of val, I think we can evaluate its first char. I made the following change locally:
```
diff --git a/src/test/modules/injection_points/expected/reindex_concurrently_toast.out b/src/test/modules/injection_points/expected/reindex_concurrently_toast.out
index 4ce089806b0..3892b8df413 100644
--- a/src/test/modules/injection_points/expected/reindex_concurrently_toast.out
+++ b/src/test/modules/injection_points/expected/reindex_concurrently_toast.out
@@ -74,11 +74,11 @@ injection_points_wakeup

step reindex: <... completed>
step reindexed:
-step read: SELECT id, length(val) FROM reind_toast ORDER BY id;
-id|length
---+------
- 1| 10000
- 2| 10000
- 3| 10000
+step read: SELECT id, left(val, 1) FROM reind_toast ORDER BY id;
+id|left
+--+----
+ 1|x
+ 2|y
+ 3|z
(3 rows)

diff --git a/src/test/modules/injection_points/specs/reindex_concurrently_toast.spec b/src/test/modules/injection_points/specs/reindex_concurrently_toast.spec
index ba34a81adef..0ce146be848 100644
--- a/src/test/modules/injection_points/specs/reindex_concurrently_toast.spec
+++ b/src/test/modules/injection_points/specs/reindex_concurrently_toast.spec
@@ -52,7 +52,7 @@ step ins1 { INSERT INTO reind_toast VALUES (2, repeat('y', 10000)); }
step inserted { }
# Written after the validation, so only a maintained index has it.
step ins2 { INSERT INTO reind_toast VALUES (3, repeat('z', 10000)); }
-step read { SELECT id, length(val) FROM reind_toast ORDER BY id; }
+step read { SELECT id, left(val, 1) FROM reind_toast ORDER BY id; }

# Holds the rebuild back until the writer has the TOAST indexes open.
session s3
```

With this change, the test fails without the fix and passes with it.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message David Rowley 2026-10-28 00:31:00 Re: SUM(int2)/SUM(int4) do not detect overflow of the int8 accumulator
Previous Message Michael Paquier 2026-10-28 00:00:44 Re: Return pg_control from pg_backup_stop().