mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-12 16:16:55 +00:00
@@ -21,9 +21,6 @@ use super::{
|
||||
};
|
||||
use super::{endpoint::Endpoint, error::DiskError, format::FormatV3};
|
||||
|
||||
use crate::bucket::metadata_sys::{self};
|
||||
use crate::bucket::versioning::VersioningApi;
|
||||
use crate::bucket::versioning_sys::BucketVersioningSys;
|
||||
use crate::disk::error::FileAccessDeniedWithContext;
|
||||
use crate::disk::error_conv::{to_access_error, to_file_error, to_unformatted_disk_error, to_volume_error};
|
||||
use crate::disk::fs::{
|
||||
@@ -36,16 +33,6 @@ use crate::disk::{
|
||||
};
|
||||
use crate::disk::{FileWriter, STORAGE_FORMAT_FILE};
|
||||
use crate::global::{GLOBAL_IsErasureSD, GLOBAL_RootDiskThreshold};
|
||||
use crate::heal::data_scanner::{
|
||||
ScannerItem, ShouldSleepFn, SizeSummary, lc_has_active_rules, rep_has_active_rules, scan_data_folder,
|
||||
};
|
||||
use crate::heal::data_usage_cache::{DataUsageCache, DataUsageEntry};
|
||||
use crate::heal::error::{ERR_IGNORE_FILE_CONTRIB, ERR_SKIP_FILE};
|
||||
use crate::heal::heal_commands::{HealScanMode, HealingTracker};
|
||||
use crate::heal::heal_ops::HEALING_TRACKER_FILENAME;
|
||||
use crate::new_object_layer_fn;
|
||||
use crate::store_api::{ObjectInfo, StorageAPI};
|
||||
use rustfs_common::metrics::{Metric, Metrics};
|
||||
use rustfs_utils::path::{
|
||||
GLOBAL_DIR_SUFFIX, GLOBAL_DIR_SUFFIX_WITH_SLASH, SLASH_SEPARATOR, clean, decode_dir_object, encode_dir_object, has_suffix,
|
||||
path_join, path_join_buf,
|
||||
@@ -55,19 +42,18 @@ use tokio::time::interval;
|
||||
use crate::erasure_coding::bitrot_verify;
|
||||
use bytes::Bytes;
|
||||
use path_absolutize::Absolutize;
|
||||
use rustfs_common::defer;
|
||||
use rustfs_filemeta::{
|
||||
Cache, FileInfo, FileInfoOpts, FileMeta, MetaCacheEntry, MetacacheWriter, ObjectPartInfo, Opts, RawFileInfo, UpdateFn,
|
||||
get_file_info, read_xl_meta_no_data,
|
||||
};
|
||||
use rustfs_utils::HashAlgorithm;
|
||||
use rustfs_utils::os::get_info;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Debug;
|
||||
use std::io::SeekFrom;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
fs::Metadata,
|
||||
path::{Path, PathBuf},
|
||||
@@ -76,7 +62,6 @@ use time::OffsetDateTime;
|
||||
use tokio::fs::{self, File};
|
||||
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWrite, AsyncWriteExt, ErrorKind};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -2268,184 +2253,6 @@ impl DiskAPI for LocalDisk {
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "info", skip_all)]
|
||||
async fn ns_scanner(
|
||||
&self,
|
||||
cache: &DataUsageCache,
|
||||
updates: Sender<DataUsageEntry>,
|
||||
scan_mode: HealScanMode,
|
||||
we_sleep: ShouldSleepFn,
|
||||
) -> Result<DataUsageCache> {
|
||||
self.scanning.fetch_add(1, Ordering::SeqCst);
|
||||
defer!(|| { self.scanning.fetch_sub(1, Ordering::SeqCst) });
|
||||
|
||||
// must before metadata_sys
|
||||
let Some(store) = new_object_layer_fn() else {
|
||||
return Err(Error::other("errServerNotInitialized"));
|
||||
};
|
||||
|
||||
let mut cache = cache.clone();
|
||||
// Check if the current bucket has a configured lifecycle policy
|
||||
if let Ok((lc, _)) = metadata_sys::get_lifecycle_config(&cache.info.name).await {
|
||||
if lc_has_active_rules(&lc, "") {
|
||||
cache.info.lifecycle = Some(lc);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the current bucket has replication configuration
|
||||
if let Ok((rcfg, _)) = metadata_sys::get_replication_config(&cache.info.name).await {
|
||||
if rep_has_active_rules(&rcfg, "", true) {
|
||||
// TODO: globalBucketTargetSys
|
||||
}
|
||||
}
|
||||
|
||||
let vcfg = BucketVersioningSys::get(&cache.info.name).await.ok();
|
||||
|
||||
let loc = self.get_disk_location();
|
||||
// TODO: 这里需要处理错误
|
||||
let disks = store
|
||||
.get_disks(loc.pool_idx.unwrap(), loc.disk_idx.unwrap())
|
||||
.await
|
||||
.map_err(|e| Error::other(e.to_string()))?;
|
||||
let disk = Arc::new(LocalDisk::new(&self.endpoint(), false).await?);
|
||||
let disk_clone = disk.clone();
|
||||
cache.info.updates = Some(updates.clone());
|
||||
let mut data_usage_info = scan_data_folder(
|
||||
&disks,
|
||||
disk,
|
||||
&cache,
|
||||
Box::new(move |item: &ScannerItem| {
|
||||
let mut item = item.clone();
|
||||
let disk = disk_clone.clone();
|
||||
let vcfg = vcfg.clone();
|
||||
Box::pin(async move {
|
||||
if !item.path.ends_with(&format!("{SLASH_SEPARATOR}{STORAGE_FORMAT_FILE}")) {
|
||||
return Err(Error::other(ERR_SKIP_FILE).into());
|
||||
}
|
||||
let stop_fn = Metrics::log(Metric::ScanObject);
|
||||
let mut res = HashMap::new();
|
||||
let done_sz = Metrics::time_size(Metric::ReadMetadata);
|
||||
let buf = match disk.read_metadata(item.path.clone()).await {
|
||||
Ok(buf) => buf,
|
||||
Err(err) => {
|
||||
res.insert("err".to_string(), err.to_string());
|
||||
stop_fn(&res);
|
||||
return Err(Error::other(ERR_SKIP_FILE).into());
|
||||
}
|
||||
};
|
||||
done_sz(buf.len() as u64);
|
||||
res.insert("metasize".to_string(), buf.len().to_string());
|
||||
item.transform_meta_dir();
|
||||
let meta_cache = MetaCacheEntry {
|
||||
name: item.object_path().to_string_lossy().to_string(),
|
||||
metadata: buf,
|
||||
..Default::default()
|
||||
};
|
||||
let fivs = match meta_cache.file_info_versions(&item.bucket) {
|
||||
Ok(fivs) => fivs,
|
||||
Err(err) => {
|
||||
res.insert("err".to_string(), err.to_string());
|
||||
stop_fn(&res);
|
||||
return Err(Error::other(ERR_SKIP_FILE).into());
|
||||
}
|
||||
};
|
||||
let mut size_s = SizeSummary::default();
|
||||
let done = Metrics::time(Metric::ApplyAll);
|
||||
let obj_infos = match item.apply_versions_actions(&fivs.versions).await {
|
||||
Ok(obj_infos) => obj_infos,
|
||||
Err(err) => {
|
||||
res.insert("err".to_string(), err.to_string());
|
||||
stop_fn(&res);
|
||||
return Err(Error::other(ERR_SKIP_FILE).into());
|
||||
}
|
||||
};
|
||||
|
||||
let versioned = if let Some(vcfg) = vcfg.as_ref() {
|
||||
vcfg.versioned(item.object_path().to_str().unwrap_or_default())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut obj_deleted = false;
|
||||
for info in obj_infos.iter() {
|
||||
let done = Metrics::time(Metric::ApplyVersion);
|
||||
let sz: i64;
|
||||
(obj_deleted, sz) = item.apply_actions(info, &mut size_s).await;
|
||||
done();
|
||||
|
||||
if obj_deleted {
|
||||
break;
|
||||
}
|
||||
|
||||
let actual_sz = match info.get_actual_size() {
|
||||
Ok(size) => size,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if info.delete_marker {
|
||||
size_s.delete_markers += 1;
|
||||
}
|
||||
|
||||
if info.version_id.is_some() && sz == actual_sz {
|
||||
size_s.versions += 1;
|
||||
}
|
||||
|
||||
size_s.total_size += sz as usize;
|
||||
|
||||
if info.delete_marker {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for free_version in fivs.free_versions.iter() {
|
||||
let _obj_info = ObjectInfo::from_file_info(
|
||||
free_version,
|
||||
&item.bucket,
|
||||
&item.object_path().to_string_lossy(),
|
||||
versioned,
|
||||
);
|
||||
let done = Metrics::time(Metric::TierObjSweep);
|
||||
done();
|
||||
}
|
||||
|
||||
// todo: global trace
|
||||
if obj_deleted {
|
||||
return Err(Error::other(ERR_IGNORE_FILE_CONTRIB).into());
|
||||
}
|
||||
done();
|
||||
Ok(size_s)
|
||||
})
|
||||
}),
|
||||
scan_mode,
|
||||
we_sleep,
|
||||
)
|
||||
.await?;
|
||||
data_usage_info.info.last_update = Some(SystemTime::now());
|
||||
debug!("ns_scanner completed: {data_usage_info:?}");
|
||||
Ok(data_usage_info)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn healing(&self) -> Option<HealingTracker> {
|
||||
let healing_file = path_join(&[
|
||||
self.path(),
|
||||
PathBuf::from(RUSTFS_META_BUCKET),
|
||||
PathBuf::from(BUCKET_META_PREFIX),
|
||||
PathBuf::from(HEALING_TRACKER_FILENAME),
|
||||
]);
|
||||
let b = match fs::read(healing_file).await {
|
||||
Ok(b) => b,
|
||||
Err(_) => return None,
|
||||
};
|
||||
if b.is_empty() {
|
||||
return None;
|
||||
}
|
||||
match HealingTracker::unmarshal_msg(&b) {
|
||||
Ok(h) => Some(h),
|
||||
Err(_) => Some(HealingTracker::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_disk_info(drive_path: PathBuf) -> Result<(rustfs_utils::os::DiskInfo, bool)> {
|
||||
|
||||
@@ -21,9 +21,9 @@ use rustfs_protos::{
|
||||
node_service_time_out_client,
|
||||
proto_gen::node_service::{
|
||||
CheckPartsRequest, DeletePathsRequest, DeleteRequest, DeleteVersionRequest, DeleteVersionsRequest, DeleteVolumeRequest,
|
||||
DiskInfoRequest, ListDirRequest, ListVolumesRequest, MakeVolumeRequest, MakeVolumesRequest, NsScannerRequest,
|
||||
ReadAllRequest, ReadMultipleRequest, ReadPartsRequest, ReadVersionRequest, ReadXlRequest, RenameDataRequest,
|
||||
RenameFileRequest, StatVolumeRequest, UpdateMetadataRequest, VerifyFileRequest, WriteAllRequest, WriteMetadataRequest,
|
||||
DiskInfoRequest, ListDirRequest, ListVolumesRequest, MakeVolumeRequest, MakeVolumesRequest, ReadAllRequest,
|
||||
ReadMultipleRequest, ReadPartsRequest, ReadVersionRequest, ReadXlRequest, RenameDataRequest, RenameFileRequest,
|
||||
StatVolumeRequest, UpdateMetadataRequest, VerifyFileRequest, WriteAllRequest, WriteMetadataRequest,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -32,26 +32,15 @@ use crate::disk::{
|
||||
ReadMultipleReq, ReadMultipleResp, ReadOptions, RenameDataResp, UpdateMetadataOpts, VolumeInfo, WalkDirOptions,
|
||||
endpoint::Endpoint,
|
||||
};
|
||||
use crate::disk::{FileReader, FileWriter};
|
||||
use crate::{
|
||||
disk::error::{Error, Result},
|
||||
rpc::build_auth_headers,
|
||||
};
|
||||
use crate::{
|
||||
disk::{FileReader, FileWriter},
|
||||
heal::{
|
||||
data_scanner::ShouldSleepFn,
|
||||
data_usage_cache::{DataUsageCache, DataUsageEntry},
|
||||
heal_commands::{HealScanMode, HealingTracker},
|
||||
},
|
||||
};
|
||||
use rustfs_filemeta::{FileInfo, ObjectPartInfo, RawFileInfo};
|
||||
use rustfs_protos::proto_gen::node_service::RenamePartRequest;
|
||||
use rustfs_rio::{HttpReader, HttpWriter};
|
||||
use tokio::{
|
||||
io::AsyncWrite,
|
||||
sync::mpsc::{self, Sender},
|
||||
};
|
||||
use tokio_stream::{StreamExt, wrappers::ReceiverStream};
|
||||
use tokio::io::AsyncWrite;
|
||||
use tonic::Request;
|
||||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
@@ -927,55 +916,6 @@ impl DiskAPI for RemoteDisk {
|
||||
|
||||
Ok(disk_info)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, cache, scan_mode, _we_sleep))]
|
||||
async fn ns_scanner(
|
||||
&self,
|
||||
cache: &DataUsageCache,
|
||||
updates: Sender<DataUsageEntry>,
|
||||
scan_mode: HealScanMode,
|
||||
_we_sleep: ShouldSleepFn,
|
||||
) -> Result<DataUsageCache> {
|
||||
info!("ns_scanner");
|
||||
let cache = serde_json::to_string(cache)?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not get client, err: {err}")))?;
|
||||
|
||||
let (tx, rx) = mpsc::channel(10);
|
||||
let in_stream = ReceiverStream::new(rx);
|
||||
let mut response = client.ns_scanner(in_stream).await?.into_inner();
|
||||
let request = NsScannerRequest {
|
||||
disk: self.endpoint.to_string(),
|
||||
cache,
|
||||
scan_mode: scan_mode as u64,
|
||||
};
|
||||
tx.send(request)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not send request, err: {err}")))?;
|
||||
|
||||
loop {
|
||||
match response.next().await {
|
||||
Some(Ok(resp)) => {
|
||||
if !resp.update.is_empty() {
|
||||
let data_usage_cache = serde_json::from_str::<DataUsageEntry>(&resp.update)?;
|
||||
let _ = updates.send(data_usage_cache).await;
|
||||
} else if !resp.data_usage_cache.is_empty() {
|
||||
let data_usage_cache = serde_json::from_str::<DataUsageCache>(&resp.data_usage_cache)?;
|
||||
return Ok(data_usage_cache);
|
||||
} else {
|
||||
return Err(Error::other("scan was interrupted"));
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::other("scan was interrupted")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn healing(&self) -> Option<HealingTracker> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+128
-895
File diff suppressed because it is too large
Load Diff
@@ -970,6 +970,7 @@ pub trait StorageAPI: ObjectIO {
|
||||
// Walk TODO:
|
||||
|
||||
async fn get_object_info(&self, bucket: &str, object: &str, opts: &ObjectOptions) -> Result<ObjectInfo>;
|
||||
async fn verify_object_integrity(&self, bucket: &str, object: &str, opts: &ObjectOptions) -> Result<()>;
|
||||
async fn copy_object(
|
||||
&self,
|
||||
src_bucket: &str,
|
||||
|
||||
Reference in New Issue
Block a user