use std::time::{Duration, Instant};
use serde::{Deserialize, Serialize};
use crate::adapter::net::current_timestamp_micros as unix_micros_now;
use super::state::{FoldEntry, FoldState, NodeId};
use super::FoldKind;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FoldSnapshotEntry<K: FoldKind> {
pub key: K::Key,
pub payload: K::Payload,
pub node_id: NodeId,
pub generation: u64,
pub received_offset_ns: u64,
pub expires_offset_ns: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FoldSnapshot<K: FoldKind> {
pub kind: u16,
pub taken_at_unix_us: u64,
pub entries: Vec<FoldSnapshotEntry<K>>,
}
impl<K: FoldKind> FoldSnapshot<K> {
pub fn from_state(state: &FoldState<K>) -> Self {
let now = Instant::now();
let taken_at_unix_us = unix_micros_now();
let mut entries = Vec::with_capacity(state.entries.len());
for (key, entry) in state.entries.iter() {
if entry.expires_at <= now {
continue;
}
let received_offset_ns =
now.saturating_duration_since(entry.received_at).as_nanos() as u64;
let expires_offset_ns =
entry.expires_at.saturating_duration_since(now).as_nanos() as u64;
entries.push(FoldSnapshotEntry {
key: key.clone(),
payload: entry.payload.clone(),
node_id: entry.node_id,
generation: entry.generation,
received_offset_ns,
expires_offset_ns,
});
}
Self {
kind: K::KIND_ID,
taken_at_unix_us,
entries,
}
}
pub(super) fn rehydrate_entry(
snap_entry: &FoldSnapshotEntry<K>,
anchor: Instant,
elapsed_since_dump: Duration,
) -> Option<FoldEntry<K>> {
let expires_offset = Duration::from_nanos(snap_entry.expires_offset_ns);
if elapsed_since_dump >= expires_offset {
return None;
}
let remaining_ttl = expires_offset - elapsed_since_dump;
let aged_received_offset =
Duration::from_nanos(snap_entry.received_offset_ns).saturating_add(elapsed_since_dump);
let received_at = anchor.checked_sub(aged_received_offset).unwrap_or(anchor);
let expires_at = anchor.checked_add(remaining_ttl).unwrap_or(anchor);
Some(FoldEntry {
payload: snap_entry.payload.clone(),
node_id: snap_entry.node_id,
generation: snap_entry.generation,
received_at,
expires_at,
})
}
}