Garage v0.9 #473

Merged
lx merged 175 commits from next into main 2023-10-10 13:28:29 +00:00
Showing only changes of commit 55c514999e - Show all commits

View file

@ -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
} }