diff --git a/crates/ecstore/src/cluster/rpc/network_probe.rs b/crates/ecstore/src/cluster/rpc/network_probe.rs index 9f0e273b6..a3c8e9882 100644 --- a/crates/ecstore/src/cluster/rpc/network_probe.rs +++ b/crates/ecstore/src/cluster/rpc/network_probe.rs @@ -306,7 +306,7 @@ mod tests { let wrong_version = PingResponse { version: 2, - ..valid.clone() + body: valid.body, }; assert_eq!(validate_ping_response(&wrong_version), Err(NetworkPeerProbeError::ProtocolFailure)); let malformed = PingResponse { diff --git a/flake.lock b/flake.lock index 9fc362d30..12ca3e340 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1788549839, - "narHash": "sha256-kOrCcSIA6w9J1hX5DqHy2k9pDTJymExTsbV74U9UtCA=", + "lastModified": 1789073787, + "narHash": "sha256-xfX/toC2QV707s06GbP4II/TxYF0fNQj7s5/LClNDKc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "17de0b976395537756f30a3e78f2f06e5cec89ed", + "rev": "aff8a0b28396750446e5537a96461bc4facdb287", "type": "github" }, "original": { @@ -29,11 +29,11 @@ ] }, "locked": { - "lastModified": 1788591095, - "narHash": "sha256-Vh+BeLWfbTT9AecazIsQ/Tkg/RzJeX3lEduANf256WA=", + "lastModified": 1789196581, + "narHash": "sha256-yJr1Bt4fKkKIpPYbsKGqJ0VFdoDnURgRwuffmBQ2WzY=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "c361047d3a538f547f1617bb6b410411929ac9cc", + "rev": "228ecefb6329d5a531b77b46b581a2f0c26ee056", "type": "github" }, "original": { diff --git a/rustfs/src/connect/diagnostics/logs.rs b/rustfs/src/connect/diagnostics/logs.rs index 01270fe34..b2138d020 100644 --- a/rustfs/src/connect/diagnostics/logs.rs +++ b/rustfs/src/connect/diagnostics/logs.rs @@ -397,8 +397,7 @@ pub(crate) async fn export_logs_from( reason_code: LogReasonCode::Complete, duration_millis: u64::try_from(started.elapsed().as_millis()) .unwrap_or(u64::MAX) - .max(1) - .min(30_000), + .clamp(1, 30_000), provenance: request.provenance.clone(), coverage: Coverage { requested_units: 1, diff --git a/rustfs/src/connect/diagnostics/profile_memory.rs b/rustfs/src/connect/diagnostics/profile_memory.rs index ba77f83da..0ff38a78e 100644 --- a/rustfs/src/connect/diagnostics/profile_memory.rs +++ b/rustfs/src/connect/diagnostics/profile_memory.rs @@ -103,7 +103,7 @@ fn collect_window<'a>( sample_period: Duration, cancel: &'a CancellationToken, source: &'a dyn AllocationProfileSource, -) -> Pin> + Send + 'a>> { +) -> AllocationWindowFuture<'a> { Box::pin(async move { let before = source.snapshot()?; tokio::select! { @@ -115,6 +115,9 @@ fn collect_window<'a>( }) } +type AllocationWindowFuture<'a> = + Pin> + Send + 'a>>; + pub(crate) fn parse_allocator_stats(stats: &str) -> Result { if stats.is_empty() || stats.len() > MAX_ALLOCATOR_STATS_BYTES { return Err(ProfileError::SourceUnavailable); diff --git a/rustfs/src/connect/diagnostics/schedule.rs b/rustfs/src/connect/diagnostics/schedule.rs index e156cec71..1e4182353 100644 --- a/rustfs/src/connect/diagnostics/schedule.rs +++ b/rustfs/src/connect/diagnostics/schedule.rs @@ -370,10 +370,10 @@ async fn run_collection_schedule( break; } let policy = policies.borrow().clone(); - if let Err(error) = policy.validate() { - if policy.should_run() { - return Err(error); - } + if let Err(error) = policy.validate() + && policy.should_run() + { + return Err(error); } if !policy.should_run() { state.next_due_at = None; diff --git a/rustfs/src/connect/license.rs b/rustfs/src/connect/license.rs index 00efa75bb..7d994210f 100644 --- a/rustfs/src/connect/license.rs +++ b/rustfs/src/connect/license.rs @@ -147,7 +147,7 @@ impl LicenseReport { pub struct LicenseArtifactError { pub status: LicenseArtifactStatus, pub message: String, - pub license: Option, + pub license: Option>, } impl LicenseArtifactError { @@ -156,7 +156,7 @@ impl LicenseArtifactError { status: self.status, installed, idempotent: false, - license: self.license, + license: self.license.map(|license| *license), message: Some(self.message), } } @@ -302,10 +302,10 @@ pub fn verify_license_artifact( ) -> Result { let candidate = validate_artifact(read_artifact(artifact_path)?, context, true)?; let state_path = state_path(state_directory, context); - if let Some(current) = load_installed(&state_path, context)? { - if matches!(compare_sequence(&candidate, ¤t)?, SequenceDecision::Idempotent) { - return Ok(LicenseReport::valid(candidate.claims, true, true)); - } + if let Some(current) = load_installed(&state_path, context)? + && matches!(compare_sequence(&candidate, ¤t)?, SequenceDecision::Idempotent) + { + return Ok(LicenseReport::valid(candidate.claims, true, true)); } Ok(LicenseReport::valid(candidate.claims, false, false)) } @@ -712,7 +712,7 @@ fn failure_with_license( LicenseArtifactError { status, message: message.into(), - license: Some(license), + license: Some(Box::new(license)), } } diff --git a/rustfs/src/startup_entrypoint.rs b/rustfs/src/startup_entrypoint.rs index 5d2c8903d..3dc120136 100644 --- a/rustfs/src/startup_entrypoint.rs +++ b/rustfs/src/startup_entrypoint.rs @@ -257,18 +257,19 @@ async fn execute_connect_logs(options: ConnectLogsOpts) -> Result<()> { ), }; let cancel = tokio_util::sync::CancellationToken::new(); - let capture = export_logs(&request, &key, &cancel); - tokio::pin!(capture); - let export = tokio::select! { - biased; - signal = tokio::signal::ctrl_c() => { - signal.map_err(Error::other)?; - cancel.cancel(); - return Err(Error::other("log collection cancelled")); + let export = { + let capture = export_logs(&request, &key, &cancel); + tokio::pin!(capture); + tokio::select! { + biased; + signal = tokio::signal::ctrl_c() => { + signal.map_err(Error::other)?; + cancel.cancel(); + return Err(Error::other("log collection cancelled")); + } + result = capture.as_mut() => result.map_err(Error::other)?, } - result = capture.as_mut() => result.map_err(Error::other)?, }; - drop(capture); let output = options.output; let writer_cancel = cancel.clone(); let mut writer = tokio::task::spawn_blocking(move || save_signed_log_export(&output, &export, &writer_cancel)); @@ -1047,41 +1048,42 @@ async fn execute_connect_profile(options: ConnectProfileOpts) -> Result<()> { ), }; let cancel = tokio_util::sync::CancellationToken::new(); - let capture = async { - match options.tool { - ConnectProfileTool::Cpu => { - if options.thread_scope.is_some() { - return Err(Error::other("--thread-scope is valid only for the threads profile")); + let export = { + let capture = async { + match options.tool { + ConnectProfileTool::Cpu => { + if options.thread_scope.is_some() { + return Err(Error::other("--thread-scope is valid only for the threads profile")); + } + export_cpu_profile(&request, &key, &cancel).map_err(Error::other) } - export_cpu_profile(&request, &key, &cancel).map_err(Error::other) - } - ConnectProfileTool::Memory => { - if options.thread_scope.is_some() { - return Err(Error::other("--thread-scope is valid only for the threads profile")); + ConnectProfileTool::Memory => { + if options.thread_scope.is_some() { + return Err(Error::other("--thread-scope is valid only for the threads profile")); + } + export_memory_profile(&request, &key, &cancel).await.map_err(Error::other) + } + ConnectProfileTool::Threads => { + let scope = match options.thread_scope { + Some(ConnectThreadProfileScope::TokioRuntime) => ThreadProfileScope::TokioRuntime, + Some(ConnectThreadProfileScope::NativeThreads) => ThreadProfileScope::NativeThreads, + None => return Err(Error::other("--thread-scope is required for the threads profile")), + }; + export_thread_profile(&request, scope, &key, &cancel).map_err(Error::other) } - export_memory_profile(&request, &key, &cancel).await.map_err(Error::other) } - ConnectProfileTool::Threads => { - let scope = match options.thread_scope { - Some(ConnectThreadProfileScope::TokioRuntime) => ThreadProfileScope::TokioRuntime, - Some(ConnectThreadProfileScope::NativeThreads) => ThreadProfileScope::NativeThreads, - None => return Err(Error::other("--thread-scope is required for the threads profile")), - }; - export_thread_profile(&request, scope, &key, &cancel).map_err(Error::other) + }; + tokio::pin!(capture); + tokio::select! { + biased; + signal = tokio::signal::ctrl_c() => { + signal.map_err(Error::other)?; + cancel.cancel(); + return Err(Error::other("profile collection cancelled")); } + result = capture.as_mut() => result?, } }; - tokio::pin!(capture); - let export = tokio::select! { - biased; - signal = tokio::signal::ctrl_c() => { - signal.map_err(Error::other)?; - cancel.cancel(); - return Err(Error::other("profile collection cancelled")); - } - result = capture.as_mut() => result?, - }; - drop(capture); let tool = export.tool; let outcome = export.outcome; let reason_code = export.reason_code; diff --git a/rustfs/tests/connect_logs.rs b/rustfs/tests/connect_logs.rs index 98ffd6921..2dff2fd75 100644 --- a/rustfs/tests/connect_logs.rs +++ b/rustfs/tests/connect_logs.rs @@ -24,7 +24,6 @@ use std::fs::{self, OpenOptions}; use std::io::{Cursor, Read as _, Write as _}; #[cfg(unix)] use std::os::unix::fs::{PermissionsExt as _, symlink}; -use std::sync::Mutex; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use base64_simd::URL_SAFE_NO_PAD; @@ -37,10 +36,11 @@ use p256::ecdsa::{Signature, VerifyingKey}; use p256::pkcs8::DecodePublicKey as _; use sha2::{Digest as _, Sha256}; use time::{Duration as TimeDuration, OffsetDateTime, format_description::well_known::Rfc3339}; +use tokio::sync::Mutex; use tokio_util::sync::CancellationToken; use zip::ZipArchive; -static TEST_LOCK: Mutex<()> = Mutex::new(()); +static TEST_LOCK: Mutex<()> = Mutex::const_new(()); fn now() -> i64 { SystemTime::now().duration_since(UNIX_EPOCH).expect("current time").as_secs() as i64 @@ -100,7 +100,7 @@ fn archive_entry(archive: &mut ZipArchive>>, name: &str) -> Vec = Mutex::new(()); - -fn profile_test_lock() -> std::sync::MutexGuard<'static, ()> { - TEST_PROFILE_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner) -} +static TEST_PROFILE_LOCK: AsyncMutex<()> = AsyncMutex::const_new(()); #[global_allocator] static GLOBAL: rustfs_mimalloc::MiMalloc = rustfs_mimalloc::MiMalloc; @@ -116,7 +113,7 @@ fn archive_entry(archive: &mut ZipArchive>>, name: &str) -> Vec ProfileCaptureRequest { @@ -60,6 +60,7 @@ fn request() -> ProfileCaptureRequest { #[test] fn tokio_and_native_thread_scopes_remain_explicitly_unsupported() { + let key = connect::DeviceIdentity::generate(); for scope in [ThreadProfileScope::TokioRuntime, ThreadProfileScope::NativeThreads] { let result = capture_thread_profile(&request(), scope, &CancellationToken::new()).expect("unsupported result"); assert_eq!(result.outcome(), ProfileOutcome::Unsupported); @@ -69,6 +70,11 @@ fn tokio_and_native_thread_scopes_remain_explicitly_unsupported() { assert_eq!(json["toolId"], "profile.threads"); assert_eq!(json["capability"], "profile.threads@1"); assert!(json["data"].is_null()); + + let export = export_thread_profile(&request(), scope, &key, &CancellationToken::new()).expect("unsupported export"); + assert_eq!(export.tool.id(), "profile.threads"); + assert_eq!(export.outcome, ProfileOutcome::Unsupported); + assert_eq!(export.reason_code, ProfileReasonCode::UnsupportedTool); } } diff --git a/rustfs/tests/connect_trace_otlp.rs b/rustfs/tests/connect_trace_otlp.rs index 01cf6f4d5..47e113277 100644 --- a/rustfs/tests/connect_trace_otlp.rs +++ b/rustfs/tests/connect_trace_otlp.rs @@ -35,8 +35,10 @@ fn encoded_batch(span_count: usize) -> Vec { } fn encoded_named_batch(name_length: usize) -> Vec { - let mut span = Span::default(); - span.name = "x".repeat(name_length); + let span = Span { + name: "x".repeat(name_length), + ..Default::default() + }; ExportTraceServiceRequest { resource_spans: vec![ResourceSpans { scope_spans: vec![ScopeSpans {