multi-hdd support (fix #218) #625
1 changed files with 33 additions and 23 deletions
|
@ -11,25 +11,28 @@ type Idx = u16;
|
||||||
|
|
||||||
const DRIVE_NPART: usize = 1024;
|
const DRIVE_NPART: usize = 1024;
|
||||||
|
|
||||||
const DPART_BYTES: (usize, usize) = (2, 3);
|
const HASH_DRIVE_BYTES: (usize, usize) = (2, 3);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub(crate) struct DataLayout {
|
pub(crate) struct DataLayout {
|
||||||
pub(crate) data_dirs: Vec<DataDir>,
|
pub(crate) data_dirs: Vec<DataDir>,
|
||||||
|
|
||||||
/// Primary storage location (index in data_dirs) for each partition
|
/// Primary storage location (index in data_dirs) for each partition
|
||||||
|
/// = the location where the data is supposed to be, blocks are always
|
||||||
|
/// written there (copies in other dirs may be deleted if they exist)
|
||||||
pub(crate) part_prim: Vec<Idx>,
|
pub(crate) part_prim: Vec<Idx>,
|
||||||
/// Secondary storage locations for each partition
|
/// Secondary storage locations for each partition = locations
|
||||||
|
/// where data blocks might be, we check from these dirs when reading
|
||||||
pub(crate) part_sec: Vec<Vec<Idx>>,
|
pub(crate) part_sec: Vec<Vec<Idx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||||
pub(crate) struct DataDir {
|
pub(crate) struct DataDir {
|
||||||
pub(crate) path: PathBuf,
|
pub(crate) path: PathBuf,
|
||||||
pub(crate) state: DataDirState,
|
pub(crate) state: DataDirState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub(crate) enum DataDirState {
|
pub(crate) enum DataDirState {
|
||||||
Active { capacity: u64 },
|
Active { capacity: u64 },
|
||||||
ReadOnly,
|
ReadOnly,
|
||||||
|
@ -55,8 +58,9 @@ impl DataLayout {
|
||||||
assert_eq!(cum_cap, total_cap);
|
assert_eq!(cum_cap, total_cap);
|
||||||
assert_eq!(part_prim.len(), DRIVE_NPART);
|
assert_eq!(part_prim.len(), DRIVE_NPART);
|
||||||
|
|
||||||
// If any of the storage locations is non-empty, add it as a secondary
|
// If any of the storage locations is non-empty, it probably existed before
|
||||||
// storage location for all partitions
|
// this algorithm was added, so add it as a secondary storage location for all partitions
|
||||||
|
// to make sure existing files are not lost
|
||||||
let mut part_sec = vec![vec![]; DRIVE_NPART];
|
let mut part_sec = vec![vec![]; DRIVE_NPART];
|
||||||
for (i, dd) in data_dirs.iter().enumerate() {
|
for (i, dd) in data_dirs.iter().enumerate() {
|
||||||
if dir_not_empty(&dd.path)? {
|
if dir_not_empty(&dd.path)? {
|
||||||
|
@ -75,10 +79,14 @@ impl DataLayout {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update(&mut self, dirs: &DataDirEnum) -> Result<Self, Error> {
|
pub(crate) fn update(&mut self, dirs: &DataDirEnum) -> Result<(), Error> {
|
||||||
// Compute list of new data directories and mapping of old indices
|
// Make list of new data directories, exit if nothing changed
|
||||||
// to new indices
|
|
||||||
let data_dirs = make_data_dirs(dirs)?;
|
let data_dirs = make_data_dirs(dirs)?;
|
||||||
|
if data_dirs == self.data_dirs {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute mapping of old indices to new indices
|
||||||
let old2new = self
|
let old2new = self
|
||||||
.data_dirs
|
.data_dirs
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -114,15 +122,13 @@ impl DataLayout {
|
||||||
// Compute the target number of partitions per data directory
|
// Compute the target number of partitions per data directory
|
||||||
let total_cap = data_dirs.iter().filter_map(|x| x.capacity()).sum::<u64>();
|
let total_cap = data_dirs.iter().filter_map(|x| x.capacity()).sum::<u64>();
|
||||||
let mut cum_cap = 0;
|
let mut cum_cap = 0;
|
||||||
let mut npart_per_dir = vec![];
|
let mut npart_per_dir = vec![0; data_dirs.len()];
|
||||||
for dd in data_dirs.iter() {
|
for (idir, dd) in data_dirs.iter().enumerate() {
|
||||||
if let DataDirState::Active { capacity } = dd.state {
|
if let DataDirState::Active { capacity } = dd.state {
|
||||||
let begin = (cum_cap * DRIVE_NPART as u64) / total_cap;
|
let begin = (cum_cap * DRIVE_NPART as u64) / total_cap;
|
||||||
cum_cap += capacity;
|
cum_cap += capacity;
|
||||||
let end = (cum_cap * DRIVE_NPART as u64) / total_cap;
|
let end = (cum_cap * DRIVE_NPART as u64) / total_cap;
|
||||||
npart_per_dir.push((end - begin) as usize);
|
npart_per_dir[idir] = (end - begin) as usize;
|
||||||
} else {
|
|
||||||
npart_per_dir.push(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert_eq!(cum_cap, total_cap);
|
assert_eq!(cum_cap, total_cap);
|
||||||
|
@ -161,13 +167,16 @@ impl DataLayout {
|
||||||
// add partitions from unassigned
|
// add partitions from unassigned
|
||||||
for (idir, (parts, tgt_npart)) in dir_prim.iter_mut().zip(npart_per_dir.iter()).enumerate()
|
for (idir, (parts, tgt_npart)) in dir_prim.iter_mut().zip(npart_per_dir.iter()).enumerate()
|
||||||
{
|
{
|
||||||
assert!(unassigned.len() >= *tgt_npart - parts.len());
|
if parts.len() < *tgt_npart {
|
||||||
for _ in parts.len()..*tgt_npart {
|
let required = *tgt_npart - parts.len();
|
||||||
|
assert!(unassigned.len() >= required);
|
||||||
|
for _ in 0..required {
|
||||||
let new_part = unassigned.pop().unwrap();
|
let new_part = unassigned.pop().unwrap();
|
||||||
part_prim[new_part] = Some(idir as Idx);
|
part_prim[new_part] = Some(idir as Idx);
|
||||||
part_sec[new_part].retain(|x| *x != idir as Idx);
|
part_sec[new_part].retain(|x| *x != idir as Idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
assert!(part_prim.iter().all(|x| x.is_some()));
|
assert!(part_prim.iter().all(|x| x.is_some()));
|
||||||
|
@ -183,11 +192,12 @@ impl DataLayout {
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
> 0));
|
> 0));
|
||||||
|
|
||||||
Ok(Self {
|
*self = Self {
|
||||||
data_dirs,
|
data_dirs,
|
||||||
part_prim,
|
part_prim,
|
||||||
part_sec,
|
part_sec,
|
||||||
})
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn primary_block_dir(&self, hash: &Hash) -> PathBuf {
|
pub(crate) fn primary_block_dir(&self, hash: &Hash) -> PathBuf {
|
||||||
|
@ -208,8 +218,8 @@ impl DataLayout {
|
||||||
|
|
||||||
fn partition_from(&self, hash: &Hash) -> usize {
|
fn partition_from(&self, hash: &Hash) -> usize {
|
||||||
u16::from_be_bytes([
|
u16::from_be_bytes([
|
||||||
hash.as_slice()[DPART_BYTES.0],
|
hash.as_slice()[HASH_DRIVE_BYTES.0],
|
||||||
hash.as_slice()[DPART_BYTES.1],
|
hash.as_slice()[HASH_DRIVE_BYTES.1],
|
||||||
]) as usize % DRIVE_NPART
|
]) as usize % DRIVE_NPART
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue