From 0918e09a84c90061ef003dc2f29df22518c1140f Mon Sep 17 00:00:00 2001 From: Joey Riches Date: Sun, 17 May 2026 14:28:01 +0100 Subject: [PATCH] ferryd: Proactively remove stale deltas as new packages are indexed Previously, when packages were received any existing deltas packages would get silently dropped from the DB and effectively becoming on-disk orphans taking up huge amounts of disk space. Now, when we index a package in the repo, check if any deltas for that package exist and remove them proactively We have to do a small amount of additional dancing with the locking to avoid a deadlock when using AddLocalPackage() but otherwise, relatively straight-forward. --- src/ferryd/core/repo.go | 49 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/ferryd/core/repo.go b/src/ferryd/core/repo.go index ee392d4..1be5473 100644 --- a/src/ferryd/core/repo.go +++ b/src/ferryd/core/repo.go @@ -467,6 +467,10 @@ func (r *Repository) removePackageInternal(db libdb.Database, pool *Pool, id str r.insertMut.Lock() defer r.insertMut.Unlock() + return r.removePackageInternalLocked(db, pool, id) +} + +func (r *Repository) removePackageInternalLocked(db libdb.Database, pool *Pool, id string) error { poolEntry, err := pool.GetEntry(db, id) if err != nil { return nil @@ -503,6 +507,10 @@ func (r *Repository) removeDeltaInternal(db libdb.Database, pool *Pool, id strin return r.removePackageInternal(db, pool, id) } +func (r *Repository) removeDeltaInternalLocked(db libdb.Database, pool *Pool, id string) error { + return r.removePackageInternalLocked(db, pool, id) +} + // UnrefPackage will remove a package from our storage, and potentially remove the // entire RepoEntry for the package if none are left. // @@ -538,9 +546,16 @@ func (r *Repository) UnrefPackage(db libdb.Database, pool *Pool, pkgID string) e // Check out all the deltas for _, deltaID := range entry.Deltas { + if deltaID == pkgID { + continue + } pkgDelta, err := pool.GetEntry(db, deltaID) if err != nil { - return err + log.WithFields(log.Fields{ + "deltaID": deltaID, + "error": err, + }).Warning("Failed to find delta in pool during unref") + continue } // We found a delta that is referencing us, we must garbage collect it now @@ -652,6 +667,7 @@ func (r *Repository) buildSaneEntry(db libdb.Database, pool *Pool, newPkg *libeo if entry != nil { repoEntry.Available = entry.Available repoEntry.Published = entry.Published + repoEntry.Deltas = entry.Deltas pkgAvail, err := pool.GetEntry(db, repoEntry.Published) if err == nil { @@ -707,6 +723,37 @@ func (r *Repository) AddLocalPackage(db libdb.Database, pool *Pool, pkg *libeopk return nil } + // Proactively clean up stale deltas that don't target our new release + var remainDeltas []string + newRelease := pkg.Meta.Package.GetRelease() + for _, deltaID := range repoEntry.Deltas { + poolEntry, err := pool.GetEntry(db, deltaID) + if err != nil { + log.WithFields(log.Fields{ + "id": deltaID, + "error": err, + }).Warning("Failed to find delta in pool during proactive cleanup") + continue + } + if poolEntry.Delta == nil || poolEntry.Delta.ToRelease != newRelease { + log.WithFields(log.Fields{ + "id": deltaID, + "package": repoEntry.Name, + "repo": r.ID, + }).Info("Removing stale delta during package update") + if err := r.removeDeltaInternalLocked(db, pool, deltaID); err != nil { + log.WithFields(log.Fields{ + "id": deltaID, + "error": err, + }).Warning("Failed to remove stale delta during package update") + } + continue + } + remainDeltas = append(remainDeltas, deltaID) + } + repoEntry.Deltas = remainDeltas + sort.Strings(repoEntry.Deltas) + // Construct root dirs if err := os.MkdirAll(pkgDir, 00755); err != nil { return err