#![warn(missing_docs, unreachable_pub)]
#![feature(
io_error_more,
type_alias_impl_trait,
hash_drain_filter,
pointer_is_aligned
)]
pub mod env;
pub mod raw;
pub mod std;
pub mod photon;
pub use photon::{Table, TableStats};
mod error;
pub use error::{Error, Result};
mod tree;
pub use tree::{Options as TableOptions, PageIter, ReadOptions, TreeStats, WriteOptions};
mod page_store;
pub use page_store::{
ChecksumType, Compression, FlushOptions, Options as PageStoreOptions, StoreStats,
};
mod page;
mod util;
#[cfg(test)]
mod tests {
use rand::random;
use tempfile::tempdir;
use super::*;
use crate::page_store::{ChecksumType, Compression};
const OPTIONS: TableOptions = TableOptions {
page_size: 128,
page_chain_length: 4,
page_store: PageStoreOptions {
write_buffer_capacity: 1 << 20,
max_write_buffers: 8,
use_direct_io: false,
disable_space_reclaiming: false,
max_space_amplification_percent: 10,
space_used_high: u64::MAX,
file_base_size: 1 << 20,
cache_capacity: 2 << 10,
cache_estimated_entry_charge: 1,
cache_file_reader_capacity: 1000,
cache_strict_capacity_limit: false,
prepopulate_cache_on_flush: true,
compression_on_flush: Compression::SNAPPY,
compression_on_cold_compact: Compression::ZSTD,
page_checksum_type: ChecksumType::CRC32,
avoid_flush_during_shutdown: false,
},
};
async fn must_put(table: &Table, i: u64, lsn: u64) {
let buf = i.to_be_bytes();
table.put(&buf, lsn, &buf).await.unwrap()
}
async fn must_get(table: &Table, i: u64, lsn: u64, expect: Option<u64>) {
let buf = i.to_be_bytes();
let value = table.get(&buf, lsn).await.unwrap();
assert_eq!(value, expect.map(|v| v.to_be_bytes().to_vec()));
}
#[photonio::test]
async fn crud() {
let path = tempdir().unwrap();
let table = Table::open(&path, OPTIONS).await.unwrap();
const N: u64 = 1 << 10;
for i in 0..N {
must_put(&table, i, i).await;
must_get(&table, i, i, Some(i)).await;
}
for i in 0..N {
must_get(&table, i, i, Some(i)).await;
}
let guard = table.pin();
let mut pages = guard.pages();
let mut i = 0u64;
while let Some(page) = pages.next().await.unwrap() {
for (k, v) in page {
assert_eq!(k, &i.to_be_bytes());
assert_eq!(v, &i.to_be_bytes());
i += 1;
}
}
assert_eq!(i, N);
table.close().await.unwrap();
}
#[photonio::test]
async fn random_crud() {
let path = tempdir().unwrap();
let table = Table::open(&path, OPTIONS).await.unwrap();
const N: u64 = 1 << 12;
for _ in 0..N {
let i = random();
must_put(&table, i, i).await;
must_get(&table, i, i, Some(i)).await;
}
table.close().await.unwrap();
}
#[photonio::test]
async fn concurrent_crud() {
let path = tempdir().unwrap();
let table = Table::open(&path, OPTIONS).await.unwrap();
let mut tasks = Vec::new();
for _ in 0..4 {
let table = table.clone();
let handle = photonio::task::spawn(async move {
const N: u64 = 1 << 10;
for _ in 0..N {
let i = random();
must_put(&table, i, i).await;
must_get(&table, i, i, Some(i)).await;
}
});
tasks.push(handle);
}
for task in tasks {
task.await.unwrap();
}
table.close().await.unwrap();
}
#[photonio::test]
async fn graceful_shutdown() {
let path = tempdir().unwrap();
let opts = TableOptions {
page_store: PageStoreOptions {
avoid_flush_during_shutdown: false,
..Default::default()
},
..Default::default()
};
let table = Table::open(&path, opts.clone()).await.unwrap();
let i = random();
must_put(&table, i, i).await;
must_get(&table, i, i, Some(i)).await;
table.close().await.unwrap();
let table = Table::open(&path, opts).await.unwrap();
must_get(&table, i, i, Some(i)).await;
}
}