Skip to content

Commit 847d0ff

Browse files
Do not use the system's tmp dir (#56)
FsStore: Do not use the system's tmp dir by default Co-authored-by: Issac Kelly <[email protected]>
1 parent bb5f678 commit 847d0ff

File tree

2 files changed

+68
-15
lines changed

2 files changed

+68
-15
lines changed

src/fs_store.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::{
22
collections::{HashMap, HashSet},
3+
fs::File,
34
io::Write,
45
path::{Path, PathBuf},
56
str,
@@ -65,19 +66,43 @@ use error::ErrorKind;
6566
/// half finished snapshot file. Deleting the inputs after the rename means that
6667
/// the worst case is that we have some leftover incremental files which will
6768
/// be deleted on the next compaction.
69+
#[derive(Debug)]
6870
pub struct FsStore {
6971
root: std::path::PathBuf,
72+
tmpdir: std::path::PathBuf,
7073
}
7174

7275
impl FsStore {
73-
/// Create an [`FsStore`] from a [`std::path::PathBuf`]
76+
/// Creates a new [`FsStore`] from a [`Path`].
7477
///
7578
/// This will attempt to create the root directory and throw an error if
7679
/// it does not exist.
7780
pub fn open<P: AsRef<Path>>(root: P) -> Result<Self, std::io::Error> {
78-
std::fs::create_dir_all(root.as_ref())?;
81+
let root = root.as_ref();
82+
std::fs::create_dir_all(root)?;
7983
Ok(Self {
80-
root: root.as_ref().into(),
84+
root: root.into(),
85+
tmpdir: root.into(),
86+
})
87+
}
88+
89+
/// Overrides the tmpdir directory used for temporary files.
90+
///
91+
/// The default is to use the root directory passed to [`FsStore::open`].
92+
///
93+
/// The tmpdir used must be on the same mount point as the root directory,
94+
/// otherwise the store will throw an error on writing data.
95+
///
96+
/// # Errors
97+
///
98+
/// This will attempt to create the tmpdir directory and throw an error if
99+
/// it does not exist.
100+
pub fn with_tmpdir<P: AsRef<Path>>(self, tmpdir: P) -> Result<Self, std::io::Error> {
101+
let tmpdir = tmpdir.as_ref();
102+
std::fs::create_dir_all(tmpdir)?;
103+
Ok(Self {
104+
tmpdir: tmpdir.into(),
105+
..self
81106
})
82107
}
83108

@@ -150,7 +175,7 @@ impl FsStore {
150175
})?;
151176

152177
let chunk_name = SavedChunkName::new_incremental(changes);
153-
write_chunk(&self.root, &paths, changes, chunk_name)?;
178+
write_chunk(&self.root, &paths, changes, chunk_name, &self.tmpdir)?;
154179

155180
Ok(())
156181
}
@@ -170,7 +195,7 @@ impl FsStore {
170195
// Write the snapshot
171196
let output_chunk_name = SavedChunkName::new_snapshot(doc.get_heads());
172197
let chunk = doc.save();
173-
write_chunk(&self.root, &paths, &chunk, output_chunk_name)?;
198+
write_chunk(&self.root, &paths, &chunk, output_chunk_name, &self.tmpdir)?;
174199

175200
// Remove all the old data
176201
for incremental in chunks.incrementals.keys() {
@@ -190,17 +215,18 @@ fn write_chunk(
190215
paths: &DocIdPaths,
191216
chunk: &[u8],
192217
name: SavedChunkName,
218+
tmpdir: &Path,
193219
) -> Result<(), Error> {
194220
// Write to a temp file and then rename to avoid partial writes
195-
let mut temp_save =
196-
tempfile::NamedTempFile::new().map_err(|e| Error(ErrorKind::CreateTempFile(e)))?;
197-
let temp_save_path = temp_save.path().to_owned();
198-
temp_save
199-
.as_file_mut()
221+
let temp_dir =
222+
tempfile::TempDir::new_in(tmpdir).map_err(|e| Error(ErrorKind::CreateTempFile(e)))?;
223+
let temp_save_path = temp_dir.path().join(name.filename());
224+
let mut temp_save_file =
225+
File::create(&temp_save_path).map_err(|e| Error(ErrorKind::CreateTempFile(e)))?;
226+
temp_save_file
200227
.write_all(chunk)
201228
.map_err(|e| Error(ErrorKind::WriteTempFile(temp_save_path.clone(), e)))?;
202-
temp_save
203-
.as_file_mut()
229+
temp_save_file
204230
.sync_all()
205231
.map_err(|e| Error(ErrorKind::WriteTempFile(temp_save_path.clone(), e)))?;
206232

src/tokio/fs_storage.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
use std::sync::{Arc, Mutex};
1+
use std::{
2+
path::Path,
3+
sync::{Arc, Mutex},
4+
};
25

36
use futures::{future::BoxFuture, FutureExt, TryFutureExt};
47

58
use crate::{DocumentId, Storage, StorageError};
69

710
/// A wrapper around [`crate::fs_store::FsStore`] that implements [`crate::Storage`]
11+
#[derive(Debug)]
812
pub struct FsStorage {
913
inner: Arc<Mutex<crate::fs_store::FsStore>>,
1014
handle: tokio::runtime::Handle,
1115
}
1216

1317
impl FsStorage {
14-
/// Create a new [`FsStorage`] from a [`std::path::PathBuf`]
18+
/// Creates a new [`FsStorage`] from a [`Path`].
1519
///
1620
/// # Errors
1721
///
@@ -21,11 +25,34 @@ impl FsStorage {
2125
/// # Panics
2226
///
2327
/// If there is not a tokio runtime available
24-
pub fn open(root: std::path::PathBuf) -> Result<Self, std::io::Error> {
28+
pub fn open<P: AsRef<Path>>(root: P) -> Result<Self, std::io::Error> {
2529
let handle = tokio::runtime::Handle::current();
2630
let inner = Arc::new(Mutex::new(crate::fs_store::FsStore::open(root)?));
2731
Ok(Self { inner, handle })
2832
}
33+
34+
/// Overrides the tmpdir directory used for temporary files.
35+
///
36+
/// The default is to use the root directory passed to [`FsStorage::open`].
37+
///
38+
/// The tmpdir used must be on the same mount point as the root directory,
39+
/// otherwise the storage will throw an error on writing data.
40+
///
41+
/// # Errors
42+
///
43+
/// This will attempt to create the tmpdir directory and throw an error if
44+
/// it does not exist.
45+
pub fn with_tmpdir<P: AsRef<Path>>(self, tmpdir: P) -> Option<Result<Self, std::io::Error>> {
46+
let Self { inner, handle } = self;
47+
let inner = Arc::into_inner(inner)?.into_inner().ok()?;
48+
let inner = inner.with_tmpdir(tmpdir);
49+
let Ok(inner) = inner else {
50+
let e = inner.unwrap_err();
51+
return Some(Err(e));
52+
};
53+
let inner = Arc::new(Mutex::new(inner));
54+
Some(Ok(Self { inner, handle }))
55+
}
2956
}
3057

3158
impl Storage for FsStorage {

0 commit comments

Comments
 (0)