neondatabase/neon
Page service
The page service is the pageserver's read-side network surface. It accepts GetPage@LSN requests and a few related commands from compute nodes, looks up the right layers, runs WAL-redo if needed, and writes the resulting page back. It speaks two protocols today: the original libpq replication-stream protocol and a newer gRPC variant.
Two protocols, one implementation
libpq protocol (original)
Implemented in pageserver/src/page_service.rs (≈4.7k lines). When a compute opens a connection, the pageserver hands the socket to a per-connection task. The wire framing reuses Postgres's libpq replication protocol — specifically, the BasebackupRequest, PagestreamRequest, and copy-mode messages — but the semantics are Neon's.
Message types (defined in libs/pageserver_api/src/models.rs):
| Request | Response |
|---|---|
PagestreamGetPageRequest { tenant, timeline, key, lsn } |
PagestreamGetPageResponse { page_image } |
PagestreamExistsRequest { rel, lsn } |
PagestreamExistsResponse { exists } |
PagestreamNblocksRequest { rel, lsn } |
PagestreamNblocksResponse { nblocks } |
PagestreamGetSlruSegmentRequest |
PagestreamGetSlruSegmentResponse |
PagestreamDbSizeRequest |
PagestreamDbSizeResponse |
The compute also uses the same channel to request a basebackup (pageserver/src/basebackup.rs) when it boots — a tarball of the files Postgres needs to start up at a given LSN. This avoids any per-page initialization round trips for cold starts.
gRPC protocol (newer)
Defined in pageserver/page_api/proto/. The same logical operations are exposed as a tonic-based gRPC service. The reasons for adding gRPC:
- Easier for non-Postgres clients to consume (e.g. lambda-style serverless functions).
- Better support for streaming and cancellation primitives.
- Compatible with HTTP/2 connection pooling, which simplifies multiplexing many compute connections through fewer TCP sockets.
The gRPC service is implemented in pageserver/src/page_service_grpc.rs (in newer commits) and uses the same per-tenant timeline lookup and Timeline::get_at_lsn() call as the libpq variant. Sharded routing in the gRPC client lives in pageserver/client_grpc/.
pageserver/page_api/ is the standalone crate exposing the protobuf definitions and a low-level client builder. pageserver/client/ is the libpq-flavored client; pageserver/client_grpc/ is the gRPC one.
Request lifecycle
sequenceDiagram
participant C as Compute
participant Net as page_service.rs
participant TL as Timeline
participant LM as LayerMap
participant Redo as WAL-redo
C->>Net: PagestreamGetPage(key, lsn)
Net->>TL: get_at_lsn(key, lsn)
TL->>LM: search(key, lsn)
LM-->>TL: layers covering (key, lsn)
alt layer not local
TL->>RemoteStorage: download(layer)
end
TL->>TL: read base image + delta records
TL->>Redo: request_redo(base, [records])
Redo-->>TL: materialized page
TL-->>Net: page bytes
Net-->>C: PagestreamGetPageResponse(page)Per-connection tasks log a span at info (the request_id field is propagated from the compute side) so operators can trace a single request from compute through to layer-map lookup to redo.
Authentication
The page service authenticates compute nodes using JWTs. The token signing key is a per-tenant key that the compute receives from the control plane at boot time; the pageserver verifies tokens with a public key configured at startup.
See pageserver/src/auth.rs and libs/utils/src/auth.rs. The same JWT machinery is reused by the safekeeper and the proxy.
Throttling and cancellation
- Per-tenant throttling.
pageserver/src/tenant/throttle.rsrate-limits expensive operations per tenant when a tenant is misbehaving (e.g. asking for many cold pages). - Cancellation. Each request is bound to a
CancellationToken; when the compute disconnects or the timeline is detached, the in-flight request is cancelled before it expensively reaches WAL-redo. - Backpressure. If the WAL receiver is far behind the requested LSN, the page service can either wait for the WAL to be ingested or return a "not yet" status. The latter is exposed via the
PagestreamGetPageRequest'snot_modified_sincefield.
Basebackup
pageserver/src/basebackup.rs builds a tar stream containing every file a Postgres needs to start: pg_control, pg_xlog/000…, the relation files for the system catalog, etc. It also writes a small neon.signal and zenith.signal file to mark the data directory as Neon-managed.
A basebackup_cache (pageserver/src/basebackup_cache.rs) memoizes recent basebackups so that "warm" cold starts (compute restarts within a short window) can replay an existing tarball instead of regenerating it.
Metrics
Look for pageserver_smgr_query_seconds_* (per-message-type histograms), pageserver_smgr_*_request_total, and pageserver_request_in_flight_count. These are the key SLOs for the page service.
Key source files
| File | Purpose |
|---|---|
pageserver/src/page_service.rs |
libpq replication-flavored connection handler. |
pageserver/src/basebackup.rs |
Tar stream generator for compute bootstrap. |
pageserver/src/basebackup_cache.rs |
Memoization layer for basebackups. |
pageserver/page_api/proto/ |
gRPC service definitions. |
pageserver/client/ |
libpq client (used by neon_local, tests, the compute side). |
pageserver/client_grpc/ |
gRPC client (used by newer compute builds). |
libs/pageserver_api/src/models.rs |
Request/response types shared between client and server. |
See also
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.