Skip to content

Commit 4005979

Browse files
Merge pull request #3415 from dmcgowan/gc-flat-lease
Add flat GC label for leases
2 parents 5631fe3 + dd0a45d commit 4005979

4 files changed

Lines changed: 149 additions & 6 deletions

File tree

gc/gc.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ import (
3030
// ResourceType represents type of resource at a node
3131
type ResourceType uint8
3232

33+
// ResourceMax represents the max resource.
34+
// Upper bits are stripped out during the mark phase, allowing the upper 3 bits
35+
// to be used by the caller reference function.
36+
const ResourceMax = ResourceType(0x1F)
37+
3338
// Node presents a resource which has a type and key,
3439
// this node can be used to lookup other nodes.
3540
type Node struct {
@@ -80,6 +85,8 @@ func Tricolor(roots []Node, refs func(ref Node) ([]Node, error)) (map[Node]struc
8085
}
8186
}
8287

88+
// strip bits above max resource type
89+
id.Type = id.Type & ResourceMax
8390
// mark as black when done
8491
reachable[id] = struct{}{}
8592
}

metadata/db_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/containerd/containerd/errdefs"
3737
"github.com/containerd/containerd/gc"
3838
"github.com/containerd/containerd/images"
39+
"github.com/containerd/containerd/leases"
3940
"github.com/containerd/containerd/log/logtest"
4041
"github.com/containerd/containerd/namespaces"
4142
"github.com/containerd/containerd/snapshots"
@@ -345,6 +346,40 @@ func TestMetadataCollector(t *testing.T) {
345346
newSnapshot("5", "3", false, true),
346347
container("1", "4"),
347348
image("image-1", digestFor(2)),
349+
350+
// Test lease preservation
351+
blob(bytesFor(5), false, "containerd.io/gc.ref.content.0", digestFor(6).String()),
352+
blob(bytesFor(6), false),
353+
blob(bytesFor(7), false),
354+
newSnapshot("6", "", false, false, "containerd.io/gc.ref.content.0", digestFor(7).String()),
355+
lease("lease-1", []leases.Resource{
356+
{
357+
ID: digestFor(5).String(),
358+
Type: "content",
359+
},
360+
{
361+
ID: "6",
362+
Type: "snapshots/native",
363+
},
364+
}, false),
365+
366+
// Test flat lease
367+
blob(bytesFor(8), false, "containerd.io/gc.ref.content.0", digestFor(9).String()),
368+
blob(bytesFor(9), true),
369+
blob(bytesFor(10), true),
370+
newSnapshot("7", "", false, false, "containerd.io/gc.ref.content.0", digestFor(10).String()),
371+
newSnapshot("8", "7", false, false),
372+
newSnapshot("9", "8", false, false),
373+
lease("lease-2", []leases.Resource{
374+
{
375+
ID: digestFor(8).String(),
376+
Type: "content",
377+
},
378+
{
379+
ID: "9",
380+
Type: "snapshots/native",
381+
},
382+
}, false, "containerd.io/gc.flat", time.Now().String()),
348383
}
349384
remaining []gc.Node
350385
)
@@ -588,6 +623,26 @@ func create(obj object, tx *bolt.Tx, is images.Store, cs content.Store, sn snaps
588623
if err != nil {
589624
return nil, err
590625
}
626+
case testLease:
627+
lm := NewLeaseManager(tx)
628+
l, err := lm.Create(ctx, leases.WithID(v.id), leases.WithLabels(obj.labels))
629+
if err != nil {
630+
return nil, err
631+
}
632+
633+
for _, ref := range v.refs {
634+
if err := lm.AddResource(ctx, l, ref); err != nil {
635+
return nil, err
636+
}
637+
}
638+
639+
if !obj.removed {
640+
node = &gc.Node{
641+
Type: ResourceLease,
642+
Namespace: namespace,
643+
Key: v.id,
644+
}
645+
}
591646
}
592647

593648
return node, nil
@@ -641,6 +696,17 @@ func container(id, s string, l ...string) object {
641696
}
642697
}
643698

699+
func lease(id string, refs []leases.Resource, r bool, l ...string) object {
700+
return object{
701+
data: testLease{
702+
id: id,
703+
refs: refs,
704+
},
705+
removed: r,
706+
labels: labelmap(l...),
707+
}
708+
}
709+
644710
type testContent struct {
645711
data []byte
646712
}
@@ -661,6 +727,11 @@ type testContainer struct {
661727
snapshot string
662728
}
663729

