Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wfM6c-005PRB-1L for pgsql-bugs@arkaria.postgresql.org; Thu, 02 Jul 2026 18:25:46 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wfM6b-002kE6-15 for pgsql-bugs@arkaria.postgresql.org; Thu, 02 Jul 2026 18:25:45 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1weq3Z-00Cc4V-0R for pgsql-bugs@lists.postgresql.org; Wed, 01 Jul 2026 08:12:29 +0000 Received: from mail-yw1-f171.google.com ([209.85.128.171]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1weq3V-0000000133k-1ZQa for pgsql-bugs@lists.postgresql.org; Wed, 01 Jul 2026 08:12:28 +0000 Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-80f5edd1858so4227657b3.3 for ; Wed, 01 Jul 2026 01:12:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1782893544; cv=none; d=google.com; s=arc-20260327; b=J7/bE+ugefL5sSuWf4gVTzlEDm9GrmIB2pjV2v8nVj3jnKVJH+hb9EokWrOYZlBCQQ iHW5eL6sRpfXS3Qxv9GQo/houEJKVDRGTFa7ObvJ+lvAzPk3rn293WYXnau05v1Udytz kgQGGvXFVLTZcc+6vkwOrcy7n/zx0f2g3wI4Cp1RwGPYpFNk1EoFOon1E2tuS6pSxUci tRfGD4kgAr3NWMdhkmMdt7vXKoE3sl9kKRgLcOEc3ombTMCsBw9ucn8fKE4Q5m4Qz9uN hiDu70pQZWA6Ua5UqJxJq7q2ZQ+/Rqkwq7/4s5cCWbte/OQF/AOR3EtayOVnAquh0g1i 4cvA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=to:subject:message-id:date:from:mime-version; bh=CJONRe43VRcUisrNO9BAtIbQL0H2fUYwVuFZVjPTptg=; fh=/gQe77b11iMZdcPj/nJr/Ghqi6rQp5FPrPbdO93bmOA=; b=nb9CEfdhjz8DPlTNCWRprdJRO+9KDORoW+XKAUIky9tfCyMW5sKBPqSmnwnUzVZpuT tEDvK6In2AvQ6szGcNanJuxCGtjwHJnYEv+HH3Jtwkh2ZSGoNKeBxvIz3H8xjjAdf87M nU5pQpZAvTMT5SD4E5pi7fIIwaBeews2TLyXrtZCHxHkjyMalq1/jVHpIZEE7S6o8Tm1 Zz0qptZZWiWjfEQSs7Tl0EqYlcgs2iIrWfP/Jhd+4pv/F3D70hOaPWydDskR5HNsZuKm F8bQ9/SVHy1BkFz09ckDxcMGSxXgtlpziO4qcOAOm5Z0F6c7eSi8RAXdNKegBha0jyip mJ1Q==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782893544; x=1783498344; h=to:subject:message-id:date:from:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=CJONRe43VRcUisrNO9BAtIbQL0H2fUYwVuFZVjPTptg=; b=SzJ1Ex1a6SNd8mPU/jyDDyh/ZvmTFpqC8VHbOJjOjEOCt/ELr3zA+lLxvHhwAQ0Tw9 1kAc57GQegJZAQayD9C+Lk3ZachIfK0CzxF+30pyyDSE/A1BnVHK4ku7gSiMtGxsFKxJ sehFsumpQ/ocf+WPFL4dwJnpG95OHI4Z1sUgQlExf3eVhU8KbnUGOELSJcqcxnt7DbDo psn8Ewc4Yo/JR+GcMwNUlnw2q+WagYw/z8odolIR9HzK1BPb+w9acn8wchGFSSdBeD7A BQizTnqccDb756l3gvrTHuq/IOIepOg6iRQduRSTtSoDlfayyrmkjVsDJdvOjrmjYKNu GVDw== X-Gm-Message-State: AOJu0YwTAg23gDYDB/DCMvNYX1W16aoVztzEy987EG2kEz0YxHNLUGuP D8bXkwOb7H9i2GiXXv4CWAMGnxNUUed/XYawvoZBWLmh2bleiTisYNM6JswCO9dQn9s5J6Lkcsz xNYfzaho4W1cnu5xSaP8rx932qsmDVBhLRsmZ X-Gm-Gg: AfdE7ckEMb2HYpPUz89aLQyJPwpSr9OIaXJRmGCjNWLo9v7svFU6v5vxu+dKD+Pnp5z TEjunQsqoR8yzIUpSf3LUzwi77Q/X0r4Da0B+GwLvqmc2+4PMF/4tHPPstxGFJFo23df3TYajV4 IifgFszqOJqbVR7hkLkQk3DRL00/iWVNQ1KJaYzIf+9Q+PwK+DUM7yOPh/6kUeNeo1iUYoJxXUG B+cJLmfxjS6LcxxE6iPYAIkT7KHO4aPrlwXB1usDPPKhZcQQzzlJxpSb+IrGbN2f/RbCeev X-Received: by 2002:a05:690c:6e07:b0:7fa:c896:8982 with SMTP id 00721157ae682-812e98d29c7mr6109907b3.26.1782893544218; Wed, 01 Jul 2026 01:12:24 -0700 (PDT) MIME-Version: 1.0 From: Alessandro Regolini Date: Wed, 1 Jul 2026 10:12:13 +0200 X-Gm-Features: AVVi8CeYpmi-akf4rBxLQYx764gfp98YUKg-jC4QMlf-sWXMUpOR9wGGrPjYuMg Message-ID: Subject: BTree index corruption (heap-tid-past-end, unexpected zero page, misplaced TID in posting list) recurring on high-churn tables, PG 18.3, data_checksums=on, no preceding crash To: pgsql-bugs@lists.postgresql.org Content-Type: multipart/alternative; boundary="00000000000060274706558840b4" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000060274706558840b4 Content-Type: text/plain; charset="UTF-8" PostgreSQL version: PostgreSQL 18.3 (Ubuntu 18.3-1.pgdg24.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0, 64-bit OS: Ubuntu 24.04 (noble), running as a KVM/QEMU guest data_checksums: on shared_buffers: 128MB btree deduplication: enabled (default) Storage: ext4 (rw,relatime,errors=remount-ro) on a dedicated virtual disk Relevant GUCs: fsync=on, full_page_writes=on, wal_level=replica, autovacuum=on *Summary*------- Over ~4 weeks we hit recurring btree index corruption on multiple independent databases hosted on the same physical host (~80 PG 18 clusters, one DB per cluster). Corruption concentrates exclusively on high-churn tables (INSERT + DELETE + HOT update + frequent autovacuum: application cache and a message-queue table). We observed three distinct signatures, all of them btree-vs-heap desync: 1. ERROR: heap tid from index tuple (X,Y) points past end of heap page line pointer array at offset NNN of block B in index "" 2. index "" contains unexpected zero page at block B 3. posting list contains misplaced TID in index "" (btree dedup) Timeline (verbatim runtime + amcheck log lines, times CEST) ----------------------------------------------------------- *DB "soave":* 2026-06-05 .. 2026-06-08 ERROR: heap tid from index tuple points past end of heap page line pointer array at offset 272 of block N in index "django_webix_sender_messagerecipient_pkey" -> emitted every 4h by a periodic query, then stopped on its own. 2026-06-30 09:35 / 09:46 bt_index_check(heapallindexed=>true) reported: - index "qdc_magazzino_articolo_id_prev_a0195938" contains unexpected zero page at block N - index "django_webix_sender_messagerecipient_content_type_id_7552397e" posting list contains misplaced TID *DB "ermes":* 2026-06-24 .. 2026-06-30 ERROR: heap tid from index tuple points past end of heap page line pointer array at offset 248 of block N in index "mpaapp_cache_content_type_id_object_i_f6ecfefa_uniq" -> dozens of occurrences over the window. Key facts --------- - data_checksums=on and NO invalid-page / checksum-failure error was ever logged. The corrupt pages therefore carry valid checksums but structurally wrong content: they were written by PostgreSQL, this is not a torn write or a storage-layer bit rot detected by checksums. - No crash, PANIC, OOM or immediate shutdown preceded any event. The postmaster was up continuously across each window. Host kernel journal shows zero MCE / ECC / OOM events in the last 90 days. - REINDEX INDEX CONCURRENTLY fixed every case with zero data loss (the heap was always intact and fully readable), which points to an index-vs-heap desync rather than heap damage. - The affected indexes are the busiest tables (application cache and message queue), constantly vacuumed. Corruption is intermittent and self-heals after VACUUM / REINDEX. - The same signature family reproduced on independent databases with identical schema (same application deployed many times), which argues against random hardware/memory corruption: a random flip would not preferentially target the same vacuum-heavy indexes with the same error text across separate clusters. - This is not a collation/glibc issue: the symptoms are physical structural desync (heap-tid-past-end, zero page, misplaced TID), not wrong sort order. - fsync=on and full_page_writes=on, so the two most common self-inflicted corruption causes are excluded. *What we can provide on the next occurrence* ------------------------------------------ We run a periodic amcheck sweep, so we usually catch a fresh case before reindexing. Before REINDEX we can capture, for the affected block: - contrib/pageinspect: bt_page_items() of the index block and heap_page_items() / page_header() of the referenced heap block - full bt_index_check / bt_index_parent_check output - verify_heapam() of the underlying table Please tell us which dumps would be most useful to root-cause this (btree deduplication / VACUUM interaction is our current suspicion). # Alessandro Regolini --00000000000060274706558840b4 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
PostgreSQL version:
PostgreSQL 18.3 (Ubuntu 18.3-1= .pgdg24.04+1) on x86_64-pc-linux-gnu,
compiled by gcc (Ubuntu 13.3.0-6ub= untu2~24.04.1) 13.3.0, 64-bit
OS: Ubuntu 24.04 (noble), running as a KVM= /QEMU guest
data_checksums: on
shared_buffers: 128MB
btree dedupli= cation: enabled (default)
Storage: ext4 (rw,relatime,errors=3Dremount-ro= ) on a dedicated virtual disk
Relevant GUCs: fsync=3Don, full_page_write= s=3Don, wal_level=3Dreplica, autovacuum=3Don

Summary
-----= --
Over ~4 weeks we hit recurring btree index corruption on multiple ind= ependent
databases hosted on the same physical host (~80 PG 18 clusters,= one DB per
cluster). Corruption concentrates exclusively on high-churn = tables
(INSERT + DELETE + HOT update + frequent autovacuum: application = cache and a
message-queue table). We observed three distinct signatures,= all of them
btree-vs-heap desync:

=C2=A0 1. ERROR: heap tid from= index tuple (X,Y) points past end of heap page
=C2=A0 =C2=A0 =C2=A0line= pointer array at offset NNN of block B in index "<idx>"=C2=A0 2. index "<idx>" contains unexpected zero page at b= lock B
=C2=A0 3. posting list contains misplaced TID in index "<= idx>" =C2=A0 (btree dedup)

Timeline (verbatim runtime + amch= eck log lines, times CEST)
---------------------------------------------= --------------
DB "soave":
=C2=A0 2026-06-05 .. 2026= -06-08 =C2=A0ERROR: heap tid from index tuple points past end
=C2=A0 =C2= =A0 of heap page line pointer array at offset 272 of block N in index
= =C2=A0 =C2=A0 "django_webix_sender_messagerecipient_pkey"
=C2= =A0 =C2=A0 -> emitted every 4h by a periodic query, then stopped on its = own.
=C2=A0 2026-06-30 09:35 / 09:46 =C2=A0bt_index_check(heapallindexed= =3D>true) reported:
=C2=A0 =C2=A0 - index "qdc_magazzino_articol= o_id_prev_a0195938"
=C2=A0 =C2=A0 =C2=A0 contains unexpected zero p= age at block N
=C2=A0 =C2=A0 - index "django_webix_sender_messagere= cipient_content_type_id_7552397e"
=C2=A0 =C2=A0 =C2=A0 posting list= contains misplaced TID

DB "ermes":
=C2=A0 2026-= 06-24 .. 2026-06-30 =C2=A0ERROR: heap tid from index tuple points past end<= br>=C2=A0 =C2=A0 of heap page line pointer array at offset 248 of block N i= n index
=C2=A0 =C2=A0 "mpaapp_cache_content_type_id_object_i_f6ecfe= fa_uniq"
=C2=A0 =C2=A0 -> dozens of occurrences over the window.=

Key facts
---------
- data_checksums=3Don and NO invalid-page= / checksum-failure error was ever
=C2=A0 logged. The corrupt pages ther= efore carry valid checksums but structurally
=C2=A0 wrong content: they = were written by PostgreSQL, this is not a torn write or
=C2=A0 a storage= -layer bit rot detected by checksums.
- No crash, PANIC, OOM or immediat= e shutdown preceded any event. The postmaster
=C2=A0 was up continuously= across each window. Host kernel journal shows zero MCE /
=C2=A0 ECC / O= OM events in the last 90 days.
- REINDEX INDEX CONCURRENTLY fixed every = case with zero data loss (the heap was
=C2=A0 always intact and fully re= adable), which points to an index-vs-heap desync
=C2=A0 rather than heap= damage.
- The affected indexes are the busiest tables (application cach= e and message
=C2=A0 queue), constantly vacuumed. Corruption is intermit= tent and self-heals after
=C2=A0 VACUUM / REINDEX.
- The same signatu= re family reproduced on independent databases with identical
=C2=A0 sche= ma (same application deployed many times), which argues against random
= =C2=A0 hardware/memory corruption: a random flip would not preferentially t= arget the
=C2=A0 same vacuum-heavy indexes with the same error text acro= ss separate clusters.
- This is not a collation/glibc issue: the symptom= s are physical structural
=C2=A0 desync (heap-tid-past-end, zero page, m= isplaced TID), not wrong sort order.
- fsync=3Don and full_page_writes= =3Don, so the two most common self-inflicted
=C2=A0 corruption causes ar= e excluded.

What we can provide on the next occurrence
---= ---------------------------------------
We run a periodic amcheck sweep,= so we usually catch a fresh case before
reindexing. Before REINDEX we c= an capture, for the affected block:
=C2=A0 - contrib/pageinspect: bt_pag= e_items() of the index block and
=C2=A0 =C2=A0 heap_page_items() / page_= header() of the referenced heap block
=C2=A0 - full bt_index_check / bt_= index_parent_check output
=C2=A0 - verify_heapam() of the underlying tab= le
Please tell us which dumps would be most useful to root-cause this (b= tree
deduplication / VACUUM interaction is our current suspicion).

# Alessandro Regolini
--00000000000060274706558840b4--