Skip to content

Commit 7cfb99a

Browse files
committed
Add content gc ref labels from containers, images, and snapshots
Currently the objects which can retain content from labels are limited. This limitation has required clients to work around this and and in some cases add outside reference counting (e.g. buildkit keeping content for snapshots). Updated the logic to treat content and snapshot labels equally and simplified the code in the process. Signed-off-by: Derek McGowan <[email protected]>
1 parent 30b6f46 commit 7cfb99a

2 files changed

Lines changed: 69 additions & 90 deletions

File tree

metadata/gc.go

Lines changed: 49 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
6464
// iterate through each namespace
6565
v1c := v1bkt.Cursor()
6666

67+
// cerr indicates the scan did not successfully send all
68+
// the roots. The scan does not need to be cancelled but
69+
// must return error at the end.
70+
var cerr error
71+
fn := func(n gc.Node) {
72+
select {
73+
case nc <- n:
74+
case <-ctx.Done():
75+
cerr = ctx.Err()
76+
}
77+
}
78+
6779
for k, v := v1c.First(); k != nil; k, v = v1c.Next() {
6880
if v != nil {
6981
continue
@@ -92,11 +104,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
92104
}
93105
}
94106

95-
select {
96-
case nc <- gcnode(ResourceLease, ns, string(k)):
97-
case <-ctx.Done():
98-
return ctx.Err()
99-
}
107+
fn(gcnode(ResourceLease, ns, string(k)))
100108

101109
// Emit content and snapshots as roots instead of implementing
102110
// in references. Since leases cannot be referenced there is
@@ -106,11 +114,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
106114
cbkt := libkt.Bucket(bucketKeyObjectContent)
107115
if cbkt != nil {
108116
if err := cbkt.ForEach(func(k, v []byte) error {
109-
select {
110-
case nc <- gcnode(ResourceContent, ns, string(k)):
111-
case <-ctx.Done():
112-
return ctx.Err()
113-
}
117+
fn(gcnode(ResourceContent, ns, string(k)))
114118
return nil
115119
}); err != nil {
116120
return err
@@ -126,11 +130,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
126130
snbkt := sbkt.Bucket(sk)
127131

128132
return snbkt.ForEach(func(k, v []byte) error {
129-
select {
130-
case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)):
131-
case <-ctx.Done():
132-
return ctx.Err()
133-
}
133+
fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)))
134134
return nil
135135
})
136136
}); err != nil {
@@ -141,11 +141,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
141141
ibkt := libkt.Bucket(bucketKeyObjectIngests)
142142
if ibkt != nil {
143143
if err := ibkt.ForEach(func(k, v []byte) error {
144-
select {
145-
case nc <- gcnode(ResourceIngest, ns, string(k)):
146-
case <-ctx.Done():
147-
return ctx.Err()
148-
}
144+
fn(gcnode(ResourceIngest, ns, string(k)))
149145
return nil
150146
}); err != nil {
151147
return err
@@ -168,18 +164,9 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
168164
target := ibkt.Bucket(k).Bucket(bucketKeyTarget)
169165
if target != nil {
170166
contentKey := string(target.Get(bucketKeyDigest))
171-
select {
172-
case nc <- gcnode(ResourceContent, ns, contentKey):
173-
case <-ctx.Done():
174-
return ctx.Err()
175-
}
167+
fn(gcnode(ResourceContent, ns, contentKey))
176168
}
177-
return sendSnapshotRefs(ns, ibkt.Bucket(k), func(n gc.Node) {
178-
select {
179-
case nc <- n:
180-
case <-ctx.Done():
181-
}
182-
})
169+
return sendLabelRefs(ns, ibkt.Bucket(k), fn)
183170
}); err != nil {
184171
return err
185172
}
@@ -200,11 +187,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
200187
if ea == nil || expThreshold.After(*ea) {
201188
return nil
202189
}
203-
select {
204-
case nc <- gcnode(ResourceIngest, ns, string(k)):
205-
case <-ctx.Done():
206-
return ctx.Err()
207-
}
190+
fn(gcnode(ResourceIngest, ns, string(k)))
208191
return nil
209192
}); err != nil {
210193
return err
@@ -216,7 +199,12 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
216199
if v != nil {
217200
return nil
218201
}
219-
return sendRootRef(ctx, nc, gcnode(ResourceContent, ns, string(k)), cbkt.Bucket(k))
202+
203+
if isRootRef(cbkt.Bucket(k)) {
204+
fn(gcnode(ResourceContent, ns, string(k)))
205+
}
206+
207+
return nil
220208
}); err != nil {
221209
return err
222210
}
@@ -229,23 +217,15 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
229217
if v != nil {
230218
return nil
231219
}
232-
snapshotter := string(cbkt.Bucket(k).Get(bucketKeySnapshotter))
220+
221+
cibkt := cbkt.Bucket(k)
222+
snapshotter := string(cibkt.Get(bucketKeySnapshotter))
233223
if snapshotter != "" {
234-
ss := string(cbkt.Bucket(k).Get(bucketKeySnapshotKey))
235-
select {
236-
case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, ss)):
237-
case <-ctx.Done():
238-
return ctx.Err()
239-
}
224+
ss := string(cibkt.Get(bucketKeySnapshotKey))
225+
fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, ss)))
240226
}
241227

