Skip to content

feat(l1): improve snap sync progress logging with phase-based display#6018

Merged
ilitteri merged 6 commits into
mainfrom
feature/snap-sync-progress-logs
Jan 26, 2026
Merged

feat(l1): improve snap sync progress logging with phase-based display#6018
ilitteri merged 6 commits into
mainfrom
feature/snap-sync-progress-logs

Conversation

@ilitteri
Copy link
Copy Markdown
Collaborator

@ilitteri ilitteri commented Jan 25, 2026

Motivation

Improve snap sync progress logging with a clearer phase-based display that provides better visibility into sync stages and progress.

Description

Replace the periodic table-based progress display with structured phase-based logging that shows:

  • A "SNAP SYNC STARTED" banner at the beginning with target block info and peer count
  • Phase separators (── PHASE X/8: NAME ──) when transitioning between phases
  • Context-relevant progress metrics for the current phase only (headers, accounts, storage, healing, bytecodes)
  • Rate calculations (items/second) based on the last interval for more accurate throughput visibility
  • Phase completion messages (✓ Phase complete: X items in HH:MM:SS)
  • Empty line separators between progress iterations for readability

The 8 phases tracked are:

  1. Block Headers (with progress bar)
  2. Account Ranges
  3. Account Insertion (with progress bar)
  4. Storage Ranges
  5. Storage Insertion
  6. State Healing
  7. Storage Healing
  8. Bytecodes (with progress bar)

Checklist

  • Updated STORE_SCHEMA_VERSION (crates/storage/lib.rs) if the PR includes breaking changes to the Store requiring a re-sync.

Copilot AI review requested due to automatic review settings January 25, 2026 22:14
@ilitteri ilitteri requested a review from a team as a code owner January 25, 2026 22:14
@github-actions github-actions Bot added the L1 Ethereum client label Jan 25, 2026
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

Improves snap sync progress logging by replacing the previous periodic table-style output with a phase-based display (start banner, phase separators, phase-specific metrics/rates, and completion summaries).

Changes:

  • Add a “SNAP SYNC STARTED” banner plus phase transition/completion logging during snap sync.
  • Report per-phase progress metrics and rates (headers/accounts/storage/healing/bytecodes), including progress bars for headers/bytecodes.
  • Update snap sync step tracking to include account-range insertion.

Reviewed changes

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

File Description
crates/networking/p2p/sync.rs Sets METRICS.current_step when transitioning into account-range insertion to support phase-based logging.
crates/networking/p2p/network.rs Reworks sync-time peer/progress logging into an 8-phase display with summaries, rates, and progress bars.
crates/networking/p2p/discv5/server.rs Minor formatting-only change in imports.

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

Comment thread crates/networking/p2p/network.rs Outdated
Comment on lines +198 to +478
let headers_to_download = METRICS.sync_head_block.load(Ordering::Relaxed);
// We may download more than expected headers due to duplicates
// We just clamp it to the max to avoid showing the user confusing data
let headers_downloaded =
u64::min(METRICS.downloaded_headers.get(), headers_to_download);
let headers_percentage = if headers_to_download == 0 {
let phase_downloaded = headers_downloaded - phase_start_headers;
let percentage = if headers_to_download == 0 {
0.0
} else {
(headers_downloaded as f64 / headers_to_download as f64) * 100.0
};
let elapsed_secs = start.elapsed().as_secs();
let headers_per_second = if elapsed_secs == 0 {
0
let rate = phase_downloaded / phase_secs;
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

In the headers phase, headers_downloaded is clamped to min(downloaded_headers, headers_to_download), but phase_start_headers is captured from the unclamped counter at phase start. If the counter ever exceeds headers_to_download (duplicates were mentioned in the previous implementation), headers_downloaded - phase_start_headers will underflow and yield an incorrect rate (or panic in debug builds). Use saturating_sub here and/or store a clamped phase_start_headers for this phase.

Copilot uses AI. Check for mistakes.
Comment on lines +354 to +355
tokio::time::sleep(Duration::from_secs(10)).await;
}
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The sync progress loop interval changed from 30s to 10s. Given each iteration logs multiple lines, this increases log volume by ~3x during snap sync and may have operational impact (disk usage / log shipping). Consider making the interval configurable or keeping a higher default (while still allowing a shorter one when running interactively).

Copilot uses AI. Check for mistakes.
Comment thread crates/networking/p2p/network.rs Outdated
fn log_phase_separator(step: CurrentStepValue) {
let (phase_num, phase_name) = phase_info(step);
let header = format!("━━━ PHASE {}/8: {} ", phase_num, phase_name);
let padding = "━".repeat(80 - header.len());
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

log_phase_separator computes padding with "━".repeat(80 - header.len()). Because header.len() is byte length (and these box-drawing characters are multi-byte), header.len() can easily exceed 80, causing an underflow to a huge usize and attempting to allocate an enormous string (OOM/panic). Use a saturating subtraction and compute display width with header.chars().count() (or switch to ASCII separators) before repeating.

Suggested change
let padding = "━".repeat(80 - header.len());
let header_width = header.chars().count();
let padding_width = 80usize.saturating_sub(header_width);
let padding = "━".repeat(padding_width);

Copilot uses AI. Check for mistakes.
Comment on lines 286 to +609
);
info!(" {:<col1_width$} │ Elapsed: {}", col1, phase_elapsed_str);
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

progress_bar always prefixes the bar with an extra (format!("▓{}░{}", ...)), so even 0% displays as partially filled and the rendered width is off. Also, filled isn't clamped to width, so percentages > 100 can produce an overlong bar. Build the bar as exactly width characters (filled + empty), and clamp filled to 0..=width (or clamp percentage to 0.0..=100.0).

Suggested change
);
info!(" {:<col1_width$} │ Elapsed: {}", col1, phase_elapsed_str);
let filled = filled.min(width);
let empty = width.saturating_sub(filled);
format!("{}{}", "▓".repeat(filled), "░".repeat(empty))

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 25, 2026

