Phase 6c fix: TagStore delete()/contains() also see stamped tags
Build with clawstor cache / Cargo build (clawstor-cached) (pull_request) Failing after 3s
Build with clawstor cache / Cargo build (clawstor-cached) (pull_request) Failing after 3s
Companion to the previous list/get fix. Same layer-split problem: * delete() only unlinked tags/, leaving stamped tags-v2/ behind. Result: `unpin` prints \"no such tag\" for pins created via Phase 3c+ RPC even though the tag is right there on disk. * contains() only checked tags/. Same false-negative. Fix: both APIs now inspect BOTH layers. delete() unlinks whichever files exist (either or both) AND removes the TTL expiry sidecar if present. Returns true when anything was actually removed. Legacy behavior preserved: tests unchanged, 381 tests pass.
This commit is contained in:
@@ -268,22 +268,39 @@ impl TagStore {
|
|||||||
/// no such tag existed.
|
/// no such tag existed.
|
||||||
pub async fn delete(&self, key: &str) -> Result<bool> {
|
pub async fn delete(&self, key: &str) -> Result<bool> {
|
||||||
validate_key(key)?;
|
validate_key(key)?;
|
||||||
let path = self.tag_path(key);
|
// Phase 6c fix (2026-07-14): try both layers. Modern pins
|
||||||
match tokio::fs::remove_file(&path).await {
|
// land in tags-v2/; without unlinking the stamped file too,
|
||||||
Ok(()) => Ok(true),
|
// `unpin` prints "no such tag" and leaves the tag behind.
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
let mut removed = false;
|
||||||
Err(e) => Err(anyhow::Error::from(e)),
|
let legacy = self.tag_path(key);
|
||||||
|
match tokio::fs::remove_file(&legacy).await {
|
||||||
|
Ok(()) => removed = true,
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||||
|
Err(e) => return Err(anyhow::Error::from(e)),
|
||||||
}
|
}
|
||||||
|
let stamped = self.stamped_tag_path(key);
|
||||||
|
match tokio::fs::remove_file(&stamped).await {
|
||||||
|
Ok(()) => removed = true,
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||||
|
Err(e) => return Err(anyhow::Error::from(e)),
|
||||||
|
}
|
||||||
|
// Also clear any TTL sidecar.
|
||||||
|
let expiry = self.stamped_expiry_path(key);
|
||||||
|
let _ = tokio::fs::remove_file(&expiry).await;
|
||||||
|
Ok(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a tag with the given name exists.
|
/// Whether a tag with the given name exists.
|
||||||
pub async fn contains(&self, key: &str) -> Result<bool> {
|
pub async fn contains(&self, key: &str) -> Result<bool> {
|
||||||
validate_key(key)?;
|
validate_key(key)?;
|
||||||
match tokio::fs::metadata(self.tag_path(key)).await {
|
// Same fallthrough as get(): visible via either layer counts.
|
||||||
Ok(_) => Ok(true),
|
if tokio::fs::metadata(self.tag_path(key)).await.is_ok() {
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
return Ok(true);
|
||||||
Err(e) => Err(anyhow::Error::from(e)),
|
|
||||||
}
|
}
|
||||||
|
if tokio::fs::metadata(self.stamped_tag_path(key)).await.is_ok() {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List every stored tag. Sorted by key for deterministic output.
|
/// List every stored tag. Sorted by key for deterministic output.
|
||||||
|
|||||||
Reference in New Issue
Block a user