PostgreSQL can place more than one transaction behind a tuple’s xmax. This occurs when concurrent transactions hold compatible row-level locks on the same tuple. A single transaction ID cannot represent that set, so PostgreSQL records a multixact identifier that refers to members stored outside the heap tuple.

That indirection creates its own age boundary. Multixact identifiers are finite, and their member records occupy SLRU-backed storage. Old tuple metadata therefore cannot retain multixact references indefinitely. VACUUM participates in keeping both identifier age and member storage bounded.

A multixact represents several tuple lockers

A tuple header has limited space for transaction metadata. When several transactions need compatible row locks, PostgreSQL can store a MultiXactId in xmax rather than one ordinary transaction ID. The MultiXactId identifies a set of members, with each member carrying a transaction ID and a status describing its lock or update role.

The tuple does not contain that member array. PostgreSQL keeps multixact offsets and members in separate pg_multixact SLRU areas. The offset associated with a MultiXactId locates its member sequence. This lets a compact tuple header refer to multiple lockers, but it also means tuple metadata depends on external records remaining available.

A multixact is not equivalent to a long-lived lock object. Its members can finish, and later maintenance can determine that the old reference no longer has a role in current tuple visibility or locking decisions.

Identifier age has a wraparound boundary

MultiXactId values use a finite identifier space and eventually wrap. PostgreSQL therefore tracks multixact age in a manner analogous to transaction-ID age: old identifiers must be removed from tuples before reuse could make their relative age ambiguous.

Database metadata records a multixact horizon used by vacuum scheduling. As that horizon ages, autovacuum can run anti-wraparound work even on relations that would not otherwise qualify from ordinary dead-tuple activity. The purpose is preservation of safe identifier interpretation, not reclamation driven solely by table bloat.

This distinction matters because a quiet table can still contain old multixact references. Low write volume does not by itself eliminate the maintenance requirement.

Member storage adds a second pressure

Multixact maintenance also has a storage dimension absent from a simple identifier counter. A single MultiXactId may refer to several members, and member consumption depends on locking patterns rather than advancing at a fixed ratio with identifiers.

Workloads that repeatedly place multiple compatible row locks on tuples can consume member space faster than the MultiXactId counter alone suggests. PostgreSQL tracks member-related limits so vacuum can advance old multixact horizons before member offsets approach unsafe reuse conditions.

This is one reason multixact health cannot be inferred from ordinary transaction-ID age alone. XID freezing and multixact cleanup protect related MVCC metadata, but they govern different finite resources.

Vacuum can replace obsolete tuple metadata

When vacuum examines a tuple containing an old multixact, it can inspect the referenced members and determine which states still matter. If no member needs to remain represented, the tuple’s old multixact state can be cleared or replaced as permitted by tuple visibility and update state.

The exact result depends on what the multixact represented. A multixact may include lockers and can also include an updater. Maintenance cannot discard metadata that is still required to preserve tuple semantics. The safe transformation is therefore conditional on member status and transaction state rather than a simple age threshold applied in isolation.

Once old references have been removed across relations, database multixact horizons can advance. Storage older than the retained horizon can then become eligible for truncation when no database still requires it.

Row locking can create maintenance work without row updates

Multixacts expose a useful boundary in PostgreSQL’s storage model: tuple maintenance is not driven only by inserted, updated, or deleted row versions. Row-lock activity can create persistent metadata that later requires vacuum attention even when the protected row values remain unchanged.

For systems with heavy concurrent locking, multixact age and member consumption belong beside transaction-ID age in maintenance monitoring. They represent separate exhaustion paths, and healthy values for one do not establish safety for the other.

The practical boundary is straightforward: shared row-lock metadata is compact because heap tuples refer to external multixact state, but that state is finite and recyclable. Vacuum closes the lifecycle by removing obsolete references early enough for both identifier and member storage to be reused safely.