use std::collections::BTreeMap;
use std::sync::Arc;
use crate::shard::actor::decode_sequence_key;
use crate::store::NodeStore;
use crate::tree::{Hash, Node, TreeError};
use crate::ttl::filter::{Visibility, visible_value};
use crate::wal::{Mutation, WalBuffer, WalError};
use super::handle::{ShardError, StreamSeq};
pub(super) type LiveStreamIndex = BTreeMap<Vec<u8>, u64>;
pub(super) type SequenceIndexErrors = BTreeMap<Vec<u8>, String>;
#[derive(Debug)]
pub(super) struct StreamIndexSnapshot {
pub(super) live: LiveStreamIndex,
pub(super) errors: SequenceIndexErrors,
}
pub(super) fn scan_index(
index: &LiveStreamIndex,
errors: &SequenceIndexErrors,
) -> Result<Vec<StreamSeq>, ShardError> {
if let Some(message) = errors.values().next() {
return Err(ShardError::Wal(WalError::TreeError(message.clone())));
}
Ok(index.iter().map(|(key, seq)| (key.clone(), *seq)).collect())
}
pub(super) fn rebuild<S>(
store: &S,
committed_root: Option<Hash>,
) -> Result<StreamIndexSnapshot, ShardError>
where
S: NodeStore + ?Sized,
{
let mut sequence_entries = BTreeMap::new();
if let Some(root) = committed_root {
collect_sequence_entries(store, root, &mut sequence_entries)?;
}
Ok(sequence_entries_to_snapshot(sequence_entries))
}
#[cfg(test)]
pub(super) fn full_walk_with_buffer<S>(
store: &S,
committed_root: Option<Hash>,
buffer: &WalBuffer,
) -> Result<Vec<StreamSeq>, ShardError>
where
S: NodeStore + ?Sized,
{
let mut sequence_entries = BTreeMap::new();
if let Some(root) = committed_root {
collect_sequence_entries(store, root, &mut sequence_entries)?;
}
apply_buffer_to_entries(buffer, &mut sequence_entries);
let snapshot = sequence_entries_to_snapshot(sequence_entries);
scan_index(&snapshot.live, &snapshot.errors)
}
pub(super) fn apply_committed_buffer(
index: &mut LiveStreamIndex,
errors: &mut SequenceIndexErrors,
buffer: &WalBuffer,
) {
for mutation in buffer {
match mutation {
Mutation::Put { key, value } => apply_sequence_put(index, errors, key, value),
Mutation::Delete { key } => apply_sequence_delete(index, errors, key),
}
}
}
fn apply_sequence_put(
index: &mut LiveStreamIndex,
errors: &mut SequenceIndexErrors,
key: &[u8],
value: &[u8],
) {
let Some(stream_key) = decode_sequence_key(key) else {
return;
};
match visible_value(value) {
Ok(Visibility::Live(logical)) => match decode_seq_value(&logical) {
Ok(next_seq) => {
errors.remove(stream_key);
index.insert(stream_key.to_vec(), next_seq);
}
Err(message) => {
index.remove(stream_key);
errors.insert(stream_key.to_vec(), message);
}
},
Ok(Visibility::Expired) => {
index.remove(stream_key);
errors.remove(stream_key);
}
Err(error) => {
index.remove(stream_key);
errors.insert(stream_key.to_vec(), error.to_string());
}
}
}
fn apply_sequence_delete(
index: &mut LiveStreamIndex,
errors: &mut SequenceIndexErrors,
key: &[u8],
) {
if let Some(stream_key) = decode_sequence_key(key) {
index.remove(stream_key);
errors.remove(stream_key);
}
}
#[cfg(test)]
fn apply_buffer_to_entries(buffer: &WalBuffer, entries: &mut BTreeMap<Vec<u8>, Vec<u8>>) {
for mutation in buffer {
match mutation {
Mutation::Put { key, value } if decode_sequence_key(key).is_some() => {
entries.insert(key.clone(), value.clone());
}
Mutation::Delete { key } if decode_sequence_key(key).is_some() => {
entries.remove(key);
}
Mutation::Put { .. } | Mutation::Delete { .. } => {}
}
}
}
fn sequence_entries_to_snapshot(entries: BTreeMap<Vec<u8>, Vec<u8>>) -> StreamIndexSnapshot {
let mut snapshot = StreamIndexSnapshot {
live: LiveStreamIndex::new(),
errors: SequenceIndexErrors::new(),
};
for (key, value) in entries {
apply_sequence_put(&mut snapshot.live, &mut snapshot.errors, &key, &value);
}
snapshot
}
fn collect_sequence_entries<S>(
store: &S,
root: Hash,
out: &mut BTreeMap<Vec<u8>, Vec<u8>>,
) -> Result<(), ShardError>
where
S: NodeStore + ?Sized,
{
let mut stack = vec![root];
while let Some(hash) = stack.pop() {
match &*load_node(store, hash)? {
Node::Leaf(leaf) => {
for (key, value) in leaf.entries() {
if decode_sequence_key(key).is_some() {
out.insert(key.clone(), value.clone());
}
}
}
Node::Internal(internal) => {
for (_separator, child) in internal.children() {
stack.push(*child);
}
}
}
}
Ok(())
}
fn load_node<S>(store: &S, hash: Hash) -> Result<Arc<Node>, ShardError>
where
S: NodeStore + ?Sized,
{
store
.get(&hash)
.map_err(|error| ShardError::Wal(WalError::TreeError(error.to_string())))?
.ok_or(ShardError::Tree(TreeError::MissingNode { hash }))
}
fn decode_seq_value(value: &[u8]) -> Result<u64, String> {
value
.try_into()
.map(u64::from_be_bytes)
.map_err(|_| "invalid sequence metadata".to_owned())
}