prune: clean up internal interface

This commit is contained in:
Michael Eischer 2024-04-14 12:32:29 +02:00
parent 8d507c1372
commit d8622c86eb
2 changed files with 31 additions and 15 deletions

View File

@ -191,7 +191,7 @@ func runPruneWithRepo(ctx context.Context, opts PruneOptions, gopts GlobalOption
RepackUncompressed: opts.RepackUncompressed, RepackUncompressed: opts.RepackUncompressed,
} }
plan, stats, err := repository.PlanPrune(ctx, popts, repo, func(ctx context.Context, repo restic.Repository) (usedBlobs restic.CountedBlobSet, err error) { plan, err := repository.PlanPrune(ctx, popts, repo, func(ctx context.Context, repo restic.Repository) (usedBlobs restic.CountedBlobSet, err error) {
return getUsedBlobs(ctx, repo, ignoreSnapshots, printer) return getUsedBlobs(ctx, repo, ignoreSnapshots, printer)
}, printer) }, printer)
if err != nil { if err != nil {
@ -202,7 +202,7 @@ func runPruneWithRepo(ctx context.Context, opts PruneOptions, gopts GlobalOption
printer.P("\nWould have made the following changes:") printer.P("\nWould have made the following changes:")
} }
err = printPruneStats(printer, stats) err = printPruneStats(printer, plan.Stats())
if err != nil { if err != nil {
return err return err
} }
@ -210,7 +210,7 @@ func runPruneWithRepo(ctx context.Context, opts PruneOptions, gopts GlobalOption
// Trigger GC to reset garbage collection threshold // Trigger GC to reset garbage collection threshold
runtime.GC() runtime.GC()
return repository.DoPrune(ctx, popts, repo, plan, printer) return plan.Execute(ctx, printer)
} }
// printPruneStats prints out the statistics // printPruneStats prints out the statistics

View File

