Skip to content

client stats: send multiple snapshots in a single request#495

Open
mattklein123 wants to merge 2 commits into
mainfrom
single-stats-upload
Open

client stats: send multiple snapshots in a single request#495
mattklein123 wants to merge 2 commits into
mainfrom
single-stats-upload

Conversation

@mattklein123
Copy link
Copy Markdown
Contributor

No description provided.

Signed-off-by: Matt Klein <mklein@bitdrift.io>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the bd-client-stats upload path to batch multiple on-disk aggregated snapshot files into a single StatsUploadRequest, reducing the number of separate upload requests and enabling stable retries via a deterministic batch transport UUID.

Changes:

  • Batch multiple pending snapshot files into one StatsUploadRequest (multi-snapshot upload) and track completion using the batch’s source file IDs.
  • Introduce a stable batch upload_uuid derived from an ordered SHA-256 digest of source file IDs (while preserving single-file UUID behavior).
  • Update test helpers and add new tests covering startup batching, corruption handling, retry UUID stability, and batching behavior after successful uploads.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Cargo.lock Adds sha2 to the resolved dependency set.
bd-client-stats/Cargo.toml Adds sha2 workspace dependency for batch UUID hashing.
bd-client-stats/src/file_manager.rs Returns PendingUpload { request, source_file_ids } and batches eligible pending files into multi-snapshot requests.
bd-client-stats/src/stats.rs Integrates batched pending uploads, generates stable batch transport UUIDs, and completes uploads using source file IDs.
bd-client-stats/src/stats_test.rs Adds extensive test coverage for multi-snapshot batching scenarios and retry semantics.
bd-test-helpers/src/stats.rs Extends StatsRequestHelper with snapshot-indexed accessors for multi-snapshot assertions.
Comments suppressed due to low confidence (2)

bd-test-helpers/src/stats.rs:261

  • aggregation_window_start() no longer asserts that the request contains exactly one snapshot. This makes it easy for tests to accidentally inspect only snapshot[0] when multi-snapshot uploads are present, unlike get_counter()/get_metric() which still enforce single-snapshot semantics. Consider restoring the snapshot.len() == 1 assertion here (and relying on the *_for_snapshot accessor for multi-snapshot tests).
  pub fn aggregation_window_start(&self) -> OffsetDateTime {
    self.aggregation_window_start_for_snapshot(0)
  }

bd-test-helpers/src/stats.rs:276

  • aggregation_window_end() no longer asserts that the request contains exactly one snapshot. For consistency with get_counter()/get_metric() and to avoid silently reading only snapshot[0] in multi-snapshot uploads, consider restoring the snapshot.len() == 1 assertion here and using aggregation_window_end_for_snapshot() in multi-snapshot tests.
  pub fn aggregation_window_end(&self) -> OffsetDateTime {
    self.aggregation_window_end_for_snapshot(0)
  }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 250 to 252
pub fn overflows(&self) -> &HashMap<String, u64> {
&self.request.snapshot.first().unwrap().metric_id_overflows
}
Comment thread bd-client-stats/src/file_manager.rs Outdated
Comment on lines 349 to 353
let mut inner = self.inner.lock().await;
let initialized_inner = inner.get_initialized().await?;
let now = self.time_provider.now();
let max_aggregation_window_per_file = *self.max_aggregation_window_per_file.read();

Comment thread bd-client-stats/src/file_manager.rs Outdated
Comment on lines +485 to +496
for uuid in source_file_ids {
let found_index =
InitializedInner::find_index(&initialized_inner.index, |file| file.name == *uuid);

if let Some(index) = found_index {
log::debug!(
"completing pending upload: {}",
initialized_inner.index[index].name
);
debug_assert!(initialized_inner.index[index].period_end.is_some());
initialized_inner.delete_pending_upload(index).await?;
} else {
// There is a race condition in which we could theoretically have reached max files, but
// there is an upload in flight that comes back after we already popped the first entry.
// We could handle this by having the max file code not pop inflight uploads, but that is
// more complicated than just ignoring the response here.
log::debug!("pending upload {uuid} not found in index");
if let Some(index) = found_index {
log::debug!(
"completing pending upload: {}",
initialized_inner.index[index].name
);
debug_assert!(initialized_inner.index[index].period_end.is_some());
initialized_inner.delete_pending_upload(index).await?;
} else {
Signed-off-by: Matt Klein <mklein@bitdrift.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants