pub struct ControlDb { /* private fields */ }Expand description
The one redb::Database under the control plane — and the door the
runtime data-set growth takes it off the stripe through.
RedbControlStore and [RedbSecretStore] both hold an Arc of this
rather than of the database, so that ONE call can close the file under
every handle in the process and one call can reopen it, with nothing
upstream — AuthTable, RepoCatalog, the SSH Control, the gRPC gate
— holding a stale descriptor that would pin the mount. That is the whole
mechanism behind DATA-SET-GROWTH-FLOW.md G4/T2: control.redb lives
on the stripe, every SSH authentication reads it at call time, and the
stripe has to be unmounted for the members to be grown. So:
ControlDb::suspendtakes the write side (waiting for in-flight transactions), copies the tables into aRamCopy, and drops the database — the file is closed andumount /datacan succeed;- while suspended every read is served from the copy, byte for byte, so
principal_by_fingerprint, the grant check and the bootstrap state keep answering; every write isError::OffStripe; ControlDb::resumereopens the file, compares every document pointer on disk with the copy (they must agree — no write got through) and drops the copy.
An in-memory database (tests, --dry-run) has no file to close: it is
kept, and suspend still switches every reader onto the copy and every
writer onto the refusal, so the semantics are testable without a disk.
Implementations§
Source§impl ControlDb
impl ControlDb
Sourcepub fn export(&self) -> Result<ControlExport>
pub fn export(&self) -> Result<ControlExport>
ControlExport of the live database (or of the RAM copy while suspended), in one
read transaction.
Source§impl ControlDb
impl ControlDb
Sourcepub fn export_each<E>(
&self,
row: &mut dyn FnMut(ExportRow<'_>) -> Result<(), E>,
) -> Result<Result<(), E>>
pub fn export_each<E>( &self, row: &mut dyn FnMut(ExportRow<'_>) -> Result<(), E>, ) -> Result<Result<(), E>>
ControlDb::export, row by row — every blob, then every document pointer, then
every secret row, each handed to row as it is read, in one read transaction. What a
twin snapshot streams: the database never lies in memory whole. row’s error stops the
scan and comes back as Ok(Err(e)); the database’s own faults are the outer error.
Sourcepub fn view(&self) -> Result<ControlView>
pub fn view(&self) -> Result<ControlView>
A consistent view of the whole database, taken NOW and read LATER (R5.1-A, 2026-10-07): a redb read transaction (MVCC — it holds the state of this instant while writers go on), or the RAM copy while the store is off the stripe. What a snapshot takes under the log’s freeze, so it can stream the rows AFTER the freeze is gone.
Said plainly: while a view lives, redb keeps the pages it reads and the file stays open — a growth that takes the store off the stripe meanwhile cannot unmount until the view goes.
Source§impl ControlDb
impl ControlDb
Sourcepub fn is_suspended(&self) -> bool
pub fn is_suspended(&self) -> bool
Is the store off the stripe right now?
Sourcepub fn begin_group(&self) -> Result<()>
pub fn begin_group(&self) -> Result<()>
Open a group on this thread (perf twin-apply-fsync, 2026-10-06): every write this
thread makes through the stores over this database goes into ONE transaction until
ControlDb::commit_group or ControlDb::abort_group. Writes from other threads wait
for it (redb takes one writer at a time); reads see the state before it. A twin’s
follower holds one for the control-plane entries of each group it takes.
Sourcepub fn commit_group(&self, key: &str, seq: u64, mark: bool) -> Result<bool>
pub fn commit_group(&self, key: &str, seq: u64, mark: bool) -> Result<bool>
End the group with ONE durable commit, the marker key = seq inside it — when it
wrote anything, or mark asks for the marker anyway. A group that wrote nothing and is not
asked to mark is dropped (no I/O). A group a write failed in is aborted whole and the
failure answered: none of it is kept. Answers whether it committed.
Sourcepub fn durable_commits(&self) -> u64
pub fn durable_commits(&self) -> u64
How many durable commits (one fsync each) this database made since it was opened — what the I/O tests hold to a bound.
Sourcepub fn abort_group(&self)
pub fn abort_group(&self)
Drop the group on this thread, keeping none of it.
Sourcepub fn set_mark(&self, key: &str, seq: u64) -> Result<()>
pub fn set_mark(&self, key: &str, seq: u64) -> Result<()>
Move the marker key to seq, durably — inside this thread’s group when one is open
(made durable with it), else in a commit of its own (one fsync, and every earlier
non-durable commit with it).
Sourcepub fn mark(&self, key: &str) -> Result<Option<u64>>
pub fn mark(&self, key: &str) -> Result<Option<u64>>
The marker key, None when it was never set (a box from before markers, or a fresh one).
Sourcepub fn principal_activity(&self) -> Result<Vec<(String, Vec<u8>)>>
pub fn principal_activity(&self) -> Result<Vec<(String, Vec<u8>)>>
Every principal’s last-seen row (crate::activity): principal id →
its encoded stamps. Empty for a database that never kept one, and while
the store is off the stripe (the RAM copy carries no such table).
Sourcepub fn put_principal_activity(
&self,
rows: &[(String, Vec<u8>)],
mark: Option<&str>,
) -> Result<()>
pub fn put_principal_activity( &self, rows: &[(String, Vec<u8>)], mark: Option<&str>, ) -> Result<()>
Write last-seen rows in ONE transaction (one durable commit for the
whole batch), and the marker mark = 1 in the same transaction when one
is named. Refused while the store is off the stripe; the caller keeps
the rows and tries again.
Sourcepub fn suspend(&self, why: &str) -> Result<RamCopy>
pub fn suspend(&self, why: &str) -> Result<RamCopy>
Take the store off the stripe. Copies every table into RAM, then
closes the file (an in-memory database is kept). Refuses when already
suspended, and when the copy would weigh more than
RAM_COPY_CEILING_BYTES — nothing is changed in that case.
Sourcepub fn resume(&self) -> Result<ResumeReport>
pub fn resume(&self) -> Result<ResumeReport>
Put the store back on the stripe. Reopens the file (the mount must be back; a missing file is an error, never a fresh database), compares every document pointer on disk with the copy, and drops the copy. Refuses when not suspended.