pub struct Tuning {
pub cadence: CadencePolicy,
pub clock: Option<Arc<dyn Clock>>,
pub wal_autocheckpoint: WalCheckpointPolicy,
pub writer_cache_size: Option<i32>,
pub reader_cache_size: Option<i32>,
}Expand description
Everything Database::open_tuned can be told, in one growable struct
(0.12.12, W5.1, D-155).
§Why a struct rather than a fourth constructor
There were three — Database::open, Database::open_with_cadence,
Database::open_with_clock — and each new knob added one more, with the
combinatorics of the ones before it. 0.13.0 alone wanted three knobs
(wal_autocheckpoint, and a page cache each for the writer and the
readers), which is the point at which the naming stops being possible.
Default plus functional update is the whole design. They make a new
knob an additive change: callers construct with ..Default::default() and
keep compiling, and the fields that arrive after them are the ones they did
not ask about. That is not a hypothetical — W5.1 ships this struct with two
fields, and W5.3/W5.4 add the three tuning knobs to it without touching a
caller.
§Why this is not #[non_exhaustive]
The plan for this wave specified #[non_exhaustive] alongside Default,
on the usual reasoning that the attribute is what makes a struct growable.
It does not compile: a #[non_exhaustive] struct cannot be built with
literal syntax outside its own crate at all, and the functional-update
form is literal syntax, so Tuning { cadence, ..Default::default() } is
E0639 for every external caller — the exact expression the attribute was
added to protect. (The rule differs from #[non_exhaustive] on an enum,
which only forces a wildcard arm; CadencePolicy keeps it for that
reason.) The two ways to have both are a builder with setters, or plain
Default — and Default is chosen because the field-literal form is the
legible one, and because the growth this needs to survive is additive
fields, which ..Default::default() already absorbs.
The cost is real and worth stating: a caller who writes an exhaustive
literal, with no ..Default::default(), breaks when a field is added. That
is a compile error at the call site with an obvious fix, not a silent
behaviour change, and it is the price of the readable form.
§The three constructors stay
They delegate here and are not deprecated. open(path) is the right call for
most callers and should not acquire a warning for being the common case; the
consolidation is about where the next knob goes, not about moving anyone.
let db = Database::open_tuned(
"graph.db",
Tuning {
cadence: CadencePolicy::Disabled,
..Default::default()
},
)
.await?;Fields§
§cadence: CadencePolicyWhat the snapshot cadence should do. Defaults to
SnapshotCadence::default, as Database::open does.
clock: Option<Arc<dyn Clock>>A clock to stamp recorded_at with, for tests (§5.1.2, D-062). None
is SystemClock. Floored against the database exactly as
Database::open_with_clock describes — read that before injecting
one against a non-empty file.
wal_autocheckpoint: WalCheckpointPolicyWhen SQLite checkpoints the WAL on its own (0.12.14, W5.3, F-30).
Applied to the write connection, which is the only connection in
this crate that commits, and therefore the only one whose autocheckpoint
setting can ever fire. Pair WalCheckpointPolicy::Disabled with an
explicit Database::checkpoint or the WAL grows without bound.
writer_cache_size: Option<i32>Page cache for the write connection, as SQLite’s cache_size
(0.12.15, W5.4).
None leaves SQLite’s default of −2000, which is −2000 kibibytes, or
2 MB. Negative values are KiB and positive values are pages — that
is SQLite’s convention and it is preserved rather than smoothed over,
because a caller who knows the pragma should not have to discover that
this crate redefined it. Some(-64_000) is 64 MB; Some(64_000) is
64,000 pages, which at the 4 KiB page size this crate gets is 256 MB.
The writer wants a large cache: it is one connection, it holds the write lock while it works, and every page it has to re-read from disk is time no other writer can use.
§Unlike the two above, None here is not a policy enum
Because SQLite’s default is a value rather than a mechanism. Absence still means “leave it alone” — it just happens that leaving this alone is expressible as not running a pragma, where leaving the automatic checkpointer alone required saying which of two things “alone” meant.
reader_cache_size: Option<i32>Page cache for every read-only connection: the shared
Database::read_conn, the snapshot cadence’s own connection, and
(since W5.5) each Database::diagnostic_conn (0.12.15, W5.4).
Same units as Self::writer_cache_size, and the same None.
Split from the writer’s because the profiles are opposite and one number
cannot serve both. There is exactly one writer and it is long-lived, so
its cache is a fixed cost paid once. Read-only connections are plural —
diagnostic_conn mints a new one per call — so a large value here is
multiplied by however many a caller opens, and the R15 hazard that
method documents is about concurrent opens. A single shared number
therefore has to be small enough for the multiplied case, which is the
wrong size for the one connection that holds the write lock.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for Tuning
impl !UnwindSafe for Tuning
impl Freeze for Tuning
impl Send for Tuning
impl Sync for Tuning
impl Unpin for Tuning
impl UnsafeUnpin for Tuning
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request