Skip to content

Commit 26671bf

Browse files
committed
mirror_worker: stream and incrementally commit add-entries uploads
Replace the whole-body buffer with a streaming decode: gunzip and parse the body as it arrives through a small stream-buffer adapter (retry the parse as chunks land, consume on success), so a large upload no longer holds the entire body in memory. Persist incrementally every `commit_packages` packages (default 32, config capped at 1024) so a long or interrupted upload advances the frontier as it streams rather than only at the end. Each flush commits from the last persisted frontier and is resumable; the running frontier is threaded locally across flushes so the DO is not re-queried between chunks. Tiles are immutable and content-addressed and the DO advance is a monotone compare-and-swap, so a repeated or concurrent flush of the same range is harmless.
1 parent 628f4c7 commit 26671bf

8 files changed

Lines changed: 811 additions & 204 deletions

File tree

crates/mirror_worker/config.dev.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"submission_prefix": "http://localhost:8787/",
66
"monitoring_prefix": "http://localhost:8787/",
77
"clean_interval_secs": 5,
8+
"commit_packages": 2,
89
"logs": {
910
"oid/1.3.6.1.4.1.32473.2": {
1011
"description": "Dev-only MTC CA cosigner. Key name is the CA ID; the mirror serves log numbers 1-6 as origins oid/1.3.6.1.4.1.32473.2.0.<N>. log_public_keys holds a dev-only ML-DSA-44 SPKI.",

crates/mirror_worker/config.schema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@
3030
"default": 3600,
3131
"description": "How often (in seconds) the per-origin partial-tile cleaner wakes to clean orphaned partial tiles from object storage. Defaults to 3600 (one hour) when omitted."
3232
},
33+
"commit_packages": {
34+
"type": "integer",
35+
"minimum": 1,
36+
"maximum": 1024,
37+
"default": 32,
38+
"description": "How many entry packages add-entries verifies before flushing them to storage and advancing the persisted-entry frontier. Bounds in-memory buffering and gives durable mid-request progress on large uploads. Defaults to 32 (the recommended per-request package budget) when omitted; capped at 1024 to bound worst-case buffering."
39+
},
3340
"logs": {
3441
"type": "object",
3542
"description": "CAs this mirror mirrors, keyed by log_key_name: the CA cosigner's note-signature name (the CA ID) on the checkpoints it ingests. Used as a signed-note key name at runtime, so per c2sp.org/signed-note it MUST NOT contain '+', whitespace, or control characters.",

crates/mirror_worker/config/src/lib.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ pub struct AppConfig {
6262
/// back to a one-hour default (see [`Self::clean_interval_secs`]).
6363
/// Consumed by [`mirror_worker`](../mirror_worker/)'s `cleaner_do`.
6464
pub clean_interval_secs: Option<u64>,
65+
/// How many entry packages the `add-entries` handler verifies before
66+
/// flushing them to storage and advancing the persisted-entry
67+
/// frontier. Bounds in-memory buffering and gives durable mid-request
68+
/// progress on large uploads. `None` falls back to a default of 32
69+
/// (see [`Self::commit_packages`]). Consumed by
70+
/// [`mirror_worker`](../mirror_worker/)'s `add_entries`.
71+
pub commit_packages: Option<u64>,
6572
/// CAs this mirror mirrors, keyed by `log_key_name`: the CA
6673
/// cosigner's note-signature name (the CA ID) carried by the
6774
/// checkpoints it ingests.
@@ -118,6 +125,17 @@ impl AppConfig {
118125
self.clean_interval_secs.unwrap_or(3600)
119126
}
120127

128+
/// How many entry packages `add-entries` commits per flush, falling
129+
/// back to 32 when `commit_packages` is unset. 32 matches the
130+
/// per-request package budget clients are recommended to stay within
131+
/// (tlog-mirror "Implementation Considerations"), so a compliant
132+
/// single-request upload still commits once, while larger uploads
133+
/// flush every 32 packages instead of buffering the whole body.
134+
#[must_use]
135+
pub fn commit_packages(&self) -> u64 {
136+
self.commit_packages.unwrap_or(32)
137+
}
138+
121139
/// Validate the configuration beyond what `serde` and the JSON schema
122140
/// can express.
123141
///
@@ -141,9 +159,9 @@ impl AppConfig {
141159
/// signed-note key name length cap, since each origin is itself
142160
/// used as a checkpoint origin.
143161
///
144-
/// Simple single-field bounds (e.g. the log-number ranges) are
145-
/// expressed in `config.schema.json` and enforced by the build
146-
/// script, so they are not re-checked here.
162+
/// Simple single-field bounds (e.g. `commit_packages` and the
163+
/// log-number ranges) are expressed in `config.schema.json` and
164+
/// enforced by the build script, so they are not re-checked here.
147165
///
148166
/// `log_key_name` uniqueness across log entries is not checked here;
149167
/// it is enforced earlier, during deserialization (see
@@ -340,6 +358,7 @@ mod tests {
340358
submission_prefix: "https://mirror.example/".to_owned(),
341359
monitoring_prefix: None,
342360
clean_interval_secs: None,
361+
commit_packages: None,
343362
logs: HashMap::from([(
344363
"example.com/log1".to_owned(),
345364
LogParams {
@@ -464,6 +483,14 @@ mod tests {
464483
.expect("a valid log-number window is accepted");
465484
}
466485

486+
#[test]
487+
fn commit_packages_defaults_to_32() {
488+
let mut cfg = good_app_config();
489+
assert_eq!(cfg.commit_packages(), 32);
490+
cfg.commit_packages = Some(8);
491+
assert_eq!(cfg.commit_packages(), 8);
492+
}
493+
467494
#[test]
468495
fn validate_rejects_inverted_window() {
469496
let cfg = with_log(|log| {
@@ -532,6 +559,7 @@ mod tests {
532559
submission_prefix: "https://mirror.example/".to_owned(),
533560
monitoring_prefix: None,
534561
clean_interval_secs: None,
562+
commit_packages: None,
535563
logs: HashMap::from([(
536564
"a".repeat(250),
537565
LogParams {

0 commit comments

Comments
 (0)