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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// For writing to the log
#![ feature(map_first_last) ]
#![ feature(trait_alias) ]
#![ feature(async_stream) ]
#![ feature(write_all_vectored) ]
#![ feature(array_methods) ]
#![ feature(get_mut_unchecked) ]

use std::marker::PhantomData;
use std::path::{Path, PathBuf};

use bincode::Options;

pub mod sync_iter;

#[ cfg(feature="sync") ]
pub use sync_iter as iterate;

#[ cfg(not(feature="sync")) ]
pub mod async_iter;

#[ cfg(not(feature="sync")) ]
pub use async_iter as iterate;

mod values;
use values::Value;

mod sorted_table;
use sorted_table::Key;

mod tasks;
mod memtable;

mod logic;
use logic::DbLogic;

mod level;
mod wal;
mod manifest;
mod cond_var;
mod entry;
mod data_blocks;
mod index_blocks;

#[ cfg(not(feature="sync")) ]
mod async_api;
#[ cfg(not(feature="sync")) ]
pub use async_api::Database;

#[ cfg(feature="sync") ]
mod sync_api;
#[ cfg(feature="sync") ]
pub use sync_api::Database;

/// Keys and values must be (de-)serializable
pub trait KV_Trait = Send+serde::Serialize+serde::de::DeserializeOwned+'static+Unpin+Clone;

/// A WriteBatch allows to bundle multiple updates together for higher throughput
///
/// Note: The batch will not be applied to the database until it is passed to `Database::write`
pub struct WriteBatch<K: KV_Trait, V: KV_Trait> {
    _marker: PhantomData<fn(K,V)>,
    writes: Vec<(Key, Value)>
}

#[ derive(Clone, Debug) ]
pub enum WriteError {
    Unknown
}

impl<K: KV_Trait, V: KV_Trait> WriteBatch<K, V> {
    pub fn new() -> Self {
        Self{
            writes: Vec::new(),
            _marker: PhantomData
        }
    }

    /// Record a put operation in the write batch
    /// Will not be applied to the Database until the WriteBatch is written
    pub fn put(&mut self, key: &K, value: &V) {
        let enc = get_encoder();
        self.writes.push(
            (enc.serialize(key).unwrap(), enc.serialize(value).unwrap()));
    }
}

impl<K: KV_Trait, V: KV_Trait> Default for WriteBatch<K, V> {
    fn default() -> Self {
        Self::new()
    }
}

/// Allows specifying details of a write
pub struct WriteOptions {
    /// Should the call block until it is guaranteed to be written to disk?
    pub sync: bool
}

impl WriteOptions {
    pub const fn new() -> Self {
        Self{ sync: true }
    }
}

impl Default for WriteOptions {
    fn default() -> Self {
        Self::new()
    }
}

/// Allow specifying how the datastore behaves during startup
pub enum StartMode {
    /// Reuse existing database, or create if non-existent
    CreateOrOpen,
    /// Open existing database, or fail if non-existent
    Open,
    /// Create a new, or override an existing, database
    CreateOrOverride
}

/// Parameters to customize the creation of the database
pub struct Params {
    /// Where in the filesystem should the databasse be stored?
    pub db_path: PathBuf,
    /// Maximum size of a memtable (keys+values),
    /// This indirectly also defines how large a value block can be
    pub max_memtable_size: usize,
    /// How many levels does this store have (default: 5)
    pub num_levels: usize,
    /// How many open files should be held in memory?
    pub max_open_files: usize,
    /// Maximum number of entries in a key block
    pub max_key_block_size: usize,
    /// How often should the full key be stored in a data block?
    /// Larger numbers result in smaller on-disk files, but seeks will be slower
    pub block_restart_interval: usize,
}

impl Default for Params {
    fn default() -> Self {
        Self {
            db_path: Path::new("./storage.lsm").to_path_buf(),
            max_memtable_size: 64*1024,
            num_levels: 5,
            max_open_files: 1000,
            max_key_block_size: 1024,
            block_restart_interval: 16,
        }
    }
}

#[inline]
fn get_encoder() ->
        bincode::config::WithOtherEndian<bincode::DefaultOptions, bincode::config::BigEndian> {
    // Use BigEndian to make integers sortable properly
    bincode::options().with_big_endian()
}