Skip to content

Commit 174fb8a

Browse files
committed
cache: convert prune loop from recursive invocation to iterative
The prune logic would prune multiple times because one prune could cause more things to be capable of pruning and change the logic. This was done through a recursive invocation. Since go doesn't have support for function tail calls, this would result in a new stack entry for each loop. This unrolls the logic so the prune function is invoked iteratively rather than recursively. `prune` and `pruneOnce` have also had their names swapped. In general, `pruneOnce` implies that it runs a single prune while `prune` sounds like the top level function. The current code had this reversed and `pruneOnce` would call `prune` and `prune` would call itself recursively. I've also updated a section in the controller that invoked prune on each worker. In older versions of Go, the current version was correct because those versions of Go would reuse the location for each loop which would cause goroutines to all reference the same worker instead of different workers. Recent versions of Go have changed the behavior so this is no longer needed. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent d995796 commit 174fb8a

File tree

3 files changed

+32
-26
lines changed

3 files changed

+32
-26
lines changed

cache/manager.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,7 @@ func (cm *cacheManager) Prune(ctx context.Context, ch chan client.UsageInfo, opt
10121012
cm.muPrune.Lock()
10131013

10141014
for _, opt := range opts {
1015-
if err := cm.pruneOnce(ctx, ch, opt); err != nil {
1015+
if err := cm.prune(ctx, ch, opt); err != nil {
10161016
cm.muPrune.Unlock()
10171017
return err
10181018
}
@@ -1029,7 +1029,7 @@ func (cm *cacheManager) Prune(ctx context.Context, ch chan client.UsageInfo, opt
10291029
return nil
10301030
}
10311031

1032-
func (cm *cacheManager) pruneOnce(ctx context.Context, ch chan client.UsageInfo, opt client.PruneInfo) error {
1032+
func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo, opt client.PruneInfo) error {
10331033
filter, err := filters.ParseAll(opt.Filter...)
10341034
if err != nil {
10351035
return errors.Wrapf(err, "failed to parse prune filters %v", opt.Filter)
@@ -1066,14 +1066,21 @@ func (cm *cacheManager) pruneOnce(ctx context.Context, ch chan client.UsageInfo,
10661066
}
10671067
}
10681068

1069-
return cm.prune(ctx, ch, pruneOpt{
1069+
popt := pruneOpt{
10701070
filter: filter,
10711071
all: opt.All,
10721072
checkShared: check,
10731073
keepDuration: opt.KeepDuration,
10741074
keepBytes: calculateKeepBytes(totalSize, dstat, opt),
10751075
totalSize: totalSize,
1076-
})
1076+
}
1077+
for {
1078+
releasedSize, releasedCount, err := cm.pruneOnce(ctx, ch, popt)
1079+
if err != nil || releasedCount == 0 {
1080+
return err
1081+
}
1082+
popt.totalSize -= releasedSize
1083+
}
10771084
}
10781085

10791086
func calculateKeepBytes(totalSize int64, dstat disk.DiskStat, opt client.PruneInfo) int64 {
@@ -1100,9 +1107,9 @@ func calculateKeepBytes(totalSize int64, dstat disk.DiskStat, opt client.PruneIn
11001107
return keepBytes
11011108
}
11021109

1103-
func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo, opt pruneOpt) (err error) {
1110+
func (cm *cacheManager) pruneOnce(ctx context.Context, ch chan client.UsageInfo, opt pruneOpt) (releasedSize, releasedCount int64, err error) {
11041111
if opt.keepBytes != 0 && opt.totalSize < opt.keepBytes {
1105-
return nil
1112+
return 0, 0, nil
11061113
}
11071114

11081115
var toDelete []*deleteRecord
@@ -1206,11 +1213,11 @@ func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo, opt
12061213
// mark metadata as deleted in case we crash before cleanup finished
12071214
if err := cr.queueDeleted(); err != nil {
12081215
releaseLocks()
1209-
return err
1216+
return 0, 0, err
12101217
}
12111218
if err := cr.commitMetadata(); err != nil {
12121219
releaseLocks()
1213-
return err
1220+
return 0, 0, err
12141221
}
12151222
}
12161223
cr.mu.Unlock()
@@ -1221,7 +1228,7 @@ func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo, opt
12211228
cm.mu.Unlock()
12221229

12231230
if len(toDelete) == 0 {
1224-
return nil
1231+
return 0, 0, nil
12251232
}
12261233

12271234
// calculate sizes here so that lock does not need to be held for slow process
@@ -1234,7 +1241,7 @@ func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo, opt
12341241
if size == sizeUnknown {
12351242
// calling size will warm cache for next call
12361243
if _, err := cr.size(ctx); err != nil {
1237-
return err
1244+
return 0, 0, err
12381245
}
12391246
}
12401247
}
@@ -1277,15 +1284,18 @@ func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo, opt
12771284
c.Size = cr.equalImmutable.getSize() // benefit from DiskUsage calc
12781285
}
12791286

1280-
opt.totalSize -= c.Size
1287+
releasedSize += c.Size
12811288

12821289
if cr.equalImmutable != nil {
12831290
if err1 := cr.equalImmutable.remove(ctx, false); err == nil {
12841291
err = err1
12851292
}
12861293
}
1287-
if err1 := cr.remove(ctx, true); err == nil {
1294+
1295+
if err1 := cr.remove(ctx, true); err1 != nil && err == nil {
12881296
err = err1
1297+
} else if err1 == nil {
1298+
releasedCount++
12891299
}
12901300

12911301
if err == nil && ch != nil {
@@ -1294,16 +1304,17 @@ func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo, opt
12941304
cr.mu.Unlock()
12951305
}
12961306
cm.mu.Unlock()
1307+
12971308
if err != nil {
1298-
return err
1309+
return releasedSize, releasedCount, err
12991310
}
13001311

13011312
select {
13021313
case <-ctx.Done():
1303-
return context.Cause(ctx)
1314+
err = context.Cause(ctx)
13041315
default:
1305-
return cm.prune(ctx, ch, opt)
13061316
}
1317+
return releasedSize, releasedCount, err
13071318
}
13081319

13091320
func (cm *cacheManager) markShared(m map[string]*cacheUsageInfo) error {

control/control.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -623,14 +623,12 @@ func (c *Controller) gc() {
623623
}()
624624

625625
for _, w := range workers {
626-
func(w worker.Worker) {
627-
eg.Go(func() error {
628-
if policy := w.GCPolicy(); len(policy) > 0 {
629-
return w.Prune(ctx, ch, policy...)
630-
}
631-
return nil
632-
})
633-
}(w)
626+
eg.Go(func() error {
627+
if policy := w.GCPolicy(); len(policy) > 0 {
628+
return w.Prune(ctx, ch, policy...)
629+
}
630+
return nil
631+
})
634632
}
635633

636634
err = eg.Wait()

hack/composefiles/compose.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@ services:
44
container_name: buildkit-dev
55
build:
66
context: ../..
7-
args:
8-
BUILDKIT_DEBUG: 1
97
image: moby/buildkit:local
108
ports:
119
- 127.0.0.1:5000:5000
1210
- 127.0.0.1:6060:6060
1311
restart: always
1412
privileged: true
1513
environment:
16-
DELVE_PORT: 5000
1714
OTEL_SERVICE_NAME: buildkitd
1815
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
1916
configs:

0 commit comments

Comments
 (0)