diff --git a/crates/ecstore/src/store.rs b/crates/ecstore/src/store.rs index 03ed09d96..21ccb78f2 100644 --- a/crates/ecstore/src/store.rs +++ b/crates/ecstore/src/store.rs @@ -163,7 +163,7 @@ mod rebalance; use peer::init_local_peer; pub use peer::{ all_local_disk, all_local_disk_path, find_local_disk, find_local_disk_by_ref, get_disk_infos, get_disk_via_endpoint, - has_space_for, init_local_disks, init_lock_clients, + has_space_for, init_local_disks, init_lock_clients, prewarm_local_disk_id_map, }; #[derive(Debug)] @@ -671,6 +671,17 @@ impl ServerPoolsAvailableSpace { #[cfg(test)] mod tests { use super::*; + use crate::endpoints::{Endpoints, PoolEndpoints}; + use crate::global::{GLOBAL_LOCAL_DISK_ID_MAP, GLOBAL_LOCAL_DISK_MAP, GLOBAL_LOCAL_DISK_SET_DRIVES}; + use crate::store_init::{connect_load_init_formats, init_disks}; + use serial_test::serial; + use tempfile::TempDir; + + async fn reset_local_disk_globals() { + GLOBAL_LOCAL_DISK_MAP.write().await.clear(); + GLOBAL_LOCAL_DISK_ID_MAP.write().await.clear(); + GLOBAL_LOCAL_DISK_SET_DRIVES.write().await.clear(); + } #[tokio::test] async fn test_get_disk_infos() { @@ -723,6 +734,73 @@ mod tests { assert!(result.is_none(), "Should return None for nonexistent path"); } + #[tokio::test] + #[serial] + async fn test_find_local_disk_by_ref_backfills_uuid_map() { + reset_local_disk_globals().await; + + let temp_dir = TempDir::new().expect("create temp dir for local disk ref test"); + let disk_paths = (0..4) + .map(|idx| temp_dir.path().join(format!("disk{}", idx + 1))) + .collect::>(); + for disk_path in &disk_paths { + std::fs::create_dir_all(disk_path).expect("create disk path"); + } + + let mut endpoints = Vec::new(); + for (idx, disk_path) in disk_paths.iter().enumerate() { + let mut endpoint = Endpoint::try_from(disk_path.to_str().expect("disk path to str")).expect("endpoint"); + endpoint.set_pool_index(0); + endpoint.set_set_index(0); + endpoint.set_disk_index(idx); + endpoints.push(endpoint); + } + + let endpoint_pools = EndpointServerPools(vec![PoolEndpoints { + legacy: false, + set_count: 1, + drives_per_set: 4, + endpoints: Endpoints::from(endpoints), + cmd_line: "find-local-disk-by-ref-test".to_string(), + platform: "test".to_string(), + }]); + + init_local_disks(endpoint_pools.clone()).await.expect("init local disks"); + + let (disks, errs) = init_disks( + &endpoint_pools.as_ref().first().expect("pool endpoints").endpoints, + &DiskOption { + cleanup: true, + health_check: false, + }, + ) + .await; + + assert!(errs.iter().all(|err| err.is_none()), "disk init should succeed: {errs:?}"); + connect_load_init_formats(true, &disks, 1, 4, None) + .await + .expect("initialize format metadata"); + + GLOBAL_LOCAL_DISK_ID_MAP.write().await.clear(); + + let local_disks = all_local_disk().await; + let first_disk = local_disks.first().expect("local disk exists"); + let disk_id = first_disk + .get_disk_id() + .await + .expect("get disk id should succeed") + .expect("disk id should exist"); + + let found = find_local_disk_by_ref(&disk_id.to_string()).await; + assert!(found.is_some(), "disk lookup by id should backfill cache"); + assert_eq!( + GLOBAL_LOCAL_DISK_ID_MAP.read().await.get(&disk_id).cloned(), + Some(first_disk.endpoint().to_string()) + ); + + reset_local_disk_globals().await; + } + #[tokio::test] async fn test_all_local_disk_path() { let paths = all_local_disk_path().await; diff --git a/crates/ecstore/src/store/peer.rs b/crates/ecstore/src/store/peer.rs index ecd0f706e..13d64b838 100644 --- a/crates/ecstore/src/store/peer.rs +++ b/crates/ecstore/src/store/peer.rs @@ -15,6 +15,15 @@ use super::*; use crate::global::GLOBAL_LOCAL_DISK_ID_MAP; +async fn remember_local_disk_id(disk: &DiskStore) -> Option { + let disk_id = disk.get_disk_id().await.ok().flatten()?; + GLOBAL_LOCAL_DISK_ID_MAP + .write() + .await + .insert(disk_id, disk.endpoint().to_string()); + Some(disk_id) +} + pub async fn find_local_disk(disk_path: &String) -> Option { let disk_map = GLOBAL_LOCAL_DISK_MAP.read().await; @@ -27,6 +36,7 @@ pub async fn find_local_disk(disk_path: &String) -> Option { pub async fn find_local_disk_by_ref(disk_ref: &str) -> Option { if let Some(disk) = find_local_disk(&disk_ref.to_string()).await { + let _ = remember_local_disk_id(&disk).await; return Some(disk); } @@ -34,8 +44,19 @@ pub async fn find_local_disk_by_ref(disk_ref: &str) -> Option { return None; }; - let disk_path = GLOBAL_LOCAL_DISK_ID_MAP.read().await.get(&disk_id).cloned()?; - find_local_disk(&disk_path).await + if let Some(disk_path) = GLOBAL_LOCAL_DISK_ID_MAP.read().await.get(&disk_id).cloned() + && let Some(disk) = find_local_disk(&disk_path).await + { + return Some(disk); + } + + for disk in all_local_disk().await { + if remember_local_disk_id(&disk).await == Some(disk_id) { + return Some(disk); + } + } + + None } pub async fn get_disk_via_endpoint(endpoint: &Endpoint) -> Option { @@ -70,6 +91,17 @@ pub async fn all_local_disk() -> Vec { .collect() } +pub async fn prewarm_local_disk_id_map() { + for disk in all_local_disk().await { + if let Err(err) = disk.get_disk_id().await { + warn!("prewarm_local_disk_id_map: failed to load disk id for {}: {}", disk.endpoint(), err); + continue; + } + + let _ = remember_local_disk_id(&disk).await; + } +} + pub async fn init_local_disks(endpoint_pools: EndpointServerPools) -> Result<()> { let opt = &DiskOption { cleanup: true, diff --git a/rustfs/src/main.rs b/rustfs/src/main.rs index cdb9eed8b..fe406ab1f 100644 --- a/rustfs/src/main.rs +++ b/rustfs/src/main.rs @@ -44,6 +44,7 @@ use rustfs_ecstore::{ set_global_endpoints, store::ECStore, store::init_local_disks, + store::prewarm_local_disk_id_map, store_api::BucketOperations, store_api::BucketOptions, update_erasure_type, @@ -295,6 +296,7 @@ async fn run(config: rustfs::config::Config) -> Result<()> { // Initialize the local disk init_local_disks(endpoint_pools.clone()).await.map_err(Error::other)?; + prewarm_local_disk_id_map().await; // Initialize the lock clients init_lock_clients(endpoint_pools.clone());