Re: Historic snapshot doesn't track txns committed in BUILDING_SNAPSHOT state

From: Manu <manuelreyesbravo(at)gmail(dot)com>
To: Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>, cca5507 <cca5507(at)qq(dot)com>
Cc: Ajin Cherian <itsajin(at)gmail(dot)com>, Bertrand Drouvot <bertranddrouvot(dot)pg(at)gmail(dot)com>, Michael Paquier <michael(at)paquier(dot)xyz>, Haiyang Li <ocean_li_996(at)163(dot)com>, pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: Historic snapshot doesn't track txns committed in BUILDING_SNAPSHOT state
Date: 2026-09-22 17:23:09
Message-ID: 179009778919.1885930.8061516199789221795@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com> wrote (in August 2024):
> My question is;
> in order to track just catalog-change transactions, whether it's
> sufficient to check if XLOG_XACT_COMMIT[_PREPARED] has the
> XACT_XINFO_HAS_INVALS flag. If yes, we probably should change only
> xact_decode() to check the commit records even in BUILDING_SNAPSHOT.
> Otherwise, we would need to change mostly all paths where we mark the
> transaction as catalog-change as the patch does.

I tried to answer that with data, and it looks like the answer is no.

I added two LOG lines to master (the attached instrumentation diff, not
meant to be applied): SnapBuildCommitTxn() logs, for the xid and each
subxid, whether the reorder buffer has it as catalog-changing and
whether the commit has XACT_XINFO_HAS_INVALS; SnapBuildProcessNewCid()
logs which relation the NEW_CID is for. Then I ran installcheck-parallel
with a logical slot created beforehand, and decoded all of it. The
attached catchk_run.sh does all of this on a build with that diff,
including the user catalog table check below; three runs gave the same
counts.

catalog changes invals in commit top-level xacts
no no 5087
yes no 37
yes yes 15540

The 37 are two kinds. 33 changed only pg_largeobject and
pg_largeobject_metadata. In the other 4 the catalog changes were made
only in subtransactions that were rolled back (TRUNCATE and DROP TABLE
in stats.sql, and one pg_statistic write), so the top transaction was
marked through its child but had nothing left to invalidate. In that
run, every committed change to a catalog with a syscache or relcache
had invalidations.

But the regression tests don't write to user catalog tables, and those
behave like large objects:

CREATE TABLE uc (k int PRIMARY KEY, v text)
WITH (user_catalog_table = true);

INSERT INTO uc ... catalog changes: yes invals: no
UPDATE uc ... catalog changes: yes invals: no
DELETE FROM uc ... catalog changes: yes invals: no
ALTER TABLE plain ... catalog changes: yes invals: yes

Output plugins may read user catalog tables with the historic snapshot,
so a transaction that writes one during BUILDING_SNAPSHOT has to be
tracked, and XACT_XINFO_HAS_INVALS would miss it. So I think the
patch's approach, marking the transaction from the NEW_CID records as
well, is the one needed.

The same data says something about master that I can't turn into a
failure: SnapBuildXidHasCatalogChanges() relies on "The transactions
that have changed catalogs must have invalidation info" to skip the
catchange array lookup, which doesn't hold for user catalog tables or
large objects. Is that a problem when a snapshot is restored, for a
plugin that reads user catalog tables?

On the patch itself, v6 applied on master e8a3ee5b197, against that
master, with --enable-cassert:

- snapshot_build fails on master with "could not map filenumber
"base/16384/16772" to relation OID" and passes with v6.
- The same test with the catalog change in a subtransaction, and with
it committed by COMMIT PREPARED, fails the same way on master and
passes with v6.
- With ALTER TABLE ... ADD COLUMN on an existing table instead of
CREATE TABLE, master raises no error: the insert that follows is
decoded with the old tuple descriptor, and the new columns are
silently dropped:

master: table public.tbl3: INSERT: val1[integer]:1
v6: table public.tbl3: INSERT: val1[integer]:1 val2[text]:'two' val3[bigint]:3

That seems worse than the error, since nothing tells the user.

The attached diff, on top of v6-0002, adds these three as permutations
of snapshot_build. They fail on master and pass with v6.

About Ajin's question in March 2025 on the DecodeTXNNeedSkip() change
(the "SnapBuildCurrentState(...) < SNAPBUILD_CONSISTENT" test), which
ChangAo said might be redundant: on v6 I logged every call made before
CONSISTENT, and whether SnapBuildXactNeedsSkip() already skipped it.
Over test_decoding, src/test/subscription and the recovery tests below
plus 040, it was called 41 times before CONSISTENT (28 in
BUILDING_SNAPSHOT, 13 in FULL_SNAPSHOT), and SnapBuildXactNeedsSkip()
had already skipped every one of them. So it never changes the result
there. Maybe an Assert would say the same thing and catch a case where
it does.

With v6: test_decoding (20 and 15, plus the new permutations),
src/test/subscription (605 tests), the logical decoding tests in
src/test/recovery (006, 035 and 038, 119 tests) and make check (239)
pass.

v6-0001 applies cleanly to master and REL_18_STABLE; REL_19_STABLE
conflicts in DecodeTXNNeedSkip(), and REL_17_STABLE and older in the
XLOG_XACT_INVALIDATIONS case of xact_decode(). As Haiyang reported in
bug #19109, it reproduces back to 11.

I haven't looked at Ajin's alternative beyond your question about it.

Regards,
Manu

Attachment Content-Type Size
nocfbot-snapshot_build-more-permutations.diff.txt text/plain 6.4 KB
nocfbot-catchk-instrumentation.diff.txt text/plain 1.4 KB
catchk_run.sh.txt text/plain 3.3 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Nisha Moond 2026-09-22 17:26:01 Re: Proposal: Conflict log history table for Logical Replication
Previous Message Heikki Linnakangas 2026-09-22 16:40:40 Re: Catversion bumps during beta (was Re: [Proposal] Expose internal MultiXact member count function for efficient monitoring)