add week 1 day 2 tutorial

Signed-off-by: Alex Chi <iskyzh@gmail.com>
This commit is contained in:
Alex Chi
2024-01-21 11:56:09 +08:00
parent df35a954c9
commit 892e6ab8f7
11 changed files with 605 additions and 15 deletions

340
mini-lsm/src/tests/day2.rs Normal file
View File

@@ -0,0 +1,340 @@
use std::ops::Bound;
use bytes::Bytes;
use tempfile::tempdir;
use crate::{
iterators::{merge_iterator::MergeIterator, StorageIterator},
lsm_iterator::FusedIterator,
lsm_storage::{LsmStorageInner, LsmStorageOptions},
mem_table::MemTable,
};
use super::harness::MockIterator;
#[test]
fn test_task1_memtable_iter() {
use std::ops::Bound;
let memtable = MemTable::create(0);
memtable.put(b"key1", b"value1").unwrap();
memtable.put(b"key2", b"value2").unwrap();
memtable.put(b"key3", b"value3").unwrap();
{
let mut iter = memtable.scan(Bound::Unbounded, Bound::Unbounded);
assert_eq!(iter.key(), b"key1");
assert_eq!(iter.value(), b"value1");
assert!(iter.is_valid());
iter.next().unwrap();
assert_eq!(iter.key(), b"key2");
assert_eq!(iter.value(), b"value2");
assert!(iter.is_valid());
iter.next().unwrap();
assert_eq!(iter.key(), b"key3");
assert_eq!(iter.value(), b"value3");
assert!(iter.is_valid());
iter.next().unwrap();
assert!(!iter.is_valid());
}
{
let mut iter = memtable.scan(Bound::Included(b"key1"), Bound::Included(b"key2"));
assert_eq!(iter.key(), b"key1");
assert_eq!(iter.value(), b"value1");
assert!(iter.is_valid());
iter.next().unwrap();
assert_eq!(iter.key(), b"key2");
assert_eq!(iter.value(), b"value2");
assert!(iter.is_valid());
iter.next().unwrap();
assert!(!iter.is_valid());
}
{
let mut iter = memtable.scan(Bound::Excluded(b"key1"), Bound::Excluded(b"key3"));
assert_eq!(iter.key(), b"key2");
assert_eq!(iter.value(), b"value2");
assert!(iter.is_valid());
iter.next().unwrap();
assert!(!iter.is_valid());
}
}
#[test]
fn test_task1_empty_memtable_iter() {
use std::ops::Bound;
let memtable = MemTable::create(0);
{
let iter = memtable.scan(Bound::Excluded(b"key1"), Bound::Excluded(b"key3"));
assert!(!iter.is_valid());
}
{
let iter = memtable.scan(Bound::Included(b"key1"), Bound::Included(b"key2"));
assert!(!iter.is_valid());
}
{
let iter = memtable.scan(Bound::Unbounded, Bound::Unbounded);
assert!(!iter.is_valid());
}
}
fn as_bytes(x: &[u8]) -> Bytes {
Bytes::copy_from_slice(x)
}
fn check_iter_result(iter: &mut impl StorageIterator, expected: Vec<(Bytes, Bytes)>) {
for (k, v) in expected {
assert!(iter.is_valid());
assert_eq!(
k,
iter.key(),
"expected key: {:?}, actual key: {:?}",
k,
as_bytes(iter.key()),
);
assert_eq!(
v,
iter.value(),
"expected value: {:?}, actual value: {:?}",
v,
as_bytes(iter.value()),
);
iter.next().unwrap();
}
assert!(!iter.is_valid());
}
fn expect_iter_error(mut iter: impl StorageIterator) {
loop {
match iter.next() {
Ok(_) if iter.is_valid() => continue,
Ok(_) => panic!("expect an error"),
Err(_) => break,
}
}
}
#[test]
fn test_task2_merge_1() {
let i1 = MockIterator::new(vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
]);
let i2 = MockIterator::new(vec![
(Bytes::from("a"), Bytes::from("1.2")),
(Bytes::from("b"), Bytes::from("2.2")),
(Bytes::from("c"), Bytes::from("3.2")),
(Bytes::from("d"), Bytes::from("4.2")),
]);
let i3 = MockIterator::new(vec![
(Bytes::from("b"), Bytes::from("2.3")),
(Bytes::from("c"), Bytes::from("3.3")),
(Bytes::from("d"), Bytes::from("4.3")),
]);
let mut iter = MergeIterator::create(vec![
Box::new(i1.clone()),
Box::new(i2.clone()),
Box::new(i3.clone()),
]);
check_iter_result(
&mut iter,
vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
(Bytes::from("d"), Bytes::from("4.2")),
],
);
let mut iter = MergeIterator::create(vec![Box::new(i3), Box::new(i1), Box::new(i2)]);
check_iter_result(
&mut iter,
vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.3")),
(Bytes::from("c"), Bytes::from("3.3")),
(Bytes::from("d"), Bytes::from("4.3")),
],
);
}
#[test]
fn test_task2_merge_2() {
let i1 = MockIterator::new(vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
]);
let i2 = MockIterator::new(vec![
(Bytes::from("d"), Bytes::from("1.2")),
(Bytes::from("e"), Bytes::from("2.2")),
(Bytes::from("f"), Bytes::from("3.2")),
(Bytes::from("g"), Bytes::from("4.2")),
]);
let i3 = MockIterator::new(vec![
(Bytes::from("h"), Bytes::from("1.3")),
(Bytes::from("i"), Bytes::from("2.3")),
(Bytes::from("j"), Bytes::from("3.3")),
(Bytes::from("k"), Bytes::from("4.3")),
]);
let i4 = MockIterator::new(vec![]);
let result = vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
(Bytes::from("d"), Bytes::from("1.2")),
(Bytes::from("e"), Bytes::from("2.2")),
(Bytes::from("f"), Bytes::from("3.2")),
(Bytes::from("g"), Bytes::from("4.2")),
(Bytes::from("h"), Bytes::from("1.3")),
(Bytes::from("i"), Bytes::from("2.3")),
(Bytes::from("j"), Bytes::from("3.3")),
(Bytes::from("k"), Bytes::from("4.3")),
];
let mut iter = MergeIterator::create(vec![
Box::new(i1.clone()),
Box::new(i2.clone()),
Box::new(i3.clone()),
Box::new(i4.clone()),
]);
check_iter_result(&mut iter, result.clone());
let mut iter = MergeIterator::create(vec![
Box::new(i2.clone()),
Box::new(i4.clone()),
Box::new(i3.clone()),
Box::new(i1.clone()),
]);
check_iter_result(&mut iter, result.clone());
let mut iter =
MergeIterator::create(vec![Box::new(i4), Box::new(i3), Box::new(i2), Box::new(i1)]);
check_iter_result(&mut iter, result);
}
#[test]
fn test_task2_merge_empty() {
let mut iter = MergeIterator::<MockIterator>::create(vec![]);
check_iter_result(&mut iter, vec![]);
let i1 = MockIterator::new(vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
]);
let i2 = MockIterator::new(vec![]);
let mut iter = MergeIterator::<MockIterator>::create(vec![Box::new(i1), Box::new(i2)]);
check_iter_result(
&mut iter,
vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
],
);
}
#[test]
fn test_task2_merge_error() {
let mut iter = MergeIterator::<MockIterator>::create(vec![]);
check_iter_result(&mut iter, vec![]);
let i1 = MockIterator::new(vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
]);
let i2 = MockIterator::new_with_error(
vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("b"), Bytes::from("2.1")),
(Bytes::from("c"), Bytes::from("3.1")),
],
1,
);
let iter = MergeIterator::<MockIterator>::create(vec![Box::new(i1), Box::new(i2)]);
// your implementation should correctly throw an error instead of panic
expect_iter_error(iter);
}
#[test]
fn test_task3_fused_iterator() {
let iter = MockIterator::new(vec![]);
let mut fused_iter = FusedIterator::new(iter);
assert!(!fused_iter.is_valid());
fused_iter.next().unwrap();
fused_iter.next().unwrap();
fused_iter.next().unwrap();
assert!(!fused_iter.is_valid());
let iter = MockIterator::new_with_error(
vec![
(Bytes::from("a"), Bytes::from("1.1")),
(Bytes::from("a"), Bytes::from("1.1")),
],
1,
);
let mut fused_iter = FusedIterator::new(iter);
assert!(fused_iter.is_valid());
assert!(fused_iter.next().is_err());
assert!(!fused_iter.is_valid());
assert!(fused_iter.next().is_err());
assert!(fused_iter.next().is_err());
}
#[test]
fn test_task4_integration() {
let dir = tempdir().unwrap();
let storage =
LsmStorageInner::open(dir.path(), LsmStorageOptions::default_for_week1_test()).unwrap();
storage.put(b"1", b"233").unwrap();
storage.put(b"2", b"2333").unwrap();
storage.put(b"3", b"23333").unwrap();
storage
.force_freeze_memtable(&storage.state_lock.lock())
.unwrap();
storage.delete(b"1").unwrap();
storage.delete(b"2").unwrap();
storage.put(b"3", b"2333").unwrap();
storage.put(b"4", b"23333").unwrap();
storage
.force_freeze_memtable(&storage.state_lock.lock())
.unwrap();
storage.put(b"1", b"233333").unwrap();
storage.put(b"3", b"233333").unwrap();
{
let mut iter = storage.scan(Bound::Unbounded, Bound::Unbounded).unwrap();
check_iter_result(
&mut iter,
vec![
(Bytes::from_static(b"1"), Bytes::from_static(b"233333")),
(Bytes::from_static(b"3"), Bytes::from_static(b"233333")),
(Bytes::from_static(b"4"), Bytes::from_static(b"23333")),
],
);
assert!(!iter.is_valid());
iter.next().unwrap();
iter.next().unwrap();
iter.next().unwrap();
assert!(!iter.is_valid());
}
{
let mut iter = storage
.scan(Bound::Included(b"2"), Bound::Included(b"3"))
.unwrap();
check_iter_result(
&mut iter,
vec![(Bytes::from_static(b"3"), Bytes::from_static(b"233333"))],
);
assert!(!iter.is_valid());
iter.next().unwrap();
iter.next().unwrap();
iter.next().unwrap();
assert!(!iter.is_valid());
}
}

