Skip to content

Commit 2f675e4

Browse files
committed
Restrict tag pruning to active tag namespace
If Cleaner is given a repo with a tag namespace configured, respect the tag namespace when pruning tags. Avoid pruning tags that do not belong to the active tag namespace. Because the tag walking and pruning logic are intertwined, the prune function is still called for all tag namespaces. Signed-off-by: J Robert Ray <[email protected]>
1 parent 3b5d1ed commit 2f675e4

File tree

2 files changed

+43
-15
lines changed

2 files changed

+43
-15
lines changed

crates/spfs/src/clean.rs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use super::prune::PruneParameters;
2020
use crate::io::Pluralize;
2121
use crate::prelude::*;
2222
use crate::runtime::makedirs_with_perms;
23-
use crate::storage::TagNamespaceBuf;
2423
use crate::storage::fs::OpenFsRepository;
24+
use crate::storage::{TagNamespace, TagNamespaceBuf};
2525
use crate::{Digest, Error, Result, encoding, graph, storage, tracking};
2626

2727
#[cfg(test)]
@@ -319,6 +319,7 @@ where
319319
/// partially complete depending on the nature of the errors.
320320
pub async fn prune_all_tags_and_clean(&self) -> Result<CleanResult> {
321321
let mut result = CleanResult::default();
322+
let tag_namespace_to_prune = self.repo.get_tag_namespace();
322323
let namespaces = self.repo.ls_tag_namespaces();
323324
let mut stream = namespaces
324325
.map(|ns| {
@@ -346,7 +347,11 @@ where
346347
futures.try_next().await?;
347348
}
348349
}
349-
futures.push(self.prune_tag_stream_and_walk(tag_namespace, tag_spec));
350+
futures.push(self.prune_tag_stream_and_walk(
351+
tag_namespace_to_prune.as_ref(),
352+
tag_namespace,
353+
tag_spec,
354+
));
350355
}
351356
drop(stream);
352357
while let Some(r) = futures.try_next().await? {
@@ -375,12 +380,18 @@ where
375380
Ok(result)
376381
}
377382

378-
async fn prune_tag_stream_and_walk(
383+
async fn prune_tag_stream_and_walk<T>(
379384
&self,
380-
tag_namespace: Option<TagNamespaceBuf>,
385+
tag_namespace_to_prune: Option<T>,
386+
tag_namespace_to_visit: Option<TagNamespaceBuf>,
381387
tag_spec: tracking::TagSpec,
382-
) -> Result<CleanResult> {
383-
let (mut result, to_keep) = self.prune_tag_stream(tag_namespace, tag_spec).await?;
388+
) -> Result<CleanResult>
389+
where
390+
T: AsRef<TagNamespace>,
391+
{
392+
let (mut result, to_keep) = self
393+
.prune_tag_stream(tag_namespace_to_prune, tag_namespace_to_visit, tag_spec)
394+
.await?;
384395
result += self.walk_attached_objects(&to_keep).await?;
385396

386397
Ok(result)
@@ -404,14 +415,31 @@ where
404415
/// Visit the tag and its history, pruning as configured and
405416
/// returning a cleaning (pruning) result and list of tags that
406417
/// were kept.
407-
pub async fn prune_tag_stream(
418+
pub async fn prune_tag_stream<T>(
408419
&self,
409-
tag_namespace: Option<TagNamespaceBuf>,
420+
tag_namespace_to_prune: Option<T>,
421+
tag_namespace_to_visit: Option<TagNamespaceBuf>,
410422
tag_spec: tracking::TagSpec,
411-
) -> Result<(CleanResult, Vec<encoding::Digest>)> {
423+
) -> Result<(CleanResult, Vec<encoding::Digest>)>
424+
where
425+
T: AsRef<TagNamespace>,
426+
{
427+
// The Cleaner needs to visit all namespaces to learn what objects are
428+
// still alive, but if the repo passed to the Cleaner has a namespace
429+
// set on it then one would expect that only tags in that namespace are
430+
// pruned.
431+
let should_prune_this_namespace = match (
432+
tag_namespace_to_prune.as_ref(),
433+
tag_namespace_to_visit.as_deref(),
434+
) {
435+
(Some(prune), Some(visit)) if prune.as_ref() == visit => true,
436+
(None, None) => true,
437+
_ => false,
438+
};
439+
412440
let history = self
413441
.repo
414-
.read_tag_in_namespace(tag_namespace.as_deref(), &tag_spec)
442+
.read_tag_in_namespace(tag_namespace_to_visit.as_deref(), &tag_spec)
415443
.await?
416444
.try_collect::<Vec<_>>()
417445
.await?;
@@ -423,7 +451,7 @@ where
423451
self.reporter.visit_tag(&tag);
424452
let count = if let Some(seen_count) = seen_targets.get(&tag.target) {
425453
if let Some(keep_number) = self.prune_repeated_tags {
426-
if *seen_count >= keep_number.get() {
454+
if should_prune_this_namespace && *seen_count >= keep_number.get() {
427455
to_prune.push(tag);
428456
continue;
429457
}
@@ -435,7 +463,7 @@ where
435463

436464
seen_targets.insert(tag.target, count);
437465

438-
if self.prune_params.should_prune(&spec, &tag) {
466+
if should_prune_this_namespace && self.prune_params.should_prune(&spec, &tag) {
439467
to_prune.push(tag);
440468
} else {
441469
to_keep.push(tag.target);
@@ -450,15 +478,15 @@ where
450478
for tag in to_prune.iter() {
451479
if !self.dry_run {
452480
self.repo
453-
.remove_tag_in_namespace(tag_namespace.as_deref(), tag)
481+
.remove_tag_in_namespace(tag_namespace_to_visit.as_deref(), tag)
454482
.await?;
455483
}
456484
self.reporter.tag_removed(tag);
457485
}
458486

459487
result
460488
.pruned_tags
461-
.entry(tag_namespace)
489+
.entry(tag_namespace_to_visit)
462490
.or_default()
463491
.insert(tag_spec, to_prune);
464492

crates/spfs/src/storage/tag_namespace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub const TAG_NAMESPACE_MARKER: &str = "#ns";
1212

1313
/// A borrowed tag namespace name
1414
#[repr(transparent)]
15-
#[derive(Debug)]
15+
#[derive(Debug, PartialEq)]
1616
pub struct TagNamespace(RelativePath);
1717

1818
impl TagNamespace {

0 commit comments

Comments
 (0)