mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-07 20:46:11 +00:00
feat(scanner): add storage seam (#7105)
This commit is contained in:
@@ -15,7 +15,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
use http::HeaderMap;
|
||||
use rustfs_lock::NamespaceLockWrapper;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Notify;
|
||||
|
||||
pub(crate) use s3s::dto::{
|
||||
BucketLifecycleConfiguration as EcstoreBucketLifecycleConfiguration, LifecycleRuleFilter as EcstoreLifecycleRuleFilter,
|
||||
@@ -29,11 +33,7 @@ pub(crate) use rustfs_ecstore::api::bucket::lifecycle::bucket_lifecycle_audit::L
|
||||
pub(crate) use rustfs_ecstore::api::bucket::lifecycle::bucket_lifecycle_ops::{
|
||||
apply_expiry_rule as ecstore_apply_expiry_rule, apply_transition_rule as ecstore_apply_transition_rule,
|
||||
};
|
||||
pub(crate) use rustfs_ecstore::api::bucket::lifecycle::evaluator::Evaluator as EcstoreEvaluator;
|
||||
pub(crate) use rustfs_ecstore::api::bucket::lifecycle::lifecycle::{
|
||||
Event as EcstoreEvent, Lifecycle as EcstoreLifecycle, ObjectOpts as EcstoreObjectOpts,
|
||||
TRANSITION_COMPLETE as ECSTORE_TRANSITION_COMPLETE, object_opts_from_object_info as ecstore_object_opts_from_object_info,
|
||||
};
|
||||
pub(crate) use rustfs_ecstore::api::bucket::lifecycle::lifecycle::object_opts_from_object_info as ecstore_object_opts_from_object_info;
|
||||
#[cfg(test)]
|
||||
pub(crate) use rustfs_ecstore::api::bucket::metadata_sys::init_bucket_metadata_sys as ecstore_init_bucket_metadata_sys;
|
||||
pub(crate) use rustfs_ecstore::api::bucket::metadata_sys::{
|
||||
@@ -92,6 +92,7 @@ pub(crate) use rustfs_ecstore::api::event::{EventArgs as EcstoreEventArgs, send_
|
||||
pub(crate) use rustfs_ecstore::api::layout::{
|
||||
EndpointServerPools as EcstoreEndpointServerPools, Endpoints as EcstoreEndpoints, PoolEndpoints as EcstorePoolEndpoints,
|
||||
};
|
||||
pub(crate) use rustfs_ecstore::api::notification::NotificationSys as EcstoreNotificationSys;
|
||||
pub(crate) use rustfs_ecstore::api::notification::scanner_peer_transport_error_message_is_retryable;
|
||||
pub(crate) use rustfs_ecstore::api::object::{
|
||||
SCANNER_PUBLICATION_LEASE_FENCE_METADATA_KEY, ScannerPublicationCommitScope, ScannerPublicationCommitState,
|
||||
@@ -101,19 +102,25 @@ pub(crate) use rustfs_ecstore::api::rebalance::{
|
||||
RebalStatus as EcstoreRebalStatus, RebalanceInfo as EcstoreRebalanceInfo, RebalanceMeta as EcstoreRebalanceMeta,
|
||||
RebalanceStats as EcstoreRebalanceStats,
|
||||
};
|
||||
pub(crate) use rustfs_ecstore::api::rpc::ScannerBucketListing as EcstoreScannerBucketListing;
|
||||
#[cfg(test)]
|
||||
pub(crate) use rustfs_ecstore::api::runtime::InstanceContext as EcstoreInstanceContext;
|
||||
pub(crate) use rustfs_ecstore::api::runtime::{
|
||||
expiry_state_handle as ecstore_expiry_state_handle, global_tier_config_mgr as ecstore_get_global_tier_config_mgr,
|
||||
object_store_handle as ecstore_resolve_object_store_handle, setup_is_erasure as ecstore_is_erasure,
|
||||
setup_is_erasure_sd as ecstore_is_erasure_sd,
|
||||
};
|
||||
pub(crate) use rustfs_ecstore::api::set_disk::SetDisks as EcstoreSetDisks;
|
||||
pub(crate) use rustfs_ecstore::api::storage::ECStore as EcstoreStore;
|
||||
#[cfg(test)]
|
||||
pub(crate) use rustfs_ecstore::api::storage::SCANNER_PUBLICATION_LEASE_TTL_MS as ECSTORE_SCANNER_PUBLICATION_LEASE_TTL_MS;
|
||||
#[cfg(test)]
|
||||
pub(crate) use rustfs_ecstore::api::storage::init_local_disks_with_instance_ctx as ecstore_init_local_disks_with_instance_ctx;
|
||||
pub(crate) use rustfs_ecstore::api::storage::{
|
||||
ECStore as EcstoreStore, ScannerDataMovementPauseStatus as EcstoreScannerDataMovementPauseStatus,
|
||||
};
|
||||
pub(crate) use rustfs_lifecycle::{
|
||||
Evaluator as EcstoreEvaluator, Event as EcstoreEvent, Lifecycle as EcstoreLifecycle, ObjectOpts as EcstoreObjectOpts,
|
||||
TRANSITION_COMPLETE as ECSTORE_TRANSITION_COMPLETE,
|
||||
};
|
||||
use rustfs_storage_api as storage_contracts;
|
||||
|
||||
pub(crate) mod owner {
|
||||
@@ -134,11 +141,11 @@ pub(crate) mod owner {
|
||||
ScannerReplicationQueueAdmission, ecstore_apply_expiry_rule, ecstore_apply_transition_rule, ecstore_expiry_state_handle,
|
||||
ecstore_get_global_tier_config_mgr, ecstore_get_lifecycle_config, ecstore_get_object_lock_config,
|
||||
ecstore_get_replication_config, ecstore_invalidate_admin_data_usage_snapshot_cache,
|
||||
ecstore_invalidate_data_usage_snapshot_cache, ecstore_is_erasure, ecstore_is_erasure_sd,
|
||||
ecstore_is_reserved_or_invalid_bucket, ecstore_list_path_raw, ecstore_object_opts_from_object_info,
|
||||
ecstore_path2_bucket_object, ecstore_path2_bucket_object_with_base_path, ecstore_read_config,
|
||||
ecstore_replace_bucket_usage_memory_from_info, ecstore_resolve_object_store_handle, ecstore_save_config,
|
||||
ecstore_send_event, scanner_replication_config_for_lifecycle_eval,
|
||||
ecstore_invalidate_data_usage_snapshot_cache, ecstore_is_erasure, ecstore_is_reserved_or_invalid_bucket,
|
||||
ecstore_list_path_raw, ecstore_object_opts_from_object_info, ecstore_path2_bucket_object,
|
||||
ecstore_path2_bucket_object_with_base_path, ecstore_read_config, ecstore_replace_bucket_usage_memory_from_info,
|
||||
ecstore_resolve_object_store_handle, ecstore_save_config, ecstore_send_event,
|
||||
scanner_replication_config_for_lifecycle_eval,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -289,9 +296,10 @@ impl From<EcstoreReplicationHealQueueResult> for ScannerReplicationHealResult {
|
||||
}
|
||||
|
||||
pub(crate) mod scan {
|
||||
#[cfg(test)]
|
||||
pub(crate) use super::storage_contracts::BucketOperations;
|
||||
pub(crate) use super::storage_contracts::{
|
||||
BucketOperations, BucketOptions, NamespaceLocking, SCANNER_ACTIVITY_LEGACY_PROTOCOL_VERSION,
|
||||
SCANNER_ACTIVITY_PREVIOUS_PROTOCOL_VERSION,
|
||||
BucketOptions, NamespaceLocking, SCANNER_ACTIVITY_LEGACY_PROTOCOL_VERSION, SCANNER_ACTIVITY_PREVIOUS_PROTOCOL_VERSION,
|
||||
};
|
||||
#[cfg(test)]
|
||||
pub(crate) use super::storage_contracts::{DeleteBucketOptions, MakeBucketOptions, ObjectIO};
|
||||
@@ -303,3 +311,263 @@ pub(crate) mod scanner_io {
|
||||
#[cfg(test)]
|
||||
pub(crate) use super::storage_contracts::{HTTPRangeSpec, ObjectIO};
|
||||
}
|
||||
|
||||
pub(crate) type ScannerBucketListing = EcstoreScannerBucketListing;
|
||||
pub(crate) type ScannerDataMovementPauseStatus = EcstoreScannerDataMovementPauseStatus;
|
||||
pub(crate) type ScannerNotificationSys = EcstoreNotificationSys;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub(crate) trait ScannerStorage:
|
||||
crate::ScannerObjectIO
|
||||
+ crate::ScannerConfigObjectDelete
|
||||
+ storage_contracts::BucketOperations<Error = EcstoreErrorType>
|
||||
+ storage_contracts::NamespaceLocking<Error = EcstoreErrorType, NamespaceLock = NamespaceLockWrapper>
|
||||
{
|
||||
fn scanner_topology_digest(&self) -> [u8; 32];
|
||||
fn scanner_namespace_mutation_generation(&self) -> u64;
|
||||
async fn scanner_data_movement_activity(&self) -> (bool, bool, u64);
|
||||
async fn scanner_data_usage_publication_blocked(&self) -> bool;
|
||||
async fn scanner_data_movement_pause_status(&self) -> ScannerDataMovementPauseStatus;
|
||||
fn scanner_data_movement_generation(&self) -> u64;
|
||||
fn scanner_data_movement_changed(&self) -> Arc<Notify>;
|
||||
fn scanner_notification_system(&self) -> Option<Arc<ScannerNotificationSys>>;
|
||||
async fn setup_is_erasure(&self) -> bool;
|
||||
async fn setup_is_dist_erasure(&self) -> bool;
|
||||
async fn setup_is_erasure_sd(&self) -> bool;
|
||||
async fn list_bucket_for_scanner(&self, opts: &storage_contracts::BucketOptions) -> EcstoreResultType<ScannerBucketListing>;
|
||||
fn all_set_disks(&self) -> Vec<Arc<EcstoreSetDisks>>;
|
||||
async fn scanner_pause_backlog_writable_set_disks(&self) -> Vec<Arc<EcstoreSetDisks>>;
|
||||
#[cfg(test)]
|
||||
fn scanner_observed_probe_store_key(&self) -> usize;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ScannerStorage for EcstoreStore {
|
||||
fn scanner_topology_digest(&self) -> [u8; 32] {
|
||||
crate::scanner::scanner_topology_digest(self)
|
||||
}
|
||||
|
||||
fn scanner_namespace_mutation_generation(&self) -> u64 {
|
||||
EcstoreStore::scanner_namespace_mutation_generation(self)
|
||||
}
|
||||
|
||||
async fn scanner_data_movement_activity(&self) -> (bool, bool, u64) {
|
||||
EcstoreStore::scanner_data_movement_activity(self).await
|
||||
}
|
||||
|
||||
async fn scanner_data_usage_publication_blocked(&self) -> bool {
|
||||
EcstoreStore::scanner_data_usage_publication_blocked(self).await
|
||||
}
|
||||
|
||||
async fn scanner_data_movement_pause_status(&self) -> ScannerDataMovementPauseStatus {
|
||||
EcstoreStore::scanner_data_movement_pause_status(self).await
|
||||
}
|
||||
|
||||
fn scanner_data_movement_generation(&self) -> u64 {
|
||||
EcstoreStore::scanner_data_movement_generation(self)
|
||||
}
|
||||
|
||||
fn scanner_data_movement_changed(&self) -> Arc<Notify> {
|
||||
EcstoreStore::scanner_data_movement_changed(self)
|
||||
}
|
||||
|
||||
fn scanner_notification_system(&self) -> Option<Arc<ScannerNotificationSys>> {
|
||||
EcstoreStore::notification_system(self)
|
||||
}
|
||||
|
||||
async fn setup_is_erasure(&self) -> bool {
|
||||
EcstoreStore::setup_is_erasure(self).await
|
||||
}
|
||||
|
||||
async fn setup_is_dist_erasure(&self) -> bool {
|
||||
EcstoreStore::setup_is_dist_erasure(self).await
|
||||
}
|
||||
|
||||
async fn setup_is_erasure_sd(&self) -> bool {
|
||||
EcstoreStore::setup_is_erasure_sd(self).await
|
||||
}
|
||||
|
||||
async fn list_bucket_for_scanner(&self, opts: &storage_contracts::BucketOptions) -> EcstoreResultType<ScannerBucketListing> {
|
||||
EcstoreStore::list_bucket_for_scanner(self, opts).await
|
||||
}
|
||||
|
||||
fn all_set_disks(&self) -> Vec<Arc<EcstoreSetDisks>> {
|
||||
EcstoreStore::all_set_disks(self)
|
||||
}
|
||||
|
||||
async fn scanner_pause_backlog_writable_set_disks(&self) -> Vec<Arc<EcstoreSetDisks>> {
|
||||
EcstoreStore::scanner_pause_backlog_writable_set_disks(self).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn scanner_observed_probe_store_key(&self) -> usize {
|
||||
std::ptr::from_ref(self).cast::<()>() as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct FakeScannerStorage;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl storage_contracts::ObjectIO for FakeScannerStorage {
|
||||
type Error = EcstoreErrorType;
|
||||
type RangeSpec = storage_contracts::HTTPRangeSpec;
|
||||
type HeaderMap = HeaderMap;
|
||||
type ObjectOptions = <EcstoreStore as storage_contracts::ObjectIO>::ObjectOptions;
|
||||
type ObjectInfo = <EcstoreStore as storage_contracts::ObjectIO>::ObjectInfo;
|
||||
type GetObjectReader = <EcstoreStore as storage_contracts::ObjectIO>::GetObjectReader;
|
||||
type PutObjectReader = <EcstoreStore as storage_contracts::ObjectIO>::PutObjectReader;
|
||||
|
||||
async fn get_object_reader(
|
||||
&self,
|
||||
_bucket: &str,
|
||||
_object: &str,
|
||||
_range: Option<Self::RangeSpec>,
|
||||
_h: Self::HeaderMap,
|
||||
_opts: &Self::ObjectOptions,
|
||||
) -> Result<Self::GetObjectReader, Self::Error> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage has no object reader"))
|
||||
}
|
||||
|
||||
async fn put_object(
|
||||
&self,
|
||||
_bucket: &str,
|
||||
_object: &str,
|
||||
_data: &mut Self::PutObjectReader,
|
||||
_opts: &Self::ObjectOptions,
|
||||
) -> Result<Self::ObjectInfo, Self::Error> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage has no object writer"))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl storage_contracts::BucketOperations for FakeScannerStorage {
|
||||
type Error = EcstoreErrorType;
|
||||
|
||||
async fn make_bucket(&self, _bucket: &str, _opts: &storage_contracts::MakeBucketOptions) -> Result<(), Self::Error> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage cannot make buckets"))
|
||||
}
|
||||
|
||||
async fn get_bucket_info(
|
||||
&self,
|
||||
_bucket: &str,
|
||||
_opts: &storage_contracts::BucketOptions,
|
||||
) -> Result<storage_contracts::BucketInfo, Self::Error> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage cannot read buckets"))
|
||||
}
|
||||
|
||||
async fn list_bucket(
|
||||
&self,
|
||||
_opts: &storage_contracts::BucketOptions,
|
||||
) -> Result<Vec<storage_contracts::BucketInfo>, Self::Error> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn delete_bucket(&self, _bucket: &str, _opts: &storage_contracts::DeleteBucketOptions) -> Result<(), Self::Error> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage cannot delete buckets"))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl storage_contracts::NamespaceLocking for FakeScannerStorage {
|
||||
type Error = EcstoreErrorType;
|
||||
type NamespaceLock = NamespaceLockWrapper;
|
||||
|
||||
async fn new_ns_lock(&self, _bucket: &str, _object: &str) -> Result<Self::NamespaceLock, Self::Error> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage has no namespace lock"))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::ScannerConfigObjectDelete for FakeScannerStorage {
|
||||
async fn delete_config_object(
|
||||
&self,
|
||||
_bucket: &str,
|
||||
_object: &str,
|
||||
_opts: <EcstoreStore as storage_contracts::ObjectIO>::ObjectOptions,
|
||||
) -> EcstoreResultType<<EcstoreStore as storage_contracts::ObjectIO>::ObjectInfo> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage cannot delete objects"))
|
||||
}
|
||||
|
||||
async fn scanner_data_usage_publication_admission(&self) -> Option<crate::ScannerDataUsagePublicationAdmission> {
|
||||
Some(crate::ScannerDataUsagePublicationAdmission::unfenced())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ScannerStorage for FakeScannerStorage {
|
||||
fn scanner_topology_digest(&self) -> [u8; 32] {
|
||||
[0; 32]
|
||||
}
|
||||
|
||||
fn scanner_namespace_mutation_generation(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
async fn scanner_data_movement_activity(&self) -> (bool, bool, u64) {
|
||||
(false, false, 0)
|
||||
}
|
||||
|
||||
async fn scanner_data_usage_publication_blocked(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn scanner_data_movement_pause_status(&self) -> ScannerDataMovementPauseStatus {
|
||||
ScannerDataMovementPauseStatus::default()
|
||||
}
|
||||
|
||||
fn scanner_data_movement_generation(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn scanner_data_movement_changed(&self) -> Arc<Notify> {
|
||||
Arc::new(Notify::new())
|
||||
}
|
||||
|
||||
fn scanner_notification_system(&self) -> Option<Arc<ScannerNotificationSys>> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn setup_is_erasure(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn setup_is_dist_erasure(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn setup_is_erasure_sd(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn list_bucket_for_scanner(
|
||||
&self,
|
||||
_opts: &storage_contracts::BucketOptions,
|
||||
) -> EcstoreResultType<ScannerBucketListing> {
|
||||
Err(EcstoreErrorType::other("fake scanner storage cannot list scanner buckets"))
|
||||
}
|
||||
|
||||
fn all_set_disks(&self) -> Vec<Arc<EcstoreSetDisks>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
async fn scanner_pause_backlog_writable_set_disks(&self) -> Vec<Arc<EcstoreSetDisks>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn scanner_observed_probe_store_key(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_scanner_storage<S: ScannerStorage>() {}
|
||||
|
||||
#[test]
|
||||
fn scanner_storage_contract_accepts_fake_without_ecstore() {
|
||||
assert_scanner_storage::<FakeScannerStorage>();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user