use std::sync::Arc;
use super::{txn_key_entry_comparison::TxnKeyEntryComparison, watch_version_map::WatchVersionMap};
#[derive(Debug, Clone)]
struct WatchedKeySlice {
key: Box<[u8]>,
hash: u64,
version: u64,
is_watched: bool,
}
pub struct TxnWatchedKeysContainer {
key_slices: Vec<WatchedKeySlice>,
version_map: Arc<WatchVersionMap>,
}
impl TxnWatchedKeysContainer {
pub fn new(version_map: Arc<WatchVersionMap>) -> Self {
Self {
key_slices: Vec::new(),
version_map,
}
}
pub fn reset(&mut self) {
self.key_slices.clear();
}
pub fn remove_watch(&mut self, key: &[u8]) -> bool {
for slice in &mut self.key_slices {
if slice.key.as_ref() == key {
slice.is_watched = false;
return true;
}
}
false
}
pub fn add_watch(&mut self, key: &[u8]) {
let hash = TxnKeyEntryComparison::key_hash(key) as u64;
let version = self.version_map.read_version(hash);
self.key_slices.push(WatchedKeySlice {
key: key.into(),
hash,
version,
is_watched: true,
});
}
pub fn validate_watch_version(&self) -> bool {
for slice in &self.key_slices {
if !slice.is_watched {
continue;
}
if self.version_map.read_version(slice.hash) != slice.version {
return false;
}
}
true
}
pub fn save_keys_to_lock(&self) -> impl Iterator<Item = &[u8]> {
self
.key_slices
.iter()
.filter(|slice| slice.is_watched)
.map(|slice| slice.key.as_ref())
}
pub fn save_keys_to_key_list(&self) -> impl Iterator<Item = &[u8]> {
self.key_slices.iter().map(|slice| slice.key.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn container() -> (TxnWatchedKeysContainer, Arc<WatchVersionMap>) {
let map = Arc::new(WatchVersionMap::new(64));
(TxnWatchedKeysContainer::new(Arc::clone(&map)), map)
}
#[test]
fn add_watch_then_untouched_key_validates() {
let (mut c, _map) = container();
c.add_watch(b"user:1");
assert!(c.validate_watch_version());
}
#[test]
fn modified_watched_key_fails_validation() {
let (mut c, map) = container();
c.add_watch(b"user:1");
map.increment_version(TxnKeyEntryComparison::key_hash(b"user:1") as u64);
assert!(!c.validate_watch_version());
}
#[test]
fn remove_watch_excludes_key_from_validation() {
let (mut c, map) = container();
c.add_watch(b"k");
map.increment_version(TxnKeyEntryComparison::key_hash(b"k") as u64);
assert!(c.remove_watch(b"k"));
assert!(c.remove_watch(b"k"));
assert!(c.validate_watch_version());
}
#[test]
fn reset_clears_all_watches() {
let (mut c, map) = container();
c.add_watch(b"a");
c.add_watch(b"b");
map.increment_version(TxnKeyEntryComparison::key_hash(b"a") as u64);
c.reset();
assert!(c.validate_watch_version());
}
}