refactor(ecstore): relocate NamespaceLocking + finalize God-Object split (backlog#822) (#4291)

This commit is contained in:
Zhengchao An
2026-07-05 23:34:46 +08:00
committed by GitHub
parent f737b39cfc
commit a8d8e56478
4 changed files with 82 additions and 86 deletions
+26 -34
View File
@@ -12,6 +12,32 @@
// 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.
//! `SetDisks` — one erasure set's worth of disks and the object storage logic
//! over them. Historically a single ~19.7k-line God-Object; split into a Core
//! plus borrow-based operation families during backlog#815 (Epic #728).
//!
//! Module layout after the split:
//!
//! - `mod.rs` — the `SetDisks` core struct, its construction, and the shared
//! inherent state/helpers the operation families borrow.
//! - `ctx` — `SetDisksCtx`, the borrow context that hands operation units
//! access to the core without cloning `Arc`s (P0, backlog#816).
//! - `core::io_primitives` — the shared low-level read/write/erasure IO
//! primitives (metadata-fanout quorum, bitrot readers, rename/delete, quorum
//! helpers) that the operation families call through the core (P5,
//! backlog#820).
//! - `ops/` — one module per storage-api contract family, each
//! `impl <Contract> for SetDisks`: `ops::object` (`ObjectIO` +
//! `ObjectOperations`, the object read/write hot path, P6/backlog#821),
//! `ops::heal` (`HealOperations`, P1/backlog#817), `ops::multipart`
//! (`MultipartOperations`, P2/backlog#818), `ops::list` (`ListOperations`)
//! and `ops::bucket` (`BucketOperations`, P3+P4/backlog#819), `ops::locking`
//! (`NamespaceLocking` + lock helpers, P7/backlog#822).
//! - `read.rs` — the object-read operation pipeline (`get_object_*`,
//! `read_version_optimized`) and its metadata cache, kept separate from the
//! read primitives it drives.
//! - `metadata.rs`, `replication.rs`, `shard_source.rs` — supporting helpers.
// #730: SetDisks still hosts staged read/heal/write migration helpers. // #730: SetDisks still hosts staged read/heal/write migration helpers.
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_imports)] #![allow(unused_imports)]
@@ -445,7 +471,6 @@ static OBJECT_LOCK_DIAG_ENABLED: OnceLock<bool> = OnceLock::new();
mod core; mod core;
mod ctx; mod ctx;
mod lock;
mod metadata; mod metadata;
mod ops; mod ops;
mod read; mod read;
@@ -2487,39 +2512,6 @@ impl SetDisks {
} }
} }
#[async_trait::async_trait]
impl crate::storage_api_contracts::namespace::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 runtime_sources::setup_is_dist_erasure().await {
// Calculate quorum based on lockers count (majority)
let lockers_count = self.lockers.len();
let write_quorum = if lockers_count > 1 { (lockers_count / 2) + 1 } else { 1 };
NamespaceLock::with_clients_and_quorum(
format!("set-{}-{}", self.pool_index, self.set_index),
self.lockers.clone(),
write_quorum,
)
} else {
NamespaceLock::Local(LocalLock::new(
format!("set-{}-{}", self.pool_index, self.set_index),
self.local_lock_manager.clone(),
))
};
let resource = ObjectKey {
bucket: Arc::from(bucket),
object: Arc::from(object),
version: None,
};
Ok(NamespaceLockWrapper::new(set_lock, resource, self.locker_owner.clone()))
}
}
fn check_object_lock_retention_update(bucket: &str, object: &str, obj_info: &ObjectInfo, opts: &ObjectOptions) -> Result<()> { fn check_object_lock_retention_update(bucket: &str, object: &str, obj_info: &ObjectInfo, opts: &ObjectOptions) -> Result<()> {
if let Some(retention) = &opts.object_lock_retention if let Some(retention) = &opts.object_lock_retention
&& check_retention_for_modification( && check_retention_for_modification(
@@ -12,12 +12,52 @@
// 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 super::*; //! `NamespaceLocking` contract impl for `SetDisks` plus the set-disk locking
//! helpers (P7 of the God-Object split, tracking backlog#815, issue
//! backlog#822). The `NamespaceLocking` impl is relocated from set_disk/mod.rs
//! and the lock formatting/mapping helpers from set_disk/lock.rs are collected
//! here; the contract stays implemented `for SetDisks`, so its associated-type
//! bounds are unchanged and helper access is via inherent calls.
use super::super::*;
use crate::disk::health_state::DriveMembershipSnapshot; use crate::disk::health_state::DriveMembershipSnapshot;
use crate::runtime::sources as runtime_sources; use crate::runtime::sources as runtime_sources;
#[async_trait::async_trait]
impl crate::storage_api_contracts::namespace::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 runtime_sources::setup_is_dist_erasure().await {
// Calculate quorum based on lockers count (majority)
let lockers_count = self.lockers.len();
let write_quorum = if lockers_count > 1 { (lockers_count / 2) + 1 } else { 1 };
NamespaceLock::with_clients_and_quorum(
format!("set-{}-{}", self.pool_index, self.set_index),
self.lockers.clone(),
write_quorum,
)
} else {
NamespaceLock::Local(LocalLock::new(
format!("set-{}-{}", self.pool_index, self.set_index),
self.local_lock_manager.clone(),
))
};
let resource = ObjectKey {
bucket: Arc::from(bucket),
object: Arc::from(object),
version: None,
};
Ok(NamespaceLockWrapper::new(set_lock, resource, self.locker_owner.clone()))
}
}
impl SetDisks { impl SetDisks {
pub(super) fn format_lock_error(&self, bucket: &str, object: &str, mode: &str, err: &LockResult) -> String { pub(in crate::set_disk) fn format_lock_error(&self, bucket: &str, object: &str, mode: &str, err: &LockResult) -> String {
match err { match err {
LockResult::Timeout => { LockResult::Timeout => {
format!("{mode} lock acquisition timed out on {bucket}/{object} (owner={})", self.locker_owner) format!("{mode} lock acquisition timed out on {bucket}/{object} (owner={})", self.locker_owner)
@@ -30,7 +70,7 @@ impl SetDisks {
} }
} }
pub(super) fn format_lock_error_from_error( pub(in crate::set_disk) fn format_lock_error_from_error(
&self, &self,
bucket: &str, bucket: &str,
object: &str, object: &str,
@@ -51,7 +91,7 @@ impl SetDisks {
} }
} }
pub(super) fn map_namespace_lock_error( pub(in crate::set_disk) fn map_namespace_lock_error(
&self, &self,
bucket: &str, bucket: &str,
object: &str, object: &str,
@@ -72,7 +112,7 @@ impl SetDisks {
} }
} }
pub(super) async fn get_disks_internal(&self) -> Vec<Option<DiskStore>> { pub(in crate::set_disk) async fn get_disks_internal(&self) -> Vec<Option<DiskStore>> {
let rl = self.disks.read().await; let rl = self.disks.read().await;
rl.clone() rl.clone()
@@ -94,7 +134,7 @@ impl SetDisks {
disks disks
} }
pub(super) async fn get_online_disks(&self) -> Vec<Option<DiskStore>> { pub(in crate::set_disk) async fn get_online_disks(&self) -> Vec<Option<DiskStore>> {
let snapshot = self.drive_membership_snapshot().await; let snapshot = self.drive_membership_snapshot().await;
let mut disks = snapshot.strict_online_candidates().into_iter().map(Some).collect::<Vec<_>>(); let mut disks = snapshot.strict_online_candidates().into_iter().map(Some).collect::<Vec<_>>();
@@ -104,7 +144,7 @@ impl SetDisks {
disks disks
} }
pub(super) async fn get_online_local_disks(&self) -> Vec<Option<DiskStore>> { pub(in crate::set_disk) async fn get_online_local_disks(&self) -> Vec<Option<DiskStore>> {
let snapshot = self.drive_membership_snapshot().await; let snapshot = self.drive_membership_snapshot().await;
let mut disks = snapshot let mut disks = snapshot
.strict_online_local_candidates() .strict_online_local_candidates()
@@ -236,7 +276,7 @@ impl SetDisks {
} }
} }
pub(super) async fn _get_local_disks(&self) -> Vec<Option<DiskStore>> { pub(in crate::set_disk) async fn _get_local_disks(&self) -> Vec<Option<DiskStore>> {
let mut disks = self.get_disks_internal().await; let mut disks = self.get_disks_internal().await;
let mut rng = rand::rng(); let mut rng = rand::rng();
@@ -322,7 +362,7 @@ impl SetDisks {
disk_lock[disk_idx] = Some(new_disk); disk_lock[disk_idx] = Some(new_disk);
} }
pub(super) fn find_disk_index(&self, fm: &FormatV3) -> Result<(usize, usize)> { pub(in crate::set_disk) fn find_disk_index(&self, fm: &FormatV3) -> Result<(usize, usize)> {
self.format.check_other(fm)?; self.format.check_other(fm)?;
if fm.erasure.this.is_nil() { if fm.erasure.this.is_nil() {
@@ -340,7 +380,7 @@ impl SetDisks {
Err(Error::other("DriveID: not found")) Err(Error::other("DriveID: not found"))
} }
pub(super) async fn connect_endpoint(ep: &Endpoint) -> disk::error::Result<(DiskStore, FormatV3)> { pub(in crate::set_disk) async fn connect_endpoint(ep: &Endpoint) -> disk::error::Result<(DiskStore, FormatV3)> {
let disk = new_disk( let disk = new_disk(
ep, ep,
&DiskOption { &DiskOption {
@@ -355,12 +395,15 @@ impl SetDisks {
Ok((disk, fm)) Ok((disk, fm))
} }
pub(super) async fn get_online_disk_with_healing(&self, incl_healing: bool) -> Result<(Vec<Option<DiskStore>>, bool)> { pub(in crate::set_disk) async fn get_online_disk_with_healing(
&self,
incl_healing: bool,
) -> Result<(Vec<Option<DiskStore>>, bool)> {
let (new_disks, _, healing) = self.get_online_disk_with_healing_and_info(incl_healing).await?; let (new_disks, _, healing) = self.get_online_disk_with_healing_and_info(incl_healing).await?;
Ok((new_disks, healing > 0)) Ok((new_disks, healing > 0))
} }
pub(super) async fn get_online_disk_with_healing_and_info( pub(in crate::set_disk) async fn get_online_disk_with_healing_and_info(
&self, &self,
incl_healing: bool, incl_healing: bool,
) -> Result<(Vec<Option<DiskStore>>, Vec<DiskInfo>, usize)> { ) -> Result<(Vec<Option<DiskStore>>, Vec<DiskInfo>, usize)> {
+1
View File
@@ -20,5 +20,6 @@
pub(crate) mod bucket; pub(crate) mod bucket;
pub(crate) mod heal; pub(crate) mod heal;
pub(crate) mod list; pub(crate) mod list;
pub(crate) mod locking;
pub(crate) mod multipart; pub(crate) mod multipart;
pub(crate) mod object; pub(crate) mod object;
@@ -1,6 +1,3 @@
// #730: storage contract traits are staged for facade migration and external owners.
#![allow(dead_code)]
use crate::error::Error; use crate::error::Error;
use crate::object_api::{GetObjectReader, ObjectInfo, ObjectOptions, PutObjReader}; use crate::object_api::{GetObjectReader, ObjectInfo, ObjectOptions, PutObjReader};
use rustfs_filemeta::FileInfo; use rustfs_filemeta::FileInfo;
@@ -27,48 +24,11 @@ pub(crate) mod lifecycle {
} }
pub(crate) mod list { pub(crate) mod list {
use super::{Debug, Error, FileInfo, ObjectInfo};
pub(crate) use rustfs_storage_api::{ pub(crate) use rustfs_storage_api::{
ListObjectVersionsInfo as StorageListObjectVersionsInfo, ListObjectsInfo, ListObjectsV2Info as StorageListObjectsV2Info, ListObjectVersionsInfo as StorageListObjectVersionsInfo, ListObjectsInfo, ListObjectsV2Info as StorageListObjectsV2Info,
ListOperations, ObjectInfoOrErr as StorageObjectInfoOrErr, VersionMarker, WalkOptions as StorageWalkOptions, ListOperations, ObjectInfoOrErr as StorageObjectInfoOrErr, VersionMarker, WalkOptions as StorageWalkOptions,
WalkVersionsSortOrder, WalkVersionsSortOrder,
}; };
use tokio_util::sync::CancellationToken;
type ListObjectsV2Info = StorageListObjectsV2Info<ObjectInfo>;
type ListObjectVersionsInfo = StorageListObjectVersionsInfo<ObjectInfo>;
type ObjectInfoOrErr = StorageObjectInfoOrErr<ObjectInfo, Error>;
type WalkOptions = StorageWalkOptions<fn(&FileInfo) -> bool>;
pub(crate) trait EcstoreListOperations:
ListOperations<
Error = Error,
ListObjectsV2Info = ListObjectsV2Info,
ListObjectVersionsInfo = ListObjectVersionsInfo,
ObjectInfoOrErr = ObjectInfoOrErr,
WalkOptions = WalkOptions,
WalkCancellation = CancellationToken,
WalkResultSender = tokio::sync::mpsc::Sender<ObjectInfoOrErr>,
> + Send
+ Sync
+ Debug
{
}
impl<T> EcstoreListOperations for T where
T: ListOperations<
Error = Error,
ListObjectsV2Info = ListObjectsV2Info,
ListObjectVersionsInfo = ListObjectVersionsInfo,
ObjectInfoOrErr = ObjectInfoOrErr,
WalkOptions = WalkOptions,
WalkCancellation = CancellationToken,
WalkResultSender = tokio::sync::mpsc::Sender<ObjectInfoOrErr>,
> + Send
+ Sync
+ Debug
{
}
} }
pub(crate) mod multipart { pub(crate) mod multipart {