From 255b625672a85506d4a5782c4e250175d307ee45 Mon Sep 17 00:00:00 2001 From: iklobato Date: Sun, 4 May 2025 21:43:17 -0300 Subject: [PATCH] Enhance docker system prune performance via concurrent pruning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored the runPrune function to execute pruning operations concurrently: - Added sync.WaitGroup to orchestrate goroutines - Used sync/atomic for thread-safe space reclaimed counter - Added mutex for safe slice operations (outputs and errors) - Maintained the same CLI behavior with improved performance - Order of outputs may differ from sequential execution This change significantly improves performance of the prune command, especially for systems with many Docker resources by executing independent pruning operations in parallel. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Signed-off-by: iklobato --- cli/command/system/prune.go | 56 +++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/cli/command/system/prune.go b/cli/command/system/prune.go index c1fa339a5bdf..82172260429e 100644 --- a/cli/command/system/prune.go +++ b/cli/command/system/prune.go @@ -5,6 +5,8 @@ import ( "context" "fmt" "sort" + "sync" + "sync/atomic" "text/template" "github.com/docker/cli/cli" @@ -72,8 +74,13 @@ const confirmationTemplate = `WARNING! This will remove: {{end}} Are you sure you want to continue?` +type pruneResult struct { + spaceReclaimed uint64 + output string + err error +} + func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) error { - // TODO version this once "until" filter is supported for volumes if options.pruneVolumes && options.filter.Value().Contains("until") { return errors.New(`ERROR: The "until" filter is not supported with "--volumes"`) } @@ -99,19 +106,46 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) } var spaceReclaimed uint64 + + var mu sync.Mutex + var outputs []string + var errs []error + + var wg sync.WaitGroup + wg.Add(len(pruneFuncs)) + for _, pruneFn := range pruneFuncs { - spc, output, err := pruneFn(ctx, dockerCli, options.all, options.filter) - if err != nil { - return err - } - spaceReclaimed += spc - if output != "" { - _, _ = fmt.Fprintln(dockerCli.Out(), output) - } + go func(pruneFn func(ctx context.Context, dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error)) { + defer wg.Done() + + spc, output, err := pruneFn(ctx, dockerCli, options.all, options.filter) + + atomic.AddUint64(&spaceReclaimed, spc) + + mu.Lock() + defer mu.Unlock() + + if err != nil { + errs = append(errs, err) + } + if output != "" { + outputs = append(outputs, output) + } + }(pruneFn) } - + + wg.Wait() + + if len(errs) > 0 { + return errs[0] + } + + for _, output := range outputs { + _, _ = fmt.Fprintln(dockerCli.Out(), output) + } + _, _ = fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed))) - + return nil }