pub type TxnId = u64;
pub type Timestamp = u64;
#[derive(Debug, Clone)]
pub struct VisibilityContext {
pub reader_txn_id: TxnId,
pub snapshot_ts: Timestamp,
pub active_txn_ids: std::collections::HashSet<TxnId>,
}
impl VisibilityContext {
pub fn new(reader_txn_id: TxnId, snapshot_ts: Timestamp) -> Self {
Self {
reader_txn_id,
snapshot_ts,
active_txn_ids: std::collections::HashSet::new(),
}
}
pub fn with_active_txns(
reader_txn_id: TxnId,
snapshot_ts: Timestamp,
active_txn_ids: std::collections::HashSet<TxnId>,
) -> Self {
Self {
reader_txn_id,
snapshot_ts,
active_txn_ids,
}
}
pub fn is_committed_before(&self, txn_id: TxnId, commit_ts: Option<Timestamp>) -> bool {
match commit_ts {
Some(ts) => ts < self.snapshot_ts && !self.active_txn_ids.contains(&txn_id),
None => false,
}
}
}
#[derive(Debug, Clone)]
pub struct VersionMeta {
pub created_by: TxnId,
pub created_ts: Timestamp,
pub deleted_by: TxnId,
pub deleted_ts: Timestamp,
pub commit_ts: Timestamp,
}
impl VersionMeta {
pub fn new_uncommitted(created_by: TxnId, created_ts: Timestamp) -> Self {
Self {
created_by,
created_ts,
deleted_by: 0,
deleted_ts: 0,
commit_ts: 0,
}
}
pub fn is_visible(&self, ctx: &VisibilityContext) -> bool {
if self.commit_ts == 0 {
return self.created_by == ctx.reader_txn_id;
}
if self.commit_ts >= ctx.snapshot_ts {
return false;
}
if self.deleted_by != 0 && self.deleted_ts < ctx.snapshot_ts {
return false;
}
true
}
pub fn commit(&mut self, commit_ts: Timestamp) {
self.commit_ts = commit_ts;
}
pub fn delete(&mut self, deleted_by: TxnId, deleted_ts: Timestamp) {
self.deleted_by = deleted_by;
self.deleted_ts = deleted_ts;
}
pub fn is_committed(&self) -> bool {
self.commit_ts != 0
}
pub fn is_deleted(&self) -> bool {
self.deleted_by != 0
}
}
pub trait MvccVersionChain {
type Value;
fn get_visible(&self, ctx: &VisibilityContext) -> Option<&Self::Value>;
fn get_latest(&self) -> Option<&Self::Value>;
fn version_count(&self) -> usize;
fn is_empty(&self) -> bool {
self.version_count() == 0
}
}
pub trait MvccVersionChainMut: MvccVersionChain {
fn add_uncommitted(&mut self, value: Self::Value, txn_id: TxnId);
fn commit_version(&mut self, txn_id: TxnId, commit_ts: Timestamp) -> bool;
fn delete_version(&mut self, txn_id: TxnId, delete_ts: Timestamp) -> bool;
fn gc(&mut self, min_visible_ts: Timestamp) -> (usize, usize);
}
pub trait WriteConflictDetection {
fn has_write_conflict(&self, txn_id: TxnId) -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_meta_visibility() {
let mut meta = VersionMeta::new_uncommitted(1, 100);
let ctx = VisibilityContext::new(1, 200);
assert!(meta.is_visible(&ctx));
let ctx2 = VisibilityContext::new(2, 200);
assert!(!meta.is_visible(&ctx2));
meta.commit(150);
assert!(meta.is_visible(&ctx2));
let ctx3 = VisibilityContext::new(3, 100);
assert!(!meta.is_visible(&ctx3));
}
#[test]
fn test_version_meta_deletion() {
let mut meta = VersionMeta::new_uncommitted(1, 100);
meta.commit(150);
meta.delete(2, 200);
let ctx = VisibilityContext::new(3, 180);
assert!(meta.is_visible(&ctx));
let ctx2 = VisibilityContext::new(3, 250);
assert!(!meta.is_visible(&ctx2));
}
#[test]
fn test_visibility_context_committed_before() {
let mut active = std::collections::HashSet::new();
active.insert(5);
let ctx = VisibilityContext::with_active_txns(1, 200, active);
assert!(ctx.is_committed_before(2, Some(100)));
assert!(!ctx.is_committed_before(3, Some(250)));
assert!(!ctx.is_committed_before(5, Some(100)));
assert!(!ctx.is_committed_before(6, None));
}
}