fix(targets): unify runtime health snapshots (#5110)

* fix(targets): unify runtime health snapshots

* fix(targets): stabilize health snapshot merge

* fix(admin): import runtime health test type
This commit is contained in:
cxymds
2026-07-22 14:50:36 +08:00
committed by GitHub
parent 31dc78eab0
commit e0bac66941
14 changed files with 884 additions and 176 deletions
+42 -2
View File
@@ -61,7 +61,8 @@ impl NotifyRuntimeView {
}
pub async fn snapshot_target_health(&self) -> Vec<RuntimeTargetHealthSnapshot> {
self.target_list.read().await.runtime_health_snapshots().await
let targets = self.target_list.read().await.values();
rustfs_targets::health_snapshots_for_targets(targets).await
}
pub async fn runtime_status_snapshot(&self) -> rustfs_targets::RuntimeStatusSnapshot {
@@ -84,7 +85,7 @@ mod tests {
Arc,
atomic::{AtomicU64, Ordering},
};
use tokio::sync::RwLock;
use tokio::sync::{Notify, RwLock};
#[derive(Clone)]
struct TestTarget {
@@ -93,6 +94,8 @@ mod tests {
failed_messages: Arc<AtomicU64>,
failed_store_length: u64,
id: TargetID,
health_started: Option<Arc<Notify>>,
health_release: Option<Arc<Notify>>,
total_messages: Arc<AtomicU64>,
}
@@ -104,6 +107,8 @@ mod tests {
failed_messages: Arc::new(AtomicU64::new(0)),
failed_store_length: 0,
id: TargetID::new(id.to_string(), name.to_string()),
health_started: None,
health_release: None,
total_messages: Arc::new(AtomicU64::new(0)),
}
}
@@ -123,6 +128,12 @@ mod tests {
self
}
fn with_health_gate(mut self, started: Arc<Notify>, release: Arc<Notify>) -> Self {
self.health_started = Some(started);
self.health_release = Some(release);
self
}
fn record_successes(&self, count: u64) {
self.total_messages.store(count, Ordering::Relaxed);
}
@@ -142,6 +153,10 @@ mod tests {
}
async fn is_active(&self) -> Result<bool, TargetError> {
if let (Some(started), Some(release)) = (&self.health_started, &self.health_release) {
started.notify_one();
release.notified().await;
}
Ok(self.active)
}
@@ -268,4 +283,29 @@ mod tests {
assert_eq!(status.target_count, 2);
assert_eq!(status.replay_worker_count, 0);
}
#[tokio::test]
async fn health_probe_does_not_hold_the_target_list_read_lock() {
let target_list = Arc::new(RwLock::new(TargetList::new()));
let started = Arc::new(Notify::new());
let release = Arc::new(Notify::new());
let target = Arc::new(TestTarget::new("blocked", "webhook").with_health_gate(started.clone(), release.clone()));
target_list
.write()
.await
.add(target as Arc<dyn Target<Event> + Send + Sync>)
.expect("test target should be added");
let runtime_view = NotifyRuntimeView::new(target_list.clone(), Arc::new(RwLock::new(ReplayWorkerManager::new())));
let snapshot_task = tokio::spawn(async move { runtime_view.snapshot_target_health().await });
started.notified().await;
let write_guard = tokio::time::timeout(std::time::Duration::from_secs(1), target_list.write())
.await
.expect("network health probe must not retain the target-list read lock");
drop(write_guard);
release.notify_one();
assert_eq!(snapshot_task.await.expect("snapshot task should finish").len(), 1);
}
}