diff --git a/server/db/db.rs b/server/db/db.rs index 3c22e63..63d91e0 100644 --- a/server/db/db.rs +++ b/server/db/db.rs @@ -334,7 +334,7 @@ impl SampleFileDir { } /// Returns expected existing metadata when opening this directory. - fn meta(&self, db_uuid: &Uuid) -> schema::DirMeta { + fn expected_meta(&self, db_uuid: &Uuid) -> schema::DirMeta { let mut meta = schema::DirMeta::default(); meta.db_uuid.extend_from_slice(&db_uuid.as_bytes()[..]); meta.dir_uuid.extend_from_slice(&self.uuid.as_bytes()[..]); @@ -1182,20 +1182,20 @@ impl LockedDatabase { if dir.dir.is_some() { continue; } - let mut meta = dir.meta(&self.uuid); + let mut expected_meta = dir.expected_meta(&self.uuid); if let Some(o) = self.open.as_ref() { - let open = meta.in_progress_open.set_default(); + let open = expected_meta.in_progress_open.set_default(); open.id = o.id; open.uuid.extend_from_slice(&o.uuid.as_bytes()[..]); } - let d = dir::SampleFileDir::open(&dir.path, &meta) + let d = dir::SampleFileDir::open(&dir.path, &expected_meta) .map_err(|e| e.context(format!("Failed to open dir {}", dir.path)))?; if self.open.is_none() { // read-only mode; it's already fully opened. dir.dir = Some(d); } else { // read-write mode; there are more steps to do. - e.insert((meta, d)); + e.insert((expected_meta, d)); } } @@ -1221,8 +1221,7 @@ impl LockedDatabase { for (id, (mut meta, d)) in in_progress.drain() { let dir = self.sample_file_dirs_by_id.get_mut(&id).unwrap(); - meta.last_complete_open.clear(); - mem::swap(&mut meta.last_complete_open, &mut meta.in_progress_open); + meta.last_complete_open = meta.in_progress_open.take().into(); d.write_meta(&meta)?; dir.dir = Some(d); } @@ -1786,14 +1785,13 @@ impl LockedDatabase { path, uuid, dir: Some(dir), - last_complete_open: None, + last_complete_open: Some(*o), garbage_needs_unlink: FnvHashSet::default(), garbage_unlinked: Vec::new(), }), Entry::Occupied(_) => bail!("duplicate sample file dir id {}", id), }; - d.last_complete_open = Some(*o); - mem::swap(&mut meta.last_complete_open, &mut meta.in_progress_open); + meta.last_complete_open = meta.in_progress_open.take().into(); d.dir.as_ref().unwrap().write_meta(&meta)?; Ok(id) } @@ -1815,7 +1813,7 @@ impl LockedDatabase { ); } let dir = match d.get_mut().dir.take() { - None => dir::SampleFileDir::open(&d.get().path, &d.get().meta(&self.uuid))?, + None => dir::SampleFileDir::open(&d.get().path, &d.get().expected_meta(&self.uuid))?, Some(arc) => match Arc::strong_count(&arc) { 1 => { d.get_mut().dir = Some(arc); // put it back. @@ -1830,7 +1828,7 @@ impl LockedDatabase { &d.get().path ); } - let mut meta = d.get().meta(&self.uuid); + let mut meta = d.get().expected_meta(&self.uuid); meta.in_progress_open = meta.last_complete_open.take().into(); dir.write_meta(&meta)?; if self diff --git a/server/db/dir/mod.rs b/server/db/dir/mod.rs index ad48e7f..f0f362e 100644 --- a/server/db/dir/mod.rs +++ b/server/db/dir/mod.rs @@ -212,8 +212,8 @@ impl SampleFileDir { /// /// `db_meta.in_progress_open` should be filled if the directory should be opened in read/write /// mode; absent in read-only mode. - pub fn open(path: &str, db_meta: &schema::DirMeta) -> Result, Error> { - let read_write = db_meta.in_progress_open.is_some(); + pub fn open(path: &str, expected_meta: &schema::DirMeta) -> Result, Error> { + let read_write = expected_meta.in_progress_open.is_some(); let s = SampleFileDir::open_self(path, false)?; s.fd.lock(if read_write { FlockArg::LockExclusiveNonblock @@ -222,45 +222,50 @@ impl SampleFileDir { }) .map_err(|e| e.context(format!("unable to lock dir {}", path)))?; let dir_meta = read_meta(&s.fd).map_err(|e| e.context("unable to read meta file"))?; - if !SampleFileDir::consistent(db_meta, &dir_meta) { - let serialized = db_meta - .write_length_delimited_to_bytes() - .expect("proto3->vec is infallible"); + if let Err(e) = SampleFileDir::check_consistent(expected_meta, &dir_meta) { bail!( - "metadata mismatch.\ndb: {:#?}\ndir: {:#?}\nserialized db: {:#?}", - db_meta, - &dir_meta, - &serialized + "metadata mismatch: {}.\nexpected:\n{:#?}\n\nactual:\n{:#?}", + e, + expected_meta, + &dir_meta ); } - if db_meta.in_progress_open.is_some() { - s.write_meta(db_meta)?; + if expected_meta.in_progress_open.is_some() { + s.write_meta(expected_meta)?; } Ok(s) } - /// Returns true if the existing directory and database metadata are consistent; the directory + /// Checks that the existing directory and database metadata are consistent; the directory /// is then openable. - pub(crate) fn consistent(db_meta: &schema::DirMeta, dir_meta: &schema::DirMeta) -> bool { - if dir_meta.db_uuid != db_meta.db_uuid { - return false; + pub(crate) fn check_consistent( + expected_meta: &schema::DirMeta, + actual_meta: &schema::DirMeta, + ) -> Result<(), String> { + if actual_meta.db_uuid != expected_meta.db_uuid { + return Err("db uuid mismatch".into()); } - if dir_meta.dir_uuid != db_meta.dir_uuid { - return false; + if actual_meta.dir_uuid != expected_meta.dir_uuid { + return Err("dir uuid mismatch".into()); } - if db_meta.last_complete_open.is_some() - && (db_meta.last_complete_open != dir_meta.last_complete_open - && db_meta.last_complete_open != dir_meta.in_progress_open) + if expected_meta.last_complete_open.is_some() + && (expected_meta.last_complete_open != actual_meta.last_complete_open + && expected_meta.last_complete_open != actual_meta.in_progress_open) { - return false; + return Err(format!( + "expected open {:?}; but got {:?} (complete) or {:?} (in progress)", + &expected_meta.last_complete_open, + &actual_meta.last_complete_open, + &actual_meta.in_progress_open, + )); } - if db_meta.last_complete_open.is_none() && dir_meta.last_complete_open.is_some() { - return false; + if expected_meta.last_complete_open.is_none() && actual_meta.last_complete_open.is_some() { + return Err("expected never opened".into()); } - true + Ok(()) } pub(crate) fn create( diff --git a/server/db/upgrade/v4_to_v5.rs b/server/db/upgrade/v4_to_v5.rs index c2eec28..3940114 100644 --- a/server/db/upgrade/v4_to_v5.rs +++ b/server/db/upgrade/v4_to_v5.rs @@ -37,11 +37,12 @@ fn maybe_upgrade_meta(dir: &dir::Fd, db_meta: &schema::DirMeta) -> Result