242-
// TODO: Send additional snapshot refs through labels
243-
return sendSnapshotRefs(ns, cbkt.Bucket(k), func(n gc.Node) {
244-
select {
245-
case nc <- n:
246-
case <-ctx.Done():
247-
}
248-
})
228+
return sendLabelRefs(ns, cibkt, fn)
249229
}); err != nil {
250230
return err
251231
}
@@ -263,15 +243,17 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
263243
if v != nil {
264244
return nil
265245
}
266-
267-
return sendRootRef(ctx, nc, gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)), snbkt.Bucket(k))
246+
if isRootRef(snbkt.Bucket(k)) {
247+
fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)))
248+
}
249+
return nil
268250
})
269251
}); err != nil {
270252
return err
271253
}
272254
}
273255
}
274-
return nil
256+
return cerr
275257
}
276258

277259
func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)) error {
@@ -282,10 +264,7 @@ func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)
282264
return nil
283265
}
284266

285-
if err := sendSnapshotRefs(node.Namespace, bkt, fn); err != nil {
286-
return err
287-
}
288-
return sendContentRefs(node.Namespace, bkt, fn)
267+
return sendLabelRefs(node.Namespace, bkt, fn)
289268
} else if node.Type == ResourceSnapshot {
290269
parts := strings.SplitN(node.Key, "/", 2)
291270
if len(parts) != 2 {
@@ -304,7 +283,7 @@ func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)
304283
fn(gcnode(ResourceSnapshot, node.Namespace, fmt.Sprintf("%s/%s", ss, pv)))
305284
}
306285

307-
return sendSnapshotRefs(node.Namespace, bkt, fn)
286+
return sendLabelRefs(node.Namespace, bkt, fn)
308287
} else if node.Type == ResourceIngest {
309288
// Send expected value
310289
bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectContent, bucketKeyObjectIngests, []byte(node.Key))
@@ -456,25 +435,8 @@ func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error {
456435
return nil
457436
}
458437

459-
// sendSnapshotRefs sends all snapshot references referred to by the labels in the bkt
460-
func sendSnapshotRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error {
461-
lbkt := bkt.Bucket(bucketKeyObjectLabels)
462-
if lbkt != nil {
463-
lc := lbkt.Cursor()
464-
465-
for k, v := lc.Seek(labelGCSnapRef); k != nil && strings.HasPrefix(string(k), string(labelGCSnapRef)); k, v = lc.Next() {
466-
snapshotter := k[len(labelGCSnapRef):]
467-
if i := bytes.IndexByte(snapshotter, '/'); i >= 0 {
468-
snapshotter = snapshotter[:i]
469-
}
470-
fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, v)))
471-
}
472-
}
473-
return nil
474-
}
475-
476-
// sendContentRefs sends all content references referred to by the labels in the bkt
477-
func sendContentRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error {
438+
// sendLabelRefs sends all snapshot and content references referred to by the labels in the bkt
439+
func sendLabelRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error {
478440
lbkt := bkt.Bucket(bucketKeyObjectLabels)
479441
if lbkt != nil {
480442
lc := lbkt.Cursor()
@@ -490,6 +452,15 @@ func sendContentRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error {
490452

491453
fn(gcnode(ResourceContent, ns, string(v)))
492454
}
455+
456+
for k, v := lc.Seek(labelGCSnapRef); k != nil && strings.HasPrefix(string(k), string(labelGCSnapRef)); k, v = lc.Next() {
457+
snapshotter := k[len(labelGCSnapRef):]
458+
if i := bytes.IndexByte(snapshotter, '/'); i >= 0 {
459+
snapshotter = snapshotter[:i]
460+
}
461+
fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, v)))
462+
}
463+
493464
}
494465
return nil
495466
}
@@ -506,17 +477,6 @@ func isRootRef(bkt *bolt.Bucket) bool {
506477
return false
507478
}
508479

