chorus_client/lib.rs
1#![warn(missing_docs)]
2//! Database-ready quorum WAL over 1, 3, or 5 GCS Rapid zonal buckets
3//! (typically three).
4//!
5//! Start with [`SegmentedVolume::recover`] using the database's durable
6//! checkpoint boundary. Consume the returned [`Recovery`] stream through its
7//! fixed end, then call [`Recovery::start`]. Submit caller-numbered opaque records
8//! through [`WalHandle::enqueue_append`]; admission returns an
9//! [`AppendCompletion`] without waiting for durability. A completion success is
10//! durable on a strict-majority quorum of the configured zones. Use
11//! [`Error::may_have_committed`] on a completion failure: ambiguous outcomes
12//! may replay after takeover, so delivery is at-least-once.
13//! [`Error::ActiveSegmentFull`] is different: it is definitive admission
14//! backpressure, consumes no sequence number, and leaves the writer healthy.
15//! Truncate retained sealed history so a deferred rotation can proceed, then
16//! retry the same record.
17//!
18//! One sequence number identifies one application-encoded transaction record;
19//! the WAL never adds another application-level batching layer. Startup replay
20//! uses record boundaries through [`WalSeqNo`] and is available only on
21//! [`Recovery`]. Once the database has
22//! durably checkpointed its own state, call [`WalHandle::truncate_before`] to
23//! delete whole sealed segments below that checkpoint. Startup and periodic
24//! maintenance retry already-authorized deletion tombstones before repairing
25//! missing immutable sealed copies; degraded rotations also schedule a targeted
26//! repair. Active segments are never repaired in place, and maintenance never
27//! advances the database's checkpoint floor autonomously. See the `database_wal`
28//! example for the complete lifecycle.
29//! Every fallible public operation returns [`Error`].
30//!
31//! # Operational constraints
32//!
33//! A typical production volume uses three Rapid buckets in distinct zones of
34//! one region plus a regional bucket for the default GCS manifest store. Bucket
35//! arguments are full v2 resource names, and zonal list order is durable replica
36//! identity. Current manifests bind that ordered set in `chorus.buckets`;
37//! its length is the replica count, and missing, duplicate, or later mismatched
38//! bindings are rejected. Physical zone placement is not discoverable through
39//! this API and remains an operator check.
40//!
41//! Rapid zonal buckets have neither Object Versioning nor soft delete, so
42//! replacement and truncation deletes are permanent. Archive immutable sealed
43//! segments before truncation when the database needs point-in-time recovery.
44//! The default GCS manifest directory retains roughly 115 current checksummed
45//! segments before rotation defers; custom [`ManifestStore`] implementations
46//! may report a larger budget. [`WalEngineConfig::max_active_segment_bytes`]
47//! bounds that deferral with non-poisoning [`Error::ActiveSegmentFull`]
48//! backpressure.
49//!
50//! The default GCS manifest is one repeatedly updated object. Treat
51//! [`WalEngineConfig::max_segment_bytes`] as the single-pending refill floor:
52//! for encoded throughput `T` bytes/s and worst-case provision-plus-fold
53//! latency `L`, size it above `T * L` with operational headroom. Exceeding that
54//! bound is fail-closed: append dispatch pauses before an unregistered segment
55//! can receive records.
56
57#![doc = ""]
58#![doc = "# Complete example"]
59#![doc = ""]
60#![doc = "This is the same source built as the `database_wal` Cargo example."]
61#![doc = ""]
62#![doc = "```no_run"]
63#![doc = include_str!("../examples/database_wal.rs")]
64#![doc = "```"]
65
66mod auth;
67mod engine;
68mod error;
69mod grpc;
70mod maintenance;
71mod manifest;
72mod manifest_store;
73mod metrics;
74mod protocol;
75mod record;
76mod segment;
77mod transport;
78
79#[cfg(test)]
80mod grpc_internal_tests;
81
82pub use auth::{AccessTokenSource, BearerAuth, RefreshingAuthConfig};
83pub use engine::{AppendCompletion, AppendReceipt, WalEngineConfig, WalHandle};
84pub use error::Error;
85pub use grpc::GrpcReplicaFactory;
86pub use manifest_store::{ManifestStore, ManifestStoreError, ManifestVersion, VersionedManifest};
87pub use metrics::{
88 CounterFn, GaugeFn, HistogramFn, MetricsRecorder, NoopMetricsRecorder, UpDownCounterFn,
89};
90pub use protocol::ClientConfig;
91pub use segment::{
92 Recovery, RecoveryTimings, RepairReport, SegmentedVolume, TruncationReport, WalRecord, WalSeqNo,
93};
94pub use transport::TransportCode;
95
96/// Transport seam exposed only to the deterministic-simulation harness, which
97/// supplies an in-memory `ReplicaFactory` in place of the gRPC transport.
98#[cfg(feature = "dst-support")]
99pub use transport::{
100 AppendToken, LaneDurableChange, ListedObject, Replica, ReplicaFactory, ReplicaSnapshot,
101 TransportError,
102};
103
104/// Helpers for repository probes that intentionally share transport details.
105///
106/// This module is excluded from the default API and is available only with the
107/// `probe-support` Cargo feature.
108#[cfg(feature = "probe-support")]
109pub mod probe_support {
110 pub use crate::grpc::{
111 probe_generation_zero_takeover, redirect_routing_token, GenerationZeroOpenObservation,
112 GenerationZeroTakeoverProbeResult,
113 };
114 pub use crate::transport::TransportError;
115}
116
117/// Capacity introspection for the deterministic simulation harness.
118///
119/// Excluded from the default API and available only with the `dst-support`
120/// Cargo feature. The harness uses this to mirror the engine's rotation gate:
121/// a swap may only begin when the directory can hold both the old tail and the
122/// in-flight pending that a swap-window crash would force recovery to seal.
123#[cfg(feature = "dst-support")]
124pub mod dst_support {
125 use crate::manifest::directory_has_room_for;
126 use crate::manifest_store::GCS_MAX_DIRECTORY_BYTES;
127
128 /// Whether the GCS-backed segment directory whose entries are currently
129 /// `encoded_segments` (the `chorus.segments` register value) can take
130 /// `additional` more sealed entries. Matches the byte budget the engine
131 /// reserves before a fold CAS, so a harness check and the engine's
132 /// `rotation_due` gate agree exactly.
133 pub fn gcs_segment_directory_has_room(encoded_segments: &str, additional: usize) -> bool {
134 directory_has_room_for(encoded_segments.len(), additional, GCS_MAX_DIRECTORY_BYTES)
135 }
136}