pub struct CapacitySection {
pub max_accounts: Option<u64>,
pub max_records_per_account: Option<u64>,
pub log_budget_bytes: Option<u64>,
pub checkpoint_log_bytes: Option<u64>,
pub checkpoint_interval_secs: Option<u64>,
pub evaluation_log_retention_days: Option<u64>,
pub write_log_retention_days: Option<u64>,
pub disk_warning_free_percent: Option<u8>,
pub disk_critical_free_percent: Option<u8>,
pub firehose_replay_frames: Option<usize>,
pub firehose_replay_bytes: Option<u64>,
}Expand description
How many accounts this deployment may hold at once, and other bounds on what this deployment keeps rather than what an agent may do.
Reload: needs a restart today. Enforced against the value AppState was
built with, the same as BlobsSection’s quotas.
Fields§
§max_accounts: Option<u64>Mirrors --max-accounts. See plan/capacity.md.
Reasonable range: 1 to 100_000. One account is a deployment that exists to run a single agent. The high end is where the costs this cap is holding back stop being about accounts at all: every account is a repository replayed from the log at startup, so a deployment near it should be sized against its restart time rather than against this number. The built-in default is 1000.
max_records_per_account: Option<u64>The most records one account’s repository may hold. No flag mirrors
this, only this key. A caller’s write that would add records past it
is refused with 403 TooManyRecords. Updates and deletes are always
accepted, and the server’s own records are written past it. An
account already past it keeps what it has and cannot create more.
Reasonable range: 1_000 to 100_000. A write to a repository the server holds built costs the same at any size, about 200µs. A write that finds it not held builds it from every record: after a restart, or once more than 32 repositories have written since. That build took 10ms at a thousand records, 107ms at ten thousand, 690ms at fifty thousand and 2.6s at a hundred thousand, on a loaded 16-thread laptop. Below a thousand, an agent writing a record an hour fills it in six weeks. The built-in default is 50_000. Startup refuses a value outside the range.
log_budget_bytes: Option<u64>Bytes the write-ahead log may reach before a further write is
refused with StorageFull. Mirrors --log-budget. Applied by
didbot_pds::wal::Wal::set_capacity.
Reasonable range: a tenth of the volume to most of it. This is a
budget, not a disk check: it exists so a full volume is a deployment
that says StorageFull at a size the operator chose, rather than
one that discovers the size of its disk mid-write. Set it below what
the volume holds, with room for the blobs beside it, which this does
not count and [blobs] total_quota_bytes bounds. Unset, the log grows
until the filesystem stops it — an append that fails is still refused
cleanly, just at a size nobody picked.
checkpoint_log_bytes: Option<u64>Bytes the write-ahead log reaches before the running server takes a
checkpoint and trims the log against it. No flag mirrors this, only
this key. Applied by didbot_pds::Durable::checkpoint_when.
Reasonable range: 128 MiB (134_217_728) to three quarters of
log_budget_bytes. Each checkpoint writes the whole state, so a
low watermark rewrites the state for every few segments of log it
reclaims. Near the budget, the writes that land while a checkpoint
runs can reach the budget first, and those are refused with
StorageFull. Unset, it is half of log_budget_bytes, or 1 GiB
with no budget. Startup refuses a value below 1 MiB or at or above
the budget.
checkpoint_interval_secs: Option<u64>How often the running server takes a checkpoint while the log stays
under checkpoint_log_bytes, in seconds, if anything was written
since the last. No flag mirrors this, only this key.
Reasonable range: 3_600 to 604_800 (an hour to a week). This bounds how much log a restart replays and how long churn sits in the log. An hour costs a write of the whole state every hour; a week lets a restart replay a week of writes. The built-in default is 86_400, a day. Startup refuses a value outside the range.
evaluation_log_retention_days: Option<u64>How long a sealed segment of the policy evaluation log is kept before
it is trimmed, in days. No flag mirrors this yet, only this key.
Applied by didbot-serve/src/bin/didbot-pds/’s Enforcement::open
via didbot_pds::evaluation_log::FileSink::set_retention; unset
leaves that sink’s own DEFAULT_RETENTION, thirty days.
This log’s volume is driven by what gets refused, which an attacker controls: a single client retrying a denied write writes a row every time. A week (7) is enough to answer “why was this refused” while it is still being asked; a quarter (90) keeps a season of denials for looking at patterns, and costs disk in proportion to how noisy the deployment’s callers are. One day is the shortest that survives an operator sleeping through an incident.
write_log_retention_days: Option<u64>How long a sealed segment of the write log is kept before it is
trimmed, in days. The same shape as
Self::evaluation_log_retention_days and applied the same way,
against didbot_pds::write_log::FileWriteLog.
This log grows with real use rather than with refusals: one row per record written or deleted, naming the app that presented it. It is the only record of which app wrote what, so the window is how far back an attribution question can be answered. Thirty days (the default) covers “what did this app do last month”; a year (365) makes attribution answerable for as long as most operators keep backups, at roughly a row per write. The two logs are set separately because they fill at unrelated rates.
disk_warning_free_percent: Option<u8>The data volume’s free space, as a percentage of its size, below
which GET /health’s disk.alert reads warning and the journal
writes a warn line. The built-in default is 20.
Reasonable range: 10 to 50. The log and the blobs refuse at their own budgets, but the record heap, a checkpoint and the small files beside the log grow until the volume is full. Twenty percent of the default 20 GiB volume is 4 GiB, days of ordinary writes to act in. Above 50 the alert stays raised on a volume whose budgets are in use. Startup refuses a value outside the range.
disk_critical_free_percent: Option<u8>The same, for critical. The built-in default is 10.
Reasonable range: 2 to 20, and below
Self::disk_warning_free_percent. A boot writes a checkpoint as
large as the state, so this should leave room for one. Startup
refuses a value outside the range, or at or above the warning.
firehose_replay_frames: Option<usize>How many frames subscribeRepos keeps in memory for a consumer to
resume from. No flag mirrors this, only this key. Default 4096,
didbot_serve::DEFAULT_REPLAY_FRAMES.
Range: 256 to 65_536, and startup refuses a value outside it.
This is a relay’s reconnect
window: a cursor older than the frames kept gets OutdatedCursor and
must read the repositories to fill the hole. Below 256 a short relay
outage on a busy server is already a hole. Each frame kept also costs
a few dozen bytes that Self::firehose_replay_bytes does not count,
which is what this bound limits when frames are small.
firehose_replay_bytes: Option<u64>How many encoded bytes those frames may come to. No flag mirrors
this, only this key. Default 67_108_864 (64 MiB),
didbot_serve::DEFAULT_REPLAY_BYTES. The oldest frames are evicted
first, whichever of the two bounds is reached. Frames an open stream
has not sent yet are kept past both bounds, up to twice them.
Range: 16_777_216 (16 MiB) to 1_073_741_824 (1 GiB), and startup
refuses a value outside it. Within that, stay at or under a sixteenth
of the instance’s memory. A commit may carry 2,000,000 bytes, so this
is the bound that holds when records are large. The firehose’s memory
is at most twice this, plus the one frame each open connection is
writing, up to subscribeRepos’ connection limit. Below 16 MiB a
burst of large records evicts frames before a relay has read them.
Above a sixteenth, twice this competes with the rest of the server
for memory.
Trait Implementations§
Source§impl Clone for CapacitySection
impl Clone for CapacitySection
Source§fn clone(&self) -> CapacitySection
fn clone(&self) -> CapacitySection
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for CapacitySection
impl Debug for CapacitySection
Source§impl Default for CapacitySection
impl Default for CapacitySection
Source§fn default() -> CapacitySection
fn default() -> CapacitySection
Source§impl<'de> Deserialize<'de> for CapacitySection
impl<'de> Deserialize<'de> for CapacitySection
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for CapacitySection
impl PartialEq for CapacitySection
Source§fn eq(&self, other: &CapacitySection) -> bool
fn eq(&self, other: &CapacitySection) -> bool
self and other values to be equal, and is used by ==.