Summary
vac_truncate_clog (crates/backend/commands/vacuum/src/lib.rs:1439), which runs after every VACUUM (manual or autovacuum) once per-database frozen/minmxid horizons are recomputed, calls TruncateMultiXact (crates/backend/access/transam/multixact/src/lib.rs:1940-1956). That function has an early-return-only fast path; as soon as new_oldest_multi actually advances past the current oldestMultiXactId — i.e. any time relminmxid legitimately advances across all tables in the database — it hits:
panic!("unported caller path reached: TruncateMultiXact (multixact.c) — vacuum lane \
(vac_truncate_clog); needs delay-chkpt seam + WAL truncate record");
Why this matters
This is not a rare/edge-case trigger. Any workload that uses row locking (SELECT FOR UPDATE/SELECT FOR SHARE), foreign keys, or multi-transaction updates will produce MultiXacts, and any subsequent VACUUM cycle that legitimately retires old MultiXacts (i.e. normal, expected steady-state operation, not just anti-wraparound emergency vacuum) will hit this panic. This effectively crashes the vacuum lane under ordinary sustained usage, not just at wraparound extremes.
Reproduction sketch
- Create a table, run transactions that produce MultiXacts (e.g. concurrent
SELECT ... FOR UPDATE from multiple sessions on overlapping rows, or FK-referencing updates).
- Let enough VACUUM cycles run (manual
VACUUM or autovacuum) that relminmxid can advance database-wide.
- Observe panic in
TruncateMultiXact from vac_truncate_clog.
Root cause (as far as I could tell from source only — not runtime-reproduced)
The porting of multixact.c's TruncateMultiXact appears to have stopped at the fast-path/no-op case. The actual truncation logic — including the delay-chkpt seam (the checkpoint-delay interlock the real function uses to avoid concurrent-checkpoint races) and the corresponding WAL truncate record — is explicitly marked unported in the panic message itself.
Suggested scope for a fix
Port the remaining body of TruncateMultiXact from multixact.c, specifically:
- the delay-checkpoint interlock around the truncation
- segment-boundary computation and
SlruDeleteSegment-equivalent calls for members/offsets SLRUs
- the WAL record for multixact truncation (
XLOG_MULTIXACT_TRUNCATE_ID equivalent) so truncation is crash-safe and replayed correctly on recovery
Environment / where found
Found via static source review of a fresh clone of this repo. Have not attempted to build/run pgrust myself to runtime-confirm the panic; this report is based on reading crates/backend/access/transam/multixact/src/lib.rs and crates/backend/commands/vacuum/src/lib.rs directly.
Summary
vac_truncate_clog(crates/backend/commands/vacuum/src/lib.rs:1439), which runs after every VACUUM (manual or autovacuum) once per-database frozen/minmxid horizons are recomputed, callsTruncateMultiXact(crates/backend/access/transam/multixact/src/lib.rs:1940-1956). That function has an early-return-only fast path; as soon asnew_oldest_multiactually advances past the currentoldestMultiXactId— i.e. any timerelminmxidlegitimately advances across all tables in the database — it hits:Why this matters
This is not a rare/edge-case trigger. Any workload that uses row locking (
SELECT FOR UPDATE/SELECT FOR SHARE), foreign keys, or multi-transaction updates will produce MultiXacts, and any subsequent VACUUM cycle that legitimately retires old MultiXacts (i.e. normal, expected steady-state operation, not just anti-wraparound emergency vacuum) will hit this panic. This effectively crashes the vacuum lane under ordinary sustained usage, not just at wraparound extremes.Reproduction sketch
SELECT ... FOR UPDATEfrom multiple sessions on overlapping rows, or FK-referencing updates).VACUUMor autovacuum) thatrelminmxidcan advance database-wide.TruncateMultiXactfromvac_truncate_clog.Root cause (as far as I could tell from source only — not runtime-reproduced)
The porting of
multixact.c'sTruncateMultiXactappears to have stopped at the fast-path/no-op case. The actual truncation logic — including thedelay-chkptseam (the checkpoint-delay interlock the real function uses to avoid concurrent-checkpoint races) and the corresponding WAL truncate record — is explicitly marked unported in the panic message itself.Suggested scope for a fix
Port the remaining body of
TruncateMultiXactfrommultixact.c, specifically:SlruDeleteSegment-equivalent calls for members/offsets SLRUsXLOG_MULTIXACT_TRUNCATE_IDequivalent) so truncation is crash-safe and replayed correctly on recoveryEnvironment / where found
Found via static source review of a fresh clone of this repo. Have not attempted to build/run pgrust myself to runtime-confirm the panic; this report is based on reading
crates/backend/access/transam/multixact/src/lib.rsandcrates/backend/commands/vacuum/src/lib.rsdirectly.