logical decoding: skip unnecessary snapshot distribution.

From: "yangboyu" <yangboyu(dot)yby(at)alibaba-inc(dot)com>
To: "pgsql-hackers" <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Cc: "rhaas" <rhaas(at)postgresql(dot)org>
Subject: logical decoding: skip unnecessary snapshot distribution.
Date: 2026-08-31 07:02:34
Message-ID: 312a1b4c-f341-40dc-b822-08e5b0cc1848.yangboyu.yby@alibaba-inc.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi hackers,
I recently hit a disk bloat issue caused by logical decoding:
(1) A cluster has two databases, db1 and db2. There is a single
logical replication slot on db1 while db2 holds plenty of tables.
(2) A long-running transaction sits open in db2, pinning the xmin of
the historic snapshots during decoding.
(3) db2's tables get ANALYZEd -- manual run over all tables.
Each analyzed table commits separately and writes pg_statistic,
pg_statistic_ext and pg_class row, so every such commit is a
catalog-changing commit carrying invalidation messages.
(4) Every one of these commits makes the decoding session of db1's
slot rebuild its historic snapshot and distribute it to every
in-progress transaction in its reorder buffer -- including db2's
long transaction, whose changes the slot will never decode however.
Since xmin is pinned, the distributed snapshot's xip array grows,
the snapshot handed out by the k-th ANALYZE commit is O(k) in size,
and with the total spill files grow O(k^2), even though the slot's
own database is completely idle.
Note that a decoding session is connected to a single database and can only
reads that database's catalogs plus the shared catalogs, so changes on other
databases' catalog doesn't need a snapshot rebuild and distribution. The
attached patch exploits this: in DecodeCommit(), if the committing
transaction's dbId differs from the slot's database and none of the
commit record's invalidation messages touches a shared catalog, it skips
both the snapshot rebuild and the distribution.
Correctness relies on that, every write to a shared catalog
produces invalidation messages, SHAREDINVALSNAPSHOT_ID or
SHAREDINVALRELCACHE_ID.
Repro/measurement (script attached): one test_decoding slot on db1;
3000 tables created in db2; a long transaction open in db2; then a
single ANALYZE over all of db2's tables, which commits each table
separately (logical_decoding_work_mem = 64kB); finally decode the WAL
with the slot. spill_bytes from pg_stat_replication_slots for db1's
slot:
master ~19 MB
patched 0
So the slot of an idle database is completely unaffected by another
database's ANALYZE traffic or other operations that can generate
invalidation messages, while decoding of its own database's
catalog changes is unchanged.
Any feedback is appreciated.
Regards,
Boyu Yang

Attachment Content-Type Size
0001-Skip-snapshot-distribution-for-catalog-commits-in-ot.patch application/octet-stream 7.3 KB
reproduce_spill_email.sh application/octet-stream 2.8 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Xuneng Zhou 2026-08-31 07:05:12 Re: timeout value overflow in wait for lsn
Previous Message Ewan Young 2026-08-31 06:53:38 Re: Implicit shell type creation, little fixes