View File

@@ -0,0 +1,70 @@
use anyhow::{bail, Result};
use bytes::Bytes;
use crate::iterators::StorageIterator;
#[derive(Clone)]
pub struct MockIterator {
pub data: Vec<(Bytes, Bytes)>,
pub error_when: Option<usize>,
pub index: usize,
}
impl MockIterator {
pub fn new(data: Vec<(Bytes, Bytes)>) -> Self {
Self {
data,
index: 0,
error_when: None,
}
}
pub fn new_with_error(data: Vec<(Bytes, Bytes)>, error_when: usize) -> Self {
Self {
data,
index: 0,
error_when: Some(error_when),
}
}
}
impl StorageIterator for MockIterator {
fn next(&mut self) -> Result<()> {
if self.index < self.data.len() {
self.index += 1;
}
if let Some(error_when) = self.error_when {
if self.index == error_when {
bail!("fake error!");
}
}
Ok(())
}
fn key(&self) -> &[u8] {
if let Some(error_when) = self.error_when {
if self.index >= error_when {
panic!("invalid access after next returns an error!");
}
}
self.data[self.index].0.as_ref()
}
fn value(&self) -> &[u8] {
if let Some(error_when) = self.error_when {
if self.index >= error_when {
panic!("invalid access after next returns an error!");
}
}
self.data[self.index].1.as_ref()
}
fn is_valid(&self) -> bool {
if let Some(error_when) = self.error_when {
if self.index >= error_when {
panic!("invalid access after next returns an error!");
}
}
self.index < self.data.len()
}
}