730+
type testLease struct {
731+
id string
732+
refs []leases.Resource
733+
}
734+
664735
func newStores(t testing.TB) (*DB, content.Store, snapshots.Snapshotter, func()) {
665736
td, err := ioutil.TempDir("", "gc-test-")
666737
if err != nil {

metadata/gc.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,17 @@ const (
4646
ResourceIngest
4747
)
4848

49+
const (
50+
resourceContentFlat = ResourceContent | 0x20
51+
resourceSnapshotFlat = ResourceSnapshot | 0x20
52+
)
53+
4954
var (
5055
labelGCRoot = []byte("containerd.io/gc.root")
5156
labelGCSnapRef = []byte("containerd.io/gc.ref.snapshot.")
5257
labelGCContentRef = []byte("containerd.io/gc.ref.content")
5358
labelGCExpire = []byte("containerd.io/gc.expire")
59+
labelGCFlat = []byte("containerd.io/gc.flat")
5460
)
5561

5662
func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
@@ -90,6 +96,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
9096
return nil
9197
}
9298
libkt := lbkt.Bucket(k)
99+
var flat bool
93100

94101
if lblbkt := libkt.Bucket(bucketKeyObjectLabels); lblbkt != nil {
95102
if expV := lblbkt.Get(labelGCExpire); expV != nil {
@@ -102,6 +109,10 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
102109
return nil
103110
}
104111
}
112+
113+
if flatV := lblbkt.Get(labelGCFlat); flatV != nil {
114+
flat = true
115+
}
105116
}
106117

107118
fn(gcnode(ResourceLease, ns, string(k)))
@@ -111,16 +122,26 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
111122
// no need to allow the lookup to be recursive, handling here
112123
// therefore reduces the number of database seeks.
113124

125+
ctype := ResourceContent
126+
if flat {
127+
ctype = resourceContentFlat
128+
}
129+
114130
cbkt := libkt.Bucket(bucketKeyObjectContent)
115131
if cbkt != nil {
116132
if err := cbkt.ForEach(func(k, v []byte) error {
117-
fn(gcnode(ResourceContent, ns, string(k)))
133+
fn(gcnode(ctype, ns, string(k)))
118134
return nil
119135
}); err != nil {
120136
return err
121137
}
122138
}
123139

140+
stype := ResourceSnapshot
141+
if flat {
142+
stype = resourceSnapshotFlat
143+
}
144+
124145
sbkt := libkt.Bucket(bucketKeyObjectSnapshots)
125146
if sbkt != nil {
126147
if err := sbkt.ForEach(func(sk, sv []byte) error {
@@ -130,7 +151,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
130151
snbkt := sbkt.Bucket(sk)
131152

132153
return snbkt.ForEach(func(k, v []byte) error {
133-
fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)))
154+
fn(gcnode(stype, ns, fmt.Sprintf("%s/%s", sk, k)))
134155
return nil
135156
})
136157
}); err != nil {
@@ -257,15 +278,16 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
257278
}
258279

