Skip to content

Commit 6c8371d

Browse files
authored
Merge pull request #1117 from spkenv/remove-tag-lock
Add method to unlock tags manually
2 parents 81953ad + 738387f commit 6c8371d

File tree

1 file changed

+60
-1
lines changed
  • crates/spfs/src/storage/fs

1 file changed

+60
-1
lines changed

crates/spfs/src/storage/fs/tag.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,29 @@ impl TagStorage for FsRepository {
130130
}
131131
}
132132

133+
impl FsRepository {
134+
/// Forcefully remove any lock file for the identified tag.
135+
///
136+
/// # Safety
137+
/// This function is unsafe because it removes the lock file without
138+
/// ensuring that the tag file is not being written to, which can cause
139+
/// corruption in the tag file.
140+
pub async unsafe fn unlock_tag_in_namespace(
141+
&self,
142+
namespace: Option<&TagNamespace>,
143+
tag: tracking::TagSpec,
144+
) -> Result<()> {
145+
// Safety: we do not ensure the tag file is not being written to
146+
// but pass the responsibility to the caller.
147+
unsafe {
148+
self.opened()
149+
.await?
150+
.unlock_tag_in_namespace(namespace, tag)
151+
.await
152+
}
153+
}
154+
}
155+
133156
impl OpenFsRepository {
134157
fn tags_root_in_namespace(&self, namespace: Option<&TagNamespace>) -> PathBuf {
135158
let mut tags_root = self.root().join("tags");
@@ -148,6 +171,23 @@ impl OpenFsRepository {
148171
}
149172
tags_root
150173
}
174+
175+
/// Forcefully remove any lock file for the identified tag.
176+
///
177+
/// # Safety
178+
/// This function is unsafe because it removes the lock file without
179+
/// ensuring that the tag file is not being written to, which can cause
180+
/// corruption in the tag file.
181+
pub async unsafe fn unlock_tag_in_namespace(
182+
&self,
183+
namespace: Option<&TagNamespace>,
184+
tag: tracking::TagSpec,
185+
) -> Result<()> {
186+
let path = tag.to_path(self.tags_root_in_namespace(namespace));
187+
// Safety: we do not ensure the tag file is not being written to
188+
// but pass the responsibility to the caller.
189+
unsafe { TagLock::remove(path) }
190+
}
151191
}
152192

153193
#[async_trait::async_trait]
@@ -849,9 +889,11 @@ impl TagExt for tracking::TagSpec {
849889
struct TagLock(PathBuf);
850890

851891
impl TagLock {
892+
const LOCK_EXT: &'static str = "tag.lock";
893+
852894
pub async fn new<P: AsRef<Path>>(tag_file: P) -> Result<TagLock> {
853895
let mut lock_file = tag_file.as_ref().to_path_buf();
854-
lock_file.set_extension("tag.lock");
896+
lock_file.set_extension(Self::LOCK_EXT);
855897

856898
let timeout = std::time::Instant::now() + std::time::Duration::from_secs(5);
857899
loop {
@@ -883,6 +925,23 @@ impl TagLock {
883925
}
884926
}
885927
}
928+
929+
/// Remove the lock file for a tag
930+
///
931+
/// # Safety:
932+
/// Tag locks are used to ensure that only one process is writing to a tag file at a time.
933+
/// Removing the lock file without ensuring that the tag file is not being written to may
934+
/// cause the data within the file to become corrupt.
935+
pub unsafe fn remove<P: AsRef<Path>>(tag_file: P) -> Result<()> {
936+
let mut lock_file = tag_file.as_ref().to_path_buf();
937+
lock_file.set_extension(Self::LOCK_EXT);
938+
if let Err(err) = std::fs::remove_file(&lock_file) {
939+
if err.kind() != std::io::ErrorKind::NotFound {
940+
return Err(Error::StorageWriteError("unlock tag", lock_file, err));
941+
}
942+
}
943+
Ok(())
944+
}
886945
}
887946

888947
impl Drop for TagLock {

0 commit comments

Comments
 (0)