##// END OF EJS Templates
revlog: update data file record before index rename...
revlog: update data file record before index rename When migrating from inline to non-inline data storage, the data file is recorded initially as zero sized so that it is removed on failure. But the record has to be updated before the index is renamed, otherwise data is lost on rollback. Differential Revision: https://phab.mercurial-scm.org/D10725

File last commit:

r47375:842f2372 default
r48065:46b828b8 default
Show More
nodemap_docket.rs
121 lines | 3.9 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 use crate::errors::{HgError, HgResultExt};
Simon Sapin
rhg: Don’t attempt to read persistent nodemap without .hg/requires opt-in...
r47375 use crate::requirements;
Simon Sapin
rust: use the bytes-cast crate to parse persistent nodemaps...
r47119 use bytes_cast::{unaligned, BytesCast};
Simon Sapin
rhg: use persistent nodemap when available...
r46706 use memmap::Mmap;
use std::path::{Path, PathBuf};
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 use super::revlog::RevlogError;
use crate::repo::Repo;
Simon Sapin
rhg: use persistent nodemap when available...
r46706 use crate::utils::strip_suffix;
const ONDISK_VERSION: u8 = 1;
pub(super) struct NodeMapDocket {
pub data_length: usize,
// TODO: keep here more of the data from `parse()` when we need it
}
Simon Sapin
rust: use the bytes-cast crate to parse persistent nodemaps...
r47119 #[derive(BytesCast)]
#[repr(C)]
struct DocketHeader {
uid_size: u8,
_tip_rev: unaligned::U64Be,
data_length: unaligned::U64Be,
_data_unused: unaligned::U64Be,
tip_node_size: unaligned::U64Be,
}
Simon Sapin
rhg: use persistent nodemap when available...
r46706 impl NodeMapDocket {
/// Return `Ok(None)` when the caller should proceed without a persistent
/// nodemap:
///
/// * This revlog does not have a `.n` docket file (it is not generated for
/// small revlogs), or
/// * The docket has an unsupported version number (repositories created by
/// later hg, maybe that should be a requirement instead?), or
/// * The docket file points to a missing (likely deleted) data file (this
/// can happen in a rare race condition).
pub fn read_from_file(
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 repo: &Repo,
Simon Sapin
rhg: use persistent nodemap when available...
r46706 index_path: &Path,
) -> Result<Option<(Self, Mmap)>, RevlogError> {
Simon Sapin
rhg: Don’t attempt to read persistent nodemap without .hg/requires opt-in...
r47375 if !repo
.requirements()
.contains(requirements::NODEMAP_REQUIREMENT)
{
// If .hg/requires does not opt it, don’t try to open a nodemap
return Ok(None);
}
Simon Sapin
rhg: use persistent nodemap when available...
r46706 let docket_path = index_path.with_extension("n");
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 let docket_bytes = if let Some(bytes) =
repo.store_vfs().read(&docket_path).io_not_found_as_none()?
{
bytes
} else {
return Ok(None);
Simon Sapin
rhg: use persistent nodemap when available...
r46706 };
Simon Sapin
rust: use the bytes-cast crate to parse persistent nodemaps...
r47119 let input = if let Some((&ONDISK_VERSION, rest)) =
Simon Sapin
rhg: use persistent nodemap when available...
r46706 docket_bytes.split_first()
{
rest
} else {
return Ok(None);
};
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 /// Treat any error as a parse error
fn parse<T, E>(result: Result<T, E>) -> Result<T, RevlogError> {
result.map_err(|_| {
HgError::corrupted("nodemap docket parse error").into()
})
}
let (header, rest) = parse(DocketHeader::from_bytes(input))?;
Simon Sapin
rust: use the bytes-cast crate to parse persistent nodemaps...
r47119 let uid_size = header.uid_size as usize;
Simon Sapin
rhg: use persistent nodemap when available...
r46706 // TODO: do we care about overflow for 4 GB+ nodemap files on 32-bit
// systems?
Simon Sapin
rust: use the bytes-cast crate to parse persistent nodemaps...
r47119 let tip_node_size = header.tip_node_size.get() as usize;
let data_length = header.data_length.get() as usize;
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 let (uid, rest) = parse(u8::slice_from_bytes(rest, uid_size))?;
let (_tip_node, _rest) =
parse(u8::slice_from_bytes(rest, tip_node_size))?;
let uid = parse(std::str::from_utf8(uid))?;
Simon Sapin
rhg: use persistent nodemap when available...
r46706 let docket = NodeMapDocket { data_length };
let data_path = rawdata_path(&docket_path, uid);
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 // TODO: use `vfs.read()` here when the `persistent-nodemap.mmap`
Simon Sapin
rhg: use persistent nodemap when available...
r46706 // config is false?
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 if let Some(mmap) = repo
.store_vfs()
.mmap_open(&data_path)
.io_not_found_as_none()?
{
if mmap.len() >= data_length {
Ok(Some((docket, mmap)))
} else {
Err(HgError::corrupted("persistent nodemap too short").into())
Simon Sapin
rhg: use persistent nodemap when available...
r46706 }
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 } else {
Simon Sapin
rhg: Don’t attempt to read persistent nodemap without .hg/requires opt-in...
r47375 // Even if .hg/requires opted in, some revlogs are deemed small
// enough to not need a persistent nodemap.
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 Ok(None)
Simon Sapin
rhg: use persistent nodemap when available...
r46706 }
}
}
fn rawdata_path(docket_path: &Path, uid: &str) -> PathBuf {
let docket_name = docket_path
.file_name()
.expect("expected a base name")
.to_str()
.expect("expected an ASCII file name in the store");
let prefix = strip_suffix(docket_name, ".n.a")
.or_else(|| strip_suffix(docket_name, ".n"))
.expect("expected docket path in .n or .n.a");
let name = format!("{}-{}.nd", prefix, uid);
docket_path
.parent()
.expect("expected a non-root path")
.join(name)
}