mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
refactor: move heal and namespace contracts (#3560)
This commit is contained in:
@@ -21,11 +21,11 @@ use crate::bucket::utils::{deserialize, is_meta_bucketname};
|
||||
use crate::error::{Error, Result, is_err_bucket_not_found};
|
||||
use crate::global::{GLOBAL_Endpoints, is_dist_erasure, is_erasure, resolve_object_store_handle};
|
||||
use crate::store::ECStore;
|
||||
use crate::store_api::HealOperations as _;
|
||||
use futures::future::join_all;
|
||||
use lazy_static::lazy_static;
|
||||
use rustfs_common::heal_channel::HealOpts;
|
||||
use rustfs_policy::policy::BucketPolicy;
|
||||
use rustfs_storage_api::HealOperations as _;
|
||||
use s3s::dto::ReplicationConfiguration;
|
||||
use s3s::dto::{
|
||||
AccelerateConfiguration, BucketLifecycleConfiguration, BucketLoggingStatus, CORSConfiguration, NotificationConfiguration,
|
||||
|
||||
@@ -1217,7 +1217,7 @@ mod tests {
|
||||
use crate::error::{Error, Result};
|
||||
use crate::global::{is_dist_erasure, is_erasure, is_erasure_sd, update_erasure_type};
|
||||
use crate::set_disk::SetDisks;
|
||||
use crate::store_api::{GetObjectReader, NamespaceLocking, ObjectInfo, ObjectOptions, PutObjReader};
|
||||
use crate::store_api::{GetObjectReader, ObjectInfo, ObjectOptions, PutObjReader};
|
||||
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::notify::{
|
||||
@@ -1231,6 +1231,7 @@ mod tests {
|
||||
use rustfs_lock::client::LockClient;
|
||||
use rustfs_lock::client::local::LocalClient;
|
||||
use rustfs_lock::{LockError, LockInfo, LockResponse, LockStats};
|
||||
use rustfs_storage_api::NamespaceLocking as _;
|
||||
use rustfs_storage_api::{HTTPRangeSpec, StorageAdminApi};
|
||||
use serde_json::Value;
|
||||
use serial_test::serial;
|
||||
@@ -1459,7 +1460,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl NamespaceLocking for LockingConfigStorage {
|
||||
impl rustfs_storage_api::NamespaceLocking for LockingConfigStorage {
|
||||
type Error = crate::error::Error;
|
||||
type NamespaceLock = 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
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ use crate::error::{
|
||||
use crate::notification_sys::get_global_notification_sys;
|
||||
use crate::resolve_object_store_handle;
|
||||
use crate::set_disk::SetDisks;
|
||||
use crate::store_api::{GetObjectReader, HealOperations, ObjectOptions};
|
||||
use crate::store_api::{GetObjectReader, ObjectOptions};
|
||||
use crate::{global::GLOBAL_LifecycleSys, sets::Sets, store::ECStore};
|
||||
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
||||
use futures::{StreamExt, future::BoxFuture, stream::FuturesUnordered};
|
||||
@@ -51,7 +51,8 @@ use rustfs_common::heal_channel::HealOpts;
|
||||
use rustfs_concurrency::workers::Workers;
|
||||
use rustfs_filemeta::{FileInfoVersions, MetaCacheEntries, MetaCacheEntry, MetadataResolutionParams};
|
||||
use rustfs_storage_api::{
|
||||
BucketOperations, BucketOptions, MakeBucketOptions, ObjectIO as _, ObjectOperations as _, StorageAdminApi,
|
||||
BucketOperations, BucketOptions, HealOperations as _, MakeBucketOptions, ObjectIO as _, ObjectOperations as _,
|
||||
StorageAdminApi,
|
||||
};
|
||||
use rustfs_utils::path::{encode_dir_object, path_join, path_to_bucket_object, path_to_bucket_object_with_base_path};
|
||||
use s3s::dto::{BucketLifecycleConfiguration, DefaultRetention, ReplicationConfiguration};
|
||||
|
||||
@@ -52,8 +52,8 @@ use crate::{
|
||||
event_notification::{EventArgs, send_event},
|
||||
global::{GLOBAL_LOCAL_DISK_MAP, GLOBAL_LOCAL_DISK_SET_DRIVES, get_global_deployment_id, is_dist_erasure},
|
||||
store_api::{
|
||||
DeletedObject, GetObjectReader, HealOperations, ListObjectsV2Info, MultipartOperations, NamespaceLocking, ObjectIO,
|
||||
ObjectInfo, ObjectOperations, PutObjReader,
|
||||
DeletedObject, GetObjectReader, ListObjectsV2Info, MultipartOperations, ObjectIO, ObjectInfo, ObjectOperations,
|
||||
PutObjReader,
|
||||
},
|
||||
store_init::load_format_erasure,
|
||||
};
|
||||
@@ -90,7 +90,7 @@ use rustfs_storage_api::{
|
||||
BucketInfo, BucketOperations, BucketOptions, CompletePart, DeleteBucketOptions, ListMultipartsInfo, ListPartsInfo,
|
||||
MakeBucketOptions, MultipartInfo, MultipartUploadResult, PartInfo,
|
||||
};
|
||||
use rustfs_storage_api::{MultipartOperations as _, ObjectIO as _, ObjectOperations as _};
|
||||
use rustfs_storage_api::{MultipartOperations as _, NamespaceLocking as _, ObjectIO as _, ObjectOperations as _};
|
||||
use rustfs_utils::http::headers::AMZ_OBJECT_TAGGING;
|
||||
use rustfs_utils::http::headers::AMZ_STORAGE_CLASS;
|
||||
use rustfs_utils::http::headers::{
|
||||
@@ -1729,7 +1729,10 @@ impl SetDisks {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl NamespaceLocking for SetDisks {
|
||||
impl rustfs_storage_api::NamespaceLocking for SetDisks {
|
||||
type Error = Error;
|
||||
type NamespaceLock = NamespaceLockWrapper;
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
||||
let set_lock = if is_dist_erasure().await {
|
||||
@@ -4144,7 +4147,11 @@ impl rustfs_storage_api::MultipartOperations for SetDisks {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl HealOperations for SetDisks {
|
||||
impl rustfs_storage_api::HealOperations for SetDisks {
|
||||
type Error = Error;
|
||||
type HealResultItem = HealResultItem;
|
||||
type HealOptions = HealOpts;
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn heal_format(&self, _dry_run: bool) -> Result<(HealResultItem, Option<Error>)> {
|
||||
unimplemented!()
|
||||
@@ -5019,7 +5026,7 @@ mod tests {
|
||||
use rustfs_filemeta::ReplicationState;
|
||||
use rustfs_lock::client::local::LocalClient;
|
||||
use rustfs_lock::{LockError, LockInfo, LockResponse, LockStats};
|
||||
use rustfs_storage_api::{CompletePart, ObjectOperations as _};
|
||||
use rustfs_storage_api::{CompletePart, NamespaceLocking as _, ObjectOperations as _};
|
||||
use serial_test::serial;
|
||||
use std::collections::HashMap;
|
||||
use tempfile::TempDir;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
use super::*;
|
||||
use rustfs_config::{DEFAULT_OBJECT_ZERO_COPY_ENABLE, ENV_OBJECT_ZERO_COPY_ENABLE};
|
||||
use rustfs_storage_api::NamespaceLocking as _;
|
||||
|
||||
impl SetDisks {
|
||||
#[tracing::instrument(skip(self, opts), fields(bucket = %bucket, object = %object, version_id = %version_id))]
|
||||
|
||||
@@ -28,8 +28,8 @@ use crate::{
|
||||
global::{GLOBAL_LOCAL_DISK_SET_DRIVES, get_global_lock_clients, is_dist_erasure},
|
||||
set_disk::SetDisks,
|
||||
store_api::{
|
||||
DeletedObject, GetObjectReader, HealOperations, ListObjectVersionsInfo, ListObjectsV2Info, NamespaceLocking, ObjectInfo,
|
||||
ObjectOptions, ObjectToDelete, PutObjReader,
|
||||
DeletedObject, GetObjectReader, ListObjectVersionsInfo, ListObjectsV2Info, ObjectInfo, ObjectOptions, ObjectToDelete,
|
||||
PutObjReader,
|
||||
},
|
||||
store_init::{check_format_erasure_values, get_format_erasure_in_quorum, load_format_erasure_all, save_format_file},
|
||||
};
|
||||
@@ -826,7 +826,11 @@ impl rustfs_storage_api::MultipartOperations for Sets {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl HealOperations for Sets {
|
||||
impl rustfs_storage_api::HealOperations for Sets {
|
||||
type Error = Error;
|
||||
type HealResultItem = HealResultItem;
|
||||
type HealOptions = HealOpts;
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn heal_format(&self, dry_run: bool) -> Result<(HealResultItem, Option<Error>)> {
|
||||
let (disks, _) = init_storage_disks_with_errors(
|
||||
@@ -938,7 +942,10 @@ impl HealOperations for Sets {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl NamespaceLocking for Sets {
|
||||
impl rustfs_storage_api::NamespaceLocking for Sets {
|
||||
type Error = Error;
|
||||
type NamespaceLock = NamespaceLockWrapper;
|
||||
|
||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
||||
self.disk_set[0].new_ns_lock(bucket, object).await
|
||||
}
|
||||
|
||||
@@ -62,10 +62,7 @@ use crate::{
|
||||
endpoints::EndpointServerPools,
|
||||
rpc::S3PeerSys,
|
||||
sets::Sets,
|
||||
store_api::{
|
||||
DeletedObject, GetObjectReader, HealOperations, ListObjectsV2Info, NamespaceLocking, ObjectInfo, ObjectOptions,
|
||||
ObjectToDelete, PutObjReader,
|
||||
},
|
||||
store_api::{DeletedObject, GetObjectReader, ListObjectsV2Info, ObjectInfo, ObjectOptions, ObjectToDelete, PutObjReader},
|
||||
store_init,
|
||||
};
|
||||
use futures::future::join_all;
|
||||
@@ -708,7 +705,11 @@ impl rustfs_storage_api::MultipartOperations for ECStore {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl HealOperations for ECStore {
|
||||
impl rustfs_storage_api::HealOperations for ECStore {
|
||||
type Error = Error;
|
||||
type HealResultItem = HealResultItem;
|
||||
type HealOptions = HealOpts;
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn heal_format(&self, dry_run: bool) -> Result<(HealResultItem, Option<Error>)> {
|
||||
self.handle_heal_format(dry_run).await
|
||||
@@ -741,7 +742,10 @@ impl HealOperations for ECStore {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl NamespaceLocking for ECStore {
|
||||
impl rustfs_storage_api::NamespaceLocking for ECStore {
|
||||
type Error = Error;
|
||||
type NamespaceLock = NamespaceLockWrapper;
|
||||
|
||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper> {
|
||||
self.handle_new_ns_lock(bucket, object).await
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::bucket::{
|
||||
};
|
||||
use crate::global::get_global_bucket_monitor;
|
||||
use crate::set_disk::get_lock_acquire_timeout;
|
||||
use rustfs_storage_api::NamespaceLocking as _;
|
||||
|
||||
fn should_override_created_from_metadata(created: OffsetDateTime) -> bool {
|
||||
created != OffsetDateTime::UNIX_EPOCH
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use super::*;
|
||||
use rustfs_storage_api::HealOperations as _;
|
||||
|
||||
const LOG_COMPONENT_ECSTORE: &str = "ecstore";
|
||||
const LOG_SUBSYSTEM_HEAL: &str = "heal";
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
use super::*;
|
||||
use crate::config::get_global_storage_class;
|
||||
use rustfs_storage_api::{ObjectOperations as _, StorageAdminApi};
|
||||
use rustfs_storage_api::{NamespaceLocking as _, ObjectOperations as _, StorageAdminApi};
|
||||
|
||||
struct LatestObjectInfoCandidate {
|
||||
info: Option<ObjectInfo>,
|
||||
|
||||
@@ -131,24 +131,27 @@ impl<T> MultipartOperations for T where
|
||||
}
|
||||
|
||||
/// Healing and repair operations.
|
||||
#[async_trait::async_trait]
|
||||
pub trait HealOperations: Send + Sync + Debug {
|
||||
async fn heal_format(&self, dry_run: bool) -> Result<(HealResultItem, Option<Error>)>;
|
||||
async fn heal_bucket(&self, bucket: &str, opts: &HealOpts) -> Result<HealResultItem>;
|
||||
async fn heal_object(
|
||||
&self,
|
||||
bucket: &str,
|
||||
object: &str,
|
||||
version_id: &str,
|
||||
opts: &HealOpts,
|
||||
) -> Result<(HealResultItem, Option<Error>)>;
|
||||
async fn get_pool_and_set(&self, id: &str) -> Result<(Option<usize>, Option<usize>, Option<usize>)>;
|
||||
async fn check_abandoned_parts(&self, bucket: &str, object: &str, opts: &HealOpts) -> Result<()>;
|
||||
pub trait HealOperations:
|
||||
rustfs_storage_api::HealOperations<Error = Error, HealResultItem = HealResultItem, HealOptions = HealOpts> + Send + Sync + Debug
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> HealOperations for T where
|
||||
T: rustfs_storage_api::HealOperations<Error = Error, HealResultItem = HealResultItem, HealOptions = HealOpts>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Debug
|
||||
{
|
||||
}
|
||||
|
||||
/// Namespace lock operations needed by consumers that only coordinate object
|
||||
/// mutations but do not require the full storage API surface.
|
||||
#[async_trait::async_trait]
|
||||
pub trait NamespaceLocking: Send + Sync + Debug + 'static {
|
||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<NamespaceLockWrapper>;
|
||||
pub trait NamespaceLocking:
|
||||
rustfs_storage_api::NamespaceLocking<Error = Error, NamespaceLock = NamespaceLockWrapper> + Send + Sync + Debug + 'static
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> NamespaceLocking for T where
|
||||
T: rustfs_storage_api::NamespaceLocking<Error = Error, NamespaceLock = NamespaceLockWrapper> + Send + Sync + Debug + 'static
|
||||
{
|
||||
}
|
||||
|
||||
@@ -12,8 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use rustfs_ecstore::{disk::DiskStore, error::Error, store::ECStore, store_api::NamespaceLocking};
|
||||
use rustfs_storage_api::StorageAdminApi;
|
||||
use rustfs_common::heal_channel::HealOpts;
|
||||
use rustfs_ecstore::{
|
||||
disk::DiskStore,
|
||||
error::Error,
|
||||
store::ECStore,
|
||||
store_api::{HealOperations, NamespaceLocking},
|
||||
};
|
||||
use rustfs_lock::NamespaceLockWrapper;
|
||||
use rustfs_madmin::heal_commands::HealResultItem;
|
||||
use rustfs_storage_api::{HealOperations as StorageHealOperations, NamespaceLocking as StorageNamespaceLocking, StorageAdminApi};
|
||||
|
||||
fn storage_admin_api_type_name<T>() -> &'static str
|
||||
where
|
||||
@@ -34,6 +42,27 @@ where
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
|
||||
fn heal_operations_type_name<T>() -> &'static str
|
||||
where
|
||||
T: HealOperations,
|
||||
{
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
|
||||
fn storage_namespace_locking_type_name<T>() -> &'static str
|
||||
where
|
||||
T: StorageNamespaceLocking<Error = Error, NamespaceLock = NamespaceLockWrapper>,
|
||||
{
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
|
||||
fn storage_heal_operations_type_name<T>() -> &'static str
|
||||
where
|
||||
T: StorageHealOperations<Error = Error, HealResultItem = HealResultItem, HealOptions = HealOpts>,
|
||||
{
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecstore_implements_storage_admin_api_contract() {
|
||||
assert!(storage_admin_api_type_name::<ECStore>().ends_with("::ECStore"));
|
||||
@@ -43,3 +72,18 @@ fn ecstore_implements_storage_admin_api_contract() {
|
||||
fn ecstore_implements_namespace_locking_contract() {
|
||||
assert!(namespace_locking_type_name::<ECStore>().ends_with("::ECStore"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecstore_implements_heal_operations_contract() {
|
||||
assert!(heal_operations_type_name::<ECStore>().ends_with("::ECStore"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecstore_implements_storage_namespace_locking_contract() {
|
||||
assert!(storage_namespace_locking_type_name::<ECStore>().ends_with("::ECStore"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecstore_implements_storage_heal_operations_contract() {
|
||||
assert!(storage_heal_operations_type_name::<ECStore>().ends_with("::ECStore"));
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@ use rustfs_ecstore::{
|
||||
disk::{DiskStore, endpoint::Endpoint},
|
||||
error::StorageError,
|
||||
store::ECStore,
|
||||
store_api::{HealOperations, ObjectOptions},
|
||||
store_api::ObjectOptions,
|
||||
};
|
||||
use rustfs_madmin::heal_commands::HealResultItem;
|
||||
use rustfs_storage_api::{
|
||||
BucketInfo, BucketOperations, DiskSetSelector, ListOperations as _, ObjectIO as _, ObjectOperations as _, StorageAdminApi,
|
||||
BucketInfo, BucketOperations, DiskSetSelector, HealOperations as _, ListOperations as _, ObjectIO as _,
|
||||
ObjectOperations as _, StorageAdminApi,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
@@ -45,8 +45,8 @@ use rustfs_ecstore::disk::RUSTFS_META_BUCKET;
|
||||
use rustfs_ecstore::error::Error as EcstoreError;
|
||||
use rustfs_ecstore::global::is_erasure_sd;
|
||||
use rustfs_ecstore::store::ECStore;
|
||||
use rustfs_ecstore::store_api::{NamespaceLocking as _, ObjectIO};
|
||||
use rustfs_storage_api::{BucketOperations, BucketOptions};
|
||||
use rustfs_ecstore::store_api::ObjectIO;
|
||||
use rustfs_storage_api::{BucketOperations, BucketOptions, NamespaceLocking as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::time::{Duration, Instant};
|
||||
|
||||
@@ -25,7 +25,7 @@ pub use bucket::{BucketInfo, BucketOperations, BucketOptions, DeleteBucketOption
|
||||
pub use error::{StorageErrorCode, StorageResult};
|
||||
pub use multipart::{CompletePart, ListMultipartsInfo, ListPartsInfo, MultipartInfo, MultipartUploadResult, PartInfo};
|
||||
pub use object::{HTTPPreconditions, HTTPRangeError, HTTPRangeSpec, ObjectLockRetentionOptions};
|
||||
pub use object::{HealOperations, MultipartOperations, NamespaceLocking, ObjectIO, ObjectOperations};
|
||||
pub use object::{ListObjectVersionsInfo, ListObjectsInfo, ListObjectsV2Info, ListOperations, ObjectInfoOrErr};
|
||||
pub use object::{MultipartOperations, ObjectIO, ObjectOperations};
|
||||
pub use object::{ObjectPreconditionError, ObjectPreconditionPart, ObjectPreconditionState};
|
||||
pub use object::{VersionMarker, WalkOptions, WalkVersionsSortOrder};
|
||||
|
||||
@@ -408,6 +408,33 @@ pub trait MultipartOperations: Send + Sync + fmt::Debug {
|
||||
) -> Result<Self::ObjectInfo, Self::Error>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait HealOperations: Send + Sync + fmt::Debug {
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
type HealResultItem: Send + 'static;
|
||||
type HealOptions: Send + Sync + 'static;
|
||||
|
||||
async fn heal_format(&self, dry_run: bool) -> Result<(Self::HealResultItem, Option<Self::Error>), Self::Error>;
|
||||
async fn heal_bucket(&self, bucket: &str, opts: &Self::HealOptions) -> Result<Self::HealResultItem, Self::Error>;
|
||||
async fn heal_object(
|
||||
&self,
|
||||
bucket: &str,
|
||||
object: &str,
|
||||
version_id: &str,
|
||||
opts: &Self::HealOptions,
|
||||
) -> Result<(Self::HealResultItem, Option<Self::Error>), Self::Error>;
|
||||
async fn get_pool_and_set(&self, id: &str) -> Result<(Option<usize>, Option<usize>, Option<usize>), Self::Error>;
|
||||
async fn check_abandoned_parts(&self, bucket: &str, object: &str, opts: &Self::HealOptions) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait NamespaceLocking: Send + Sync + fmt::Debug + 'static {
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
type NamespaceLock: Send + 'static;
|
||||
|
||||
async fn new_ns_lock(&self, bucket: &str, object: &str) -> Result<Self::NamespaceLock, Self::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ListObjectsInfo<ObjectItem> {
|
||||
pub is_truncated: bool,
|
||||
|
||||
@@ -95,12 +95,13 @@ Required `rustfs-storage-api` public re-exports:
|
||||
- `pub use error::{StorageErrorCode, StorageResult};`
|
||||
- `pub use multipart::{CompletePart, ListMultipartsInfo, ListPartsInfo, MultipartInfo, MultipartUploadResult, PartInfo};`
|
||||
- `pub use object::{HTTPPreconditions, HTTPRangeError, HTTPRangeSpec, ObjectLockRetentionOptions};`
|
||||
- `pub use object::{HealOperations, MultipartOperations, NamespaceLocking, ObjectIO, ObjectOperations};`
|
||||
- `pub use object::{ListObjectVersionsInfo, ListObjectsInfo, ListObjectsV2Info, ListOperations, ObjectInfoOrErr};`
|
||||
- `pub use object::{ObjectPreconditionError, ObjectPreconditionPart, ObjectPreconditionState};`
|
||||
- `pub use object::{VersionMarker, WalkOptions, WalkVersionsSortOrder};`
|
||||
|
||||
ECStore must keep compile-time coverage for both `StorageAdminApi` and the
|
||||
separate `NamespaceLocking` operation group.
|
||||
ECStore must keep compile-time coverage for `StorageAdminApi`, `HealOperations`,
|
||||
and the separate `NamespaceLocking` operation group.
|
||||
|
||||
The old `StorageAPI` aggregate facade must not reappear in production
|
||||
`crates/ecstore/src` or `rustfs/src` code after the storage operation groups
|
||||
|
||||
@@ -5,19 +5,19 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
||||
## Current Context
|
||||
|
||||
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
|
||||
- Branch: `overtrue/arch-object-operation-contracts`
|
||||
- Baseline: `main` at `3fb4cb3d65a2e037fc2e5ede32bf81c1f15c9fb7`
|
||||
after the list operations contract merge.
|
||||
- Branch: `overtrue/arch-storage-operation-consumer-cleanup`
|
||||
- Baseline: `main` at `e5cad7ed207265f5312c58d8dac6042e31ba95a3`
|
||||
after the object and multipart operation contract merge.
|
||||
- PR type for this branch: `api-extraction`
|
||||
- Runtime behavior changes: no external behavior change expected.
|
||||
- Rust code changes: move `ObjectIO`, `ObjectOperations`, and
|
||||
`MultipartOperations` into `rustfs-storage-api` as generic operation
|
||||
contracts with associated ECStore-bound types, then keep ECStore's existing
|
||||
public names as fixed associated-type compatibility subtraits.
|
||||
- CI/script changes: extend migration guards for the object/multipart operation
|
||||
public re-exports and ECStore local-method regressions.
|
||||
- Docs changes: record the object and multipart operation contract extraction
|
||||
slice.
|
||||
- Rust code changes: move `HealOperations` and `NamespaceLocking` into
|
||||
`rustfs-storage-api` as generic operation contracts with associated
|
||||
ECStore-bound types, then keep ECStore's existing public names as fixed
|
||||
associated-type compatibility subtraits.
|
||||
- CI/script changes: extend migration guards for the heal/namespace-lock
|
||||
operation public re-exports and ECStore local-method regressions.
|
||||
- Docs changes: record the heal and namespace-lock operation contract
|
||||
extraction slice.
|
||||
|
||||
## Phase 0 Tasks
|
||||
|
||||
@@ -709,6 +709,28 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
||||
- Verification: focused storage-api tests, ECStore/RustFS/downstream compile
|
||||
checks, migration/layer guards, formatting, diff hygiene, Rust risk scan,
|
||||
full pre-commit, and required three-expert review passed.
|
||||
- [x] `API-023` Move heal and namespace-lock operation contracts.
|
||||
- Completed slice: move `HealOperations` and `NamespaceLocking` from ECStore
|
||||
`store_api/traits.rs` into `rustfs-storage-api` as generic public
|
||||
operation contracts over ECStore heal result/options, namespace-lock
|
||||
wrapper, and error associated types; keep ECStore's old public trait names
|
||||
as fixed associated-type compatibility subtraits.
|
||||
- Acceptance: `rustfs-storage-api` exports the heal and namespace-lock
|
||||
operation contracts, ECStore no longer defines local heal/namespace-lock
|
||||
method signatures, focused consumers use the shared trait for method
|
||||
resolution, and migration guards reject dropping the public storage-api
|
||||
re-export or reintroducing local ECStore method definitions.
|
||||
- Must preserve: heal format/bucket/object behavior, abandoned-part checks,
|
||||
pool/set lookup behavior, namespace-lock acquisition behavior, ECStore
|
||||
public compatibility bounds, and all runtime lock/heal implementation
|
||||
bodies.
|
||||
- Risk defense: only the trait contracts cross into `rustfs-storage-api`;
|
||||
ECStore keeps concrete associated type bindings, `HealOpts`,
|
||||
`HealResultItem`, `NamespaceLockWrapper`, lock implementation, peer heal
|
||||
behavior, set/pool dispatch, and storage error mapping.
|
||||
- Verification: focused storage-api/ECStore/RustFS/heal/scanner compile
|
||||
checks, migration/layer guards, formatting, diff hygiene, Rust risk scan,
|
||||
full pre-commit, and required three-expert review passed.
|
||||
|
||||
## Phase 8 Background Controller Tasks
|
||||
|
||||
@@ -986,17 +1008,17 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
||||
|
||||
| Expert | Status | Notes |
|
||||
|---|---|---|
|
||||
| Quality/architecture | passed | Generic object I/O, object operation, and multipart operation traits now live in `rustfs-storage-api`; ECStore still owns concrete type bindings and implementations. |
|
||||
| Migration preservation | passed | Object read/write, metadata/tag/delete, multipart, and existing ECStore generic bound import paths are preserved through compatibility subtraits. |
|
||||
| Testing/verification | passed | Focused storage-api tests, downstream compile checks, migration/layer guards, formatting, diff hygiene, Rust risk scan, and full `make pre-commit` passed. |
|
||||
| Quality/architecture | passed | Generic heal and namespace-lock traits now live in `rustfs-storage-api`; ECStore still owns concrete type bindings and implementations. |
|
||||
| Migration preservation | passed | Existing ECStore public trait names remain as fixed associated-type compatibility subtraits while method resolution moves to shared traits where needed. |
|
||||
| Testing/verification | passed | Focused storage-api tests, ECStore compat tests, downstream compile checks, migration/layer guards, formatting, diff hygiene, Rust risk scan, and full `make pre-commit` passed. |
|
||||
|
||||
## Verification Notes
|
||||
|
||||
Passed before push:
|
||||
|
||||
- `cargo test -p rustfs-storage-api`: passed.
|
||||
- `cargo check --tests -p rustfs-storage-api -p rustfs-ecstore -p rustfs -p rustfs-scanner -p rustfs-protocols`: passed.
|
||||
- `cargo check --tests -p rustfs-heal -p rustfs-protocols`: passed.
|
||||
- `cargo test -p rustfs-ecstore --test ecstore_contract_compat_test`: passed.
|
||||
- `cargo check --tests -p rustfs-storage-api -p rustfs-ecstore -p rustfs -p rustfs-heal -p rustfs-scanner`: passed.
|
||||
- `./scripts/check_architecture_migration_rules.sh`: passed.
|
||||
- `./scripts/check_layer_dependencies.sh`: passed.
|
||||
- `cargo fmt --all --check`: passed.
|
||||
@@ -1004,28 +1026,26 @@ Passed before push:
|
||||
- Rust risk scan: no new `unwrap`/`expect`, panic/todo markers, `unsafe`,
|
||||
process-spawning calls, lossy casts, println/eprintln, or relaxed ordering in
|
||||
added Rust lines.
|
||||
- `make pre-commit`: passed; nextest reported 6204 tests passed and 111
|
||||
- `make pre-commit`: passed; nextest reported 6207 tests passed and 111
|
||||
skipped, and doctests passed.
|
||||
|
||||
Notes:
|
||||
|
||||
- This slice follows the list operations contract branch and keeps the old
|
||||
- This slice follows the object and multipart operation contract branch and keeps the old
|
||||
aggregate facade, bucket DTO, multipart DTO, bucket operation contract, object
|
||||
helper, range helper, list helper, object precondition, list response, and
|
||||
walk/list operation contract guards active.
|
||||
- The shared object and multipart operation contracts are now owned by
|
||||
walk/list/object/multipart operation contract guards active.
|
||||
- The shared heal and namespace-lock operation contracts are now owned by
|
||||
`rustfs-storage-api`; ECStore keeps the concrete associated type bindings,
|
||||
readers, `ObjectInfo`, `ObjectOptions`, `PutObjReader`, filemeta adaptation,
|
||||
storage error mapping, lifecycle/replication, rio, compression/encryption,
|
||||
and implementation behavior.
|
||||
- The slice does not alter object, list, walk, multipart, bucket, delete,
|
||||
namespace-lock, reader, or tag/metadata runtime behavior.
|
||||
`HealOpts`, `HealResultItem`, `NamespaceLockWrapper`, lock implementation,
|
||||
peer heal behavior, set/pool dispatch, and storage error mapping.
|
||||
- The slice does not alter heal, namespace-lock, object, list, walk,
|
||||
multipart, bucket, delete, reader, or tag/metadata runtime behavior.
|
||||
|
||||
## Handoff Notes
|
||||
|
||||
- Object and multipart operation contract cleanup is stacked on the list
|
||||
operations contract
|
||||
branch.
|
||||
- After this lands, remaining storage work can continue by extracting larger
|
||||
low-coupling object metadata/option/reader slices or by narrowing remaining
|
||||
operation-group consumers.
|
||||
- Heal and namespace-lock operation contract cleanup is based on the merged
|
||||
object and multipart operation contract branch.
|
||||
- After this lands, remaining storage work can continue by removing low-value
|
||||
ECStore compatibility imports or extracting larger low-coupling object
|
||||
metadata/option/reader slices.
|
||||
|
||||
@@ -25,10 +25,10 @@ use matchit::Params;
|
||||
use rustfs_common::heal_channel::{HealChannelPriority, HealChannelRequest, HealOpts, HealRequestSource};
|
||||
use rustfs_config::MAX_HEAL_REQUEST_SIZE;
|
||||
use rustfs_ecstore::bucket::utils::is_valid_object_prefix;
|
||||
use rustfs_ecstore::store_api::HealOperations;
|
||||
use rustfs_ecstore::store_utils::is_reserved_or_invalid_bucket;
|
||||
use rustfs_policy::policy::action::{Action, AdminAction};
|
||||
use rustfs_scanner::scanner::{BackgroundHealInfo, read_background_heal_info};
|
||||
use rustfs_storage_api::HealOperations as _;
|
||||
use rustfs_utils::path::path_join;
|
||||
use s3s::header::{CONTENT_LENGTH, CONTENT_TYPE};
|
||||
use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
|
||||
|
||||
@@ -18,10 +18,10 @@ use rustfs_ecstore::{
|
||||
disk::endpoint::Endpoint,
|
||||
endpoints::{EndpointServerPools, Endpoints, PoolEndpoints},
|
||||
store::ECStore,
|
||||
store_api::{HealOperations, ObjectOptions, PutObjReader},
|
||||
store_api::{ObjectOptions, PutObjReader},
|
||||
};
|
||||
use rustfs_object_capacity::capacity_manager::{HybridStrategyConfig, create_isolated_manager};
|
||||
use rustfs_storage_api::{BucketOperations, BucketOptions, MakeBucketOptions, ObjectIO as _};
|
||||
use rustfs_storage_api::{BucketOperations, BucketOptions, HealOperations as _, MakeBucketOptions, ObjectIO as _};
|
||||
use serial_test::serial;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
|
||||
@@ -204,7 +204,7 @@ require_source_line \
|
||||
"storage-api public list response contract re-export"
|
||||
require_source_line \
|
||||
"crates/storage-api/src/lib.rs" \
|
||||
"pub use object::{MultipartOperations, ObjectIO, ObjectOperations};" \
|
||||
"pub use object::{HealOperations, MultipartOperations, NamespaceLocking, ObjectIO, ObjectOperations};" \
|
||||
"storage-api public object operation contract re-export"
|
||||
require_source_line \
|
||||
"crates/storage-api/src/lib.rs" \
|
||||
@@ -300,12 +300,12 @@ fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --no-heading 'async fn (get_object_reader|put_object|get_object_info|verify_object_integrity|copy_object|delete_object_version|delete_object|delete_objects|put_object_metadata|get_object_tags|put_object_tags|delete_object_tags|add_partial|transition_object|restore_transitioned_object|list_multipart_uploads|new_multipart_upload|copy_object_part|put_object_part|get_multipart_info|list_object_parts|abort_multipart_upload|complete_multipart_upload)\b' \
|
||||
rg -n --no-heading 'async fn (get_object_reader|put_object|get_object_info|verify_object_integrity|copy_object|delete_object_version|delete_object|delete_objects|put_object_metadata|get_object_tags|put_object_tags|delete_object_tags|add_partial|transition_object|restore_transitioned_object|list_multipart_uploads|new_multipart_upload|copy_object_part|put_object_part|get_multipart_info|list_object_parts|abort_multipart_upload|complete_multipart_upload|heal_format|heal_bucket|heal_object|get_pool_and_set|check_abandoned_parts|new_ns_lock)\b' \
|
||||
crates/ecstore/src/store_api/traits.rs || true
|
||||
) >"$STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE"
|
||||
|
||||
if [[ -s "$STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE" ]]; then
|
||||
report_failure "old ecstore object/multipart operation method signatures reintroduced: $(paste -sd '; ' "$STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE")"
|
||||
report_failure "old ecstore operation method signatures reintroduced: $(paste -sd '; ' "$STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE")"
|
||||
fi
|
||||
|
||||
require_source_contains \
|
||||
@@ -322,8 +322,12 @@ require_source_contains \
|
||||
"ECStore MultipartOperations compatibility binding"
|
||||
require_source_contains \
|
||||
"crates/ecstore/src/store_api/traits.rs" \
|
||||
"pub trait NamespaceLocking: Send + Sync + Debug + 'static" \
|
||||
"separate namespace-locking operation-group trait"
|
||||
"rustfs_storage_api::HealOperations<" \
|
||||
"ECStore HealOperations compatibility binding"
|
||||
require_source_contains \
|
||||
"crates/ecstore/src/store_api/traits.rs" \
|
||||
"rustfs_storage_api::NamespaceLocking<" \
|
||||
"ECStore NamespaceLocking compatibility binding"
|
||||
require_source_contains \
|
||||
"crates/ecstore/tests/ecstore_contract_compat_test.rs" \
|
||||
"fn ecstore_implements_storage_admin_api_contract()" \
|
||||
@@ -332,6 +336,18 @@ require_source_contains \
|
||||
"crates/ecstore/tests/ecstore_contract_compat_test.rs" \
|
||||
"fn ecstore_implements_namespace_locking_contract()" \
|
||||
"ECStore NamespaceLocking compile-time coverage test"
|
||||
require_source_contains \
|
||||
"crates/ecstore/tests/ecstore_contract_compat_test.rs" \
|
||||
"fn ecstore_implements_heal_operations_contract()" \
|
||||
"ECStore HealOperations compile-time coverage test"
|
||||
require_source_contains \
|
||||
"crates/ecstore/tests/ecstore_contract_compat_test.rs" \
|
||||
"fn ecstore_implements_storage_namespace_locking_contract()" \
|
||||
"ECStore storage-api NamespaceLocking compile-time coverage test"
|
||||
require_source_contains \
|
||||
"crates/ecstore/tests/ecstore_contract_compat_test.rs" \
|
||||
"fn ecstore_implements_storage_heal_operations_contract()" \
|
||||
"ECStore storage-api HealOperations compile-time coverage test"
|
||||
|
||||
if (( FAILURES > 0 )); then
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user