Visualisation logging for sync batch states (#6034)

* Add visualization for batch states

* Replace icons with emojis

* Reviewers comments

* Change empty emoji and improve docs comments

* Fix lints

* Move to letters rather than emojis

* Replace 'V' with 'v'. Cargo update

* Merge latest unstable

* Improve docs around visualisation

* Merge branch 'unstable' into sync-batch-state
This commit is contained in:
Age Manning
2024-07-29 15:52:07 +10:00
committed by GitHub
parent f4ddc45914
commit a3b1ef3129
3 changed files with 78 additions and 11 deletions

8
Cargo.lock generated
View File

@@ -1021,9 +1021,9 @@ dependencies = [
[[package]] [[package]]
name = "blst" name = "blst"
version = "0.3.12" version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874"
dependencies = [ dependencies = [
"cc", "cc",
"glob", "glob",
@@ -5751,9 +5751,9 @@ dependencies = [
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.1" version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View File

@@ -463,6 +463,11 @@ impl<E: EthSpec, B: BatchConfig> BatchInfo<E, B> {
} }
} }
} }
// Visualizes the state of this batch using state::visualize()
pub fn visualize(&self) -> char {
self.state.visualize()
}
} }
/// Represents a peer's attempt and providing the result for this batch. /// Represents a peer's attempt and providing the result for this batch.
@@ -539,3 +544,19 @@ impl<E: EthSpec> std::fmt::Debug for BatchState<E> {
} }
} }
} }
impl<E: EthSpec> BatchState<E> {
/// Creates a character representation/visualization for the batch state to display in logs for quicker and
/// easier recognition
fn visualize(&self) -> char {
match self {
BatchState::Downloading(..) => 'D',
BatchState::Processing(_) => 'P',
BatchState::AwaitingValidation(_) => 'v',
BatchState::AwaitingDownload => 'd',
BatchState::Failed => 'F',
BatchState::AwaitingProcessing(..) => 'p',
BatchState::Poisoned => 'X',
}
}
}

View File

