Skip to content

Commit e390951

Browse files
committed
feat: introduce changeable options for CacheBuilder/Cache
1 parent 9d3e474 commit e390951

File tree

3 files changed

+155
-90
lines changed

3 files changed

+155
-90
lines changed

src/builder.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use crate::{
2+
cache::{Cache, Options},
3+
Result,
4+
};
5+
use std::path;
6+
7+
/// A builder for the [`Cache`] object. Exposes APIs for configuring the initial setup of the
8+
/// database.
9+
///
10+
/// # Examples
11+
///
12+
/// ```rust
13+
/// # #[tokio::main(flavor = "current_thread")]
14+
/// # async fn main() {
15+
/// use forceps::CacheBuilder;
16+
///
17+
/// let cache = CacheBuilder::new("./cache")
18+
/// .dir_depth(3)
19+
/// .read_write_buffer(1024 * 16)
20+
/// .build()
21+
/// .await
22+
/// .unwrap();
23+
/// # }
24+
/// ```
25+
#[derive(Debug, Clone)]
26+
pub struct CacheBuilder {
27+
opts: Options,
28+
}
29+
30+
impl CacheBuilder {
31+
/// Creates a new [`CacheBuilder`], which can be used to customize and create a [`Cache`]
32+
/// instance.
33+
///
34+
/// The `path` supplied is the base directory of the cache instance.
35+
///
36+
/// # Examples
37+
///
38+
/// ```rust
39+
/// use forceps::CacheBuilder;
40+
///
41+
/// let builder = CacheBuilder::new("./cache");
42+
/// // Use other methods for configuration
43+
/// ```
44+
pub fn new<P: AsRef<path::Path>>(path: P) -> Self {
45+
let opts = Options {
46+
path: path.as_ref().to_owned(),
47+
dir_depth: 10,
48+
save_last_access: false,
49+
50+
// default buffer sizes to 8kb
51+
rbuff_sz: 8192,
52+
wbuff_sz: 8192,
53+
};
54+
CacheBuilder { opts }
55+
}
56+
57+
/// Sets the depth of directories created in the cache folder.
58+
///
59+
/// **Default is `2`**
60+
///
61+
/// This will set the depth of folders created and expected when reading and writing to the
62+
/// database. Increasing this value could increase the time to write to the database.
63+
///
64+
/// # Breaking Warning
65+
///
66+
/// Changing this value on a live database without migrations will cause the database `read`
67+
/// operations to essentially skip over the data. This means that all data will be
68+
/// inaccessible, despite the metadata being accessible.
69+
pub fn dir_depth(mut self, depth: u8) -> Self {
70+
self.opts.dir_depth = depth;
71+
self
72+
}
73+
74+
/// Changes the in-memory buffer sizes for reading and writing `fs` operations.
75+
///
76+
/// **Default is `8kb` (`8196`)**
77+
///
78+
/// Increasing this value may benefit performance as more bulk reading is involved. However,
79+
/// this option completely depends on the size of the data you are reading/writing.
80+
pub fn read_write_buffer(mut self, size: usize) -> Self {
81+
self.opts.rbuff_sz = size;
82+
self.opts.wbuff_sz = size;
83+
self
84+
}
85+
86+
/// Builds the new [`Cache`] instance using the configured options of the builder.
87+
///
88+
/// # Examples
89+
///
90+
/// ```rust
91+
/// # #[tokio::main(flavor = "current_thread")]
92+
/// # async fn main() {
93+
/// use forceps::CacheBuilder;
94+
///
95+
/// let cache = CacheBuilder::new("./cache")
96+
/// .build()
97+
/// .await
98+
/// .unwrap();
99+
/// # }
100+
/// ```
101+
pub async fn build(self) -> Result<Cache> {
102+
Cache::create(self.opts).await
103+
}
104+
}
105+
106+
impl Default for CacheBuilder {
107+
/// Creates a [`CacheBuilder`] with the directory set to `./cache`.
108+
///
109+
/// # Examples
110+
///
111+
/// ```rust
112+
/// # #[tokio::main(flavor = "current_thread")]
113+
/// # async fn main() {
114+
/// use forceps::CacheBuilder;
115+
///
116+
/// let cache = CacheBuilder::default()
117+
/// .build()
118+
/// .await
119+
/// .unwrap();
120+
/// # }
121+
/// ```
122+
fn default() -> Self {
123+
const DIR: &str = "./cache";
124+
Self::new(DIR)
125+
}
126+
}

