mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-21 11:56:38 +00:00
refactor: move ecstore pool-space builder (#3651)
This commit is contained in:
@@ -14,6 +14,11 @@
|
||||
|
||||
use std::slice::Iter;
|
||||
|
||||
use crate::bucket::utils::is_meta_bucketname;
|
||||
use crate::disk::DiskInfo;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::global::{DISK_ASSUME_UNKNOWN_SIZE, DISK_FILL_FRACTION, DISK_MIN_INODES, is_erasure_sd};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PoolAvailableSpace {
|
||||
pub index: usize,
|
||||
@@ -68,10 +73,118 @@ impl ServerPoolsAvailableSpace {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn has_space_for(dis: &[Option<DiskInfo>], size: i64) -> Result<bool> {
|
||||
let size = { if size < 0 { DISK_ASSUME_UNKNOWN_SIZE } else { size as u64 * 2 } };
|
||||
|
||||
let mut available = 0;
|
||||
let mut total = 0;
|
||||
let mut disks_num = 0;
|
||||
|
||||
for disk in dis.iter().flatten() {
|
||||
disks_num += 1;
|
||||
total += disk.total;
|
||||
available += disk.total - disk.used;
|
||||
}
|
||||
|
||||
if disks_num < dis.len() / 2 || disks_num == 0 {
|
||||
return Err(Error::other(format!(
|
||||
"not enough online disks to calculate the available space,need {}, found {}",
|
||||
(dis.len() / 2) + 1,
|
||||
disks_num,
|
||||
)));
|
||||
}
|
||||
|
||||
let per_disk = size / disks_num as u64;
|
||||
|
||||
for disk in dis.iter().flatten() {
|
||||
if !is_erasure_sd().await && disk.free_inodes < DISK_MIN_INODES && disk.used_inodes > 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if disk.free <= per_disk {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
if available < size {
|
||||
return Ok(false);
|
||||
}
|
||||
available -= size;
|
||||
|
||||
let want = total as f64 * (1.0 - DISK_FILL_FRACTION);
|
||||
|
||||
Ok(available > want as u64)
|
||||
}
|
||||
|
||||
pub(crate) async fn build_server_pools_available_space(
|
||||
bucket: &str,
|
||||
size: i64,
|
||||
n_sets: &[usize],
|
||||
infos: &[Vec<Option<DiskInfo>>],
|
||||
) -> ServerPoolsAvailableSpace {
|
||||
let mut server_pools = vec![PoolAvailableSpace::default(); infos.len()];
|
||||
|
||||
for (i, zinfo) in infos.iter().enumerate() {
|
||||
if zinfo.is_empty() {
|
||||
server_pools[i] = PoolAvailableSpace {
|
||||
index: i,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if !is_meta_bucketname(bucket) && !has_space_for(zinfo, size).await.unwrap_or_default() {
|
||||
server_pools[i] = PoolAvailableSpace {
|
||||
index: i,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut available = 0;
|
||||
let mut max_used_pct = 0;
|
||||
for disk in zinfo.iter().flatten() {
|
||||
if disk.total == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
available += disk.total - disk.used;
|
||||
|
||||
let pct_used = disk.used * 100 / disk.total;
|
||||
|
||||
if pct_used > max_used_pct {
|
||||
max_used_pct = pct_used;
|
||||
}
|
||||
}
|
||||
|
||||
available *= n_sets[i] as u64;
|
||||
|
||||
server_pools[i] = PoolAvailableSpace {
|
||||
index: i,
|
||||
available,
|
||||
max_used_pct,
|
||||
}
|
||||
}
|
||||
|
||||
ServerPoolsAvailableSpace(server_pools)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn disk_info(total: u64, used: u64, free: u64) -> DiskInfo {
|
||||
DiskInfo {
|
||||
total,
|
||||
used,
|
||||
free,
|
||||
free_inodes: 1_024,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_available_space_creation_preserves_fields() {
|
||||
let space = PoolAvailableSpace {
|
||||
@@ -123,4 +236,37 @@ mod tests {
|
||||
|
||||
assert_eq!(indexes, vec![0]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_server_pools_available_space_returns_zero_for_empty_pool_info() {
|
||||
let spaces = build_server_pools_available_space("bucket-a", 64, &[1], &[Vec::new()]).await;
|
||||
|
||||
assert_eq!(spaces.0.len(), 1);
|
||||
assert_eq!(spaces.0[0].index, 0);
|
||||
assert_eq!(spaces.0[0].available, 0);
|
||||
assert_eq!(spaces.0[0].max_used_pct, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_server_pools_available_space_computes_available_capacity_and_max_used_pct() {
|
||||
let infos = vec![vec![Some(disk_info(1_000, 100, 900)), Some(disk_info(1_000, 200, 800))]];
|
||||
|
||||
let spaces = build_server_pools_available_space("bucket-a", 64, &[2], &infos).await;
|
||||
|
||||
assert_eq!(spaces.0.len(), 1);
|
||||
assert_eq!(spaces.0[0].index, 0);
|
||||
assert_eq!(spaces.0[0].available, 3_400);
|
||||
assert_eq!(spaces.0[0].max_used_pct, 20);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_server_pools_available_space_skips_capacity_guard_for_meta_bucket() {
|
||||
let infos = vec![vec![Some(disk_info(10, 9, 1)), Some(disk_info(10, 9, 1))]];
|
||||
|
||||
let spaces = build_server_pools_available_space(crate::disk::RUSTFS_META_BUCKET, 1_024, &[1], &infos).await;
|
||||
|
||||
assert_eq!(spaces.0.len(), 1);
|
||||
assert_eq!(spaces.0[0].available, 2);
|
||||
assert_eq!(spaces.0[0].max_used_pct, 90);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user