From 63b1b8399e996278677941154ead8d12a6d2899e Mon Sep 17 00:00:00 2001 From: Tomas M Date: Sun, 16 Nov 2025 13:31:05 +0100 Subject: [PATCH 1/3] Fix waitsync performance - parse json sync response rather than relying on fragile substring checks --- zingo-cli/Cargo.toml | 1 + zingo-cli/src/lib.rs | 68 ++++++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/zingo-cli/Cargo.toml b/zingo-cli/Cargo.toml index 4ea126107..dccdf7a16 100644 --- a/zingo-cli/Cargo.toml +++ b/zingo-cli/Cargo.toml @@ -9,6 +9,7 @@ regtest = ["zingolib/testutils", "zingo_common_components/for_test", "zcash_loca [dependencies] zingolib = { workspace = true } pepper-sync = { workspace = true } +serde_json = { workspace = true } zcash_local_net = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev", optional = true } diff --git a/zingo-cli/src/lib.rs b/zingo-cli/src/lib.rs index 0d53470a5..df2de0f05 100644 --- a/zingo-cli/src/lib.rs +++ b/zingo-cli/src/lib.rs @@ -519,37 +519,61 @@ fn dispatch_command_or_start_interactive(cli_config: &ConfigTemplate) { if cli_config.command.is_none() { start_interactive(command_transmitter, resp_receiver); } else { - // Optionally wait for background sync to finish before executing command + // Optionally wait for background sync to finish before executing command. + // Retry requesting status until the channel closes or the percentage field + // indicates completion. if cli_config.sync && cli_config.waitsync { use std::{thread, time::Duration}; + const COMPLETE_THRESHOLD: f64 = 99.999; + loop { - // Poll sync task status + // Request machine-readable sync status. command_transmitter - .send(("sync".to_string(), vec!["poll".to_string()])) + .send(("sync".to_string(), vec!["status".to_string()])) .unwrap(); + match resp_receiver.recv() { Ok(resp) => { - if resp.starts_with("Error:") { - eprintln!( - "Sync error while waiting: {resp}\nProceeding to execute the command." - ); - break; - } else if resp.starts_with("Sync completed succesfully:") { - // Sync finished; proceed - break; - } else if resp == "Sync task has not been launched." { - // Try to launch sync and continue waiting - command_transmitter - .send(("sync".to_string(), vec!["run".to_string()])) - .unwrap(); - let _ = resp_receiver.recv(); - thread::sleep(Duration::from_millis(500)); - } else { - // Not ready yet - thread::sleep(Duration::from_millis(500)); + // Parse JSON and inspect the numeric completion field. + match serde_json::from_str::(&resp) { + Ok(json_val) => { + // Extract both percentage fields as f64. + let blocks_pct_opt = json_val + .get("percentage_total_blocks_scanned") + .and_then(|v| v.as_f64()); + + let outputs_pct_opt = json_val + .get("percentage_total_outputs_scanned") + .and_then(|v| v.as_f64()); + + // If both percentages are present and near 100, we're done. + if let (Some(blocks_pct), Some(outputs_pct)) = + (blocks_pct_opt, outputs_pct_opt) + { + if blocks_pct >= COMPLETE_THRESHOLD + && outputs_pct >= COMPLETE_THRESHOLD + { + break; + } + } else { + // missing percentage fields in sync status; stopping wait. + break; + } + + // Not complete yet; wait a short interval before re-checking. + thread::sleep(Duration::from_millis(500)); + continue; + } + Err(_) => { + // Parse error + break; + } } } - Err(_) => break, + Err(_) => { + // Channel closed; stop waiting. + break; + } } } } From eadc26e90b306d4c70112336b73e30de3c54d6b7 Mon Sep 17 00:00:00 2001 From: Tomas M Date: Fri, 21 Nov 2025 13:39:32 +0100 Subject: [PATCH 2/3] Introduce sync_complete flag --- pepper-sync/src/sync.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index 31ddcaad0..c9d1628ad 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -61,6 +61,8 @@ const VERIFY_BLOCK_RANGE_SIZE: u32 = 10; pub struct SyncStatus { pub scan_ranges: Vec, pub sync_start_height: BlockHeight, + pub total_blocks: u32, + pub total_outputs: u32, pub session_blocks_scanned: u32, pub total_blocks_scanned: u32, pub percentage_session_blocks_scanned: f32, @@ -98,6 +100,11 @@ impl From for json::JsonValue { }) .collect(); + // Derive a simple boolean completeness flag from integer counts + let sync_complete = value.total_blocks_scanned >= value.total_blocks + && (value.total_sapling_outputs_scanned + value.total_orchard_outputs_scanned) + >= value.total_outputs; + json::object! { "scan_ranges" => scan_ranges, "sync_start_height" => u32::from(value.sync_start_height), @@ -111,6 +118,7 @@ impl From for json::JsonValue { "total_orchard_outputs_scanned" => value.total_orchard_outputs_scanned, "percentage_session_outputs_scanned" => value.percentage_session_outputs_scanned, "percentage_total_outputs_scanned" => value.percentage_total_outputs_scanned, + "sync_complete" => sync_complete, } } } @@ -598,6 +606,8 @@ where return Ok(SyncStatus { scan_ranges: sync_state.scan_ranges.clone(), sync_start_height: 0.into(), + total_blocks: 0, + total_outputs: 0, session_blocks_scanned: 0, total_blocks_scanned: 0, percentage_session_blocks_scanned: 0.0, @@ -671,6 +681,8 @@ where Ok(SyncStatus { scan_ranges: sync_state.scan_ranges.clone(), sync_start_height: sync_state.initial_sync_state.sync_start_height, + total_blocks, + total_outputs, session_blocks_scanned, total_blocks_scanned, percentage_session_blocks_scanned, From f7394f5855801e1b065b77052ee88fd600987b3f Mon Sep 17 00:00:00 2001 From: Tomas M Date: Fri, 21 Nov 2025 13:40:08 +0100 Subject: [PATCH 3/3] check sync_complete flag instead of relying on floating point values for percentages --- Cargo.lock | 1 + zingo-cli/src/lib.rs | 23 +++-------------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36cdb1312..c12d6c5f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8908,6 +8908,7 @@ dependencies = [ "pepper-sync", "rustls 0.23.35", "rustyline", + "serde_json", "shellwords", "tokio", "tracing-subscriber", diff --git a/zingo-cli/src/lib.rs b/zingo-cli/src/lib.rs index df2de0f05..679f635e0 100644 --- a/zingo-cli/src/lib.rs +++ b/zingo-cli/src/lib.rs @@ -524,7 +524,6 @@ fn dispatch_command_or_start_interactive(cli_config: &ConfigTemplate) { // indicates completion. if cli_config.sync && cli_config.waitsync { use std::{thread, time::Duration}; - const COMPLETE_THRESHOLD: f64 = 99.999; loop { // Request machine-readable sync status. @@ -537,26 +536,10 @@ fn dispatch_command_or_start_interactive(cli_config: &ConfigTemplate) { // Parse JSON and inspect the numeric completion field. match serde_json::from_str::(&resp) { Ok(json_val) => { - // Extract both percentage fields as f64. - let blocks_pct_opt = json_val - .get("percentage_total_blocks_scanned") - .and_then(|v| v.as_f64()); - - let outputs_pct_opt = json_val - .get("percentage_total_outputs_scanned") - .and_then(|v| v.as_f64()); - - // If both percentages are present and near 100, we're done. - if let (Some(blocks_pct), Some(outputs_pct)) = - (blocks_pct_opt, outputs_pct_opt) + // if sync is complete, stop waiting + if let Some(true) = + json_val.get("sync_complete").and_then(|v| v.as_bool()) { - if blocks_pct >= COMPLETE_THRESHOLD - && outputs_pct >= COMPLETE_THRESHOLD - { - break; - } - } else { - // missing percentage fields in sync status; stopping wait. break; }