neondatabase/neon
Patterns and conventions
These are the conventions that show up across most of the Rust code. Following them keeps PRs reviewable and avoids inconsistency drift in a 300k-LOC codebase.
Async runtime
Everything async is on tokio. The workspace pins tokio = { version = "1.43.1", features = ["macros"] } and most binaries call #[tokio::main(flavor = "multi_thread")] directly.
Two locally-relevant points:
tokio-epoll-uringis used in the pageserver hot path for low-overhead async file I/O on Linux. Seepageserver/src/virtual_file.rs. Don't replace this with naivetokio::fsin performance-sensitive code.- Cancellation tokens.
tokio_util::sync::CancellationTokenis the convention for plumbing shutdown through long-running tasks. Thetask_mgrinpageserver/src/task_mgr.rsis the central registry.
Error handling
The two top-level error crates are anyhow and thiserror.
anyhow::Error+?is the default for binary code paths and most internal APIs.Cargo.tomlenables thebacktracefeature so panics andbail!calls capture useful traces.thiserror::Erroris used for typed errors at API boundaries — the pageserver'sPageReconstructError, the safekeeper'sWalServiceError, the proxy'sError. Reach forthiserrorwhen callers need to match on the variant.
docs/error-handling.md is the authoritative document for this. The short version:
- Internal helpers:
anyhow::Result<T>. - Public API surfaces (HTTP routes, gRPC handlers): a typed error that maps to a status code or a gRPC
Status. - Don't
unwrap()orexpect()outside of#[cfg(test)]and known-impossible cases. When you do, leave a comment.
Logging and tracing
tracing::info!, warn!, error!, debug!, trace! are the only logging macros used. The log crate appears only as a transitive dependency or in third-party adapters.
Span discipline:
- Most subsystems wrap top-level work in a span with stable fields, e.g.
tracing::info_span!("tenant", id = %tenant_id, shard = %shard_id). These propagate throughFutureexecution via.instrument(span). - Long-running tasks set
Span::current().record(...)to add fields as they discover more context. - The pageserver convention is to log a single line per request at
infowith structured fields (request_id,tenant_id,timeline_id,lsn,key).
libs/tracing-utils/ provides the JSON formatter, OTel exporter, and a Tracer builder used by every binary.
Metrics
The metrics crate (libs/metrics/) is the only Prometheus client allowed in workspace code. Convention:
- Define metrics as
staticLazy<Histogram>/Lazy<IntCounter>etc. inmetrics.rsper crate. - Names use
<service>_<noun>_<unit>form, e.g.pageserver_layer_count,safekeeper_flush_lsn. - Histograms have explicit bucket lists tuned for the metric range; don't reach for the default buckets.
- Avoid high-cardinality labels (no per-tenant labels on hot-path counters; the
pageserver_smgr_query_secondsfamily is an exception that's been carefully reviewed).
HTTP
axum is the framework for new HTTP servers. Older code uses routerify or raw hyper. New surfaces should be axum.
OpenAPI specs live alongside the code as openapi_spec.yaml (e.g. pageserver/src/http/openapi_spec.yml, compute_tools/src/openapi_spec.yml). They are linted in CI by the lint-openapi-spec make target via redocly.
gRPC
tonic is the gRPC stack; prost for protobuf codegen. Each service that exposes gRPC has a proto/ directory or generates from .proto files at build time using tonic-build. The pageserver's gRPC page API is in pageserver/page_api/ and the storage broker is storage_broker/proto/.
Postgres FFI
libs/postgres_ffi/ wraps Postgres struct definitions, magic numbers, and WAL record layouts. There is one source of truth (the vXX/ submodule) per supported Postgres version. When you add a Postgres-side struct, update postgres_ffi for every supported pg_version. The postgres_ffi/wal_craft/ test crate generates synthetic WAL streams used in unit tests.
Configuration
Most binaries take a TOML config file. Conventions:
- Config structs live in
config.rsper crate (pageserver/src/config.rs,safekeeper/src/config.rs,proxy/src/config.rs). - Use
serde_withfor things likehumantimedurations and base64 byte arrays. - All numeric tunables that affect behavior have a default in code and a comment naming the rationale.
docs/settings.md is the legacy human-facing tunable list. reference/configuration.md in this wiki has more.
Async-friendly locks
The codebase uses both parking_lot::Mutex (fast, blocking) and tokio::sync::Mutex (slower, async). The rule is: hold locks across .await only if you really need to, and prefer parking_lot for short critical sections that don't .await.
arc-swap, clashmap (a fork of dashmap), and papaya are used in performance-sensitive lookup tables. arc_swap::ArcSwap<T> is the normal pattern for "rarely-updated, frequently-read config".
Panics and process death
Panics on the hot path are escalated:
pageserver/src/lib.rsinstalls a panic hook that records a metric, logs anerror!, and (in production) callsstd::process::abort()after a short delay. Tests disable the abort.- The
tracing-errorcrate captures panic backtraces into log records.
If you find yourself wanting to panic!() to indicate "this should never happen", prefer unreachable!() or a typed error variant. Reserve panic! for true invariant violations.
Naming
- Snake-case file names; Rust's default in-module naming (CamelCase types, snake_case fns).
- The crate dir matches the crate name in
Cargo.toml(e.g.libs/pageserver_api/↔pageserver_api). - Public APIs use the singular tense:
Tenant, notTenants;Timeline, notTimelines. - Postgres-aligned terms keep their Postgres spelling:
lsn(notseqno),relnode,forknum, etc.
Python style
For the integration tests in test_runner/:
- Code style enforced by
ruff formatandruff check. Config inpyproject.toml. - Type-checked with
mypy. Run from the repo root (mypyreadspyproject.tomlfrom there). - Pytest fixtures from
test_runner/fixtures/are the building block; new tests should compose fixtures rather than re-implement setup.
Comments and TODOs
The repo has ≈785 TODO|FIXME|XXX|HACK comments. New TODOs should be specific (link a GitHub issue if there is one). The cleanup-opportunities/ view in this wiki captures the standing list — see Cleanup opportunities / TODOs.
File reading checklist when starting in a new area
When you first land in an unfamiliar subsystem, the fastest way to acclimate is to read three files:
- The
README.mdnext to the crate, if there is one. - The crate's
lib.rsormain.rs— these are kept short and serve as a tour. - The largest
*.rsfile in the directory. In Neon that's almost always where the central state machine lives (e.g.tenant.rs,timeline.rs,service.rs).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.