#[cfg(test)]
mod test;
use crate::{
DagMapId, MapxOrdRawKey, Orphan,
common::{
InstanceId, UnwindMark, ensure_writable,
error::{Result, VsdbError},
staged::StagedRows,
},
};
use serde::{Deserialize, Serialize, de};
use std::{
collections::{HashMap, HashSet},
fmt,
ops::{Deref, DerefMut},
result::Result as StdResult,
};
use vsdb_core::{
basic::mapx_raw::{self, MapxRaw},
common::RawBytes,
};
type DagHead = DagMapRaw;
const PRUNE_CLEARING_KEY: &[u8] = &[1];
const DESTROY_PARENT_KEY: &[u8] = &[2];
#[derive(Debug)]
pub struct DagMapRaw {
data: MapxRaw,
parent: Orphan<Option<DagMapRaw>>,
children: MapxOrdRawKey<DagMapRaw>,
}
impl Clone for DagMapRaw {
fn clone(&self) -> Self {
let requested = self.instance_id();
let mut sources = HashMap::new();
let mut pending = vec![unsafe { self.shadow() }];
while let Some(node) = pending.pop() {
let id = node.instance_id();
if sources.contains_key(&id) {
continue;
}
pending.extend(node.parent.get_value());
pending.extend(node.children.iter().map(|(_, child)| child));
for key in [PRUNE_CLEARING_KEY, DESTROY_PARENT_KEY] {
pending.extend(node.parent.get_aux(key).flatten());
}
sources.insert(id, node);
}
let mut copies: HashMap<_, _> = sources
.iter()
.map(|(&id, source)| {
let ns = source.namespace();
(
id,
Self {
data: source.data.clone(),
parent: Orphan::new_in(&ns, None),
children: MapxOrdRawKey::new_in(&ns),
},
)
})
.collect();
let remap = |node: Self| {
unsafe { copies[&node.instance_id()].shadow() }
};
for (id, source) in &sources {
let mut copy = unsafe { copies[id].shadow() };
copy.parent.set_value(&source.parent.get_value().map(remap));
for (key, child) in source.children.iter() {
copy.children.insert(key, &remap(child));
}
for key in [PRUNE_CLEARING_KEY, DESTROY_PARENT_KEY] {
if let Some(target) = source.parent.get_aux(key) {
copy.parent.set_aux(key, &target.map(remap));
}
}
}
copies.remove(&requested).expect("source node was copied")
}
}
impl Serialize for DagMapRaw {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeTuple;
let mut t = serializer.serialize_tuple(3)?;
t.serialize_element(&self.data)?;
t.serialize_element(&self.parent)?;
t.serialize_element(&self.children)?;
t.end()
}
}
impl<'de> Deserialize<'de> for DagMapRaw {
fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Vis;
impl<'de> de::Visitor<'de> for Vis {
type Value = DagMapRaw;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("DagMapRaw")
}
fn visit_seq<A: de::SeqAccess<'de>>(
self,
mut seq: A,
) -> StdResult<DagMapRaw, A::Error> {
let data: MapxRaw = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let parent: Orphan<Option<DagMapRaw>> = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let children: MapxOrdRawKey<DagMapRaw> = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let ns = data.namespace().id();
if parent.namespace().id() != ns || children.namespace().id() != ns {
return Err(de::Error::custom(
"DagMapRaw components belong to different namespaces",
));
}
Ok(DagMapRaw {
data,
parent,
children,
})
}
}
deserializer.deserialize_tuple(3, Vis)
}
}
impl DagMapRaw {
pub fn new_in(ns: &crate::common::Namespace, parent: Option<&mut Self>) -> Self {
if let Some(p) = &parent {
debug_assert_eq!(
ns.id(),
p.namespace().id(),
"a DAG never spans namespaces: child must live in its \
parent's namespace"
);
}
ns.scope(|| Self::new(parent))
}
pub fn namespace(&self) -> crate::common::Namespace {
self.data.namespace()
}
pub fn new(parent: Option<&mut Self>) -> Self {
match parent {
Some(p) => {
let ns = p.namespace();
ns.scope(|| Self::build(Some(p)))
}
None => Self::build(None),
}
}
fn build(parent: Option<&mut Self>) -> Self {
let r = Self {
parent: Orphan::new(parent.as_deref().map(|p| unsafe { p.shadow() })),
data: MapxRaw::new(),
children: MapxOrdRawKey::new(),
};
if let Some(p) = parent {
let child_id = super::gen_dag_map_id_num().to_le_bytes();
debug_assert!(
p.children.get(child_id).is_none(),
"Child ID already exists — possible ID counter rollback"
);
p.children.insert(child_id, &r);
}
r
}
#[inline(always)]
pub unsafe fn shadow(&self) -> Self {
unsafe {
Self {
data: self.data.shadow(),
parent: self.parent.shadow(),
children: self.children.shadow(),
}
}
}
#[inline(always)]
pub fn instance_id(&self) -> InstanceId {
self.data.instance_id()
}
pub fn save_meta(&self) -> Result<InstanceId> {
let id = self.instance_id();
crate::common::save_instance_meta(id, self)?;
Ok(id)
}
pub fn from_meta(instance_id: impl Into<InstanceId>) -> Result<Self> {
let id = instance_id.into();
crate::common::load_instance_meta_checked(id, Self::instance_id)
}
#[inline(always)]
pub fn is_dead(&self) -> bool {
self.data.iter().all(|(_, v)| v.is_empty())
&& self.parent.get_value().is_none()
&& self.no_children()
}
#[inline(always)]
pub fn no_children(&self) -> bool {
self.children.inner.iter().next().is_none()
}
pub fn child_ids(&self) -> Vec<RawBytes> {
self.children.keys().collect()
}
pub fn child_id(&self, child: &Self) -> Option<RawBytes> {
let child = child.instance_id();
self.children
.iter()
.find_map(|(id, entry)| (entry.instance_id() == child).then_some(id))
}
pub fn get(&self, key: impl AsRef<[u8]>) -> Option<RawBytes> {
let key = key.as_ref();
let mut hdr = self;
let mut hdr_owned;
let mut seen = HashSet::new();
loop {
if !seen.insert(hdr.instance_id()) {
return None;
}
if let Some(v) = hdr.data.get(key) {
return if v.is_empty() { None } else { Some(v) };
}
match hdr.parent.get_value() {
Some(p) => {
hdr_owned = p;
hdr = &hdr_owned;
}
_ => {
return None;
}
}
}
}
#[inline(always)]
pub fn get_mut(&mut self, key: impl AsRef<[u8]>) -> Option<ValueMut<'_>> {
self.data.get_mut(key.as_ref()).and_then(|inner| {
if inner.is_empty() {
return None;
}
Some(ValueMut {
value: inner.clone(),
inner,
dirty: false,
unwind: UnwindMark::new(),
})
})
}
#[inline(always)]
pub fn insert(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) {
assert!(
!value.as_ref().is_empty(),
"empty value is a tombstone; call remove() instead"
);
self.data.insert(key.as_ref(), value)
}
#[inline(always)]
pub fn remove(&mut self, key: impl AsRef<[u8]>) {
self.data.insert(key.as_ref(), [])
}
#[inline(always)]
pub fn prune(self) -> Result<DagHead> {
ensure_writable(&self.namespace(), "DAG pruning")?;
self.prune_mainline()
}
fn prune_mainline(mut self) -> Result<DagHead> {
if let Some(genesis) = self.prune_clearing_target() {
return self.finish_interrupted_clear(genesis);
}
let mut linebuf = self.prune_collect_mainline()?;
if linebuf.is_empty() {
return Ok(self);
}
let mainline_ids: Vec<InstanceId> = {
let mut ids = vec![self.instance_id()];
ids.extend(linebuf.iter().map(|n| n.instance_id()));
ids
};
let pending_reparent: HashSet<RawBytes> =
self.children.iter().map(|(id, _)| id).collect();
Self::prune_destroy_side_branches(
&mut linebuf,
&mainline_ids,
&pending_reparent,
);
self.prune_merge_into_genesis(&mut linebuf)?;
self.namespace().flush();
let kept = self.prune_reparent_children(linebuf.last_mut().unwrap());
self.namespace().flush();
self.mark_consumed_clearing(&mut linebuf);
self.namespace().flush();
self.prune_clear_consumed(&mut linebuf);
self.namespace().flush();
let mut genesis = linebuf.pop().unwrap();
genesis.prune_children_exclude(&kept);
Ok(genesis)
}
fn prune_collect_mainline(&self) -> Result<Vec<Self>> {
let p = match self.parent.get_value() {
Some(p) => p,
_ => return Ok(vec![]),
};
let mut seen = HashSet::new();
seen.insert(self.instance_id());
let mut linebuf = vec![p];
loop {
let current_id = linebuf.last().unwrap().instance_id();
if !seen.insert(current_id) {
return Err(VsdbError::Corrupt {
detail: "DAG mainline contains a parent cycle".to_owned(),
});
}
match linebuf.last().unwrap().parent.get_value() {
Some(p) => linebuf.push(p),
None => break,
}
}
Ok(linebuf)
}
fn prune_destroy_side_branches(
linebuf: &mut [Self],
mainline_ids: &[InstanceId],
pending_reparent: &HashSet<RawBytes>,
) {
for node in linebuf.iter_mut() {
let node_id = node.instance_id();
let doomed: Vec<_> = node
.children
.iter()
.filter(|(id, child)| {
!mainline_ids.contains(&child.instance_id())
&& !pending_reparent.contains(id)
})
.collect();
for (id, mut child) in doomed {
if Self::owned_or_residue(node_id, &child) {
child.destroy();
}
node.children.remove(&id);
}
}
}
fn owned_or_residue(owner_id: InstanceId, child: &Self) -> bool {
match child.parent.get_value() {
None => true,
Some(p) => p.instance_id() == owner_id,
}
}
fn prune_merge_into_genesis(&self, linebuf: &mut [Self]) -> Result<()> {
let mid = linebuf.len() - 1;
let (others, genesis) = linebuf.split_at_mut(mid);
let genesis = &mut genesis[0];
let mut staged = StagedRows::new();
for i in others.iter().rev() {
Self::prune_fold_node(&mut staged, i);
}
Self::prune_fold_node(&mut staged, self);
staged.commit(&mut genesis.data)
}
fn prune_fold_node(staged: &mut StagedRows, src: &Self) {
for (k, v) in src.data.iter() {
if v.is_empty() {
staged.del(k);
} else {
staged.put(k, v);
}
}
}
fn prune_reparent_children(&mut self, genesis: &mut Self) -> Vec<RawBytes> {
let mut kept = vec![];
for (id, mut child) in self.children.iter_mut() {
*child.parent.get_mut() = Some(unsafe { genesis.shadow() });
genesis.children.insert(&id, &child);
kept.push(id);
}
kept
}
fn prune_clearing_target(&self) -> Option<Self> {
self.parent.get_aux(PRUNE_CLEARING_KEY).flatten()
}
fn mark_consumed_clearing(&mut self, linebuf: &mut [Self]) {
let genesis = linebuf.last().unwrap();
let marker = {
Some(unsafe { genesis.shadow() })
};
self.parent.set_aux(PRUNE_CLEARING_KEY, &marker);
let last = linebuf.len() - 1;
for node in &mut linebuf[..last] {
node.parent.set_aux(PRUNE_CLEARING_KEY, &marker);
}
}
fn clear_consumed_data(&mut self) {
*self.parent.get_mut() = None;
self.data.clear();
#[cfg(test)]
test::after_clear_step();
}
fn clear_consumed_nodes(&mut self, others: &mut [Self]) {
self.clear_consumed_data();
for node in others.iter_mut() {
node.clear_consumed_data();
}
self.namespace().flush();
for node in std::iter::once(self).chain(others.iter_mut()) {
node.children.clear();
#[cfg(test)]
test::after_clear_step();
}
}
fn finish_interrupted_clear(mut self, mut genesis: Self) -> Result<DagHead> {
let mut linebuf = self.prune_collect_mainline()?;
if linebuf
.last()
.is_some_and(|node| node.instance_id() == genesis.instance_id())
{
self.mark_consumed_clearing(&mut linebuf);
self.namespace().flush();
}
if !self.no_children() {
self.prune_reparent_children(&mut genesis);
}
self.clear_marked_reachable(&mut genesis);
self.namespace().flush();
let kept = Self::survivor_ids(&genesis);
genesis.prune_children_exclude(&kept);
Ok(genesis)
}
fn survivor_ids(genesis: &Self) -> Vec<RawBytes> {
let genesis_id = genesis.instance_id();
genesis
.children
.iter()
.filter(|(_, child)| {
!child.parent.has_aux(PRUNE_CLEARING_KEY)
&& child
.parent
.get_value()
.is_some_and(|p| p.instance_id() == genesis_id)
})
.map(|(id, _)| id)
.collect()
}
fn clear_marked_reachable(&mut self, root: &mut Self) {
let mut frontier: Vec<Self> = root
.children
.iter()
.filter(|(_, child)| child.parent.has_aux(PRUNE_CLEARING_KEY))
.map(|(_, child)| child)
.collect();
let mut marked = Vec::new();
let mut seen = HashSet::new();
while let Some(node) = frontier.pop() {
if !seen.insert(node.instance_id()) {
continue;
}
frontier.extend(
node.children
.iter()
.filter(|(_, child)| child.parent.has_aux(PRUNE_CLEARING_KEY))
.map(|(_, child)| child),
);
marked.push(node);
}
let drop_from_root: Vec<_> = marked
.iter()
.filter_map(|node| root.child_id(node))
.collect();
let head_id = self.instance_id();
marked.retain(|node| node.instance_id() != head_id);
marked.reverse();
self.clear_consumed_nodes(&mut marked);
for id in drop_from_root {
root.children.remove(&id);
}
}
fn prune_clear_consumed(&mut self, linebuf: &mut [Self]) {
let mid = linebuf.len() - 1;
self.clear_consumed_nodes(&mut linebuf[..mid]);
}
#[inline(always)]
pub fn prune_children_include(&mut self, include_targets: &[impl AsRef<DagMapId>]) {
self.prune_children(include_targets, false);
}
#[inline(always)]
pub fn prune_children_exclude(&mut self, exclude_targets: &[impl AsRef<DagMapId>]) {
self.prune_children(exclude_targets, true);
}
fn prune_children(&mut self, targets: &[impl AsRef<DagMapId>], exclude_mode: bool) {
let self_id = self.instance_id();
let targets = targets.iter().map(|i| i.as_ref()).collect::<HashSet<_>>();
let dropped_children = if exclude_mode {
self.children
.iter()
.filter(|(id, _)| !targets.contains(&id.as_slice()))
.collect::<Vec<_>>()
} else {
self.children
.iter()
.filter(|(id, _)| targets.contains(&id.as_slice()))
.collect::<Vec<_>>()
};
for (id, mut child) in dropped_children.into_iter() {
if Self::owned_or_residue(self_id, &child) {
child.destroy();
}
self.children.remove(&id);
}
}
#[inline(always)]
pub fn destroy(&mut self) {
let self_id = self.instance_id();
if let Some(parent) = self.parent.get_value() {
self.parent.set_aux(DESTROY_PARENT_KEY, &Some(parent));
self.parent.sync_wal();
}
let parent = self
.parent
.get_aux(DESTROY_PARENT_KEY)
.flatten()
.or_else(|| self.parent.get_value());
*self.parent.get_mut() = None;
self.data.clear();
let mut seen = HashSet::new();
seen.insert(self_id);
let mut stack = self
.children
.iter()
.map(|(_, c)| (self_id, c))
.collect::<Vec<_>>();
let mut owned = Vec::new();
while let Some((owner_id, mut node)) = stack.pop() {
if !Self::owned_or_residue(owner_id, &node) {
continue;
}
if !seen.insert(node.instance_id()) {
continue;
}
let node_id = node.instance_id();
node.data.clear();
*node.parent.get_mut() = None;
stack.extend(node.children.iter().map(|(_, c)| (node_id, c)));
owned.push(node);
}
self.namespace().flush();
self.children.clear();
for mut node in owned {
node.children.clear();
}
if let Some(mut parent) = parent {
let child_ids = parent
.children
.iter()
.filter_map(|(id, child)| (child.instance_id() == self_id).then_some(id))
.collect::<Vec<_>>();
for id in child_ids {
parent.children.remove(id);
}
}
}
#[inline(always)]
pub fn is_the_same_instance(&self, other_hdr: &Self) -> bool {
self.data.is_the_same_instance(&other_hdr.data)
}
}
#[derive(Debug)]
pub struct ValueMut<'a> {
value: RawBytes,
inner: mapx_raw::ValueMut<'a>,
dirty: bool,
unwind: UnwindMark,
}
impl Drop for ValueMut<'_> {
fn drop(&mut self) {
if self.dirty && !self.unwind.interrupted() {
assert!(
!self.value.is_empty(),
"empty value is a tombstone; call remove() instead"
);
self.inner.clone_from(&self.value);
}
}
}
impl Deref for ValueMut<'_> {
type Target = RawBytes;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl DerefMut for ValueMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.dirty = true;
&mut self.value
}
}