Lines of code report

Total lines added: 317
Total lines removed: 0
Total lines changed: 317

Detailed view
+-----------------------------------------+-------+------+
| File                                    | Lines | Diff |
+-----------------------------------------+-------+------+
| ethrex/crates/networking/p2p/network.rs | 596   | +314 |
+-----------------------------------------+-------+------+
| ethrex/crates/networking/p2p/sync.rs    | 1381  | +3   |
+-----------------------------------------+-------+------+

Comment thread crates/networking/p2p/network.rs Outdated
info!("✓ {} complete: {} in {}", phase_name, summary, elapsed);
}

#[allow(clippy::too_many_arguments)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does it make sense to use a struct to handle all phase_start counters and avoid the too_many_arguments warning?

Comment thread crates/networking/p2p/network.rs Outdated
}
}

#[allow(clippy::too_many_arguments)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here, this could be improved using a struct

Implement structured phase-based logging for snap sync progress as specified
in the improvement plan. The new system replaces the 30-second periodic table
with context-aware logging that shows:

- SNAP SYNC STARTED banner with target block info on first phase
- Phase separators (━━━ PHASE X/8: NAME ━━━) when phases change
- Phase-specific metrics with progress bars for phases with known totals
- Phase completion messages (✓ PHASE complete: summary in elapsed)
- SNAP SYNC COMPLETE summary with total time and data downloaded

Each phase displays only its relevant metrics:
- Block Headers: progress bar, headers count, rate
- Account Ranges: accounts fetched, rate
- Account Insertion: accounts inserted, rate
- Storage Ranges: storage slots fetched, rate
- Storage Insertion: storage slots inserted, rate
- State Healing: paths healed, rate
- Storage Healing: accounts healed, rate
- Bytecodes: progress bar, codes count, rate

Also adds thousands separators for better readability of large numbers.
for border (61 dashes) and content (59-char padding with spaces before closing border)
- Revert interval from 10s to 30s (configurable interval deferred to another PR)
- Introduce PhaseCounters struct to reduce function argument count (ElFantasma)
- Calculate rate over last interval instead of phase average for more informative output
- Use saturating_sub for rate calculations to prevent potential underflow (Copilot)
- Fix log_phase_separator to use chars().count() instead of len() for correct padding (Copilot)
- Fix progress_bar to not add extra characters and clamp percentage to 0-100% (Copilot)
@ilitteri ilitteri force-pushed the feature/snap-sync-progress-logs branch from ead1d21 to abae127 Compare January 26, 2026 16:29
@ilitteri ilitteri enabled auto-merge January 26, 2026 19:04
@ilitteri ilitteri requested a review from ElFantasma January 26, 2026 19:17
Comment on lines 221 to +222
loop {
{
if blockchain.is_synced() {
return;
if blockchain.is_synced() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This could be:

while !blockchain.is_synced() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Then, the contents of the if could be unconditional after the loop.

frequent updates during sync. A follow-up issue (#6027) tracks making
this interval configurable.
@ilitteri ilitteri added this pull request to the merge queue Jan 26, 2026
@github-project-automation github-project-automation Bot moved this to In Review in ethrex_l1 Jan 26, 2026
Merged via the queue into main with commit 5202394 Jan 26, 2026
52 checks passed
@ilitteri ilitteri deleted the feature/snap-sync-progress-logs branch January 26, 2026 22:21
@github-project-automation github-project-automation Bot moved this from In Review to Done in ethrex_l1 Jan 26, 2026
pablodeymo added a commit that referenced this pull request Jan 27, 2026
Resolve conflicts from #5977 and #6018 merge to main:
- Keep modular sync structure (sync.rs delegates to full.rs and snap_sync.rs)
- Keep snap client code in snap/client.rs (removed from peer_handler.rs)
- Add InsertingAccountRanges metric from #6018 to snap_sync.rs
- Remove unused info import from peer_handler.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants