mirror of
https://github.com/facebook/sapling.git
synced 2024-12-29 16:12:23 +03:00
dag: make NameDag use MultiLog for data consistency
Summary: This ensures IdMap and IdDag are guaranteed consistent in the storage layer. Reviewed By: DurhamG Differential Revision: D19432658 fbshipit-source-id: 00f1a9b4c747baa1f14d78c31d925682317463b4
This commit is contained in:
parent
907aadcdd7
commit
124e275377
@ -54,7 +54,22 @@ impl IdMap {
|
||||
/// access, call [`IdMap::make_writable`] to get a writable instance.
|
||||
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let path = path.as_ref();
|
||||
let log = log::OpenOptions::new()
|
||||
let log = Self::log_open_options().open(path)?;
|
||||
Self::open_from_log(log)
|
||||
}
|
||||
|
||||
pub(crate) fn open_from_log(log: log::Log) -> Result<Self> {
|
||||
let path = log.path().as_opt_path().unwrap().to_path_buf();
|
||||
Ok(Self {
|
||||
log,
|
||||
path,
|
||||
cached_next_free_ids: Default::default(),
|
||||
need_rebuild_non_master: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn log_open_options() -> log::OpenOptions {
|
||||
log::OpenOptions::new()
|
||||
.create(true)
|
||||
.index("id", |data| {
|
||||
assert!(Self::MAGIC_CLEAR_NON_MASTER.len() < 8);
|
||||
@ -81,14 +96,6 @@ impl IdMap {
|
||||
.flush_filter(Some(|_, _| {
|
||||
panic!("programming error: idmap changed by other process")
|
||||
}))
|
||||
.open(path)?;
|
||||
let path = path.to_path_buf();
|
||||
Ok(Self {
|
||||
log,
|
||||
path,
|
||||
cached_next_free_ids: Default::default(),
|
||||
need_rebuild_non_master: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return a [`SyncableIdMap`] instance that provides race-free
|
||||
|
@ -17,6 +17,7 @@ use crate::idmap::SyncableIdMap;
|
||||
use crate::segment::IdDag;
|
||||
use crate::segment::SyncableIdDag;
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use indexedlog::multi;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::Path;
|
||||
|
||||
@ -28,6 +29,8 @@ pub struct NameDag {
|
||||
pub(crate) dag: IdDag,
|
||||
pub(crate) map: IdMap,
|
||||
|
||||
mlog: multi::MultiLog,
|
||||
|
||||
/// Heads added via `add_heads` that are not flushed yet.
|
||||
pending_heads: Vec<VertexName>,
|
||||
}
|
||||
@ -35,16 +38,20 @@ pub struct NameDag {
|
||||
impl NameDag {
|
||||
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let path = path.as_ref();
|
||||
let mut map = IdMap::open(path.join("idmap"))?;
|
||||
// Take a lock so map and dag are loaded consistently. A better (lock-free) way to ensure
|
||||
// this is to use a single "meta" file for both indexedlogs. However that requires some
|
||||
// API changes on the indexedlog side.
|
||||
let _locked = map.prepare_filesystem_sync()?;
|
||||
map.reload()?;
|
||||
let dag = IdDag::open(path.join("segments"))?;
|
||||
let opts = multi::OpenOptions::from_name_opts(vec![
|
||||
("idmap", IdMap::log_open_options()),
|
||||
("iddag", IdDag::log_open_options()),
|
||||
]);
|
||||
let mut mlog = opts.open(path)?;
|
||||
let mut logs = mlog.detach_logs();
|
||||
let dag_log = logs.pop().unwrap();
|
||||
let map_log = logs.pop().unwrap();
|
||||
let map = IdMap::open_from_log(map_log)?;
|
||||
let dag = IdDag::open_from_log(dag_log)?;
|
||||
Ok(Self {
|
||||
dag,
|
||||
map,
|
||||
mlog,
|
||||
pending_heads: Default::default(),
|
||||
})
|
||||
}
|
||||
@ -81,6 +88,10 @@ impl NameDag {
|
||||
}
|
||||
|
||||
// Take lock.
|
||||
//
|
||||
// Reload meta. This drops in-memory changes, which is fine because we have
|
||||
// checked there are no in-memory changes at the beginning.
|
||||
let lock = self.mlog.lock()?;
|
||||
let mut map = self.map.prepare_filesystem_sync()?;
|
||||
let mut dag = self.dag.prepare_filesystem_sync()?;
|
||||
|
||||
@ -96,6 +107,7 @@ impl NameDag {
|
||||
// Write to disk.
|
||||
map.sync()?;
|
||||
dag.sync(std::iter::once(&mut self.dag))?;
|
||||
self.mlog.write_meta(&lock)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,25 @@ impl IdDag {
|
||||
/// Open [`IdDag`] at the given directory. Create it on demand.
|
||||
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let path = path.as_ref();
|
||||
let log = log::OpenOptions::new()
|
||||
let log = Self::log_open_options().open(path)?;
|
||||
Self::open_from_log(log)
|
||||
}
|
||||
|
||||
pub(crate) fn open_from_log(log: log::Log) -> Result<Self> {
|
||||
let path = log.path().as_opt_path().unwrap().to_path_buf();
|
||||
let max_level = Self::max_level_from_log(&log)?;
|
||||
let mut dag = Self {
|
||||
log,
|
||||
path,
|
||||
max_level,
|
||||
new_seg_size: 16, // see D16660078 for this default setting
|
||||
};
|
||||
dag.build_all_high_level_segments(false)?;
|
||||
Ok(dag)
|
||||
}
|
||||
|
||||
pub(crate) fn log_open_options() -> log::OpenOptions {
|
||||
log::OpenOptions::new()
|
||||
.create(true)
|
||||
.index("level-head", |data| {
|
||||
// (level, high)
|
||||
@ -147,16 +165,6 @@ impl IdDag {
|
||||
}
|
||||
result
|
||||
})
|
||||
.open(path)?;
|
||||
let max_level = Self::max_level_from_log(&log)?;
|
||||
let mut dag = Self {
|
||||
log,
|
||||
path: path.to_path_buf(),
|
||||
max_level,
|
||||
new_seg_size: 16, // see D16660078 for this default setting
|
||||
};
|
||||
dag.build_all_high_level_segments(false)?;
|
||||
Ok(dag)
|
||||
}
|
||||
|
||||
fn max_level_from_log(log: &log::Log) -> Result<Level> {
|
||||
|
Loading…
Reference in New Issue
Block a user