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 1wfXex-005VPo-2Q for pgsql-bugs@arkaria.postgresql.org; Fri, 03 Jul 2026 06:45:59 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wfXew-005wMM-1c for pgsql-bugs@arkaria.postgresql.org; Fri, 03 Jul 2026 06:45:58 +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 1wfXev-005wME-38 for pgsql-bugs@lists.postgresql.org; Fri, 03 Jul 2026 06:45:58 +0000 Received: from mail-ej1-x629.google.com ([2a00:1450:4864:20::629]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wfXet-00000001Hp1-2nR4 for pgsql-bugs@lists.postgresql.org; Fri, 03 Jul 2026 06:45:56 +0000 Received: by mail-ej1-x629.google.com with SMTP id a640c23a62f3a-c08acccf4a4so21491966b.3 for ; Thu, 02 Jul 2026 23:45:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783061154; cv=none; d=google.com; s=arc-20260327; b=sHt9ww/NeA9PjWujUl9pUf5epdkqBmQgHDfBXlp7gPHUPRizIYQ6SPYeePfU+nBQnk q2CcjpJL4B0SqIhpPEPGbAIx7TyNtgN/EopDeAaRtS1Egbn2QDveHS9LiGY0dK/Ja3ng ueG+aVwk02DfEfxG+e2B1J4PFz3y8n50NBPfrngwqSTtCd+fcVZn1r9YqQ64jaxJrQR0 KEZ2HiB+rZ0Tm0UomLVXfLH59rI+fTtLXxr0shMoEJLRwXGAMne4ECSwNe6Ck12SLU3x 7rMRiH6IXIXUlaLXPnsc0JO9MOFl5HHYFxUqkwh6QNPQHvQ4mxl2pmZzkcyaXqfbA+Fb +Mig== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=RG26BGamm3rVSZuV54zt6KyF0Yo9gia0AVUNGkfoxEw=; fh=IC2XaVbr3UahWqq7tcE4XqJDJ6K9yNBlDQmAwdtXaWY=; b=O4nxvlELIiIT0rmrBhst9r0SmGuchL3OfbNmylbD9FdZkNkoDTPjRsZBAJ/ebGuTwh y07EL99V9d4SpdoVMLhW6XRcpS2neHNJpMP6nmY83axh1szPPBG/3i9rhgeQNi2LGHrU LM9eINzbGE+PEXpvi55Sn/eEjlGvZbAlEVyTtq+rtQeFTLm4mRl3mv48eoj9eQfBfHON uTjciLNJcHzrsDtTdPqYfAhFkbnHMHmnSpSk/cCmZVQuR+yLWM5N9avWs1fdx3OsPv7g J8nzx51cnwmhT1akztqr4dGSmwTrmjTVvkafRphFTySGA7WKunxvJm/EKTc6rWZLGocC TrGA==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783061154; x=1783665954; darn=lists.postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=RG26BGamm3rVSZuV54zt6KyF0Yo9gia0AVUNGkfoxEw=; b=XsGge9yFTDS3JnAk1OloHKSwaHy9iphR8NMPrcCLiOGIp2tK00j3vsFHbZ9i+hbeIs 5B1JcXcMPgJVqyHYIPQl2blNo/7bRxvwhSxvr5RCfOSPowcDMEOh/jLrmGm/fGtFMvm7 k24khrx97cNEs8LC3T+ZDs0wsF1tom1EWx3czfwErFPo+rMvGQ0AlmDHsIImRwckUZsN hDETksYrwtMKJx7U3uvxhxcxUM6VkHsYixojEzWQE00Z3drWhvxHbyTZV+CBWOK7+HWp 582YfAnDiGuVALaXVYAMPI6doCu2x3Hz2onReNUznCOf44Y7LvZoD93zC+14mNvSE4Yi dTTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783061154; x=1783665954; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RG26BGamm3rVSZuV54zt6KyF0Yo9gia0AVUNGkfoxEw=; b=ccQq95LqtWdhQX80BYAwE+xZRLikRdmQEREFIIheRzpTfi5M2B6i/JhMkoKKHiMH5c L1+gKJcdEb4E1b0TGq6oPI8xMW5L1TIst3At7KMijxk9T2nQBfzG3OCfY/OG1swb3wX1 rgje/93l0JIbjzLXnl7uqw7e/nV2BLq03AyIip72kdeNnkLW0VmAy9qGt3neoR5yzB+l 7vX0BgNmaqhTjZ/Udsz2gZOjaA/W6UJPjl94f4KtrYUA7Y2rwj1Bbxx/S+u0nL13QhvC xbn+H0bWn3GGuBqPVQOLRGT90LFwhJbb3IAP7eVZCdl1tLVEmzYumsus5XusBxXJUNUg cP0Q== X-Forwarded-Encrypted: i=1; AHgh+RoouGGk5CPKA6GMb6lFCEayyu2wXc5r2FeaJ1JMRX9xg+IhFI9h+ejadJEtuV89XKHeQLRAcyTNjYth@lists.postgresql.org X-Gm-Message-State: AOJu0Yy7IMcEVTz734HVP/nPXhknwgRqHRyDpPF8l+Izv/DaIgRSIU/4 noia/o7QRUBoANHVUhesiXa+m3p2iKeQmDl/yp4ID79iNW0dZIJR1iANc0WAdVYd1+QOKN8hO1L JP584O2cRVjUFoVk6C0DYTxg3VZmYwVI= X-Gm-Gg: AfdE7cnGcBxLeN8gzTNKthwWsagCd7/0sxl59YRpcWmR6rjq0urJCnFVCL07GPShsBz 3CNuv0K61slyp0WfOsNthDf2NO0S8dB9Lfou4/6Q98KOLpnjwiJVuWcEDnCo6Qgv7a984i3cud9 wHtilKbDlgfkjT/ia3VxI6QF12f5V0qO5PSYq84OGpPxAjsaQV/FHa2CBiv6zhTdxUDMU7riqp+ rjvSko/hClnHTEq+R0oy5czMFOHe6+4QXPXn6J7Ud0l3uK/QCm6x/We8ByMdmEQbnzR7S79 X-Received: by 2002:a17:907:c244:b0:c12:8cbe:b897 with SMTP id a640c23a62f3a-c12a9dfc674mr445600566b.15.1783061153695; Thu, 02 Jul 2026 23:45:53 -0700 (PDT) MIME-Version: 1.0 References: <19519-fe02d8ff679d834d@postgresql.org> In-Reply-To: From: Imran Zaheer Date: Fri, 3 Jul 2026 11:45:42 +0500 X-Gm-Features: AVVi8CfBq70x3WuvNmxc_4GgbUVnBY3Z2ESjEhO8JyvGU-rMvkTkF2jnunIxxjE Message-ID: Subject: Re: BUG #19519: REPACK can fail due to missing chunk for toast value To: Srinath Reddy Sadipiralla Cc: exclusion@gmail.com, pgsql-bugs@lists.postgresql.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, The proposed fix (skipping own xmin while computing oldest xmin) did solve the problem, but while testing this, I found another bug report showing the same error: "missing chunks." For example, this report [1] reproduces the same bug with a different repro attached, but interestingly, this thread discussion also pointed out that the bug is also reproducible with the CREATE INDEX command. The following trace was reported by Alexander Lakhin there. > The backtrace of the heap_fetch_toast_slice() call emitting error is: > 2024-03-05 17:41:07.786 UTC|law|db10|65e75933.208c6b|ERROR: missing chun= k number 0 for toast value 17314 in pg_toast_17289 > 2024-03-05 17:41:07.786 UTC|law|db10|65e75933.208c6b|BACKTRACE: > heap_fetch_toast_slice at heaptoast.c:784:3 > toast_fetch_datum at detoast.c:379:2 > detoast_external_attr at detoast.c:54:12 > index_form_tuple_context at indextuple.c:111:21 > tuplesort_putindextuplevalues at tuplesortvariants.c:678:15 > _bt_spool at nbtsort.c:537:1 > _bt_build_callback at nbtsort.c:610:24 > heapam_index_build_range_scan at heapam_handler.c:1329:22 > table_index_build_scan at tableam.h:1781:9 > (inlined by) _bt_spools_heapscan at nbtsort.c:483:15 > btbuild at nbtsort.c:329:14 > index_build at index.c:3042:10 > index_create at index.c:1265:3 > DefineIndex at indexcmds.c:1166:3 Index building, together with lazy vacuum, can create similar conditions as we saw with VACUUM FULL / CLUSTER / REPACK. In heapam_index_build_range_scan(), we compute the OldestXmin, which later is used to decide which tuples to index (in repack we use it to decide which tuples to copy). I think we should also consider fixing the index building path while addressing this bug. ``` OldestXmin =3D GetOldestNonRemovableTransactionId(heapRelation); ``` Can we safely use the same fix in the index build path too? Can we use GetOldestNonRemovableTransactionIdforRewrite or something similar here? Normal serial index builds use SnapShotAny and concurrent index builds use MVCC, but the bug only exists in the serial index build path. Other than that, after applying your patch, the bug was not reproducible with either this repro or the other report's repro [1] in the rewrite path. However, the create index bug is still there. You can use the following repro as mentioned in the thread [1]. > I've discovered that not only VACUUM FULL can stumble over such missing > toast values. CREATE INDEX behaves similarly, as the following script > shows: > for ((c=3D1;c<=3D20;c++)); do createdb db$c; done > > > for ((i=3D1;i<=3D100;i++)); do > echo "iteration $i" > for ((c=3D1;c<=3D20;c++)); do > echo "\\d sometable" | psql -d db$c >psql-1-$c.log 2>&1 & > echo "DROP TABLE IF EXISTS tbl; > CREATE TABLE tbl (i int, t text); > ALTER TABLE tbl ALTER COLUMN t SET STORAGE EXTERNAL; > INSERT INTO tbl(i, t) VALUES (1, repeat('1234567890', 250)); > DELETE FROM tbl; > > > VACUUM (VERBOSE) tbl; > CREATE INDEX t_idx ON tbl(t); > " | psql -d db$c >psql-2-$c.log 2>&1 & > done > wait > grep 'missing chunk number' server.log && break; > done Regards, Imran Zaheer [1]: https://www.postgresql.org/message-id/flat/18351-f6e06364b3a2e669%40po= stgresql.org On Wed, Jun 17, 2026 at 10:03=E2=80=AFPM Srinath Reddy Sadipiralla wrote: > > Hi Alexander, > > On Sun, Jun 14, 2026 at 10:37=E2=80=AFPM PG Bug reporting form wrote: >> >> The following bug has been logged on the website: >> >> The following script: >> createdb db1 >> createdb db2 >> >> cat << EOF | psql db1 >> SET default_statistics_target =3D 1000; >> CREATE TABLE t(i int, t text); >> INSERT INTO t SELECT 1, g::text FROM generate_series(1, 50000) g; >> ANALYZE t; >> EOF >> >> cat << EOF | psql db2 & >> BEGIN; >> CREATE TABLE t(i int); >> SELECT pg_sleep(3); >> EOF >> >> sleep 1 >> >> cat << EOF | psql db1 >> DROP TABLE t; >> VACUUM pg_toast.pg_toast_2619; >> REPACK; >> EOF >> wait >> >> triggers: >> ERROR: missing chunk number 0 for toast value 16393 in pg_toast_2619 > > > Thanks for the report and clean reproducer , I can also reproduce this. > > I looked into this. It is not REPACK-specific, it reproduces with > plain VACUUM FULL on back branches too, so it predates > the REPACK command. AFAIU The root cause is a disagreement about > the removal horizon between a table rewrite and an independent vacuum > of that table's TOAST relation. > > A rewrite (cluster_rel -> copy_table_data, copy_heap_data) preserves live > and RECENTLY_DEAD tuples, and detoasts a preserved tuple's external > values to move them into the new TOAST table.Its removal cutoff comes > from GetOldestNonRemovableTransactionId(), i.e. ComputeXidHorizons(), > which includes the rewriting backend's own snapshot xmin. > The rewrite holds an ordinary MVCC snapshot (taken so index expressions > can be evaluated) and deliberately does not set PROC_IN_VACUUM. So > The rewrite backend's snapshot xmin is the cluster-wide oldest > running xid, which the db2 transaction pins. Since that backend is > in db1 and lacks PROC_IN_VACUUM, its own xmin folds into the > relation's data/catalog horizon -> OldestXmin sits behind the > DROP, so the dead pg_statistic tuple is RECENTLY_DEAD and gets > copied. > The lazy vacuum of pg_toast_2619 sets PROC_IN_VACUUM, which excludes > its own xmin from the horizon, and the db2 transaction, being in another= database, > does not constrain a non-shared relation's horizon. Its cutoff advances = past the DROP > and the chunks are removed.The two operations thus disagree about the sa= me tuple, > and the rewrite ends up fetching TOAST the lazy vacuum already reclaimed. > > Attached Fix: > The rewrite is simply too conservative,AFAIK it's snapshot is only there = to > evaluate index expressions against other relations, and it is not a > reader of the rewritten relation's historical rows, so its own xmin > should not hold back that relation's removal horizon. Excluding it > yields exactly the horizon the lazy vacuum uses. > We cannot set PROC_IN_VACUUM on the rewrite, that would broadcast > "ignore my xmin" to every backend and let other vacuums remove tuples in > other relations that the index expressions may need (the documented > reason VACUUM FULL does not set it). The exclusion has to be local to > the rewrite's own horizon computation, so the patch adds > GetOldestNonRemovableTransactionIdForRewrite(): ComputeXidHorizons() > gains a flag to skip the calling backend (and to not publish the > resulting, more-aggressive horizons into the shared GlobalVisState), and > copy_table_data() uses it for OldestXmin. > > -- > Thanks :) > Srinath Reddy Sadipiralla > EDB: https://www.enterprisedb.com/