src/cache.rs

Lines changed: 25 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,23 @@ async fn tempfile(dir: &path::Path) -> Result<(afs::File, path::PathBuf)> {
1717
Ok((tmp, tmppath))
1818
}
1919

20+
#[derive(Debug, Clone)]
21+
pub(crate) struct Options {
22+
pub(crate) path: path::PathBuf,
23+
pub(crate) dir_depth: u8,
24+
// TODO: implement below option
25+
pub(crate) save_last_access: bool,
26+
27+
// read and write buffer sizes
28+
pub(crate) rbuff_sz: usize,
29+
pub(crate) wbuff_sz: usize,
30+
}
31+
2032
/// The main component of `forceps`, acts as the API for interacting with the on-disk API.
2133
///
2234
/// This structure exposes `read`, `write`, and misc metadata operations. `read` and `write` are
2335
/// both async, whereas all metadata operations are sync. To create this structure, use the
24-
/// [`CacheBuilder`].
36+
/// [`CacheBuilder`](crate::CacheBuilder).
2537
///
2638
/// # Examples
2739
///
@@ -39,48 +51,33 @@ async fn tempfile(dir: &path::Path) -> Result<(afs::File, path::PathBuf)> {
3951
#[derive(Debug)]
4052
pub struct Cache {
4153
meta: MetaDb,
42-
path: path::PathBuf,
43-
}
44-
45-
/// A builder for the [`Cache`] object. Exposes APIs for configuring the initial setup of the
46-
/// database.
47-
///
48-
/// # Examples
49-
///
50-
/// ```rust
51-
/// use forceps::CacheBuilder;
52-
///
53-
/// let builder = CacheBuilder::new("./cache");
54-
/// ```
55-
#[derive(Debug, Clone)]
56-
pub struct CacheBuilder {
57-
path: path::PathBuf,
54+
opts: Options,
5855
}
5956

6057
impl Cache {
6158
/// Creates a new Cache instance based on the CacheBuilder
62-
async fn new(builder: CacheBuilder) -> Result<Self> {
59+
pub(crate) async fn create(opts: Options) -> Result<Self> {
6360
// create the base directory for the cache
64-
afs::create_dir_all(&builder.path)
61+
afs::create_dir_all(&opts.path)
6562
.await
6663
.map_err(ForcepError::Io)?;
6764

68-
let mut meta_path = builder.path.clone();
65+
let mut meta_path = opts.path.clone();
6966
meta_path.push("index");
7067
Ok(Self {
7168
meta: MetaDb::new(&meta_path)?,
72-
path: builder.path,
69+
opts,
7370
})
7471
}
7572

7673
/// Creates a PathBuf based on the key provided
7774
fn path_from_key(&self, key: &[u8]) -> path::PathBuf {
7875
let hex = hex::encode(key);
79-
let mut buf = self.path.clone();
76+
let mut buf = self.opts.path.clone();
8077

8178
// push segments of key as paths to the PathBuf. If the hex isn't long enough, then push
8279
// "__" instead.
83-
for n in (0..4usize).step_by(2) {
80+
for n in (0..self.opts.dir_depth).map(|x| x as usize * 2) {
8481
let n_end = n + 2;
8582
buf.push(if n_end >= hex.len() {
8683
"__"
@@ -136,7 +133,7 @@ impl Cache {
136133
let mut buf = Vec::with_capacity(size_guess as usize);
137134

138135
// read the entire file to the buffer
139-
tokio::io::BufReader::new(file)
136+
tokio::io::BufReader::with_capacity(self.opts.rbuff_sz, file)
140137
.read_to_end(&mut buf)
141138
.await
142139
.map_err(ForcepError::Io)?;
@@ -170,10 +167,10 @@ impl Cache {
170167
let key = key.as_ref();
171168
let value = value.as_ref();
172169

173-
let (tmp, tmp_path) = tempfile(&self.path).await?;
170+
let (tmp, tmp_path) = tempfile(&self.opts.path).await?;
174171
// write all data to a temporary file
175172
{
176-
let mut writer = tokio::io::BufWriter::new(tmp);
173+
let mut writer = tokio::io::BufWriter::with_capacity(self.opts.wbuff_sz, tmp);
177174
writer.write_all(value).await.map_err(ForcepError::Io)?;
178175
writer.flush().await.map_err(ForcepError::Io)?;
179176
}
@@ -217,7 +214,7 @@ impl Cache {
217214
let key = key.as_ref();
218215

219216
let cur_path = self.path_from_key(key);
220-
let tmp_path = crate::tmp::tmppath_in(&self.path);
217+
let tmp_path = crate::tmp::tmppath_in(&self.opts.path);
221218

222219
// move then delete the file
223220
//
@@ -306,71 +303,10 @@ impl Cache {
306303
}
307304
}
308305

309-
impl CacheBuilder {
310-
/// Creates a new [`CacheBuilder`], which can be used to customize and create a [`Cache`]
311-
/// instance.
312-
///
313-
/// The `path` supplied is the base directory of the cache instance.
314-
///
315-
/// # Examples
316-
///
317-
/// ```rust
318-
/// use forceps::CacheBuilder;
319-
///
320-
/// let builder = CacheBuilder::new("./cache");
321-
/// // Use other methods for configuration
322-
/// ```
323-
pub fn new<P: AsRef<path::Path>>(path: P) -> Self {
324-
CacheBuilder {
325-
path: path.as_ref().to_owned(),
326-
}
327-
}
328-
329-
/// Builds the new [`Cache`] instance using the configured options of the builder.
330-
///
331-
/// # Examples
332-
///
333-
/// ```rust
334-
/// # #[tokio::main(flavor = "current_thread")]
335-
/// # async fn main() {
336-
/// use forceps::CacheBuilder;
337-
///
338-
/// let cache = CacheBuilder::new("./cache")
339-
/// .build()
340-
/// .await
341-
/// .unwrap();
342-
/// # }
343-
/// ```
344-
pub async fn build(self) -> Result<Cache> {
345-
Cache::new(self).await
346-
}
347-
}
348-
349-
impl Default for CacheBuilder {
350-
/// Creates a [`CacheBuilder`] with the directory set to `./cache`.
351-
///
352-
/// # Examples
353-
///
354-
/// ```rust
355-
/// # #[tokio::main(flavor = "current_thread")]
356-
/// # async fn main() {
357-
/// use forceps::CacheBuilder;
358-
///
359-
/// let cache = CacheBuilder::default()
360-
/// .build()
361-
/// .await
362-
/// .unwrap();
363-
/// # }
364-
/// ```
365-
fn default() -> Self {
366-
const DIR: &str = "./cache";
367-
Self::new(DIR)
368-
}
369-
}
370-
371306
#[cfg(test)]
372307
mod test {
373308
use super::*;
309+
use crate::CacheBuilder;
374310

375311
async fn default_cache() -> Cache {
376312
CacheBuilder::default().build().await.unwrap()

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,11 @@ impl std::error::Error for ForcepError {}
8585

8686
mod tmp;
8787

88+
mod builder;
89+
pub use builder::CacheBuilder;
90+
8891
mod cache;
89-
pub use cache::{Cache, CacheBuilder};
92+
pub use cache::Cache;
9093

9194
mod metadata;
9295
pub(crate) use metadata::MetaDb;

0 commit comments

Comments
 (0)