@@ -277,7 +277,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
let awaiting_batches = batch_id let awaiting_batches = batch_id
.saturating_sub(self.optimistic_start.unwrap_or(self.processing_target)) .saturating_sub(self.optimistic_start.unwrap_or(self.processing_target))
/ EPOCHS_PER_BATCH; / EPOCHS_PER_BATCH;
debug!(self.log, "Completed batch received"; "epoch" => batch_id, "blocks" => received, "awaiting_batches" => awaiting_batches); debug!(self.log, "Batch downloaded"; "epoch" => batch_id, "blocks" => received, "batch_state" => self.visualize_batch_state(), "awaiting_batches" => awaiting_batches);
// pre-emptively request more blocks from peers whilst we process current blocks, // pre-emptively request more blocks from peers whilst we process current blocks,
self.request_batches(network)?; self.request_batches(network)?;
@@ -460,6 +460,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
) -> ProcessingResult { ) -> ProcessingResult {
// the first two cases are possible if the chain advances while waiting for a processing // the first two cases are possible if the chain advances while waiting for a processing
// result // result
let batch_state = self.visualize_batch_state();
let batch = match &self.current_processing_batch { let batch = match &self.current_processing_batch {
Some(processing_id) if *processing_id != batch_id => { Some(processing_id) if *processing_id != batch_id => {
debug!(self.log, "Unexpected batch result"; debug!(self.log, "Unexpected batch result";
@@ -492,7 +493,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// Log the process result and the batch for debugging purposes. // Log the process result and the batch for debugging purposes.
debug!(self.log, "Batch processing result"; "result" => ?result, &batch, debug!(self.log, "Batch processing result"; "result" => ?result, &batch,
"batch_epoch" => batch_id, "client" => %network.client_type(&peer)); "batch_epoch" => batch_id, "client" => %network.client_type(&peer), "batch_state" => batch_state);
// We consider three cases. Batch was successfully processed, Batch failed processing due // We consider three cases. Batch was successfully processed, Batch failed processing due
// to a faulty peer, or batch failed processing but the peer can't be deemed faulty. // to a faulty peer, or batch failed processing but the peer can't be deemed faulty.
@@ -859,6 +860,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
peer_id: &PeerId, peer_id: &PeerId,
request_id: Id, request_id: Id,
) -> ProcessingResult { ) -> ProcessingResult {
let batch_state = self.visualize_batch_state();
if let Some(batch) = self.batches.get_mut(&batch_id) { if let Some(batch) = self.batches.get_mut(&batch_id) {
// A batch could be retried without the peer failing the request (disconnecting/ // A batch could be retried without the peer failing the request (disconnecting/
// sending an error /timeout) if the peer is removed from the chain for other // sending an error /timeout) if the peer is removed from the chain for other
@@ -870,7 +872,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
"batch_epoch" => batch_id, "batch_epoch" => batch_id,
"batch_state" => ?batch.state(), "batch_state" => ?batch.state(),
"peer_id" => %peer_id, "peer_id" => %peer_id,
"request_id" => %request_id "request_id" => %request_id,
"batch_state" => batch_state
); );
return Ok(KeepChain); return Ok(KeepChain);
} }
@@ -880,7 +883,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
"batch_epoch" => batch_id, "batch_epoch" => batch_id,
"batch_state" => ?batch.state(), "batch_state" => ?batch.state(),
"peer_id" => %peer_id, "peer_id" => %peer_id,
"request_id" => %request_id "request_id" => %request_id,
"batch_state" => batch_state
); );
if let Some(active_requests) = self.peers.get_mut(peer_id) { if let Some(active_requests) = self.peers.get_mut(peer_id) {
active_requests.remove(&batch_id); active_requests.remove(&batch_id);
@@ -898,7 +902,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
"Batch not found"; "Batch not found";
"batch_epoch" => batch_id, "batch_epoch" => batch_id,
"peer_id" => %peer_id, "peer_id" => %peer_id,
"request_id" => %request_id "request_id" => %request_id,
"batch_state" => batch_state
); );
// this could be an error for an old batch, removed when the chain advances // this could be an error for an old batch, removed when the chain advances
Ok(KeepChain) Ok(KeepChain)
@@ -948,6 +953,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
batch_id: BatchId, batch_id: BatchId,
peer: PeerId, peer: PeerId,
) -> ProcessingResult { ) -> ProcessingResult {
let batch_state = self.visualize_batch_state();
if let Some(batch) = self.batches.get_mut(&batch_id) { if let Some(batch) = self.batches.get_mut(&batch_id) {
let (request, batch_type) = batch.to_blocks_by_range_request(); let (request, batch_type) = batch.to_blocks_by_range_request();
match network.blocks_and_blobs_by_range_request( match network.blocks_and_blobs_by_range_request(
@@ -967,9 +973,9 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
.map(|epoch| epoch == batch_id) .map(|epoch| epoch == batch_id)
.unwrap_or(false) .unwrap_or(false)
{ {
debug!(self.log, "Requesting optimistic batch"; "epoch" => batch_id, &batch); debug!(self.log, "Requesting optimistic batch"; "epoch" => batch_id, &batch, "batch_state" => batch_state);
} else { } else {
debug!(self.log, "Requesting batch"; "epoch" => batch_id, &batch); debug!(self.log, "Requesting batch"; "epoch" => batch_id, &batch, "batch_state" => batch_state);
} }
// register the batch for this peer // register the batch for this peer
return self return self
@@ -1130,6 +1136,46 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
} }
} }
} }
/// Creates a string visualization of the current state of the chain, to make it easier for debugging and understanding
/// where sync is up to from glancing at the logs.
///
/// This produces a string of the form: [D,E,E,E,E]
/// to indicate the current buffer state of the chain. The symbols are defined on each of the
/// batch states. See [BatchState::visualize] for symbol definitions.
fn visualize_batch_state(&self) -> String {
let mut visualization_string = String::with_capacity((BATCH_BUFFER_SIZE * 3) as usize);
// Start of the block
visualization_string.push('[');
for mut batch_index in 0..BATCH_BUFFER_SIZE {
if let Some(batch) = self
.batches
.get(&(self.processing_target + batch_index as u64 * EPOCHS_PER_BATCH))
{
visualization_string.push(batch.visualize());
if batch_index != BATCH_BUFFER_SIZE {
// Add a comma in between elements
visualization_string.push(',');
}
} else {
// No batch exists, it is on our list to be downloaded
// Fill in the rest of the gaps
while batch_index < BATCH_BUFFER_SIZE {
visualization_string.push('E');
// Add a comma between the empty batches
if batch_index < BATCH_BUFFER_SIZE.saturating_sub(1) {
visualization_string.push(',')
}
batch_index += 1;
}
break;
}
}
visualization_string.push(']');
visualization_string
}
} }
impl<T: BeaconChainTypes> slog::KV for &mut SyncingChain<T> { impl<T: BeaconChainTypes> slog::KV for &mut SyncingChain<T> {