Re: Fix bug in multixact Oldest*MXactId initialization and access

Heikki Linnakangas <hlinnaka@iki.fi>

From: Heikki Linnakangas <hlinnaka@iki.fi>
To: Sami Imseih <samimseih@gmail.com>
Cc: Yura Sokolov <y.sokolov@postgrespro.ru>, "pgsql-hackers@lists.postgresql.org" <pgsql-hackers@lists.postgresql.org>, Chao Li <li.evan.chao@gmail.com>
Date: 2026-02-28T09:22:03Z
Lists: pgsql-hackers

Attachments

(added back pgsql-hackers, I think you replied in private by accident)

On 28/02/2026 00:07, Sami Imseih wrote:
> 1/
> 
> +MyOldestMemberMXactIdSlot(void)
> +{
> +       Assert(MyProcNumber >= 0 && MyProcNumber < MaxBackends);
> 
> It would be better to use NumVisibleSlots instead of MaxBackends in
> the second assert condition.

MaxBackends makes the assertion more strict. It verifies that we use one 
of the slots reserved for regular backends, not prepared xacts. I'll add 
a comment on that.

> 2/
> 
> +static inline MultiXactId *
> +PreparedXactOldestMemberMXactIdSlot(ProcNumber procno)
> +{
> +       Assert(procno >= FIRST_PREPARED_XACT_PROC_NUMBER);
> +       Assert(procno - FIRST_PREPARED_XACT_PROC_NUMBER <
> +              NumMemberSlots);
> +       return &OldestMemberMXactId[procno -
> +              FIRST_PREPARED_XACT_PROC_NUMBER];
> +}
> 
> given
> 
> +#define FIRST_PREPARED_XACT_PROC_NUMBER \
> +        (MaxBackends + NUM_AUXILIARY_PROCS)
> 
> let's say, MaxBackends = 100 and NUM_AUXILIARY_PROCS = 38, the
> first prepared transaction procno will be 138. The current math
> will return an index of 0 (138 - 138 = 0), but this corrupts
> backend slot 0. 

Oof, you're right.

> Should we not account for NumVisibleSlots to skip
> over the regular backend slots?
> 
> ```
> return &OldestMemberMXactId[NumVisibleSlots + (procno -
> FIRST_PREPARED_XACT_PROC_NUMBER)];
> ```

That's not quite right either. NumVisibleSlots has nothing to do with 
the OldestMemberMXactId array, MaxBackends is the right offset here. 
NumVisibleSlots == MaxBackends, but that's just a coincidence.

New version attached.

- Heikki

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Add test for row-locking and multixids with prepared transactions

  2. Skip prepared_xacts test if max_prepared_transactions < 2

  3. Fix OldestMemberMXactId and OldestVisibleMXactId array usage

  4. Redefine backend ID to be an index into the proc array

  5. Fix an old bug in multixact and two-phase commit. Prepared transactions can