Skip to content

Commit 4a8688b

Browse files
committed
mirror_worker: address streaming review nits
- Count only entry-contributing packages toward a chunk flush, so a run of already-persisted packages no longer triggers empty flushes; drop the now-redundant empty-chunk guard (commit_packages >= 1). - Mark StreamBuffer eof on a stream error so is_eof() stays consistent and a recovering caller won't re-poll the failed stream. - Explain the clean-vs-mid-package truncation branch in parse_next_package, and fix the stale parse_header doc that described a nested-Result return the code no longer uses.
1 parent 0f0c490 commit 4a8688b

2 files changed

Lines changed: 34 additions & 15 deletions

File tree

crates/mirror_worker/src/add_entries.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -393,17 +393,21 @@ where
393393
}
394394
packages_received += 1;
395395

396-
// Buffer only the not-yet-persisted tail of this package.
396+
// Buffer only the not-yet-persisted tail of this package. Packages
397+
// wholly below the request-start frontier are already persisted, so
398+
// they contribute no entries and don't count toward a chunk flush;
399+
// `chunk_pkgs` therefore tracks only packages that added buffered
400+
// entries.
397401
if pkg_end > initial_next {
398402
let skip = usize::try_from(initial_next.saturating_sub(pkg_start))
399403
.map_err(|_| Error::from("skip count overflows usize"))?;
400404
chunk.extend(pkg.entries.into_iter().skip(skip));
401405
chunk_end = pkg_end;
402-
}
403-
chunk_pkgs += 1;
406+
chunk_pkgs += 1;
404407

405-
if chunk_pkgs == commit_packages {
406-
if !chunk.is_empty() {
408+
// `commit_packages >= 1` (config), so a full chunk always holds
409+
// at least one package's entries: no empty-flush guard needed.
410+
if chunk_pkgs == commit_packages {
407411
(frontier_size, frontier_hash) = flush_chunk(
408412
bucket,
409413
env,
@@ -414,8 +418,8 @@ where
414418
&mut chunk,
415419
)
416420
.await?;
421+
chunk_pkgs = 0;
417422
}
418-
chunk_pkgs = 0;
419423
}
420424
}
421425

@@ -721,8 +725,9 @@ enum ParseOutcome {
721725

722726
/// Read the `add-entries` request header from `buf`, pulling more
723727
/// chunks from the underlying stream until the header parses or the
724-
/// stream errors. Returns `Ok(Ok(header))` on success or `Ok(Err(resp))`
725-
/// where `resp` is a fully-formed 400 response on malformed input.
728+
/// stream errors. Returns the parsed header, or
729+
/// [`AppError::BadRequest`] (400) if the input is malformed or truncated
730+
/// before the header is complete.
726731
///
727732
/// The header has a bounded maximum size (u16 origin + u64s + u16
728733
/// ticket + hash + u8 proof-size + 63 hashes <= ~131 KB), so the
@@ -787,6 +792,11 @@ where
787792
}
788793
Err(ParseError::Io(ref e)) if e.kind() == ErrorKind::UnexpectedEof => {
789794
if !buf.pull_one().await? {
795+
// Stream ended mid-parse. An empty buffer means the
796+
// previous package consumed exactly all buffered bytes
797+
// and this call started a fresh (never-arriving)
798+
// package: a clean between-package truncation. A
799+
// non-empty buffer holds a partial package.
790800
if buf.len() == 0 {
791801
return Ok(ParseOutcome::CleanEof);
792802
}

crates/mirror_worker/src/stream_buffer.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ pub(crate) struct StreamBuffer<S> {
3535
/// are reclaimed on the next [`Self::pull_one`]. The live buffer is
3636
/// `buf[start..]`.
3737
start: usize,
38-
/// Set when the underlying stream has signalled end-of-stream.
39-
/// Subsequent [`Self::pull_one`] calls return `Ok(false)`
40-
/// without polling the (already-finished) stream.
38+
/// Set when the underlying stream has signalled end-of-stream or
39+
/// yielded an error. Subsequent [`Self::pull_one`] calls return
40+
/// `Ok(false)` without polling the (already-finished) stream.
4141
eof: bool,
4242
}
4343

@@ -60,11 +60,13 @@ where
6060
/// Pull one chunk from the underlying stream and append to the
6161
/// internal buffer. Returns `Ok(true)` if a chunk was appended,
6262
/// `Ok(false)` if the stream ended (clean EOF). Once the stream has
63-
/// ended, all subsequent calls return `Ok(false)` without polling
64-
/// the stream again.
63+
/// ended or errored, all subsequent calls return `Ok(false)` without
64+
/// polling the stream again.
6565
///
6666
/// # Errors
67-
/// Propagates any error from the underlying stream.
67+
/// Propagates any error from the underlying stream. An error is
68+
/// terminal: [`Self::is_eof`] is set so a caller that recovers from
69+
/// the error does not re-poll the already-failed stream.
6870
pub async fn pull_one(&mut self) -> std::result::Result<bool, E> {
6971
if self.eof {
7072
return Ok(false);
@@ -80,7 +82,10 @@ where
8082
self.buf.extend_from_slice(&chunk);
8183
Ok(true)
8284
}
83-
Some(Err(e)) => Err(e),
85+
Some(Err(e)) => {
86+
self.eof = true;
87+
Err(e)
88+
}
8489
None => {
8590
self.eof = true;
8691
Ok(false)
@@ -191,5 +196,9 @@ mod tests {
191196
assert!(buf.pull_one().await.unwrap());
192197
let err = buf.pull_one().await.unwrap_err();
193198
assert_eq!(err.to_string(), "boom");
199+
// An error is terminal: eof is set and further pulls are no-ops
200+
// rather than re-polling the already-failed stream.
201+
assert!(buf.is_eof());
202+
assert!(!buf.pull_one().await.unwrap());
194203
}
195204
}

0 commit comments

Comments
 (0)