use tempfile::Builder;
use crate::{
Config, OpenOptions, Persy, ValueMode,
inspect::{JournalRead, PersyInspect, RecordState, TreeInspector, inspec_log},
journal::{self, JournalId, records::Start},
};
#[derive(Default)]
struct Count {
keys_count: u32,
values_count: u32,
enabled: bool,
}
impl<K, V> TreeInspector<K, V> for Count {
fn start_node(
&mut self,
_node_id: crate::PersyId,
_prev: Option<K>,
_next: Option<K>,
) -> super::TreeInspectorResult<()> {
Ok(())
}
fn end_node(&mut self, _node_id: crate::PersyId) -> super::TreeInspectorResult<()> {
Ok(())
}
fn start_leaf(
&mut self,
_node_id: crate::PersyId,
_prev: Option<K>,
_next: Option<K>,
) -> super::TreeInspectorResult<()> {
self.enabled = true;
Ok(())
}
fn end_leaf(&mut self, _node_id: crate::PersyId) -> super::TreeInspectorResult<()> {
self.enabled = false;
Ok(())
}
fn start_key(&mut self, _node_pos: u32, _k: K) -> super::TreeInspectorResult<()> {
if self.enabled {
self.keys_count += 1;
}
Ok(())
}
fn end_key(&mut self, _node_pos: u32, _k: K) -> super::TreeInspectorResult<()> {
Ok(())
}
fn value(&mut self, _node_pos: u32, _v: V) -> super::TreeInspectorResult<()> {
self.values_count += 1;
Ok(())
}
fn failed_load(&mut self, _node_id: crate::PersyId) -> super::TreeInspectorResult<()> {
Ok(())
}
fn empty(&mut self) -> super::TreeInspectorResult<()> {
Ok(())
}
}
#[test]
pub fn test_tree_inspection() {
let p = OpenOptions::new().memory().unwrap();
let mut tx = p.begin().unwrap();
tx.create_index::<u32, u32>("index", ValueMode::Cluster).unwrap();
tx.prepare().unwrap().commit().unwrap();
let mut tx = p.begin().unwrap();
for x in 0..20000 {
for z in 0..20 {
tx.put::<u32, u32>("index", x, x * 100 + z).unwrap();
}
}
tx.prepare().unwrap().commit().unwrap();
let mut ti = Count::default();
p.inspect_tree::<u32, u32, _>("index", &mut ti).unwrap();
assert_eq!(ti.keys_count, 20000);
assert_eq!(ti.values_count, 20000 * 20);
}
#[derive(Default)]
struct Value {
key: Option<u32>,
value: Option<u32>,
}
impl TreeInspector<u32, u32> for Value {
fn start_node(
&mut self,
_node_id: crate::PersyId,
_prev: Option<u32>,
_next: Option<u32>,
) -> super::TreeInspectorResult<()> {
Ok(())
}
fn end_node(&mut self, _node_id: crate::PersyId) -> super::TreeInspectorResult<()> {
Ok(())
}
fn start_leaf(
&mut self,
_node_id: crate::PersyId,
_prev: Option<u32>,
_next: Option<u32>,
) -> super::TreeInspectorResult<()> {
Ok(())
}
fn end_leaf(&mut self, _node_id: crate::PersyId) -> super::TreeInspectorResult<()> {
Ok(())
}
fn start_key(&mut self, _node_pos: u32, k: u32) -> super::TreeInspectorResult<()> {
self.key = Some(k);
Ok(())
}
fn end_key(&mut self, _node_pos: u32, _k: u32) -> super::TreeInspectorResult<()> {
Ok(())
}
fn value(&mut self, _node_pos: u32, v: u32) -> super::TreeInspectorResult<()> {
self.value = Some(v);
Ok(())
}
fn failed_load(&mut self, _node_id: crate::PersyId) -> super::TreeInspectorResult<()> {
Ok(())
}
fn empty(&mut self) -> super::TreeInspectorResult<()> {
Ok(())
}
}
#[test]
pub fn test_tree_value_inspection() {
let p = OpenOptions::new().memory().unwrap();
let mut tx = p.begin().unwrap();
tx.create_index::<u32, u32>("index", ValueMode::Cluster).unwrap();
tx.prepare().unwrap().commit().unwrap();
let mut tx = p.begin().unwrap();
tx.put::<u32, u32>("index", 100, 200).unwrap();
tx.prepare().unwrap().commit().unwrap();
let mut ti = Value::default();
p.inspect_tree::<u32, u32, _>("index", &mut ti).unwrap();
assert_eq!(ti.key, Some(100));
assert_eq!(ti.value, Some(200));
}
#[test]
pub fn test_page_scan() {
let p = OpenOptions::new().memory().unwrap();
let mut tx = p.begin().unwrap();
tx.create_segment("test").unwrap();
tx.prepare().unwrap().commit().unwrap();
let mut tx = p.begin().unwrap();
tx.insert("test", &vec![10, 10, 10]).unwrap();
tx.prepare().unwrap().commit().unwrap();
assert_eq!(p.page_state_scan().unwrap().count(), 9);
}
#[test]
pub fn test_inspect_record() {
let p = OpenOptions::new().memory().unwrap();
let mut tx = p.begin().unwrap();
tx.create_segment("test").unwrap();
tx.prepare().unwrap().commit().unwrap();
let mut tx = p.begin().unwrap();
let id = tx.insert("test", &vec![10, 10, 10]).unwrap();
tx.prepare().unwrap().commit().unwrap();
assert_eq!(
p.inspect_record_direct(&id).unwrap(),
RecordState::Exists(vec![10, 10, 10])
);
}
#[derive(Default)]
struct TestLogInspector {
start: u32,
insert_record: u32,
prepare_commit: u32,
commit: u32,
cleanup: u32,
create_segment: u32,
journal_page: u32,
}
impl JournalRead for TestLogInspector {
fn journal_page(&mut self, _allocation: u64) {
self.journal_page += 1;
}
fn start(&mut self, _journal_id: JournalId, _start: &Start) {
self.start += 1;
}
fn insert_record(&mut self, _journal_id: JournalId, _insert: &super::InsertRecord) {
self.insert_record += 1;
}
fn prepare_commit(&mut self, _journal_id: JournalId, _prepare_commit: &journal::records::PrepareCommit) {
self.prepare_commit += 1;
}
fn commit(&mut self, _journal_id: JournalId, _commit: &super::Commit) {
self.commit += 1;
}
fn update_record(&mut self, _journal_id: JournalId, _update_record: &super::UpdateRecord) {
assert!(false)
}
fn delete_record(&mut self, _journal_id: JournalId, _delete_record: &super::DeleteRecord) {
assert!(false)
}
fn rollback(&mut self, _journal_id: JournalId, _rollback: &super::Rollback) {
assert!(false)
}
fn create_segment(&mut self, _journal_id: JournalId, _create_segment: &super::CreateSegment) {
self.create_segment += 1;
}
fn drop_segment(&mut self, _journal_id: JournalId, _drop_segment: &super::DropSegment) {
assert!(false)
}
fn read_record(&mut self, _journal_id: JournalId, _read_record: &super::ReadRecord) {
assert!(false)
}
fn metadata(&mut self, _journal_id: JournalId, _metadata: &super::Metadata) {}
fn free_page(&mut self, _journal_id: JournalId, _free_page: &super::FreedPage) {}
fn new_segment_page(&mut self, _journal_id: JournalId, _new_segment_page: &super::NewSegmentPage) {}
fn cleanup(&mut self, _journal_id: JournalId, _cleanup: &super::Cleanup) {
self.cleanup += 1;
}
fn rollback_page(&mut self, _journal_id: JournalId, _rollback_page: &super::RollbackPage) {}
}
#[test]
pub fn test_simple_inspect_log() {
let tmp_file = Builder::new()
.prefix("inspect_log")
.suffix(".persy")
.tempfile()
.unwrap();
{
Persy::create_from_file(tmp_file.reopen().unwrap()).unwrap();
let p = Persy::open_from_file(tmp_file.reopen().unwrap(), Config::new()).unwrap();
let mut tx = p.begin().unwrap();
tx.create_segment("test").unwrap();
tx.prepare().unwrap().commit().unwrap();
let mut tx = p.begin().unwrap();
tx.insert("test", &vec![10, 10, 10]).unwrap();
tx.prepare().unwrap().commit().unwrap();
}
let mut log_inspector = TestLogInspector::default();
inspec_log(tmp_file.path(), &mut log_inspector).unwrap();
assert_eq!(log_inspector.start, 2);
assert_eq!(log_inspector.create_segment, 1);
assert_eq!(log_inspector.prepare_commit, 2);
assert_eq!(log_inspector.commit, 2);
assert_eq!(log_inspector.cleanup, 2);
assert_eq!(log_inspector.insert_record, 1);
}