mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 21:26:28 +00:00
refactor(ecstore): add SetDisks borrow context for God-Object split (backlog#816) (#4265)
P0 of the SetDisks split (tracking backlog#815). Introduce SetDisksCtx<'a>, a Copy borrow handle over the shared SetDisks core state (topology/config, disks, locker trio), and validate the pattern by routing the List family's delete_all through a ListOperations<'a> service unit. No trait impl is moved and runtime behavior is byte-for-byte unchanged; the public SetDisks::delete_all entry point is preserved and delegates to the borrow-based unit.
This commit is contained in:
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Borrow context for the `SetDisks` split (tracking #815, phase P0 #816).
|
||||||
|
//!
|
||||||
|
//! `SetDisks` stays the single owner of the shared set state — topology/config
|
||||||
|
//! (immutable after construction), `disks`, the locker trio, and the moka
|
||||||
|
//! caches. Operation-family service units introduced in later phases borrow
|
||||||
|
//! that state through [`SetDisksCtx`] instead of copying it, so no state is
|
||||||
|
//! duplicated as trait impls move out.
|
||||||
|
//!
|
||||||
|
//! This module only establishes the borrow handle. It moves no trait impl and
|
||||||
|
//! changes no runtime behavior.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Lightweight, `Copy` handle borrowing the shared [`SetDisks`] core state.
|
||||||
|
///
|
||||||
|
/// Accessors return references tied to the borrowed core's lifetime, so an
|
||||||
|
/// operation-family unit can read topology, disks, and lockers without holding
|
||||||
|
/// its own copy. Anything not yet exposed through a typed accessor is reachable
|
||||||
|
/// via [`SetDisksCtx::core`].
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(crate) struct SetDisksCtx<'a> {
|
||||||
|
core: &'a SetDisks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SetDisksCtx<'a> {
|
||||||
|
pub(crate) fn new(core: &'a SetDisks) -> Self {
|
||||||
|
Self { core }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The borrowed core, for state not yet fronted by a typed accessor.
|
||||||
|
pub(crate) fn core(&self) -> &'a SetDisks {
|
||||||
|
self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Immutable topology / config (fixed after construction) ---
|
||||||
|
|
||||||
|
pub(crate) fn set_index(&self) -> usize {
|
||||||
|
self.core.set_index
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pool_index(&self) -> usize {
|
||||||
|
self.core.pool_index
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_drive_count(&self) -> usize {
|
||||||
|
self.core.set_drive_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn default_parity_count(&self) -> usize {
|
||||||
|
self.core.default_parity_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_endpoints(&self) -> &'a [Endpoint] {
|
||||||
|
&self.core.set_endpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn format(&self) -> &'a FormatV3 {
|
||||||
|
&self.core.format
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn locker_owner(&self) -> &'a str {
|
||||||
|
&self.core.locker_owner
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Shared mutable state (behind their own synchronization) ---
|
||||||
|
|
||||||
|
pub(crate) fn disks(&self) -> &'a Arc<RwLock<Vec<Option<DiskStore>>>> {
|
||||||
|
&self.core.disks
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Locker trio ---
|
||||||
|
|
||||||
|
pub(crate) fn lockers(&self) -> &'a [Arc<dyn LockClient>] {
|
||||||
|
&self.core.lockers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SetDisks {
|
||||||
|
/// Borrow this set's shared core state through a lightweight handle.
|
||||||
|
///
|
||||||
|
/// Foundation for the operation-family split (#815 / #816): service units
|
||||||
|
/// take a [`SetDisksCtx`] rather than owning duplicated state.
|
||||||
|
pub(crate) fn ctx(&self) -> SetDisksCtx<'_> {
|
||||||
|
SetDisksCtx::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,12 +12,34 @@
|
|||||||
// 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::ctx::SetDisksCtx;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl SetDisks {
|
impl SetDisks {
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub async fn delete_all(&self, bucket: &str, prefix: &str) -> Result<()> {
|
pub async fn delete_all(&self, bucket: &str, prefix: &str) -> Result<()> {
|
||||||
let disks = self.disks.read().await;
|
ListOperations::new(self.ctx()).delete_all(bucket, prefix).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List/prefix maintenance operations, borrowing the `SetDisks` core state
|
||||||
|
/// through [`SetDisksCtx`].
|
||||||
|
///
|
||||||
|
/// First validation of the borrow pattern for the SetDisks split (#816). The
|
||||||
|
/// behavior here is byte-for-byte identical to the previous inherent
|
||||||
|
/// `SetDisks::delete_all`; only state access moves from `self` to the borrow
|
||||||
|
/// handle.
|
||||||
|
pub(crate) struct ListOperations<'a> {
|
||||||
|
ctx: SetDisksCtx<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ListOperations<'a> {
|
||||||
|
pub(crate) fn new(ctx: SetDisksCtx<'a>) -> Self {
|
||||||
|
Self { ctx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn delete_all(&self, bucket: &str, prefix: &str) -> Result<()> {
|
||||||
|
let disks = self.ctx.disks().read().await;
|
||||||
|
|
||||||
let disks = disks.clone();
|
let disks = disks.clone();
|
||||||
|
|
||||||
|
|||||||
@@ -654,4 +654,67 @@ mod tests {
|
|||||||
|
|
||||||
drop(temp_dirs);
|
drop(temp_dirs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDisks split P0 (#816): the borrow handle must mirror the core state and
|
||||||
|
// the List operation family must run identically through it.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn set_disks_ctx_mirrors_core_and_drives_list_operations() {
|
||||||
|
use crate::set_disk::list::ListOperations;
|
||||||
|
|
||||||
|
let disk_count = 4;
|
||||||
|
let format = FormatV3::new(1, disk_count);
|
||||||
|
|
||||||
|
let mut temp_dirs = Vec::with_capacity(disk_count);
|
||||||
|
let mut endpoints = Vec::with_capacity(disk_count);
|
||||||
|
let mut disks = Vec::with_capacity(disk_count);
|
||||||
|
|
||||||
|
for disk_idx in 0..disk_count {
|
||||||
|
let (temp_dir, endpoint, disk) = make_formatted_local_disk(disk_idx, &format).await;
|
||||||
|
temp_dirs.push(temp_dir);
|
||||||
|
endpoints.push(endpoint);
|
||||||
|
disks.push(Some(disk));
|
||||||
|
}
|
||||||
|
|
||||||
|
let set_disks = SetDisks::new(
|
||||||
|
"test-owner".to_string(),
|
||||||
|
Arc::new(RwLock::new(disks)),
|
||||||
|
disk_count,
|
||||||
|
disk_count / 2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
endpoints.clone(),
|
||||||
|
format,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// The handle reads through to the same core state, not a copy.
|
||||||
|
let ctx = set_disks.ctx();
|
||||||
|
assert_eq!(ctx.locker_owner(), "test-owner");
|
||||||
|
assert_eq!(ctx.set_drive_count(), disk_count);
|
||||||
|
assert_eq!(ctx.default_parity_count(), disk_count / 2);
|
||||||
|
assert_eq!(ctx.set_index(), 0);
|
||||||
|
assert_eq!(ctx.pool_index(), 0);
|
||||||
|
assert_eq!(ctx.set_endpoints().len(), endpoints.len());
|
||||||
|
assert!(ctx.lockers().is_empty());
|
||||||
|
assert!(
|
||||||
|
Arc::ptr_eq(ctx.disks(), &set_disks.disks),
|
||||||
|
"ctx must borrow the core disks, not clone them"
|
||||||
|
);
|
||||||
|
assert!(std::ptr::eq(ctx.core(), &*set_disks), "ctx must borrow the core, not clone it");
|
||||||
|
assert!(std::ptr::eq(ctx.format(), &set_disks.format), "ctx must borrow the core format");
|
||||||
|
|
||||||
|
// The List family runs through the borrow handle with unchanged
|
||||||
|
// behavior: delete_all reports success even when the prefix is absent.
|
||||||
|
ListOperations::new(set_disks.ctx())
|
||||||
|
.delete_all("nonexistent-bucket", "nonexistent-prefix")
|
||||||
|
.await
|
||||||
|
.expect("delete_all via borrow handle should succeed");
|
||||||
|
set_disks
|
||||||
|
.delete_all("nonexistent-bucket", "nonexistent-prefix")
|
||||||
|
.await
|
||||||
|
.expect("delete_all public entry should stay in sync");
|
||||||
|
|
||||||
|
drop(temp_dirs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -443,6 +443,7 @@ const DEFAULT_RUSTFS_GET_METADATA_VERSION_EARLY_STOP_ENABLE: bool = false;
|
|||||||
|
|
||||||
static OBJECT_LOCK_DIAG_ENABLED: OnceLock<bool> = OnceLock::new();
|
static OBJECT_LOCK_DIAG_ENABLED: OnceLock<bool> = OnceLock::new();
|
||||||
|
|
||||||
|
mod ctx;
|
||||||
mod heal;
|
mod heal;
|
||||||
mod list;
|
mod list;
|
||||||
mod lock;
|
mod lock;
|
||||||
|
|||||||
Reference in New Issue
Block a user