use super::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoryEntry {
pub timestamp_secs: u64,
pub timestamp_ns: u64,
pub record: Option<Record>,
pub is_tombstone: bool,
}
fn history_impl(txn: &Transaction, key: &str, opts: &HistoryOptions) -> Result<Vec<HistoryEntry>> {
let mut upper = key.as_bytes().to_vec();
upper.push(0x00);
let mut cursor = txn.history_with_options(key.as_bytes(), upper.as_slice(), opts)?;
let mut entries = Vec::new();
while cursor.next()? {
let key_ref = cursor.key();
if key_ref.user_key() != key.as_bytes() {
continue;
}
let is_tombstone = key_ref.is_tombstone();
let ts_ns = key_ref.timestamp();
let ts_secs = ts_ns / 1_000_000_000;
let record = if is_tombstone {
None
} else {
match cursor.value() {
Ok(bytes) => rmps::from_slice::<Record>(&bytes).ok(),
Err(_) => None,
}
};
entries.push(HistoryEntry {
timestamp_secs: ts_secs,
timestamp_ns: ts_ns,
record,
is_tombstone,
});
}
entries.sort_by_key(|e| std::cmp::Reverse(e.timestamp_ns));
Ok(entries)
}
impl Store {
pub fn history(&self, key: &str, limit: usize) -> Result<Vec<HistoryEntry>> {
anyhow::ensure!(!key.is_empty(), "history key must not be empty");
let tree = self.tree_for(key);
let txn = tree.begin_with_mode(Mode::ReadOnly)?;
let mut opts = HistoryOptions::new().with_tombstones(true);
if limit > 0 {
opts = opts.with_limit(limit);
}
history_impl(&txn, key, &opts)
}
pub fn history_since(
&self,
key: &str,
since_ts: u64,
limit: usize,
) -> Result<Vec<HistoryEntry>> {
anyhow::ensure!(!key.is_empty(), "history key must not be empty");
let tree = self.tree_for(key);
let txn = tree.begin_with_mode(Mode::ReadOnly)?;
let since_ns = since_ts.saturating_mul(1_000_000_000);
let mut opts = HistoryOptions::new()
.with_tombstones(true)
.with_ts_range(since_ns, u64::MAX);
if limit > 0 {
opts = opts.with_limit(limit);
}
history_impl(&txn, key, &opts)
}
pub async fn records_since(&self, since_ts: u64, limit: usize) -> Result<Vec<Record>> {
let mut results = Vec::new();
for ns in KNOWLEDGE_NAMESPACES {
let records = self.scan_prefix(ns).await?;
for r in records {
if r.updated_at >= since_ts {
results.push(r);
}
}
}
results.sort_by(|a, b| {
b.updated_at
.cmp(&a.updated_at)
.then_with(|| a.key.cmp(&b.key))
});
if limit > 0 && results.len() > limit {
results.truncate(limit);
}
Ok(results)
}
}