259280
func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)) error {
260-
if node.Type == ResourceContent {
281+
switch node.Type {
282+
case ResourceContent:
261283
bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectContent, bucketKeyObjectBlob, []byte(node.Key))
262284
if bkt == nil {
263285
// Node may be created from dead edge
264286
return nil
265287
}
266288

267289
return sendLabelRefs(node.Namespace, bkt, fn)
268-
} else if node.Type == ResourceSnapshot {
290+
case ResourceSnapshot, resourceSnapshotFlat:
269291
parts := strings.SplitN(node.Key, "/", 2)
270292
if len(parts) != 2 {
271293
return errors.Errorf("invalid snapshot gc key %s", node.Key)
@@ -280,11 +302,16 @@ func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)
280302
}
281303

282304
if pv := bkt.Get(bucketKeyParent); len(pv) > 0 {
283-
fn(gcnode(ResourceSnapshot, node.Namespace, fmt.Sprintf("%s/%s", ss, pv)))
305+
fn(gcnode(node.Type, node.Namespace, fmt.Sprintf("%s/%s", ss, pv)))
306+
}
307+
308+
// Do not send labeled references for flat snapshot refs
309+
if node.Type == resourceSnapshotFlat {
310+
return nil
284311
}
285312

286313
return sendLabelRefs(node.Namespace, bkt, fn)
287-
} else if node.Type == ResourceIngest {
314+
case ResourceIngest:
288315
// Send expected value
289316
bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectContent, bucketKeyObjectIngests, []byte(node.Key))
290317
if bkt == nil {

metadata/gc_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ import (
3333
bolt "go.etcd.io/bbolt"
3434
)
3535

36+
func TestResourceMax(t *testing.T) {
37+
if ResourceContent != resourceContentFlat&gc.ResourceMax {
38+
t.Fatalf("Invalid flat content type: %d (max %d)", resourceContentFlat, gc.ResourceMax)
39+
}
40+
if ResourceSnapshot != resourceSnapshotFlat&gc.ResourceMax {
41+
t.Fatalf("Invalid flat snapshot type: %d (max %d)", resourceSnapshotFlat, gc.ResourceMax)
42+
}
43+
}
44+
3645
func TestGCRoots(t *testing.T) {
3746
db, cleanup, err := newDatabase()
3847
if err != nil {
@@ -90,6 +99,11 @@ func TestGCRoots(t *testing.T) {
9099
addLeaseSnapshot("ns2", "l4", "overlay", "sn8"),
91100
addLeaseIngest("ns2", "l4", "ingest-6"),
92101
addLeaseIngest("ns2", "l4", "ingest-7"),
102+
103+
addLease("ns3", "l1", labelmap(string(labelGCFlat), time.Now().Add(time.Hour).Format(time.RFC3339))),
104+
addLeaseContent("ns3", "l1", dgst(1)),
105+
addLeaseSnapshot("ns3", "l1", "overlay", "sn1"),
106+
addLeaseIngest("ns3", "l1", "ingest-1"),
93107
}
94108

95109
expected := []gc.Node{
@@ -121,6 +135,10 @@ func TestGCRoots(t *testing.T) {
121135
gcnode(ResourceIngest, "ns1", "ingest-3"),
122136
gcnode(ResourceIngest, "ns2", "ingest-4"),
123137
gcnode(ResourceIngest, "ns2", "ingest-5"),
138+
gcnode(ResourceLease, "ns3", "l1"),
139+
gcnode(ResourceIngest, "ns3", "ingest-1"),
140+
gcnode(resourceContentFlat, "ns3", dgst(1).String()),
141+
gcnode(resourceSnapshotFlat, "ns3", "overlay/sn1"),
124142
}
125143

126144
if err := db.Update(func(tx *bolt.Tx) error {
@@ -268,6 +286,14 @@ func TestGCRefs(t *testing.T) {
268286
addSnapshot("ns2", "overlay", "sn3", "", labelmap(
269287
string(labelGCContentRef), dgst(1).String(),
270288
string(labelGCContentRef)+".keep-me", dgst(6).String())),
289+
290+
// Test flat references don't follow label references
291+
addContent("ns3", dgst(1), nil),
292+
addContent("ns3", dgst(2), labelmap(string(labelGCContentRef)+".0", dgst(1).String())),
293+
294+
addSnapshot("ns3", "overlay", "sn1", "", nil),
295+
addSnapshot("ns3", "overlay", "sn2", "sn1", nil),
296+
addSnapshot("ns3", "overlay", "sn3", "", labelmap(string(labelGCSnapRef)+"btrfs", "sn1", string(labelGCSnapRef)+"overlay", "sn1")),
271297
}
272298

273299
refs := map[gc.Node][]gc.Node{
@@ -316,6 +342,18 @@ func TestGCRefs(t *testing.T) {
316342
gcnode(ResourceIngest, "ns2", "ingest-2"): {
317343
gcnode(ResourceContent, "ns2", dgst(8).String()),
318344
},
345+
gcnode(resourceSnapshotFlat, "ns3", "overlay/sn2"): {
346+
gcnode(resourceSnapshotFlat, "ns3", "overlay/sn1"),
347+
},
348+
gcnode(ResourceSnapshot, "ns3", "overlay/sn2"): {
349+
gcnode(ResourceSnapshot, "ns3", "overlay/sn1"),
350+
},
351+
gcnode(resourceSnapshotFlat, "ns3", "overlay/sn1"): nil,
352+
gcnode(resourceSnapshotFlat, "ns3", "overlay/sn3"): nil,
353+
gcnode(ResourceSnapshot, "ns3", "overlay/sn3"): {
354+
gcnode(ResourceSnapshot, "ns3", "btrfs/sn1"),
355+
gcnode(ResourceSnapshot, "ns3", "overlay/sn1"),
356+
},
319357
}
320358

321359
if err := db.Update(func(tx *bolt.Tx) error {

0 commit comments

Comments
 (0)