1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! A high-performance storage engine for modern hardware and platforms.
//!
//! PhotonDB is designed from scratch to leverage the power of modern multi-core
//! chips, storage devices, and operating systems.
//!
//! Features:
//!
//! - Latch-free data structures, scale to many cores.
//! - Log-structured persistent stores, optimized for flash storage.
//! - Asynchronous APIs and efficient file IO, powered by io_uring on Linux.
//!
//! This crate provides three sets of APIs:
//!
//! - [`Raw`]: a set of low-level APIs that can run with different environments.
//! - [`Std`]: a set of synchronous APIs based on the raw one that doesn't
//!   require a runtime to run.
//! - [`Photon`]: a set of asynchronous APIs based on the raw one that must run
//!   with the [PhotonIO] runtime.
//!
//! The [`Photon`] APIs are the default and are re-exported to the top-level
//! module for convenience.
//!
//! [`Raw`]: crate::raw
//! [`Std`]: crate::std
//! [`Photon`]: crate::photon
//! [PhotonIO]: https://crates.io/crates/photonio

#![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;

mod error;
pub use error::{Error, Result};

mod tree;
pub use tree::{Options as TableOptions, PageIter, ReadOptions, Stats, WriteOptions};

mod page_store;
pub use page_store::Options as PageStoreOptions;

mod page;
mod util;

#[cfg(test)]
mod tests {
    use rand::random;
    use tempfile::tempdir;

    use super::*;

    const OPTIONS: TableOptions = TableOptions {
        page_size: 64,
        page_chain_length: 2,
        page_store: PageStoreOptions {
            write_buffer_capacity: 1 << 20,
            use_direct_io: 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();
    }
}