509-
func sendRootRef(ctx context.Context, nc chan<- gc.Node, n gc.Node, bkt *bolt.Bucket) error {
510-
if isRootRef(bkt) {
511-
select {
512-
case nc <- n:
513-
case <-ctx.Done():
514-
return ctx.Err()
515-
}
516-
}
517-
return nil
518-
}
519-
520480
func gcnode(t gc.ResourceType, ns, key string) gc.Node {
521481
return gc.Node{
522482
Type: t,

metadata/gc_test.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,16 @@ func TestGCRoots(t *testing.T) {
4343
alters := []alterFunc{
4444
addImage("ns1", "image1", dgst(1), nil),
4545
addImage("ns1", "image2", dgst(2), labelmap(string(labelGCSnapRef)+"overlay", "sn2")),
46+
addImage("ns2", "image3", dgst(10), labelmap(string(labelGCContentRef), dgst(11).String())),
4647
addContainer("ns1", "container1", "overlay", "sn4", nil),
4748
addContainer("ns1", "container2", "overlay", "sn5", labelmap(string(labelGCSnapRef)+"overlay", "sn6")),
48-
addContainer("ns1", "container3", "overlay", "sn7", labelmap(string(labelGCSnapRef)+"overlay/anything-1", "sn8", string(labelGCSnapRef)+"overlay/anything-2", "sn9")),
49+
addContainer("ns1", "container3", "overlay", "sn7", labelmap(
50+
string(labelGCSnapRef)+"overlay/anything-1", "sn8",
51+
string(labelGCSnapRef)+"overlay/anything-2", "sn9",
52+
string(labelGCContentRef), dgst(7).String())),
53+
addContainer("ns1", "container4", "", "", labelmap(
54+
string(labelGCContentRef)+".0", dgst(8).String(),
55+
string(labelGCContentRef)+".1", dgst(9).String())),
4956
addContent("ns1", dgst(1), nil),
5057
addContent("ns1", dgst(2), nil),
5158
addContent("ns1", dgst(3), nil),
@@ -88,10 +95,15 @@ func TestGCRoots(t *testing.T) {
8895
expected := []gc.Node{
8996
gcnode(ResourceContent, "ns1", dgst(1).String()),
9097
gcnode(ResourceContent, "ns1", dgst(2).String()),
98+
gcnode(ResourceContent, "ns1", dgst(7).String()),
99+
gcnode(ResourceContent, "ns1", dgst(8).String()),
100+
gcnode(ResourceContent, "ns1", dgst(9).String()),
91101
gcnode(ResourceContent, "ns2", dgst(2).String()),
92102
gcnode(ResourceContent, "ns2", dgst(4).String()),
93103
gcnode(ResourceContent, "ns2", dgst(5).String()),
94104
gcnode(ResourceContent, "ns2", dgst(6).String()),
105+
gcnode(ResourceContent, "ns2", dgst(10).String()),
106+
gcnode(ResourceContent, "ns2", dgst(11).String()),
95107
gcnode(ResourceSnapshot, "ns1", "overlay/sn2"),
96108
gcnode(ResourceSnapshot, "ns1", "overlay/sn3"),
97109
gcnode(ResourceSnapshot, "ns1", "overlay/sn4"),
@@ -253,6 +265,9 @@ func TestGCRefs(t *testing.T) {
253265
addSnapshot("ns1", "btrfs", "sn1", "", nil),
254266
addSnapshot("ns2", "overlay", "sn1", "", nil),
255267
addSnapshot("ns2", "overlay", "sn2", "sn1", nil),
268+
addSnapshot("ns2", "overlay", "sn3", "", labelmap(
269+
string(labelGCContentRef), dgst(1).String(),
270+
string(labelGCContentRef)+".keep-me", dgst(6).String())),
256271
}
257272

258273
refs := map[gc.Node][]gc.Node{
@@ -293,6 +308,10 @@ func TestGCRefs(t *testing.T) {
293308
gcnode(ResourceSnapshot, "ns2", "overlay/sn2"): {
294309
gcnode(ResourceSnapshot, "ns2", "overlay/sn1"),
295310
},
311+
gcnode(ResourceSnapshot, "ns2", "overlay/sn3"): {
312+
gcnode(ResourceContent, "ns2", dgst(1).String()),
313+
gcnode(ResourceContent, "ns2", dgst(6).String()),
314+
},
296315
gcnode(ResourceIngest, "ns1", "ingest-1"): nil,
297316
gcnode(ResourceIngest, "ns2", "ingest-2"): {
298317
gcnode(ResourceContent, "ns2", dgst(8).String()),

0 commit comments

Comments
 (0)