Re: Separate catalog_xmin from xmin in walsender hot standby feedback

Rui Zhao <zhaorui126@gmail.com>

From: Rui Zhao <zhaorui126@gmail.com>
To: pgsql-hackers@lists.postgresql.org
Cc: andres@anarazel.de, amit.kapila16@gmail.com
Date: 2026-06-08T07:57:25Z
Lists: pgsql-hackers

Attachments

Hi,

Attaching v2, rebased on top of current master (v1 no longer applied
cleanly per cfbot).  Two improvements were also made since v1:

1. Test stability
   The TAP test (053_hs_feedback_catalog_xmin.pl) replaced a sleep(2)
   with a poll on pg_stat_replication.reply_time, so it deterministically
   waits for a fresh hot standby feedback round to reach the primary
   instead of relying on wall-clock time.  This removes the most obvious
   source of flakiness on slower CI runners.

2. Performance impact (pgbench)
   I was concerned about adding a UINT32_ACCESS_ONCE(proc->catalog_xmin)
   read and an extra TransactionIdOlder() call per PGPROC iteration in
   ComputeXidHorizons(), which is on a hot path.  Measured on a 104-core
   box, scale=30, 64 clients / 32 jobs / 60s, -M prepared, optimized
   build (CFLAGS=-O2, no --enable-cassert):

   workload                 baseline median   patched median   delta
   ----------------------   ---------------   --------------   -----
   pgbench -S (read-only)        1,596,196        1,617,083    +1.3%
   pgbench    (read-write)         129,550          132,024    +1.9%

   Numbers are medians of 3 warm runs after a 30s warmup, after I
   discarded the cold-start runs which were dominated by cache priming.
   The patched build comes out marginally above baseline, which is of
   course noise -- adding code does not make things faster -- but it
   confirms the change is below the measurement floor on this hardware.
   Happy to rerun on different shapes (more clients, smaller scale,
   different machine) if anyone wants to see specific numbers.

Summary of the change (unchanged in substance from v1):

  - proc.h: add catalog_xmin field to PGPROC (4 bytes)
  - proc.c: initialize it in InitProcess / InitAuxiliaryProcess
  - procarray.c: accumulate proc_catalog_xmin in ComputeXidHorizons()
    and apply it only to catalog_oldest_nonremovable and
    shared_oldest_nonremovable; include it in GetReplicationHorizons()
    so the catalog_xmin propagates correctly in cascading setups
  - walsender.c: in the no-slot path of ProcessStandbyHSFeedbackMessage(),
    set MyProc->xmin and MyProc->catalog_xmin separately instead of
    folding them together

Registered in the 2026-07 commitfest:
  https://commitfest.postgresql.org/patch/6861/

Adding Andres (who wrote much of the ProcArray horizon code) and Amit
Kapila (logical replication / slots), in case you have any thoughts on
the approach here -- in particular whether tracking catalog_xmin in
PGPROC is preferable to generalizing the ephemeral-slot idea hinted at
by the existing XXX comment in ProcessStandbyHSFeedbackMessage().

Regards,
Rui Zhao