mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-28 07:57:01 +00:00
refactor(storage-api): remove namespace lock from StorageAPI (#3365)
* refactor(storage-api): remove namespace lock from StorageAPI * test(scanner): wait for runtime budget cancellation --------- Co-authored-by: loverustfs <hello@rustfs.com>
This commit is contained in:
@@ -27,7 +27,7 @@ use crate::bucket::replication::replication_state::ReplicationStats;
|
|||||||
use crate::config::com::read_config;
|
use crate::config::com::read_config;
|
||||||
use crate::disk::BUCKET_META_PREFIX;
|
use crate::disk::BUCKET_META_PREFIX;
|
||||||
use crate::error::Error as EcstoreError;
|
use crate::error::Error as EcstoreError;
|
||||||
use crate::store_api::{ObjectIO, ObjectInfo};
|
use crate::store_api::{NamespaceLocking, ObjectIO, ObjectInfo};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rustfs_filemeta::MrfReplicateEntry;
|
use rustfs_filemeta::MrfReplicateEntry;
|
||||||
use rustfs_filemeta::ReplicateDecision;
|
use rustfs_filemeta::ReplicateDecision;
|
||||||
@@ -205,7 +205,7 @@ impl Default for ReplicationPoolOpts {
|
|||||||
}
|
}
|
||||||
/// Main replication pool structure
|
/// Main replication pool structure
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ReplicationPool<S: StorageAPI> {
|
pub struct ReplicationPool<S: StorageAPI + NamespaceLocking> {
|
||||||
// Atomic counters for active workers
|
// Atomic counters for active workers
|
||||||
active_workers: Arc<AtomicI32>,
|
active_workers: Arc<AtomicI32>,
|
||||||
active_lrg_workers: Arc<AtomicI32>,
|
active_lrg_workers: Arc<AtomicI32>,
|
||||||
@@ -245,7 +245,7 @@ pub struct ReplicationPool<S: StorageAPI> {
|
|||||||
resyncer: Arc<ReplicationResyncer>,
|
resyncer: Arc<ReplicationResyncer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: StorageAPI> ReplicationPool<S> {
|
impl<S: StorageAPI + NamespaceLocking> ReplicationPool<S> {
|
||||||
/// Creates a new replication pool with specified options
|
/// Creates a new replication pool with specified options
|
||||||
pub async fn new(opts: ReplicationPoolOpts, stats: Arc<ReplicationStats>, storage: Arc<S>) -> Arc<Self> {
|
pub async fn new(opts: ReplicationPoolOpts, stats: Arc<ReplicationStats>, storage: Arc<S>) -> Arc<Self> {
|
||||||
let max_workers = opts.max_workers.unwrap_or(WORKER_MAX_LIMIT);
|
let max_workers = opts.max_workers.unwrap_or(WORKER_MAX_LIMIT);
|
||||||
@@ -1093,7 +1093,7 @@ pub trait ReplicationPoolTrait: std::fmt::Debug {
|
|||||||
|
|
||||||
// Implement the trait for ReplicationPool
|
// Implement the trait for ReplicationPool
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<S: StorageAPI> ReplicationPoolTrait for ReplicationPool<S> {
|
impl<S: StorageAPI + NamespaceLocking> ReplicationPoolTrait for ReplicationPool<S> {
|
||||||
fn active_workers(&self) -> i32 {
|
fn active_workers(&self) -> i32 {
|
||||||
ReplicationPool::<S>::active_workers(self)
|
ReplicationPool::<S>::active_workers(self)
|
||||||
}
|
}
|
||||||
@@ -1145,7 +1145,7 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes background replication with the given options
|
/// Initializes background replication with the given options
|
||||||
pub async fn init_background_replication<S: StorageAPI>(storage: Arc<S>) {
|
pub async fn init_background_replication<S: StorageAPI + NamespaceLocking>(storage: Arc<S>) {
|
||||||
let stats = GLOBAL_REPLICATION_STATS
|
let stats = GLOBAL_REPLICATION_STATS
|
||||||
.get_or_init(|| async {
|
.get_or_init(|| async {
|
||||||
let stats = Arc::new(ReplicationStats::new());
|
let stats = Arc::new(ReplicationStats::new());
|
||||||
@@ -1169,7 +1169,12 @@ pub fn get_global_replication_pool() -> Option<Arc<DynReplicationPool>> {
|
|||||||
GLOBAL_REPLICATION_POOL.get().cloned()
|
GLOBAL_REPLICATION_POOL.get().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn schedule_replication<S: StorageAPI>(oi: ObjectInfo, o: Arc<S>, dsc: ReplicateDecision, op_type: ReplicationType) {
|
pub async fn schedule_replication<S: StorageAPI + NamespaceLocking>(
|
||||||
|
oi: ObjectInfo,
|
||||||
|
o: Arc<S>,
|
||||||
|
dsc: ReplicateDecision,
|
||||||
|
op_type: ReplicationType,
|
||||||
|
) {
|
||||||
let tgt_statuses = replication_statuses_map(&oi.replication_status_internal.clone().unwrap_or_default());
|
let tgt_statuses = replication_statuses_map(&oi.replication_status_internal.clone().unwrap_or_default());
|
||||||
let purge_statuses = version_purge_statuses_map(&oi.version_purge_status_internal.clone().unwrap_or_default());
|
let purge_statuses = version_purge_statuses_map(&oi.version_purge_status_internal.clone().unwrap_or_default());
|
||||||
let tm = get_str(&oi.user_defined, SUFFIX_REPLICATION_TIMESTAMP)
|
let tm = get_str(&oi.user_defined, SUFFIX_REPLICATION_TIMESTAMP)
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ use crate::event_notification::{EventArgs, send_event};
|
|||||||
use crate::global::GLOBAL_LocalNodeName;
|
use crate::global::GLOBAL_LocalNodeName;
|
||||||
use crate::global::get_global_bucket_monitor;
|
use crate::global::get_global_bucket_monitor;
|
||||||
use crate::set_disk::get_lock_acquire_timeout;
|
use crate::set_disk::get_lock_acquire_timeout;
|
||||||
use crate::store_api::{DeletedObject, HTTPRangeSpec, ObjectIO, ObjectInfo, ObjectOptions, ObjectToDelete, WalkOptions};
|
use crate::store_api::{
|
||||||
|
DeletedObject, HTTPRangeSpec, NamespaceLocking, ObjectIO, ObjectInfo, ObjectOptions, ObjectToDelete, WalkOptions,
|
||||||
|
};
|
||||||
use crate::{StorageAPI, new_object_layer_fn};
|
use crate::{StorageAPI, new_object_layer_fn};
|
||||||
use aws_sdk_s3::error::{ProvideErrorMetadata, SdkError};
|
use aws_sdk_s3::error::{ProvideErrorMetadata, SdkError};
|
||||||
use aws_sdk_s3::operation::head_object::{HeadObjectError, HeadObjectOutput};
|
use aws_sdk_s3::operation::head_object::{HeadObjectError, HeadObjectOutput};
|
||||||
@@ -700,7 +702,7 @@ impl ReplicationResyncer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(cancellation_token, storage))]
|
#[instrument(skip(cancellation_token, storage))]
|
||||||
pub async fn resync_bucket<S: StorageAPI>(
|
pub async fn resync_bucket<S: StorageAPI + NamespaceLocking>(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
cancellation_token: CancellationToken,
|
cancellation_token: CancellationToken,
|
||||||
storage: Arc<S>,
|
storage: Arc<S>,
|
||||||
@@ -1668,7 +1670,7 @@ pub async fn must_replicate(bucket: &str, object: &str, mopts: MustReplicateOpti
|
|||||||
dsc
|
dsc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn replicate_delete<S: StorageAPI>(dobj: DeletedObjectReplicationInfo, storage: Arc<S>) {
|
pub async fn replicate_delete<S: StorageAPI + NamespaceLocking>(dobj: DeletedObjectReplicationInfo, storage: Arc<S>) {
|
||||||
if dobj.delete_object.force_delete {
|
if dobj.delete_object.force_delete {
|
||||||
replicate_force_delete_to_targets(&dobj, storage).await;
|
replicate_force_delete_to_targets(&dobj, storage).await;
|
||||||
return;
|
return;
|
||||||
@@ -2170,7 +2172,10 @@ async fn replicate_delete_marker_purge_to_targets(bucket: &str, dobj: &DeletedOb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn replicate_force_delete_to_targets<S: StorageAPI>(dobj: &DeletedObjectReplicationInfo, storage: Arc<S>) {
|
async fn replicate_force_delete_to_targets<S: StorageAPI + NamespaceLocking>(
|
||||||
|
dobj: &DeletedObjectReplicationInfo,
|
||||||
|
storage: Arc<S>,
|
||||||
|
) {
|
||||||
let bucket = &dobj.bucket;
|
let bucket = &dobj.bucket;
|
||||||
let object_name = &dobj.delete_object.object_name;
|
let object_name = &dobj.delete_object.object_name;
|
||||||
|
|
||||||
|
|||||||
@@ -1220,8 +1220,8 @@ mod tests {
|
|||||||
use crate::store_api::{
|
use crate::store_api::{
|
||||||
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
||||||
HTTPRangeSpec, HealOperations, ListMultipartsInfo, ListObjectVersionsInfo, ListObjectsV2Info, ListOperations,
|
HTTPRangeSpec, HealOperations, ListMultipartsInfo, ListObjectVersionsInfo, ListObjectsV2Info, ListOperations,
|
||||||
MakeBucketOptions, MultipartInfo, MultipartOperations, MultipartUploadResult, ObjectIO, ObjectInfo, ObjectOperations,
|
MakeBucketOptions, MultipartInfo, MultipartOperations, MultipartUploadResult, NamespaceLocking, ObjectIO, ObjectInfo,
|
||||||
ObjectOptions, ObjectToDelete, PartInfo, PutObjReader, StorageAPI, WalkOptions,
|
ObjectOperations, ObjectOptions, ObjectToDelete, PartInfo, PutObjReader, StorageAPI, WalkOptions,
|
||||||
};
|
};
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use rustfs_config::audit::{AUDIT_AMQP_SUB_SYS, AUDIT_KAFKA_SUB_SYS, AUDIT_MQTT_SUB_SYS, AUDIT_WEBHOOK_SUB_SYS};
|
use rustfs_config::audit::{AUDIT_AMQP_SUB_SYS, AUDIT_KAFKA_SUB_SYS, AUDIT_MQTT_SUB_SYS, AUDIT_WEBHOOK_SUB_SYS};
|
||||||
@@ -1726,7 +1726,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl StorageAPI for LockingConfigStorage {
|
impl StorageAPI for LockingConfigStorage {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl NamespaceLocking for LockingConfigStorage {
|
||||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<rustfs_lock::NamespaceLockWrapper> {
|
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<rustfs_lock::NamespaceLockWrapper> {
|
||||||
self.set_disks.new_ns_lock(bucket, object).await
|
self.set_disks.new_ns_lock(bucket, object).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use crate::global::get_global_endpoints;
|
|||||||
use crate::pools::ListCallback;
|
use crate::pools::ListCallback;
|
||||||
use crate::set_disk::{SetDisks, get_lock_acquire_timeout};
|
use crate::set_disk::{SetDisks, get_lock_acquire_timeout};
|
||||||
use crate::store::ECStore;
|
use crate::store::ECStore;
|
||||||
use crate::store_api::{GetObjectReader, HTTPRangeSpec, ObjectIO, ObjectInfo, ObjectOperations, ObjectOptions};
|
use crate::store_api::{GetObjectReader, HTTPRangeSpec, NamespaceLocking, ObjectIO, ObjectInfo, ObjectOperations, ObjectOptions};
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use rand::RngExt as _;
|
use rand::RngExt as _;
|
||||||
use rustfs_filemeta::{FileInfo, MetaCacheEntries, MetaCacheEntry, MetadataResolutionParams};
|
use rustfs_filemeta::{FileInfo, MetaCacheEntries, MetaCacheEntry, MetadataResolutionParams};
|
||||||
@@ -651,7 +651,7 @@ impl RebalanceMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ECStore {
|
impl ECStore {
|
||||||
async fn save_rebalance_meta_with_merge<S: StorageAPI>(
|
async fn save_rebalance_meta_with_merge<S: StorageAPI + NamespaceLocking>(
|
||||||
&self,
|
&self,
|
||||||
pool: Arc<S>,
|
pool: Arc<S>,
|
||||||
local_snapshot: &RebalanceMeta,
|
local_snapshot: &RebalanceMeta,
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ use crate::{
|
|||||||
store_api::{
|
store_api::{
|
||||||
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
||||||
HTTPRangeSpec, HealOperations, ListMultipartsInfo, ListObjectsV2Info, ListOperations, MakeBucketOptions, MultipartInfo,
|
HTTPRangeSpec, HealOperations, ListMultipartsInfo, ListObjectsV2Info, ListOperations, MakeBucketOptions, MultipartInfo,
|
||||||
MultipartOperations, MultipartUploadResult, ObjectIO, ObjectInfo, ObjectOperations, PartInfo, PutObjReader, StorageAPI,
|
MultipartOperations, MultipartUploadResult, NamespaceLocking, ObjectIO, ObjectInfo, ObjectOperations, PartInfo,
|
||||||
|
PutObjReader, StorageAPI,
|
||||||
},
|
},
|
||||||
store_init::load_format_erasure,
|
store_init::load_format_erasure,
|
||||||
};
|
};
|
||||||
@@ -1586,7 +1587,10 @@ impl SetDisks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl StorageAPI for SetDisks {
|
impl StorageAPI for SetDisks {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl NamespaceLocking for SetDisks {
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
||||||
let set_lock = if is_dist_erasure().await {
|
let set_lock = if is_dist_erasure().await {
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ use crate::{
|
|||||||
store_api::{
|
store_api::{
|
||||||
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
||||||
HTTPRangeSpec, HealOperations, ListMultipartsInfo, ListObjectVersionsInfo, ListObjectsV2Info, ListOperations,
|
HTTPRangeSpec, HealOperations, ListMultipartsInfo, ListObjectVersionsInfo, ListObjectsV2Info, ListOperations,
|
||||||
MakeBucketOptions, MultipartInfo, MultipartOperations, MultipartUploadResult, ObjectIO, ObjectInfo, ObjectOperations,
|
MakeBucketOptions, MultipartInfo, MultipartOperations, MultipartUploadResult, NamespaceLocking, ObjectIO, ObjectInfo,
|
||||||
ObjectOptions, ObjectToDelete, PartInfo, PutObjReader, StorageAPI,
|
ObjectOperations, ObjectOptions, ObjectToDelete, PartInfo, PutObjReader, StorageAPI,
|
||||||
},
|
},
|
||||||
store_init::{check_format_erasure_values, get_format_erasure_in_quorum, load_format_erasure_all, save_format_file},
|
store_init::{check_format_erasure_values, get_format_erasure_in_quorum, load_format_erasure_all, save_format_file},
|
||||||
};
|
};
|
||||||
@@ -897,7 +897,10 @@ impl HealOperations for Sets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl StorageAPI for Sets {
|
impl StorageAPI for Sets {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl NamespaceLocking for Sets {
|
||||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
||||||
self.disk_set[0].new_ns_lock(bucket, object).await
|
self.disk_set[0].new_ns_lock(bucket, object).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ use crate::{
|
|||||||
store_api::{
|
store_api::{
|
||||||
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, DeletedObject, GetObjectReader,
|
||||||
HTTPRangeSpec, HealOperations, ListObjectsV2Info, ListOperations, MakeBucketOptions, MultipartOperations,
|
HTTPRangeSpec, HealOperations, ListObjectsV2Info, ListOperations, MakeBucketOptions, MultipartOperations,
|
||||||
MultipartUploadResult, ObjectInfo, ObjectOperations, ObjectOptions, ObjectToDelete, PartInfo, PutObjReader, StorageAPI,
|
MultipartUploadResult, NamespaceLocking, ObjectInfo, ObjectOperations, ObjectOptions, ObjectToDelete, PartInfo,
|
||||||
|
PutObjReader, StorageAPI,
|
||||||
},
|
},
|
||||||
store_init,
|
store_init,
|
||||||
};
|
};
|
||||||
@@ -703,7 +704,10 @@ impl HealOperations for ECStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl StorageAPI for ECStore {
|
impl StorageAPI for ECStore {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl NamespaceLocking for ECStore {
|
||||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
||||||
self.handle_new_ns_lock(bucket, object).await
|
self.handle_new_ns_lock(bucket, object).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,25 +179,12 @@ pub trait NamespaceLocking: Send + Sync + Debug + 'static {
|
|||||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper>;
|
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<T> NamespaceLocking for T
|
|
||||||
where
|
|
||||||
T: StorageAPI + ?Sized + 'static,
|
|
||||||
{
|
|
||||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
|
||||||
StorageAPI::new_ns_lock(self, bucket, object).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unified storage API combining all operation groups.
|
/// Unified storage API combining all operation groups.
|
||||||
///
|
///
|
||||||
/// Consumers can depend on specific sub-traits (e.g., `BucketOperations`)
|
/// Consumers can depend on specific sub-traits (e.g., `BucketOperations`)
|
||||||
/// when they don't need the full API surface.
|
/// when they don't need the full API surface.
|
||||||
#[async_trait::async_trait]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub trait StorageAPI:
|
pub trait StorageAPI:
|
||||||
ObjectIO + BucketOperations + ObjectOperations + ListOperations + MultipartOperations + HealOperations + Debug
|
ObjectIO + BucketOperations + ObjectOperations + ListOperations + MultipartOperations + HealOperations + Debug
|
||||||
{
|
{
|
||||||
// RUSTFS_COMPAT_TODO(API-012): keep old StorageAPI lock callers compiling while namespace-lock-only consumers migrate. Remove after namespace-lock-only consumers depend on NamespaceLocking and StorageAPI no longer owns namespace locking.
|
|
||||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use rustfs_ecstore::{disk::DiskStore, error::Error, store::ECStore};
|
use rustfs_ecstore::{
|
||||||
|
disk::DiskStore,
|
||||||
|
error::Error,
|
||||||
|
store::ECStore,
|
||||||
|
store_api::{NamespaceLocking, StorageAPI},
|
||||||
|
};
|
||||||
use rustfs_storage_api::StorageAdminApi;
|
use rustfs_storage_api::StorageAdminApi;
|
||||||
|
|
||||||
fn storage_admin_api_type_name<T>() -> &'static str
|
fn storage_admin_api_type_name<T>() -> &'static str
|
||||||
@@ -27,7 +32,19 @@ where
|
|||||||
std::any::type_name::<T>()
|
std::any::type_name::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn storage_api_with_namespace_locking_type_name<T>() -> &'static str
|
||||||
|
where
|
||||||
|
T: StorageAPI + NamespaceLocking,
|
||||||
|
{
|
||||||
|
std::any::type_name::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ecstore_implements_storage_admin_api_contract() {
|
fn ecstore_implements_storage_admin_api_contract() {
|
||||||
assert!(storage_admin_api_type_name::<ECStore>().ends_with("::ECStore"));
|
assert!(storage_admin_api_type_name::<ECStore>().ends_with("::ECStore"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ecstore_implements_storage_api_and_namespace_locking_contracts() {
|
||||||
|
assert!(storage_api_with_namespace_locking_type_name::<ECStore>().ends_with("::ECStore"));
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ use rustfs_config::{
|
|||||||
ENV_SCANNER_CYCLE_MAX_OBJECTS,
|
ENV_SCANNER_CYCLE_MAX_OBJECTS,
|
||||||
};
|
};
|
||||||
use rustfs_config::{ENV_SCANNER_CYCLE, ENV_SCANNER_SPEED, ENV_SCANNER_START_DELAY_SECS};
|
use rustfs_config::{ENV_SCANNER_CYCLE, ENV_SCANNER_SPEED, ENV_SCANNER_START_DELAY_SECS};
|
||||||
use rustfs_ecstore::StorageAPI as _;
|
|
||||||
use rustfs_ecstore::bucket::lifecycle::lifecycle::Lifecycle as _;
|
use rustfs_ecstore::bucket::lifecycle::lifecycle::Lifecycle as _;
|
||||||
use rustfs_ecstore::bucket::metadata_sys::{get_lifecycle_config, get_replication_config};
|
use rustfs_ecstore::bucket::metadata_sys::{get_lifecycle_config, get_replication_config};
|
||||||
use rustfs_ecstore::bucket::replication::ReplicationConfigurationExt as _;
|
use rustfs_ecstore::bucket::replication::ReplicationConfigurationExt as _;
|
||||||
@@ -46,7 +45,7 @@ use rustfs_ecstore::disk::RUSTFS_META_BUCKET;
|
|||||||
use rustfs_ecstore::error::Error as EcstoreError;
|
use rustfs_ecstore::error::Error as EcstoreError;
|
||||||
use rustfs_ecstore::global::is_erasure_sd;
|
use rustfs_ecstore::global::is_erasure_sd;
|
||||||
use rustfs_ecstore::store::ECStore;
|
use rustfs_ecstore::store::ECStore;
|
||||||
use rustfs_ecstore::store_api::BucketOperations;
|
use rustfs_ecstore::store_api::{BucketOperations, NamespaceLocking as _};
|
||||||
use rustfs_storage_api::BucketOptions;
|
use rustfs_storage_api::BucketOptions;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|||||||
@@ -184,11 +184,14 @@ mod tests {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
let token = budget.token();
|
||||||
|
tokio::time::timeout(Duration::from_secs(1), token.cancelled())
|
||||||
|
.await
|
||||||
|
.expect("runtime budget did not cancel child token");
|
||||||
|
|
||||||
assert!(budget.budget_elapsed());
|
assert!(budget.budget_elapsed());
|
||||||
assert_eq!(budget.reason(), Some(ScannerCycleBudgetReason::Runtime));
|
assert_eq!(budget.reason(), Some(ScannerCycleBudgetReason::Runtime));
|
||||||
assert!(budget.token().is_cancelled());
|
assert!(token.is_cancelled());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -18,12 +18,6 @@ for later deletion.
|
|||||||
- Why: legacy KMS create-key and key-status admin grants must keep working during the dedicated KMS policy migration.
|
- Why: legacy KMS create-key and key-status admin grants must keep working during the dedicated KMS policy migration.
|
||||||
- Removal condition: remove after KMS admin clients and built-in policies use `kms:Configure`, `kms:DescribeKey`, and `kms:ListKeys`.
|
- Removal condition: remove after KMS admin clients and built-in policies use `kms:Configure`, `kms:DescribeKey`, and `kms:ListKeys`.
|
||||||
- Status: planned cleanup.
|
- Status: planned cleanup.
|
||||||
- `RUSTFS_COMPAT_TODO(API-012)`
|
|
||||||
- Task: `API-012`
|
|
||||||
- File: `crates/ecstore/src/store_api/traits.rs`
|
|
||||||
- Why: old `StorageAPI::new_ns_lock` callers must keep compiling while namespace-lock-only consumers migrate to NamespaceLocking.
|
|
||||||
- Removal condition: remove after all namespace-lock-only consumers depend on NamespaceLocking and StorageAPI no longer owns namespace lock capability.
|
|
||||||
- Status: planned cleanup.
|
|
||||||
|
|
||||||
## Review Checklist
|
## Review Checklist
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
## Current Context
|
## Current Context
|
||||||
|
|
||||||
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
|
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
|
||||||
- Branch: `overtrue/arch-storage-api-dto-compat-cleanup`
|
- Branch: `overtrue/arch-storage-api-namespace-lock-cleanup`
|
||||||
- Baseline: `origin/main` at `0a987d870b3dca248bea8d4872568a25e235d917`
|
- Baseline: `origin/main` at `7146c893cbbb84a0d5332a7a8a79b70d57191bcc`
|
||||||
- PR type for this branch: `api-extraction`
|
- PR type for this branch: `api-extraction`
|
||||||
- Runtime behavior changes: none intended.
|
- Runtime behavior changes: none intended.
|
||||||
- Rust code changes: migrate remaining in-repo bucket DTO consumers to
|
- Rust code changes: migrate remaining namespace-lock consumers to
|
||||||
`rustfs_storage_api` and remove the temporary API-003 public ECStore
|
`NamespaceLocking`, implement namespace locking directly on ECStore storage
|
||||||
`store_api` bucket DTO re-export.
|
types, and remove the temporary namespace-lock compatibility method from the
|
||||||
|
full storage trait.
|
||||||
- CI/script changes: none.
|
- CI/script changes: none.
|
||||||
- Docs changes: remove the API-003 cleanup-register entry and record the
|
- Docs changes: remove the API-012 cleanup-register entry and record the
|
||||||
completed storage API DTO compatibility cleanup in progress notes.
|
completed namespace-lock compatibility cleanup in progress notes.
|
||||||
|
|
||||||
## Phase 0 Tasks
|
## Phase 0 Tasks
|
||||||
|
|
||||||
@@ -273,9 +274,8 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
- Completed slice: `rustfs/rustfs#3340` removed duplicate admin-read methods
|
- Completed slice: `rustfs/rustfs#3340` removed duplicate admin-read methods
|
||||||
from the old `StorageAPI` trait and its ECStore/Sets/SetDisks/test
|
from the old `StorageAPI` trait and its ECStore/Sets/SetDisks/test
|
||||||
implementations after API-007 migrated their consumers.
|
implementations after API-007 migrated their consumers.
|
||||||
- Acceptance: old `StorageAPI` keeps storage operation traits and
|
- Acceptance: old `StorageAPI` keeps storage operation traits while admin
|
||||||
`new_ns_lock`, while admin inventory surfaces live only on
|
inventory surfaces live only on `StorageAdminApi`.
|
||||||
`StorageAdminApi`.
|
|
||||||
|
|
||||||
- [x] `API-009` Narrow metadata helper storage bounds.
|
- [x] `API-009` Narrow metadata helper storage bounds.
|
||||||
- Completed slice: `rustfs/rustfs#3343` narrowed server config, tier config,
|
- Completed slice: `rustfs/rustfs#3343` narrowed server config, tier config,
|
||||||
@@ -315,23 +315,26 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
|
|
||||||
- [x] `API-012` Narrow table catalog object backend bounds.
|
- [x] `API-012` Narrow table catalog object backend bounds.
|
||||||
- Completed slice: `rustfs/rustfs#3350` added a narrow `NamespaceLocking`
|
- Completed slice: `rustfs/rustfs#3350` added a narrow `NamespaceLocking`
|
||||||
operation-group trait as a compatibility facade over
|
operation-group trait as a compatibility facade, then narrowed
|
||||||
`StorageAPI::new_ns_lock`, then narrowed `EcStoreTableCatalogObjectBackend`
|
`EcStoreTableCatalogObjectBackend` from full `StorageAPI` to `ObjectIO`,
|
||||||
from full `StorageAPI` to `ObjectIO`, `ObjectOperations`,
|
`ObjectOperations`, `ListOperations`, and `NamespaceLocking`.
|
||||||
`ListOperations`, and `NamespaceLocking`.
|
- Cleanup slice: migrate the remaining scanner leader-lock and self-copy
|
||||||
|
object use-case namespace-lock consumers to `NamespaceLocking`, implement
|
||||||
|
namespace locking directly on ECStore storage types, and remove the
|
||||||
|
temporary namespace-lock compatibility method from the full storage trait
|
||||||
|
and cleanup register entry.
|
||||||
- Acceptance: table catalog object backend contracts express the actual
|
- Acceptance: table catalog object backend contracts express the actual
|
||||||
object read/write, metadata/delete, list, and namespace-lock capabilities
|
object read/write, metadata/delete, list, and namespace-lock capabilities
|
||||||
they need, while table catalog store logic and lock behavior remain
|
they need; namespace-lock consumers depend on `NamespaceLocking` instead of
|
||||||
unchanged.
|
full `StorageAPI`; and storage lock behavior remains unchanged.
|
||||||
- Must preserve: table catalog object paths, metadata pointer semantics,
|
- Must preserve: table catalog object paths, metadata pointer semantics,
|
||||||
optimistic write preconditions, object listing pagination, missing-object
|
optimistic write preconditions, object listing pagination, missing-object
|
||||||
handling, namespace write-lock acquisition, `StorageAPI::new_ns_lock`
|
handling, namespace write-lock acquisition, object APIs,
|
||||||
compatibility, object APIs, scanner/heal/replication/config persistence,
|
scanner/heal/replication/config persistence, and storage hot paths.
|
||||||
and storage hot paths.
|
- Risk defense: do not move traits into `rustfs-storage-api`, do not change
|
||||||
- Risk defense: do not remove `StorageAPI::new_ns_lock`, do not move traits
|
lock implementation code, do not alter table catalog method bodies, and do
|
||||||
into `rustfs-storage-api`, do not change lock implementation code, do not
|
not retain stale API-012 compatibility markers after the old `StorageAPI`
|
||||||
alter table catalog method bodies, and track the retained old lock method
|
lock method is removed.
|
||||||
with `RUSTFS_COMPAT_TODO(API-012)`.
|
|
||||||
- Verification: focused compile/tests, migration guards, Rust risk scan, and
|
- Verification: focused compile/tests, migration guards, Rust risk scan, and
|
||||||
required quality/architecture, migration-preservation, and
|
required quality/architecture, migration-preservation, and
|
||||||
testing/verification review passed.
|
testing/verification review passed.
|
||||||
@@ -359,43 +362,35 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
|
|
||||||
## Next PRs
|
## Next PRs
|
||||||
|
|
||||||
1. `api-extraction`: continue API-012 namespace-lock-only cleanup after all
|
1. `security-change`: make Local KMS unsafe defaults explicit development
|
||||||
callers that only need locking depend on `NamespaceLocking`.
|
|
||||||
2. `security-change`: make Local KMS unsafe defaults explicit development
|
|
||||||
opt-ins or production failures in KMSD-002.
|
opt-ins or production failures in KMSD-002.
|
||||||
3. `security-change`: make Vault unsafe defaults explicit development opt-ins
|
2. `security-change`: make Vault unsafe defaults explicit development opt-ins
|
||||||
or production failures in KMSD-003.
|
or production failures in KMSD-003.
|
||||||
|
|
||||||
## Pre-Push Review Log
|
## Pre-Push Review Log
|
||||||
|
|
||||||
| Expert | Status | Notes |
|
| Expert | Status | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Quality/architecture | pass | Confirmed the diff only removes the temporary API-003 public ECStore bucket DTO re-export and migrates remaining in-repo external consumers to `rustfs_storage_api`; bucket operation traits and runtime control flow remain unchanged. |
|
| Quality/architecture | pass | Confirmed the diff removes the temporary namespace-lock method from the full `StorageAPI` trait while keeping `NamespaceLocking` as the narrow operation-group contract. |
|
||||||
| Migration preservation | pass | Confirmed ECStore keeps crate-private DTO visibility for its own implementation while no external in-repo consumer uses the old public `rustfs_ecstore::store_api` DTO path. |
|
| Migration preservation | pass | Confirmed ECStore, Sets, SetDisks, scanner leader locking, self-copy locking, replication resync, rebalance metadata, and config test storage retain their existing lock behavior and only narrow trait dependencies. |
|
||||||
| Testing/verification | pass | Confirmed focused compile/tests, rustfs all-targets compile, heal/scanner test target compile, migration guards, dependency guard, source old-path scan, and added-line Rust risk scan cover this cleanup; full pre-commit is skipped under the current larger-granularity instruction. |
|
| Testing/verification | pass | Confirmed focused compile/tests, migration guards, dependency guard, old-path scan, and added-line Rust risk scan cover this cleanup; full pre-commit is skipped under the current larger-granularity instruction. |
|
||||||
|
|
||||||
## Verification Notes
|
## Verification Notes
|
||||||
|
|
||||||
Passed after rebasing onto `0a987d870b3dca248bea8d4872568a25e235d917`:
|
Passed on `7146c893cbbb84a0d5332a7a8a79b70d57191bcc`:
|
||||||
- `cargo check -p rustfs-storage-api -p rustfs-ecstore -p rustfs-heal -p rustfs-scanner -p rustfs-obs -p rustfs-protocols --lib`.
|
- `cargo check -p rustfs-ecstore -p rustfs-scanner -p rustfs --all-targets`.
|
||||||
- `cargo check -p rustfs --all-targets`.
|
- `cargo test -p rustfs-ecstore --test storage_api_compat_test`; 2 passed.
|
||||||
- `cargo test -p rustfs-storage-api --lib`; 7 passed.
|
- `cargo test -p rustfs-ecstore --lib new_ns_lock`; 2 passed.
|
||||||
- `cargo test -p rustfs-ecstore --test storage_api_compat_test`; 1 passed.
|
|
||||||
- `cargo test -p rustfs-heal --tests --no-run`.
|
|
||||||
- `cargo test -p rustfs-scanner --tests --no-run`.
|
- `cargo test -p rustfs-scanner --tests --no-run`.
|
||||||
- `cargo test -p rustfs-protocols --lib swift`; 0 matched, lib test target
|
- `cargo test -p rustfs --lib --no-run`.
|
||||||
compiled and ran successfully.
|
|
||||||
- `cargo test -p rustfs-obs --lib stats_collector`; 14 passed.
|
|
||||||
- `cargo fmt --all --check`.
|
- `cargo fmt --all --check`.
|
||||||
- `./scripts/check_architecture_migration_rules.sh`.
|
- `./scripts/check_architecture_migration_rules.sh`.
|
||||||
- `./scripts/check_layer_dependencies.sh`.
|
- `./scripts/check_layer_dependencies.sh`.
|
||||||
- `./scripts/check_metrics_migration_refs.sh`.
|
- `./scripts/check_metrics_migration_refs.sh`.
|
||||||
- `git diff --check`.
|
- `git diff --check`.
|
||||||
- API-003 source old-path and marker scan found no
|
- API-012 old-path and marker scan found no stale API-012 compatibility marker
|
||||||
`RUSTFS_COMPAT_TODO(API-003)` or
|
or old full-storage-trait namespace-lock method references in `crates`,
|
||||||
`rustfs_ecstore::store_api::{BucketInfo, BucketOptions,
|
`rustfs/src`, or architecture docs.
|
||||||
DeleteBucketOptions, MakeBucketOptions, SRBucketDeleteOp}` matches in
|
|
||||||
`crates/**/*.rs` or `rustfs/src`.
|
|
||||||
- Added-line Rust risk scan found no new production `unwrap`/`expect`, lossy
|
- Added-line Rust risk scan found no new production `unwrap`/`expect`, lossy
|
||||||
numeric casts, stringly public errors, boxed dynamic errors, stdout/stderr
|
numeric casts, stringly public errors, boxed dynamic errors, stdout/stderr
|
||||||
printing, or relaxed atomic ordering.
|
printing, or relaxed atomic ordering.
|
||||||
@@ -403,20 +398,21 @@ Passed after rebasing onto `0a987d870b3dca248bea8d4872568a25e235d917`:
|
|||||||
Notes:
|
Notes:
|
||||||
- Full pre-commit may be skipped if focused tests, compile checks, and guards
|
- Full pre-commit may be skipped if focused tests, compile checks, and guards
|
||||||
pass, per the current instruction to increase PR granularity.
|
pass, per the current instruction to increase PR granularity.
|
||||||
- This slice removes only the old API-003 public bucket DTO re-export.
|
- This slice removes only the old namespace-lock method from the full storage
|
||||||
ECStore retains bucket operation traits, object/listing DTOs, storage
|
trait. ECStore retains bucket, object, listing, multipart, heal, replication,
|
||||||
implementation wiring, and crate-private access to the storage API bucket
|
rebalance, scanner, config persistence, and namespace-lock implementation
|
||||||
DTOs for its own trait implementations.
|
behavior.
|
||||||
- The old public `rustfs_ecstore::store_api` bucket DTO path is no longer
|
- Consumers that need namespace locks should depend on `NamespaceLocking`
|
||||||
available after this cleanup. Consumers must use `rustfs_storage_api`.
|
instead of the full storage trait.
|
||||||
|
|
||||||
## Handoff Notes
|
## Handoff Notes
|
||||||
|
|
||||||
- Keep this API-003 cleanup slice as an `api-extraction` PR that only removes
|
- Keep this API-012 cleanup slice as an `api-extraction` PR that only removes
|
||||||
the temporary public ECStore bucket DTO compatibility re-export, migrates
|
the temporary namespace-lock method from the full storage trait, migrates
|
||||||
remaining in-repo external imports, and deletes the cleanup-register entry.
|
namespace-lock consumers to `NamespaceLocking`, and deletes the
|
||||||
- Do not move `ObjectOptions`, `ObjectInfo`, reader types, multipart DTOs,
|
cleanup-register entry.
|
||||||
list result DTOs, storage traits, bucket operation logic, storage runtime
|
- Do not move storage traits, bucket/object/list/multipart/heal operation
|
||||||
wiring, route behavior, or storage persistence logic in this PR.
|
logic, lock implementation code, storage runtime wiring, route behavior, or
|
||||||
- Do not add temporary compatibility code unless a matching
|
storage persistence logic in this PR.
|
||||||
`RUSTFS_COMPAT_TODO(<task-id>)` marker and cleanup-register entry are added.
|
- Do not add temporary compatibility code unless a matching task marker and
|
||||||
|
cleanup-register entry are added.
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ use rustfs_ecstore::new_object_layer_fn;
|
|||||||
use rustfs_ecstore::rio::{DynReader, HashReader, WritePlan, wrap_reader};
|
use rustfs_ecstore::rio::{DynReader, HashReader, WritePlan, wrap_reader};
|
||||||
use rustfs_ecstore::set_disk::{get_lock_acquire_timeout, is_valid_storage_class};
|
use rustfs_ecstore::set_disk::{get_lock_acquire_timeout, is_valid_storage_class};
|
||||||
use rustfs_ecstore::store_api::{
|
use rustfs_ecstore::store_api::{
|
||||||
HTTPRangeSpec, ObjectIO, ObjectInfo, ObjectOperations, ObjectOptions, ObjectToDelete, PutObjReader, StorageAPI,
|
HTTPRangeSpec, NamespaceLocking, ObjectIO, ObjectInfo, ObjectOperations, ObjectOptions, ObjectToDelete, PutObjReader,
|
||||||
};
|
};
|
||||||
use rustfs_filemeta::{
|
use rustfs_filemeta::{
|
||||||
REPLICATE_INCOMING_DELETE, ReplicateDecision, ReplicateTargetDecision, ReplicationState, ReplicationStatusType,
|
REPLICATE_INCOMING_DELETE, ReplicateDecision, ReplicateTargetDecision, ReplicationState, ReplicationStatusType,
|
||||||
@@ -730,7 +730,7 @@ fn copy_namespace_lock_error(bucket: &str, object: &str, mode: &'static str, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn acquire_self_copy_namespace_lock<S: StorageAPI + ?Sized>(
|
async fn acquire_self_copy_namespace_lock<S: NamespaceLocking + ?Sized>(
|
||||||
store: &S,
|
store: &S,
|
||||||
bucket: &str,
|
bucket: &str,
|
||||||
object: &str,
|
object: &str,
|
||||||
|
|||||||
Reference in New Issue
Block a user