refactor: move ecstore pool-space helpers (#3649)

This commit is contained in:
安正超
2026-06-20 12:44:16 +08:00
committed by GitHub
parent 3035cad082
commit aad5379e0c
4 changed files with 167 additions and 119 deletions
+1
View File
@@ -8,5 +8,6 @@ pub(crate) mod disks_layout;
pub(crate) mod endpoint;
pub(crate) mod endpoints;
pub(crate) mod format;
pub(crate) mod pool_space;
pub(crate) mod set_heal;
pub(crate) mod set_layout;
+126
View File
@@ -0,0 +1,126 @@
// 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.
use std::slice::Iter;
#[derive(Debug, Default, Clone)]
pub struct PoolAvailableSpace {
pub index: usize,
pub available: u64, // in bytes
pub max_used_pct: u64, // Used disk percentage of most filled disk, rounded down.
}
#[derive(Debug, Default, Clone)]
pub struct ServerPoolsAvailableSpace(pub(crate) Vec<PoolAvailableSpace>);
impl ServerPoolsAvailableSpace {
pub fn iter(&self) -> Iter<'_, PoolAvailableSpace> {
self.0.iter()
}
// TotalAvailable - total available space
pub fn total_available(&self) -> u64 {
let mut total = 0;
for pool in &self.0 {
total += pool.available;
}
total
}
// FilterMaxUsed will filter out any pools that has used percent bigger than max,
// unless all have that, in which case all are preserved.
pub fn filter_max_used(&mut self, max: u64) {
if self.0.len() <= 1 {
// Nothing to do.
return;
}
let mut ok = false;
for pool in &self.0 {
if pool.available > 0 && pool.max_used_pct < max {
ok = true;
break;
}
}
if !ok {
// All above limit.
// Do not modify
return;
}
// Remove entries that are above.
for pool in self.0.iter_mut() {
if pool.available > 0 && pool.max_used_pct < max {
continue;
}
pool.available = 0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pool_available_space_creation_preserves_fields() {
let space = PoolAvailableSpace {
index: 0,
available: 1000,
max_used_pct: 50,
};
assert_eq!(space.index, 0);
assert_eq!(space.available, 1000);
assert_eq!(space.max_used_pct, 50);
}
#[test]
fn server_pools_available_space_filter_keeps_shape_and_updates_availability() {
let mut spaces = ServerPoolsAvailableSpace(vec![
PoolAvailableSpace {
index: 0,
available: 1000,
max_used_pct: 50,
},
PoolAvailableSpace {
index: 1,
available: 2000,
max_used_pct: 80,
},
]);
assert_eq!(spaces.total_available(), 3000);
spaces.filter_max_used(60);
assert_eq!(spaces.0.len(), 2);
assert_eq!(spaces.0[0].index, 0);
assert_eq!(spaces.0[0].available, 1000);
assert_eq!(spaces.0[1].available, 0);
assert_eq!(spaces.total_available(), 1000);
}
#[test]
fn server_pools_available_space_iter_preserves_pool_order() {
let spaces = ServerPoolsAvailableSpace(vec![PoolAvailableSpace {
index: 0,
available: 1000,
max_used_pct: 50,
}]);
let indexes = spaces.iter().map(|space| space.index).collect::<Vec<_>>();
assert_eq!(indexes, vec![0]);
}
}
+2 -109
View File
@@ -87,7 +87,6 @@ use s3s::dto::{BucketVersioningStatus, ObjectLockConfiguration, ObjectLockEnable
use std::cmp::Ordering;
use std::net::SocketAddr;
use std::process::exit;
use std::slice::Iter;
use std::time::SystemTime;
use std::{
collections::HashMap,
@@ -107,6 +106,8 @@ type ListObjectVersionsInfo = StorageListObjectVersionsInfo<ObjectInfo>;
type ObjectInfoOrErr = StorageObjectInfoOrErr<ObjectInfo, Error>;
type WalkOptions = StorageWalkOptions<fn(&FileInfo) -> bool>;
pub use crate::layout::pool_space::{PoolAvailableSpace, ServerPoolsAvailableSpace};
/// Check if a directory contains any xl.meta files (indicating actual S3 objects)
/// This is used to determine if a bucket is empty for deletion purposes.
async fn has_xlmeta_files(path: &std::path::Path) -> bool {
@@ -791,59 +792,6 @@ impl rustfs_storage_api::StorageAdminApi for ECStore {
}
}
#[derive(Debug, Default, Clone)]
pub struct PoolAvailableSpace {
pub index: usize,
pub available: u64, // in bytes
pub max_used_pct: u64, // Used disk percentage of most filled disk, rounded down.
}
#[derive(Debug, Default, Clone)]
pub struct ServerPoolsAvailableSpace(Vec<PoolAvailableSpace>);
impl ServerPoolsAvailableSpace {
pub fn iter(&self) -> Iter<'_, PoolAvailableSpace> {
self.0.iter()
}
// TotalAvailable - total available space
pub fn total_available(&self) -> u64 {
let mut total = 0;
for pool in &self.0 {
total += pool.available;
}
total
}
// FilterMaxUsed will filter out any pools that has used percent bigger than max,
// unless all have that, in which case all are preserved.
pub fn filter_max_used(&mut self, max: u64) {
if self.0.len() <= 1 {
// Nothing to do.
return;
}
let mut ok = false;
for pool in &self.0 {
if pool.available > 0 && pool.max_used_pct < max {
ok = true;
break;
}
}
if !ok {
// All above limit.
// Do not modify
return;
}
// Remove entries that are above.
for pool in self.0.iter_mut() {
if pool.available > 0 && pool.max_used_pct < max {
continue;
}
pool.available = 0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -872,32 +820,6 @@ mod tests {
assert!(result.is_err());
}
#[test]
fn test_server_pools_available_space() {
let mut spaces = ServerPoolsAvailableSpace(vec![
PoolAvailableSpace {
index: 0,
available: 1000,
max_used_pct: 50,
},
PoolAvailableSpace {
index: 1,
available: 2000,
max_used_pct: 80,
},
]);
assert_eq!(spaces.total_available(), 3000);
spaces.filter_max_used(60);
// filter_max_used sets available to 0 for filtered pools, doesn't remove them
assert_eq!(spaces.0.len(), 2); // Length remains the same
assert_eq!(spaces.0[0].index, 0);
assert_eq!(spaces.0[0].available, 1000); // First pool should still be available
assert_eq!(spaces.0[1].available, 0); // Second pool should be filtered (available = 0)
assert_eq!(spaces.total_available(), 1000); // Only first pool contributes to total
}
#[tokio::test]
async fn test_find_local_disk() {
let result = find_local_disk(&"/nonexistent/path".to_string()).await;
@@ -995,33 +917,4 @@ mod tests {
assert!(!should_enqueue_transition_immediately(&oi));
}
// Test that we can create the basic structures without global state
#[test]
fn test_pool_available_space_creation() {
let space = PoolAvailableSpace {
index: 0,
available: 1000,
max_used_pct: 50,
};
assert_eq!(space.index, 0);
assert_eq!(space.available, 1000);
assert_eq!(space.max_used_pct, 50);
}
#[test]
fn test_server_pools_available_space_iter() {
let spaces = ServerPoolsAvailableSpace(vec![PoolAvailableSpace {
index: 0,
available: 1000,
max_used_pct: 50,
}]);
let mut count = 0;
for space in spaces.iter() {
assert_eq!(space.index, 0);
count += 1;
}
assert_eq!(count, 1);
}
}
+38 -10
View File
@@ -5,16 +5,16 @@ 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-ecstore-layout-set-heal-helpers`
- Baseline: merged `E-003/E-LAYOUT-002`.
- Branch: `overtrue/arch-ecstore-layout-pool-space`
- Baseline: completed `E-004/E-LAYOUT-003`.
- Stacked on: merged ECStore layout foundation, format layout ownership, and
endpoint layout move slices.
endpoint layout move slices plus the set-format heal helper layout slice.
- PR type for this branch: `pure-move`
- Runtime behavior changes: none.
- Rust code changes: move runtime-neutral ECStore set-format heal helpers into
the internal layout bucket while preserving `Sets` runtime orchestration.
- Rust code changes: move ECStore pool-space selection helper types into the
internal layout bucket while preserving the old `store` export path.
- CI/script changes: none.
- Docs changes: record the ECStore set-format heal helper layout slice.
- Docs changes: record the ECStore pool-space helper layout slice.
## Phase 0 Tasks
@@ -2267,10 +2267,25 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
checks, migration/layer guards, formatting, diff hygiene, Rust risk scan,
branch freshness check, pre-commit quality gate, and three-expert review.
- [x] `E-005/E-LAYOUT-004` Move ECStore pool-space selection helpers.
- Do: move runtime-neutral pool-space selection helper structs into the
ECStore layout bucket while keeping the old `store` export path available.
- Acceptance: `layout::pool_space` owns `PoolAvailableSpace` and
`ServerPoolsAvailableSpace`, rebalance pool selection keeps the same tuple
storage access inside the crate, and external `store` imports remain
source-compatible through re-export.
- Must preserve: pool index ordering, available-space summation,
max-used-percent filtering semantics, excluded-pool zeroing, object
placement pool selection, and rebalance pool-space behavior.
- Verification: focused ECStore pool-space tests, ECStore/RustFS/Heal
compile checks, migration/layer guards, formatting, diff hygiene, Rust risk
scan, branch freshness check, pre-commit quality gate, and three-expert
review.
## Next PRs
1. `pure-move`: continue moving runtime-neutral pool/set layout helpers once
E-004/E-LAYOUT-003 lands.
E-005/E-LAYOUT-004 lands.
2. `pure-move`: continue pruning residual embedded startup-only orchestration
once the lifecycle helpers are merged.
@@ -2278,9 +2293,9 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
| Expert | Status | Notes |
|---|---|---|
| Quality/architecture | passed | E-004/E-LAYOUT-003 extracts runtime-neutral set-format heal helpers into ECStore layout without moving disk init or `Sets` orchestration. |
| Migration preservation | passed | Drive-info state mapping, unformatted format regeneration, current disk-info preservation, dry-run/save behavior, and `Sets` runtime control flow remain preserved. |
| Testing/verification | passed | Focused set-heal helper tests, compile checks, guards, formatting, diff hygiene, Rust risk scan, and full pre-commit passed. |
| Quality/architecture | passed | E-005/E-LAYOUT-004 extracts runtime-neutral pool-space helper types into ECStore layout without moving rebalance or store orchestration. |
| Migration preservation | passed | Pool index ordering, available-space summation, max-used-percent filtering, excluded-pool zeroing, and old `store` import compatibility remain preserved. |
| Testing/verification | passed | Focused pool-space helper tests, compile checks, guards, formatting, diff hygiene, Rust risk scan, and full pre-commit passed. |
## Verification Notes
@@ -2380,6 +2395,19 @@ Passed before push:
- `make pre-commit`: passed.
- Three-expert review: passed.
- Issue #660 E-005/E-LAYOUT-004 current slice:
- `cargo test -p rustfs-ecstore layout::pool_space -- --nocapture`: passed.
- `cargo check -p rustfs-ecstore -p rustfs -p rustfs-heal`: passed.
- `./scripts/check_architecture_migration_rules.sh`: passed.
- `./scripts/check_layer_dependencies.sh`: passed.
- `cargo fmt --all --check`: passed.
- `git diff --check`: passed.
- Rust risk scan on changed Rust files: passed; only existing `store.rs`
test-only `expect` calls and an existing `Result<String>` method signature
were present outside the moved helper body.
- `make pre-commit`: passed.
- Three-expert review: passed.
- Issue #660 X-012 current slice:
- `cargo test -p rustfs-extension-schema`: passed.
- `cargo check -p rustfs-extension-schema`: passed.