use hashbrown::HashMap;
use noxu_util::lsn::NULL_LSN;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CompareSlot {
pub bin_node_id: u64,
pub key: Vec<u8>,
}
impl CompareSlot {
pub fn new(bin_node_id: u64, key: Vec<u8>) -> Self {
CompareSlot { bin_node_id, key }
}
}
#[derive(Debug, Clone)]
pub struct RevertInfo {
pub revert_lsn: u64,
pub revert_kd: bool,
pub revert_pd: bool,
pub revert_key: Option<Vec<u8>>,
pub revert_data: Option<Vec<u8>>,
pub revert_vlsn: i64,
pub revert_expiration: i32,
}
impl RevertInfo {
pub fn new(revert_lsn: u64, revert_kd: bool) -> Self {
RevertInfo {
revert_lsn,
revert_kd,
revert_pd: false,
revert_key: None,
revert_data: None,
revert_vlsn: -1,
revert_expiration: 0,
}
}
}
#[derive(Debug)]
pub struct TxnChain {
reverts: Vec<(CompareSlot, RevertInfo)>,
slot_map: HashMap<CompareSlot, usize>,
rollback_point: u64,
commit_lsn: u64,
}
impl TxnChain {
pub fn new(rollback_point: u64) -> Self {
TxnChain {
reverts: Vec::new(),
slot_map: HashMap::new(),
rollback_point,
commit_lsn: NULL_LSN.as_u64(),
}
}
pub fn add_revert(
&mut self,
slot: CompareSlot,
revert_lsn: u64,
revert_kd: bool,
) {
if self.slot_map.contains_key(&slot) {
return;
}
let idx = self.reverts.len();
let revert = RevertInfo::new(revert_lsn, revert_kd);
self.slot_map.insert(slot.clone(), idx);
self.reverts.push((slot, revert));
}
pub fn add_revert_info(&mut self, slot: CompareSlot, revert: RevertInfo) {
if self.slot_map.contains_key(&slot) {
return;
}
let idx = self.reverts.len();
self.slot_map.insert(slot.clone(), idx);
self.reverts.push((slot, revert));
}
pub fn set_commit_lsn(&mut self, lsn: u64) {
self.commit_lsn = lsn;
}
pub fn rollback_point(&self) -> u64 {
self.rollback_point
}
pub fn commit_lsn(&self) -> u64 {
self.commit_lsn
}
pub fn reverts(&self) -> &[(CompareSlot, RevertInfo)] {
&self.reverts
}
pub fn get_revert(&self, slot: &CompareSlot) -> Option<&RevertInfo> {
self.slot_map
.get(slot)
.and_then(|&idx| self.reverts.get(idx).map(|(_, r)| r))
}
pub fn len(&self) -> usize {
self.reverts.len()
}
pub fn is_empty(&self) -> bool {
self.reverts.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_chain_empty() {
let chain = TxnChain::new(100);
assert!(chain.is_empty());
assert_eq!(chain.rollback_point(), 100);
assert_eq!(chain.commit_lsn(), NULL_LSN.as_u64());
}
#[test]
fn test_add_revert_basic() {
let mut chain = TxnChain::new(0);
let slot = CompareSlot::new(1, b"key1".to_vec());
chain.add_revert(slot.clone(), 500, false);
assert_eq!(chain.len(), 1);
let r = chain.get_revert(&slot).unwrap();
assert_eq!(r.revert_lsn, 500);
assert!(!r.revert_kd);
}
#[test]
fn test_add_revert_dedup() {
let mut chain = TxnChain::new(0);
let slot = CompareSlot::new(1, b"key1".to_vec());
chain.add_revert(slot.clone(), 500, false);
chain.add_revert(slot.clone(), 200, true);
assert_eq!(chain.len(), 1);
let r = chain.get_revert(&slot).unwrap();
assert_eq!(r.revert_lsn, 500); }
#[test]
fn test_add_multiple_slots() {
let mut chain = TxnChain::new(0);
let s1 = CompareSlot::new(1, b"k1".to_vec());
let s2 = CompareSlot::new(1, b"k2".to_vec());
let s3 = CompareSlot::new(2, b"k1".to_vec());
chain.add_revert(s1.clone(), 100, false);
chain.add_revert(s2.clone(), 200, false);
chain.add_revert(s3.clone(), 300, true);
assert_eq!(chain.len(), 3);
assert_eq!(chain.get_revert(&s1).unwrap().revert_lsn, 100);
assert_eq!(chain.get_revert(&s2).unwrap().revert_lsn, 200);
assert!(chain.get_revert(&s3).unwrap().revert_kd);
}
#[test]
fn test_commit_lsn() {
let mut chain = TxnChain::new(0);
chain.set_commit_lsn(9999);
assert_eq!(chain.commit_lsn(), 9999);
}
}