1
use std::{io, ops::Mul, path::Path, sync::Arc};
2

            
3
use file_manager::{fs::StdFileManager, FileManager, PathId};
4

            
5
use crate::{LogManager, WriteAheadLog};
6

            
7
/// A [`WriteAheadLog`] configuration.
8
202
#[derive(Debug, Clone)]
9
#[must_use]
10
pub struct Configuration<M> {
11
    /// The file manager to use for storing data.
12
    ///
13
    /// Typically this is [`StdFileManager`].
14
    pub file_manager: M,
15
    /// The directory to store the log files in.
16
    pub directory: PathId,
17
    /// The number of bytes each log file should be preallocated with. Log files
18
    /// may grow to be larger than this size if needed.
19
    pub preallocate_bytes: u32,
20
    /// After this many bytes have been written to the active log file, begin a
21
    /// checkpointing process. This number should be less than
22
    /// `preallocate_bytes` to try to ensure the checkpointing process happens
23
    /// before the preallocated space is fully exhausted. If this amount is too
24
    /// close to the preallocation amount, an entry being written may need to
25
    /// extend the file which is a slow operation.
26
    pub checkpoint_after_bytes: u64,
27
    /// The number of bytes to use for the in-memory buffer when reading and
28
    /// writing from the log.
29
    pub buffer_bytes: usize,
30
    /// An arbitrary chunk of bytes that is stored in the log files. Limited to
31
    /// 255 bytes. This can be used for any purpose, but the design inspiration
32
    /// was to allow detection of what format or version of a format the data
33
    /// was inside of the log without needing to parse the entries.
34
    pub version_info: Arc<Vec<u8>>,
35
}
36

            
37
impl Default for Configuration<StdFileManager> {
38
    fn default() -> Self {
39
        Self::default_for("okaywal")
40
    }
41
}
42

            
43
impl Configuration<StdFileManager> {
44
    /// Returns the default configuration for a given directory.
45
    ///
46
    /// This currently is:
47
    ///
48
    /// - `preallocate_bytes`: 1 megabyte
49
    /// - `checkpoint_after_bytes`: 768 kilobytes
50
    /// - `buffer_bytes`: 16 kilobytes
51
26
    pub fn default_for<P: AsRef<Path>>(path: P) -> Self {
52
26
        Self::default_with_manager(path, StdFileManager::default())
53
26
    }
54
}
55

            
56
impl<M> Configuration<M>
57
where
58
    M: FileManager,
59
{
60
    /// Returns the default configuration for a given directory and file manager.
61
    ///
62
    /// This currently is:
63
    ///
64
    /// - `preallocate_bytes`: 1 megabyte
65
    /// - `checkpoint_after_bytes`: 768 kilobytes
66
    /// - `buffer_bytes`: 16 kilobytes
67
38
    pub fn default_with_manager<P: AsRef<Path>>(path: P, file_manager: M) -> Self {
68
38
        Self {
69
38
            file_manager,
70
38
            directory: PathId::from(path.as_ref()),
71
38
            preallocate_bytes: megabytes(1),
72
38
            checkpoint_after_bytes: kilobytes(768),
73
38
            buffer_bytes: kilobytes(16),
74
38
            version_info: Arc::default(),
75
38
        }
76
38
    }
77
    /// Sets the number of bytes to preallocate for each segment file. Returns `self`.
78
    ///
79
    /// Preallocating disk space allows for more consistent performance. This
80
    /// number should be large enough to allow batching multiple entries into
81
    /// one checkpointing operation.
82
    pub fn preallocate_bytes(mut self, bytes: u32) -> Self {
83
        self.preallocate_bytes = bytes;
84
        self
85
    }
86

            
87
    /// Sets the number of bytes written required to begin a checkpoint
88
    /// operation. Returns `self`.
89
    ///
90
    /// This value should be smaller than `preallocate_bytes` to ensure
91
    /// checkpoint operations begin before too much data is written in a log
92
    /// entry. If more data is written before a checkpoint occurs, the segment
93
    /// will grow to accommodate the extra data, but that write will not be as
94
    /// fast due to needing to allocate more space from the filesystem to
95
    /// perform the write.
96
2
    pub fn checkpoint_after_bytes(mut self, bytes: u64) -> Self {
97
2
        self.checkpoint_after_bytes = bytes;
98
2
        self
99
2
    }
100

            
101
    /// Sets the number of bytes to use for internal buffers when reading and
102
    /// writing data to the log. Returns `self`.
103
    pub fn buffer_bytes(mut self, bytes: usize) -> Self {
104
        self.buffer_bytes = bytes;
105
        self
106
    }
107

            
108
    /// Opens the log using the provided log manager with this configuration.
109
238
    pub fn open<Manager: LogManager<M>>(self, manager: Manager) -> io::Result<WriteAheadLog<M>> {
110
238
        WriteAheadLog::open(self, manager)
111
238
    }
112
}
113

            
114
58
fn megabytes<T: Mul<Output = T> + From<u16>>(megs: T) -> T {
115
58
    kilobytes(megs) * T::from(1024)
116
58
}
117

            
118
174
fn kilobytes<T: Mul<Output = T> + From<u16>>(bytes: T) -> T {
119
174
    bytes * T::from(1024)
120
174
}