Re: Fix bug in multixact Oldest*MXactId initialization and access
Yura Sokolov <y.sokolov@postgrespro.ru>
From: Yura Sokolov <y.sokolov@postgrespro.ru>
To: "pgsql-hackers@lists.postgresql.org"
<pgsql-hackers@lists.postgresql.org>, Heikki Linnakangas <hlinnaka@iki.fi>
Cc: Andres Freund <andres@anarazel.de>, Chao Li <li.evan.chao@gmail.com>,
Sami Imseih <samimseih@gmail.com>
Date: 2026-02-27T17:58:16Z
Lists: pgsql-hackers
27.02.2026 13:58, Heikki Linnakangas пишет: > On 27/02/2026 07:00, Yura Sokolov wrote: >> 26.02.2026 23:44, Sami Imseih пишет: >>>> (Sorry, I've used Google Translate to write this sentence). >>>> >>>> When you write assert, you protect yourself from shooting your leg far in >>>> the future. Believe me. >>> >>> The issue to me seems that we a few code paths that rely on the >>> total proc calculation in several places, and changing one and >>> forgetting to change another is what broke things. >> >> If there were asserts, then tests would have failed. >> Period. >> >> There would no the bug. There would no this discussion. >> If only array bounds were checked with asserts. > > Yeah, more asserts == good, usually. > > Here's my version of this. It's the same basic idea, some minor changes: > > - Instead of the MaxChildren, TotalProcs, TotalXactProcs variables, I > added FIRST_PREPARED_XACT_PROC_NUMBER. Rationale: The variables still > required you to know the layout of the PGPROCs, i.e. you still had to > know that aux processes come after regular backends and dummy pgprocs > after aux processes. An FIRST_PREPARED_XACT_PROC_NUMBER is more explicit > in the places where it's needed. > > I have been thinking of adding variables like that anyway, because > the current calculations with MaxBackends et al. are just so confusing. > So I'm vaguely in favor of doing something like that in the future. But > I think FIRST_PREPARED_XACT_PROC_NUMBER is more clear for this patch, > and those variable names as done here (MaxBackends, MaxChildren, > Totalprocs and TotalXactProcs) were still super confusing. > > - I added a slightly different the set of Getter/Setter functions: > MyOldestMemberMXactIdSlot(), > PreparedXactOldestMemberMXactIdSlot(ProcNumber), and > MyOldestVisibleMXactIdSlot(). I think this is slightly less error-prone, > making it harder to pass proc number to wrong function (although the > assertions would catch such misuses pretty quick anyway). And these > functions return a pointer to the slot, so we don't need separate getter > and setter functions. That's a matter of taste, having a little less > code looks nicer to me. > > What do you think? I stick to separate getters and setters because they are used in 64bit xid patch. With 32bit TransactionId PostgreSQL code in many places rely on 32bit atomicity. But uint64 is not so atomic on currently-mostly-ancient architecures. Therefore there is a need to use pg_atomic_read_u64/pg_atomic_write_u64. And without hiding them in getters/setters code become ugly. So, getters+setters are better suited for (possible far) future. But I don't mind if you stick with slot references for now. (I realized now, to be prepared for uint64 getters have to be used in GetOldestMultiXactId and MultiXactIdSetOldestVisible as well. My bad.) > I've been pondering what kind of issues this bug could cause. Because > the OldestVisibleMXactId array is allocated just after the > OldestMemberMXactId array, you don't get a buffer overflow, but a > prepared xact can overwrite a backend's value in the > OldestVisibleMXactId array. That can surely cause trouble later, but not > sure what exactly, and it seems pretty hard to write a repro script for it. With extremely low configured MaxBackends it will overflow to BufferDescriptors array. Certainly only test configurations may use such tiny settings. I found the bug because I increased NUM_AUXILIARY_PROCS by bunch of other specialized processes, so overflow to BufferDescriptors happened even with parameters already set in tests. -- regards Yura Sokolov aka funny-falcon
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Add test for row-locking and multixids with prepared transactions
- 969576dab47b 17.10 landed
- fa3b328e6dc5 18.4 landed
- fe08113aefc9 19 (unreleased) landed
-
Skip prepared_xacts test if max_prepared_transactions < 2
- ffe53037dff7 14.23 landed
- a6b11ac4c42d 15.18 landed
- 65f5c451a3e1 16.14 landed
- 80d1d4ee06fd 17.10 landed
- 201436c19f40 18.4 landed
- 19615a44b3e6 19 (unreleased) landed
-
Fix OldestMemberMXactId and OldestVisibleMXactId array usage
- dcd9c06a4203 17.10 landed
- 0a50ef094382 18.4 landed
- ccae90abdbb6 19 (unreleased) landed
-
Redefine backend ID to be an index into the proc array
- ab355e3a88de 17.0 cited
-
Fix an old bug in multixact and two-phase commit. Prepared transactions can
- cd87b6f8a508 9.0.0 cited