fix(common): remove panic paths in runtime helpers (#2116)

Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: heihutu <30542132+heihutu@users.noreply.github.com>
This commit is contained in:
安正超
2026-03-11 18:12:37 +08:00
committed by GitHub
parent e1f24f764d
commit 5625f04697
4 changed files with 101 additions and 48 deletions
+92 -34
View File
@@ -458,10 +458,7 @@ impl DataUsageEntry {
self.size += other.size; self.size += other.size;
if let Some(o_rep) = &other.replication_stats { if let Some(o_rep) = &other.replication_stats {
if self.replication_stats.is_none() { let s_rep = self.replication_stats.get_or_insert_with(ReplicationAllStats::default);
self.replication_stats = Some(ReplicationAllStats::default());
}
let s_rep = self.replication_stats.as_mut().unwrap();
s_rep.targets.clear(); s_rep.targets.clear();
s_rep.replica_size += o_rep.replica_size; s_rep.replica_size += o_rep.replica_size;
s_rep.replica_count += o_rep.replica_count; s_rep.replica_count += o_rep.replica_count;
@@ -586,7 +583,7 @@ impl DataUsageCache {
return Some(root); return Some(root);
} }
let mut flat = self.flatten(&root); let mut flat = self.flatten(&root);
if flat.replication_stats.is_some() && flat.replication_stats.as_ref().unwrap().empty() { if flat.replication_stats.as_ref().is_some_and(|stats| stats.empty()) {
flat.replication_stats = None; flat.replication_stats = None;
} }
Some(flat) Some(flat)
@@ -679,7 +676,9 @@ impl DataUsageCache {
leaves.sort_by(|a, b| a.objects.cmp(&b.objects)); leaves.sort_by(|a, b| a.objects.cmp(&b.objects));
while remove > 0 && !leaves.is_empty() { while remove > 0 && !leaves.is_empty() {
let e = leaves.first().unwrap(); let Some(e) = leaves.first() else {
break;
};
let candidate = e.path.clone(); let candidate = e.path.clone();
if candidate == *path && !compact_self { if candidate == *path && !compact_self {
break; break;
@@ -703,12 +702,9 @@ impl DataUsageCache {
} }
pub fn total_children_rec(&self, path: &str) -> usize { pub fn total_children_rec(&self, path: &str) -> usize {
let root = self.find(path); let Some(root) = self.find(path) else {
if root.is_none() {
return 0; return 0;
} };
let root = root.unwrap();
if root.children.is_empty() { if root.children.is_empty() {
return 0; return 0;
} }
@@ -721,31 +717,36 @@ impl DataUsageCache {
} }
pub fn merge(&mut self, o: &DataUsageCache) { pub fn merge(&mut self, o: &DataUsageCache) {
let mut existing_root = self.root(); let Some(mut existing_root) = self.root() else {
let other_root = o.root(); if o.root().is_none() {
if existing_root.is_none() && other_root.is_none() { return;
return; }
}
if other_root.is_none() {
return;
}
if existing_root.is_none() {
*self = o.clone(); *self = o.clone();
return; return;
} };
if o.info.last_update.gt(&self.info.last_update) {
let Some(other_root) = o.root() else {
return;
};
if o.info.last_update > self.info.last_update {
self.info.last_update = o.info.last_update; self.info.last_update = o.info.last_update;
} }
existing_root.as_mut().unwrap().merge(other_root.as_ref().unwrap()); existing_root.merge(&other_root);
self.cache.insert(hash_path(&self.info.name).key(), existing_root.unwrap()); self.cache.insert(hash_path(&self.info.name).key(), existing_root);
let e_hash = self.root_hash();
for key in other_root.as_ref().unwrap().children.iter() { let root_hash = self.root_hash();
let entry = &o.cache[key]; for key in other_root.children.iter() {
let Some(entry) = o.cache.get(key) else {
continue;
};
let flat = o.flatten(entry); let flat = o.flatten(entry);
let mut existing = self.cache[key].clone(); if let Some(existing) = self.cache.get_mut(key) {
existing.merge(&flat); existing.merge(&flat);
self.replace_hashed(&DataUsageHash(key.clone()), &Some(e_hash.clone()), &existing); } else {
self.replace_hashed(&DataUsageHash(key.clone()), &Some(root_hash.clone()), &flat);
}
} }
} }
@@ -1141,10 +1142,12 @@ impl DataUsageInfo {
self.buckets_count = self.buckets_usage.len() as u64; self.buckets_count = self.buckets_usage.len() as u64;
// Update last update time // Update last update time
if let Some(other_update) = other.last_update if let Some(other_update) = other.last_update {
&& (self.last_update.is_none() || other_update > self.last_update.unwrap()) match self.last_update {
{ None => self.last_update = Some(other_update),
self.last_update = Some(other_update); Some(self_update) if other_update > self_update => self.last_update = Some(other_update),
_ => {}
}
} }
} }
} }
@@ -1285,4 +1288,59 @@ mod tests {
assert_eq!(summary1.total_size, 300); assert_eq!(summary1.total_size, 300);
assert_eq!(summary1.versions, 15); assert_eq!(summary1.versions, 15);
} }
#[test]
fn test_data_usage_cache_merge_adds_missing_child() {
let mut base = DataUsageCache::default();
base.info.name = "bucket".to_string();
base.replace("bucket", "", DataUsageEntry::default());
let mut other = DataUsageCache::default();
other.info.name = "bucket".to_string();
let child = DataUsageEntry {
size: 42,
..Default::default()
};
other.replace("bucket/child", "bucket", child);
base.merge(&other);
let root = base.find("bucket").expect("root bucket should exist");
assert_eq!(root.size, 0);
let child_entry = base.find("bucket/child").expect("merged child should be added");
assert_eq!(child_entry.size, 42);
}
#[test]
fn test_data_usage_cache_merge_accumulates_existing_child() {
let mut base = DataUsageCache::default();
base.info.name = "bucket".to_string();
base.replace(
"bucket/child",
"bucket",
DataUsageEntry {
size: 10,
objects: 1,
..Default::default()
},
);
let mut other = DataUsageCache::default();
other.info.name = "bucket".to_string();
other.replace(
"bucket/child",
"bucket",
DataUsageEntry {
size: 20,
objects: 2,
..Default::default()
},
);
base.merge(&other);
let child_entry = base.find("bucket/child").expect("child should remain after merge");
assert_eq!(child_entry.size, 30);
assert_eq!(child_entry.objects, 3);
}
} }
+6 -5
View File
@@ -296,12 +296,13 @@ type HealResponseSender = broadcast::Sender<HealChannelResponse>;
static GLOBAL_HEAL_RESPONSE_SENDER: OnceLock<HealResponseSender> = OnceLock::new(); static GLOBAL_HEAL_RESPONSE_SENDER: OnceLock<HealResponseSender> = OnceLock::new();
/// Initialize global heal channel /// Initialize global heal channel
pub fn init_heal_channel() -> HealChannelReceiver { pub fn init_heal_channel() -> Result<HealChannelReceiver, &'static str> {
let (tx, rx) = mpsc::unbounded_channel(); let (tx, rx) = mpsc::unbounded_channel();
GLOBAL_HEAL_CHANNEL_SENDER if GLOBAL_HEAL_CHANNEL_SENDER.set(tx).is_ok() {
.set(tx) Ok(rx)
.expect("Heal channel sender already initialized"); } else {
rx Err("Heal channel sender already initialized")
}
} }
/// Get global heal channel sender /// Get global heal channel sender
+2 -8
View File
@@ -155,10 +155,7 @@ impl LastMinuteLatency {
} }
pub fn add(&mut self, t: &Duration) { pub fn add(&mut self, t: &Duration) {
let sec = SystemTime::now() let sec = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
self.forward_to(sec); self.forward_to(sec);
let win_idx = sec % 60; let win_idx = sec % 60;
self.totals[win_idx as usize].add(t); self.totals[win_idx as usize].add(t);
@@ -174,10 +171,7 @@ impl LastMinuteLatency {
pub fn get_total(&mut self) -> AccElem { pub fn get_total(&mut self) -> AccElem {
let mut res = AccElem::default(); let mut res = AccElem::default();
let sec = SystemTime::now() let sec = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
self.forward_to(sec); self.forward_to(sec);
for elem in self.totals.iter() { for elem in self.totals.iter() {
res.merge(elem); res.merge(elem);
+1 -1
View File
@@ -73,7 +73,7 @@ pub async fn init_heal_manager(
.map_err(|_| Error::Config("Heal manager already initialized".to_string()))?; .map_err(|_| Error::Config("Heal manager already initialized".to_string()))?;
// Initialize heal channel // Initialize heal channel
let channel_receiver = rustfs_common::heal_channel::init_heal_channel(); let channel_receiver = rustfs_common::heal_channel::init_heal_channel().map_err(|err| Error::Config(err.to_string()))?;
// Create channel processor // Create channel processor
let channel_processor = HealChannelProcessor::new(heal_manager.clone()); let channel_processor = HealChannelProcessor::new(heal_manager.clone());