neondatabase/neon
LSN and WAL
The Log Sequence Number is the byte-offset clock that drives every Neon component. The Write-Ahead Log is the durable journal of changes whose offsets are LSNs. Almost everything in the system is parameterized by an LSN.
LSN
A Lsn (libs/utils/src/lsn.rs) is a 64-bit unsigned integer representing a byte position in the WAL stream. It increases monotonically as new WAL is appended. It's printed as two hex numbers separated by a slash:
0/16B5A50 = 0x16B5A50 = 23,829,072 bytes
000005A2/00000000 = bytes-into-WALThis is the same format Postgres uses for the pg_lsn data type and what shows up in pg_current_wal_lsn().
A few convenient operations:
Lsn::from_str("0/16B5A50")— parse the on-the-wire format.lsn.checked_sub(other)— compute byte distances (used as a measure of replication lag).lsn.is_aligned()— true if 8-byte aligned (WAL records are always 8-byte aligned).lsn.segment_number(segsize)— which WAL segment file this LSN falls in.
Operations on Lsn are checked-arithmetic by default; the bare + operator is implemented but most code uses checked_add to catch overflow.
How LSNs are used
- In
GetPage@LSN(key, lsn)— "give me pagekeyas of LSNlsn". - In timeline state —
last_record_lsn,disk_consistent_lsn,remote_consistent_lsn,ancestor_lsn. - In layer file naming —
<key_lo>-<key_hi>__<lsn_lo>-<lsn_hi>is the on-disk filename for a delta layer. - In the safekeeper's consensus —
flush_lsn,commit_lsn,restart_lsn,VCL(volume-consistent LSN). - In compute backpressure — when the gap between the compute's
pg_current_wal_lsn()and the safekeepers'flush_lsnexceeds the configured limit, the compute pauses.
Multiple LSN species
Different components track LSNs with slightly different meanings. The glossary entry Lsn in docs/glossary.md enumerates them:
Compute-side (Postgres):
pg_current_wal_lsn()— current WAL write position.pg_current_wal_flush_lsn()— latest WAL flushed to disk on the primary.pg_last_wal_receive_lsn()— on a replica, latest received from primary.
Safekeeper-side:
flush_lsn— latest LSN durable on this safekeeper's disk.commit_lsn— LSN confirmed by quorum of safekeepers (durable).VCL(volume-consistent LSN) — guaranteed to be persistent and have all prior WAL.
Pageserver-side:
last_record_lsn— end of last applied WAL record (in-memory layer head).disk_consistent_lsn— LSN ≤ which all data is on local disk.remote_consistent_lsn— LSN ≤ which all data is in remote storage.ancestor_lsn— LSN at which a branch was forked from its parent.
A timeline maintains the invariant: remote_consistent_lsn ≤ disk_consistent_lsn ≤ last_record_lsn.
WAL: the underlying stream
The WAL itself is the same thing it is in vanilla Postgres: a stream of fixed-size records. Each record has:
- A 4-byte total length and 4-byte previous-record length (for backwards traversal).
- A resource manager id (
rmid) — heap, btree, transaction commit, custom Neon rmgrs, … - An info byte (record subtype).
- Payload: per-record-type data.
Postgres breaks the stream into WAL segments, fixed-size files (16 MB by default) named 00000001000000000000000A (timeline-id, segment-number-high, segment-number-low). The safekeeper stores WAL in this exact format on disk, which means standard tools like pg_waldump work directly on a safekeeper's disk.
How records are decoded
libs/wal_decoder/ is the crate that decodes raw WAL records into the per-page tuples the pageserver's storage layer wants. For each record:
- The decoder identifies the rmid and info.
- It extracts the list of
(RelFileNode, BlockNumber)pages the record affects. - For each affected page it produces a
(key, lsn, value)tuple, wherekeyis the encoded(rel, block),lsnis the record's LSN, andvalueis either the full WAL record (delta) or a directly produced page image (for full-page-image records).
This decoding can happen on either side: traditionally the pageserver does it (pageserver/src/walingest.rs), but newer code paths let the safekeeper do it and ship pre-decoded tuples to the pageserver (safekeeper/src/send_interpreted_wal.rs).
WAL produced by Neon vs. vanilla
Neon's WAL is not bit-for-bit compatible with upstream Postgres. The t_cid patch (docs/core_changes.md) widens heap WAL records by 4 bytes so that INSERT/UPDATE/DELETE records carry the inserter's command id. Custom Neon rmgrs (pgxn/neon_rmgr/) introduce additional record types. As a result, you can't take a Neon WAL stream and replay it on vanilla Postgres, and vice versa.
Backpressure
pgxn/neon/ exposes three GUCs that govern compute → safekeeper / pageserver flow:
max_replication_write_lag— bytes the compute is allowed to be ahead of the safekeepers.max_replication_flush_lag— bytes ahead of the safekeepers'flush_lsn.max_replication_apply_lag— bytes ahead of the pageserver'slast_record_lsn.
When any limit is exceeded, the compute's commit waits stall until the corresponding LSN catches up. This is the mechanism that prevents a runaway compute from outrunning storage.
See also
- Tenant and timeline — what the LSN is anchored to.
- Layers — how layer files key into LSN ranges.
- Pageserver / Page service — the consumer of
GetPage@LSN. - Safekeeper — the producer of durable LSN advances.
docs/glossary.md— the long-form glossary entry on LSN.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.