Skip to content

Commit

Permalink
feat(monofs): replace tombstone with deleted_at timestamp (#103)
Browse files Browse the repository at this point in the history
The main changes include:
- Added deleted_at timestamp to track when entities are marked as deleted
- Modified find operations to skip deleted entities
- Changed remove behavior to soft delete by default
- Added remove_trace for complete removal
- Improved test coverage for deletion scenarios
- Updated documentation and examples
  • Loading branch information
appcypher authored Jan 11, 2025
1 parent 472082f commit 4e01f19
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 203 deletions.
6 changes: 3 additions & 3 deletions monofs/examples/dir_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ async fn main() -> FsResult<()> {
println!("Copied file: {:?}", copied_file);

// Remove a file
let (removed_name, removed_entity) = root.remove("docs/readme.md").await?;
println!("Removed '{}': {:?}", removed_name, removed_entity);
root.remove("docs/readme.md").await?;
println!("Removed 'docs/readme.md'");

// Create and add a subdirectory
root.put_dir("subdir", Dir::new(store.clone()))?;
Expand All @@ -77,7 +77,7 @@ async fn main() -> FsResult<()> {
}

// Check if an entry exists
let file_exists = root.has_entry("example.txt").await?;
let file_exists = root.has_entry("example.txt")?;
println!("'example.txt' exists: {}", file_exists);

// Get and modify a subdirectory
Expand Down
38 changes: 26 additions & 12 deletions monofs/lib/filesystem/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,12 @@ where
///
/// dir.put_entry(file_name, file_cid.into())?;
///
/// assert!(dir.has_entry(file_name).await?);
/// assert!(!dir.has_entry("nonexistent.txt").await?);
/// assert!(dir.has_entry(file_name)?);
/// assert!(!dir.has_entry("nonexistent.txt")?);
/// # Ok(())
/// # }
/// ```
pub async fn has_entry(&self, name: impl AsRef<str>) -> FsResult<bool> {
pub fn has_entry(&self, name: impl AsRef<str>) -> FsResult<bool> {
let name = Utf8UnixPathSegment::from_str(name.as_ref())?;
Ok(self.inner.entries.contains_key(&name))
}
Expand Down Expand Up @@ -335,7 +335,14 @@ where
S: Send + Sync,
{
match self.get_entry(name)? {
Some(link) => Ok(Some(link.resolve_entity(self.inner.store.clone()).await?)),
Some(link) => {
let entity = link.resolve_entity(self.inner.store.clone()).await?;
if entity.get_metadata().get_deleted_at().is_some() {
Ok(None)
} else {
Ok(Some(entity))
}
}
None => Ok(None),
}
}
Expand All @@ -350,7 +357,14 @@ where
{
let store = self.inner.store.clone();
match self.get_entry_mut(name)? {
Some(link) => Ok(Some(link.resolve_entity_mut(store).await?)),
Some(link) => {
let entity = link.resolve_entity_mut(store).await?;
if entity.get_metadata().get_deleted_at().is_some() {
Ok(None)
} else {
Ok(Some(entity))
}
}
None => Ok(None),
}
}
Expand Down Expand Up @@ -655,8 +669,8 @@ mod tests {
loaded_dir_metadata.get_sync_type()
);
assert_eq!(
dir_metadata.get_tombstone(),
loaded_dir_metadata.get_tombstone()
dir_metadata.get_deleted_at(),
loaded_dir_metadata.get_deleted_at()
);
assert_eq!(
dir_metadata.get_created_at(),
Expand Down Expand Up @@ -689,8 +703,8 @@ mod tests {

dir.put_entry(file_name, file_cid.into())?;

assert!(dir.has_entry(file_name).await?);
assert!(!dir.has_entry("nonexistent.txt").await?);
assert!(dir.has_entry(file_name)?);
assert!(!dir.has_entry("nonexistent.txt")?);

Ok(())
}
Expand All @@ -703,11 +717,11 @@ mod tests {
"bafkreidgvpkjawlxz6sffxzwgooowe5yt7i6wsyg236mfoks77nywkptdq".parse()?;

dir.put_entry(file_name, file_cid.clone().into())?;
assert!(dir.has_entry(file_name).await?);
assert!(dir.has_entry(file_name)?);

let removed_entry = dir.remove_entry(file_name)?;
assert_eq!(removed_entry.get_cid(), Some(&file_cid));
assert!(!dir.has_entry(file_name).await?);
assert!(!dir.has_entry(file_name)?);

assert!(dir.remove_entry("nonexistent.txt").is_err());

Expand All @@ -722,7 +736,7 @@ mod tests {
assert_eq!(*metadata.get_entity_type(), EntityType::Dir);
assert_eq!(*metadata.get_symlink_depth(), DEFAULT_SYMLINK_DEPTH);
assert_eq!(*metadata.get_sync_type(), SyncType::RAFT);
assert!(!metadata.get_tombstone());
assert!(metadata.get_deleted_at().is_none());

Ok(())
}
Expand Down
70 changes: 3 additions & 67 deletions monofs/lib/filesystem/dir/find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,7 @@ pub type FindResultDirMut<'a, S> = FindResult<&'a mut Dir<S>>;
/// following the path specified by `path`. It attempts to resolve each component of the path
/// until it either finds the target directory, encounters an error, or determines that the path
/// is not found or invalid.
///
/// ## Examples
///
/// ```
/// use monofs::filesystem::{Dir, find_dir};
/// use monoutils_store::MemoryStore;
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let store = MemoryStore::default();
/// let root_dir = Dir::new(store);
/// let result = find_dir(&root_dir, "some/path/to/entity").await?;
/// # Ok(())
/// # }
/// ```
///
/// ## Note
///
/// The function does not support the following path components:
/// - `.`
/// - `..`
/// - `/`
///
/// If any of these components are present in the path, the function will return an error.
pub async fn find_dir<S>(mut dir: &Dir<S>, path: impl AsRef<str>) -> FsResult<FindResultDir<S>>
pub(crate) async fn find_dir<S>(mut dir: &Dir<S>, path: impl AsRef<str>) -> FsResult<FindResultDir<S>>
where
S: IpldStore + Send + Sync,
{
Expand Down Expand Up @@ -125,31 +101,7 @@ where
/// following the path specified by `path`. It attempts to resolve each component of the path
/// until it either finds the target directory, encounters an error, or determines that the path
/// is not found or invalid.
///
/// ## Examples
///
/// ```
/// use monofs::filesystem::{Dir, find_dir_mut};
/// use monoutils_store::MemoryStore;
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let store = MemoryStore::default();
/// let mut root_dir = Dir::new(store);
/// let result = find_dir_mut(&mut root_dir, "some/path/to/entity").await?;
/// # Ok(())
/// # }
/// ```
///
/// ## Note
///
/// The function does not support the following path components:
/// - `.`
/// - `..`
/// - `/`
///
/// If any of these components are present in the path, the function will return an error.
pub async fn find_dir_mut<S>(
pub(crate) async fn find_dir_mut<S>(
mut dir: &mut Dir<S>,
path: impl AsRef<str>,
) -> FsResult<FindResultDirMut<S>>
Expand Down Expand Up @@ -201,23 +153,7 @@ where
/// This function checks the existence of an entity at the given path. If the entity
/// exists, it returns the entity. If the entity does not exist, it creates a new
/// directory hierarchy and returns the new entity.
///
/// ## Examples
///
/// ```
/// use monofs::filesystem::{Dir, find_or_create_dir};
/// use monoutils_store::MemoryStore;
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let store = MemoryStore::default();
/// let mut root_dir = Dir::new(store);
/// let new_dir = find_or_create_dir(&mut root_dir, "new/nested/directory").await?;
/// assert!(new_dir.is_empty());
/// # Ok(())
/// # }
/// ```
pub async fn find_or_create_dir<S>(dir: &mut Dir<S>, path: impl AsRef<str>) -> FsResult<&mut Dir<S>>
pub(crate) async fn find_or_create_dir<S>(dir: &mut Dir<S>, path: impl AsRef<str>) -> FsResult<&mut Dir<S>>
where
S: IpldStore + Send + Sync,
{
Expand Down
Loading

0 comments on commit 4e01f19

Please sign in to comment.