Skip to content

Commit 0fecf46

Browse files
authored
Merge pull request moby#4589 from jedevc/cachechains-readability
chore: start to improve cachechains readability
2 parents 4438f4f + f5ddaef commit 0fecf46

File tree

3 files changed

+84
-30
lines changed

3 files changed

+84
-30
lines changed

cache/remotecache/v1/chains.go

Lines changed: 73 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,26 @@ type CacheChains struct {
2121
visited map[interface{}]struct{}
2222
}
2323

24+
var _ solver.CacheExporterTarget = &CacheChains{}
25+
2426
func (c *CacheChains) Add(dgst digest.Digest) solver.CacheExporterRecord {
2527
if strings.HasPrefix(dgst.String(), "random:") {
28+
// random digests will be different *every* run - so we shouldn't cache
29+
// it, since there's a zero chance this random digest collides again
2630
return &nopRecord{}
2731
}
28-
it := &item{c: c, dgst: dgst, backlinks: map[*item]struct{}{}}
32+
33+
it := &item{dgst: dgst, backlinks: map[*item]struct{}{}}
2934
c.items = append(c.items, it)
3035
return it
3136
}
3237

33-
func (c *CacheChains) Visit(v interface{}) {
34-
c.visited[v] = struct{}{}
38+
func (c *CacheChains) Visit(target any) {
39+
c.visited[target] = struct{}{}
3540
}
3641

37-
func (c *CacheChains) Visited(v interface{}) bool {
38-
_, ok := c.visited[v]
42+
func (c *CacheChains) Visited(target any) bool {
43+
_, ok := c.visited[target]
3944
return ok
4045
}
4146

@@ -76,6 +81,12 @@ func (c *CacheChains) normalize(ctx context.Context) error {
7681
return nil
7782
}
7883

84+
// Marshal converts the cache chains structure into a cache config and a
85+
// collection of providers for reading the results from.
86+
//
87+
// Marshal aims to validate, normalize and sort the output to ensure a
88+
// consistent digest (since cache configs are typically uploaded and stored in
89+
// content-addressable OCI registries).
7990
func (c *CacheChains) Marshal(ctx context.Context) (*CacheConfig, DescriptorProvider, error) {
8091
if err := c.normalize(ctx); err != nil {
8192
return nil, nil, err
@@ -109,19 +120,37 @@ type DescriptorProviderPair struct {
109120
Provider content.Provider
110121
}
111122

123+
// item is an implementation of a record in the cache chain. After validation,
124+
// normalization and marshalling into the cache config, the item results form
125+
// into the "layers", while the digests and the links form into the "records".
112126
type item struct {
113-
c *CacheChains
127+
// dgst is the unique identifier for each record.
128+
// This *roughly* corresponds to an edge (vertex cachekey + index) in the
129+
// solver - however, a single vertex can produce multiple unique cache keys
130+
// (e.g. fast/slow), so it's a one-to-many relation.
114131
dgst digest.Digest
115132

133+
// links are what connect records to each other (with an optional selector),
134+
// organized by input index (which correspond to vertex inputs).
135+
// We can have multiple links for each index, since *any* of these could be
136+
// used to get to this item (e.g. we could retrieve by fast/slow key).
137+
links []map[link]struct{}
138+
139+
// backlinks are the inverse of a link - these don't actually get directly
140+
// exported, but they're internally used to help efficiently navigate the
141+
// graph.
142+
backlinks map[*item]struct{}
143+
backlinksMu sync.Mutex
144+
145+
// result is the result of computing the edge - this is the target of the
146+
// data we actually want to store in the cache chain.
116147
result *solver.Remote
117148
resultTime time.Time
118149

119-
links []map[link]struct{}
120-
backlinksMu sync.Mutex
121-
backlinks map[*item]struct{}
122-
invalid bool
150+
invalid bool
123151
}
124152

153+
// link is a pointer to an item, with an optional selector.
125154
type link struct {
126155
src *item
127156
selector string
@@ -170,25 +199,46 @@ func (c *item) LinkFrom(rec solver.CacheExporterRecord, index int, selector stri
170199
src.backlinksMu.Unlock()
171200
}
172201

202+
// validate checks if an item is valid (i.e. each index has at least one link)
203+
// and marks it as such.
204+
//
205+
// Essentially, if an index has no links, it means that this cache record is
206+
// unreachable by the cache importer, so we should remove it. Once we've marked
207+
// an item as invalid, we remove it from it's backlinks and check it's
208+
// validity again - since now this linked item may be unreachable too.
173209
func (c *item) validate() {
210+
if c.invalid {
211+
// early exit, if the item is already invalid, we've already gone
212+
// through the backlinks
213+
return
214+
}
215+
174216
for _, m := range c.links {
217+
// if an index has no links, there's no way to access this record, so
218+
// mark it as invalid
175219
if len(m) == 0 {
176220
c.invalid = true
177-
for bl := range c.backlinks {
178-
changed := false
179-
for _, m := range bl.links {
180-
for l := range m {
181-
if l.src == c {
182-
delete(m, l)
183-
changed = true
184-
}
221+
break
222+
}
223+
}
224+
225+
if c.invalid {
226+
for bl := range c.backlinks {
227+
// remove ourselves from the backlinked item
228+
changed := false
229+
for _, m := range bl.links {
230+
for l := range m {
231+
if l.src == c {
232+
delete(m, l)
233+
changed = true
185234
}
186235
}
187-
if changed {
188-
bl.validate()
189-
}
190236
}
191-
return
237+
238+
// if we've removed ourselves, we need to check it again
239+
if changed {
240+
bl.validate()
241+
}
192242
}
193243
}
194244
}
@@ -211,6 +261,7 @@ func (c *item) walkAllResults(fn func(i *item) error, visited map[*item]struct{}
211261
return nil
212262
}
213263

264+
// nopRecord is used to discard cache results that we're not interested in storing.
214265
type nopRecord struct {
215266
}
216267

@@ -219,5 +270,3 @@ func (c *nopRecord) AddResult(_ digest.Digest, _ int, createdAt time.Time, resul
219270

220271
func (c *nopRecord) LinkFrom(rec solver.CacheExporterRecord, index int, selector string) {
221272
}
222-
223-
var _ solver.CacheExporterTarget = &CacheChains{}

solver/llbsolver/provenance.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -493,12 +493,12 @@ func (ce *cacheExporter) Add(dgst digest.Digest) solver.CacheExporterRecord {
493493
}
494494
}
495495

496-
func (ce *cacheExporter) Visit(v interface{}) {
497-
ce.m[v] = struct{}{}
496+
func (ce *cacheExporter) Visit(target any) {
497+
ce.m[target] = struct{}{}
498498
}
499499

500-
func (ce *cacheExporter) Visited(v interface{}) bool {
501-
_, ok := ce.m[v]
500+
func (ce *cacheExporter) Visited(target any) bool {
501+
_, ok := ce.m[target]
502502
return ok
503503
}
504504

solver/types.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,14 @@ type CacheExporter interface {
121121

122122
// CacheExporterTarget defines object capable of receiving exports
123123
type CacheExporterTarget interface {
124+
// Add creates a new object record that we can then add results to and
125+
// connect to other records.
124126
Add(dgst digest.Digest) CacheExporterRecord
125-
Visit(interface{})
126-
Visited(interface{}) bool
127+
128+
// Visit marks a target as having been visited.
129+
Visit(target any)
130+
// Vistited returns true if a target has previously been marked as visited.
131+
Visited(target any) bool
127132
}
128133

129134
// CacheExporterRecord is a single object being exported

0 commit comments

Comments
 (0)