@ -66,6 +66,10 @@ type PrunePlan struct {
keepBlobs restic.CountedBlobSet // blobs to keep during repacking keepBlobs restic.CountedBlobSet // blobs to keep during repacking
removePacks restic.IDSet // packs to remove removePacks restic.IDSet // packs to remove
ignorePacks restic.IDSet // packs to ignore when rebuilding the index ignorePacks restic.IDSet // packs to ignore when rebuilding the index
repo restic.Repository
stats PruneStats
opts PruneOptions
} }
type packInfo struct { type packInfo struct {
@ -85,7 +89,7 @@ type packInfoWithID struct {
// PlanPrune selects which files to rewrite and which to delete and which blobs to keep. // PlanPrune selects which files to rewrite and which to delete and which blobs to keep.
// Also some summary statistics are returned. // Also some summary statistics are returned.
func PlanPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, getUsedBlobs func(ctx context.Context, repo restic.Repository) (usedBlobs restic.CountedBlobSet, err error), printer progress.Printer) (PrunePlan, PruneStats, error) { func PlanPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, getUsedBlobs func(ctx context.Context, repo restic.Repository) (usedBlobs restic.CountedBlobSet, err error), printer progress.Printer) (*PrunePlan, error) {
var stats PruneStats var stats PruneStats
if opts.UnsafeRecovery { if opts.UnsafeRecovery {
@ -93,27 +97,27 @@ func PlanPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, g
opts.MaxRepackBytes = 0 opts.MaxRepackBytes = 0
} }
if repo.Connections() < 2 { if repo.Connections() < 2 {
return PrunePlan{}, stats, fmt.Errorf("prune requires a backend connection limit of at least two") return nil, fmt.Errorf("prune requires a backend connection limit of at least two")
} }
if repo.Config().Version < 2 && opts.RepackUncompressed { if repo.Config().Version < 2 && opts.RepackUncompressed {
return PrunePlan{}, stats, fmt.Errorf("compression requires at least repository format version 2") return nil, fmt.Errorf("compression requires at least repository format version 2")
} }
usedBlobs, err := getUsedBlobs(ctx, repo) usedBlobs, err := getUsedBlobs(ctx, repo)
if err != nil { if err != nil {
return PrunePlan{}, stats, err return nil, err
} }
printer.P("searching used packs...\n") printer.P("searching used packs...\n")
keepBlobs, indexPack, err := packInfoFromIndex(ctx, repo.Index(), usedBlobs, &stats, printer) keepBlobs, indexPack, err := packInfoFromIndex(ctx, repo.Index(), usedBlobs, &stats, printer)
if err != nil { if err != nil {
return PrunePlan{}, stats, err return nil, err
} }
printer.P("collecting packs for deletion and repacking\n") printer.P("collecting packs for deletion and repacking\n")
plan, err := decidePackAction(ctx, opts, repo, indexPack, &stats, printer) plan, err := decidePackAction(ctx, opts, repo, indexPack, &stats, printer)
if err != nil { if err != nil {
return PrunePlan{}, stats, err return nil, err
} }
if len(plan.repackPacks) != 0 { if len(plan.repackPacks) != 0 {
@ -137,7 +141,11 @@ func PlanPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, g
} }
plan.keepBlobs = keepBlobs plan.keepBlobs = keepBlobs
return plan, stats, nil plan.repo = repo
plan.stats = stats
plan.opts = opts
return &plan, nil
} }
func packInfoFromIndex(ctx context.Context, idx restic.MasterIndex, usedBlobs restic.CountedBlobSet, stats *PruneStats, printer progress.Printer) (restic.CountedBlobSet, map[restic.ID]packInfo, error) { func packInfoFromIndex(ctx context.Context, idx restic.MasterIndex, usedBlobs restic.CountedBlobSet, stats *PruneStats, printer progress.Printer) (restic.CountedBlobSet, map[restic.ID]packInfo, error) {
@ -489,14 +497,18 @@ func decidePackAction(ctx context.Context, opts PruneOptions, repo restic.Reposi
}, nil }, nil
} }
// DoPrune does the actual pruning: func (plan *PrunePlan) Stats() PruneStats {
return plan.stats
}
// Execute does the actual pruning:
// - remove unreferenced packs first // - remove unreferenced packs first
// - repack given pack files while keeping the given blobs // - repack given pack files while keeping the given blobs
// - rebuild the index while ignoring all files that will be deleted // - rebuild the index while ignoring all files that will be deleted
// - delete the files // - delete the files
// plan.removePacks and plan.ignorePacks are modified in this function. // plan.removePacks and plan.ignorePacks are modified in this function.
func DoPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, plan PrunePlan, printer progress.Printer) (err error) { func (plan *PrunePlan) Execute(ctx context.Context, printer progress.Printer) (err error) {
if opts.DryRun { if plan.opts.DryRun {
printer.V("Repeated prune dry-runs can report slightly different amounts of data to keep or repack. This is expected behavior.\n\n") printer.V("Repeated prune dry-runs can report slightly different amounts of data to keep or repack. This is expected behavior.\n\n")
if len(plan.removePacksFirst) > 0 { if len(plan.removePacksFirst) > 0 {
printer.V("Would have removed the following unreferenced packs:\n%v\n\n", plan.removePacksFirst) printer.V("Would have removed the following unreferenced packs:\n%v\n\n", plan.removePacksFirst)
@ -507,6 +519,10 @@ func DoPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, pla
return nil return nil
} }
repo := plan.repo
// make sure the plan can only be used once
plan.repo = nil
// unreferenced packs can be safely deleted first // unreferenced packs can be safely deleted first
if len(plan.removePacksFirst) != 0 { if len(plan.removePacksFirst) != 0 {
printer.P("deleting unreferenced packs\n") printer.P("deleting unreferenced packs\n")
@ -544,7 +560,7 @@ func DoPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, pla
plan.ignorePacks.Merge(plan.removePacks) plan.ignorePacks.Merge(plan.removePacks)
} }
if opts.UnsafeRecovery { if plan.opts.UnsafeRecovery {
printer.P("deleting index files\n") printer.P("deleting index files\n")
indexFiles := repo.Index().(*index.MasterIndex).IDs() indexFiles := repo.Index().(*index.MasterIndex).IDs()
err = deleteFiles(ctx, false, repo, indexFiles, restic.IndexFile, printer) err = deleteFiles(ctx, false, repo, indexFiles, restic.IndexFile, printer)
@ -563,7 +579,7 @@ func DoPrune(ctx context.Context, opts PruneOptions, repo restic.Repository, pla
_ = deleteFiles(ctx, true, repo, plan.removePacks, restic.PackFile, printer) _ = deleteFiles(ctx, true, repo, plan.removePacks, restic.PackFile, printer)
} }
if opts.UnsafeRecovery { if plan.opts.UnsafeRecovery {
err = rebuildIndexFiles(ctx, repo, plan.ignorePacks, nil, true, printer) err = rebuildIndexFiles(ctx, repo, plan.ignorePacks, nil, true, printer)
if err != nil { if err != nil {
return errors.Fatalf("%s", err) return errors.Fatalf("%s", err)