neondatabase/neon
Tenant and timeline
Two of the most pervasive types in the codebase. TenantId and TimelineId are 16-byte hex-formatted unique ids defined in libs/utils/src/id.rs. Almost every API takes one or both.
Tenant
A tenant is the unit of isolation in the pageserver and safekeeper. Each Neon customer corresponds to one tenant; there is no cross-tenant sharing of data or layer files.
A tenant has:
- A set of timelines (branches) — see below.
- A WAL-redo subprocess dedicated to it (in the pageserver). This is the per-tenant Postgres process that runs in
--wal-redomode. - Configuration (PITR window, compaction thresholds, throttling parameters). Some defaults come from the pageserver config; others are per-tenant overrides.
- An assignment to one or more pageserver shards. Most tenants are unsharded (one shard); large tenants are sharded across multiple pageservers.
- A generation number per attached shard, used to fence stale instances.
Tenant lifecycle in the pageserver, top to bottom:
- Create: storage controller calls the pageserver's
POST /v1/tenant. The pageserver writesmetadatato disk, assigns a timeline id, fetches the initial basebackup metadata from the broker. - Attach: a pageserver picks up an existing tenant by reading the latest
index_part.jsonfrom remote storage and reconstructing the timeline state. - Active: WAL is being ingested; reads are served.
- Detach: the pageserver releases the tenant. Local files are kept (so a re-attach is fast) until a separate eviction job removes them.
- Delete: the storage controller drives a multi-step deletion that bumps the generation, removes objects from S3, and clears local state.
Code:
pageserver/src/tenant.rs— theTenantstruct (~13 KLOC). Owns the timeline map, the WAL-redo manager, the per-tenant config, and the metadata file.pageserver/src/tenant/mgr.rs— theTenantManager(~3 KLOC). Owns the set ofTenants and the attach/detach state machine.
In the safekeeper, "tenant" is a simpler concept: just a namespace for timelines (safekeeper/src/timelines_global_map.rs).
Timeline
A timeline is a linear history of WAL belonging to one tenant. Users see them as branches.
Each timeline tracks:
last_record_lsn— the LSN of the most-recently-applied WAL record. Advances as WAL arrives.disk_consistent_lsn— the LSN such that all data ≤ this LSN is durable on the pageserver's local disk.remote_consistent_lsn— the LSN such that all data ≤ this LSN is durable in remote storage.ancestor_timeline_idandancestor_lsn— for branched timelines, the parent and the branch point.
Timeline lifecycle:
- Create root (
tenant.rs::create_empty_timeline) — initdb-bootstrapped. Has no ancestor. - Branch (
timeline.rs::create_timeline_at) — created at a specific LSN of an existing timeline. Initially shares all layer files with its ancestor; new WAL diverges. - Detach ancestor (
pageserver/src/tenant/timeline/detach_ancestor.rs) — materializes the branch's data into its own layer files, severing the ancestor dependency. Necessary if you want to delete the ancestor. - Delete (
timeline/delete.rs) — removes the timeline and all its layer files.
Code:
pageserver/src/tenant/timeline.rs— theTimelinestruct (~8 KLOC). Per-timeline state.pageserver/src/tenant/timeline/— sub-modules for compaction, deletion, ancestor detach, layer manager, walreceiver.
In the safekeeper, the Timeline (safekeeper/src/timeline.rs) is the unit of consensus — one election term per timeline.
How identifiers are formatted
Both TenantId and TimelineId are 128-bit values printed as 32 lowercase hex characters with no dashes. Examples:
tenant 9ef87a5bf0d92544f6fafeeb3239695c
timeline de200bd42b49cc1814412c7e592dd6e9The Rust types implement FromStr and Display matching this format. They are not UUIDs in the formal sense; they're random 128-bit values whose only constraint is uniqueness.
Sharding
Larger tenants are split into shards via ShardIdentity (libs/pageserver_api/src/shard.rs). The shard count is per-tenant; the shard a key belongs to is determined by a stable hash of the relation key prefix.
A shard is identified by:
- The tenant id.
- A 0-based shard number within the tenant.
- A stripe size (granularity of the hash partitioning).
The pageserver client on the compute side (pageserver/client/, pageserver/client_grpc/) maintains a ShardIdentity per tenant and routes each GetPage@LSN to the right shard.
Multi-tenancy properties
docs/multitenancy.md is the canonical doc. The TL;DR: one pageserver process holds many tenants; tenants share the pageserver process but not its filesystem layout (each tenant's layer files live under .neon/tenants/<id>/); they share the WAL receiver pool but not WAL-redo subprocesses.
This was an important design decision: per-tenant subprocesses isolate untrusted WAL records (a malformed record from one tenant cannot corrupt another's redo state), at the cost of process count when serving thousands of small tenants.
See also
- LSN and WAL — the time axis on which timelines live.
- Branches — the user view of timelines.
- Layers — the storage layout owned by a timeline.
- Pageserver — the central state machine.
docs/multitenancy.md— long-form discussion.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.