Skip to content

Commit 6bb719f

Browse files
authored
Layer transfer statistics (#358)
1 parent beea76f commit 6bb719f

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

img_tool/cmd/deploy/deploy.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ func DeployWithExtras(ctx context.Context, rawRequest []byte, additionalTags []s
200200
return fmt.Errorf("deploying images: %w", err)
201201
}
202202

203+
// Print VFS statistics to stderr
204+
stats := vfs.Stats()
205+
fmt.Fprintf(os.Stderr, " layer transfers: %d from disk, %d from container registry, %d from remote cache\n", stats.LayersFromLocalDisk.Load(), stats.LayersFromRegistry.Load(), stats.LayersFromRemoteCache.Load())
206+
203207
// Print all pushed tags to stdout, one per line.
204208
for _, tag := range pushedTags {
205209
fmt.Println(tag)

img_tool/pkg/deployvfs/deployvfs.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"path/filepath"
1212
"slices"
1313
"strings"
14+
"sync/atomic"
1415

1516
"github.com/bazelbuild/rules_go/go/runfiles"
1617
registryname "github.com/malt3/go-containerregistry/pkg/name"
@@ -22,6 +23,14 @@ import (
2223
"github.com/bazel-contrib/rules_img/img_tool/pkg/cas"
2324
)
2425

26+
// Stats tracks statistics about blob access in the VFS.
27+
// All fields are accessed atomically for thread safety.
28+
type Stats struct {
29+
LayersFromLocalDisk atomic.Uint64 // Layers opened from local disk
30+
LayersFromRegistry atomic.Uint64 // Layers opened from registry
31+
LayersFromRemoteCache atomic.Uint64 // Layers opened from remote cache
32+
}
33+
2534
// VFS represents a virtual file system for deployment manifests and their associated blobs.
2635
// It merges multiple data sources into a single coherent view:
2736
// - runfiles tree of the push/load tool
@@ -32,6 +41,12 @@ type VFS struct {
3241
dm api.DeployManifest
3342
blobs map[string]blobEntry
3443
manifests map[string]blobEntry
44+
stats *Stats
45+
}
46+
47+
// Stats returns the current statistics for the VFS.
48+
func (vfs *VFS) Stats() *Stats {
49+
return vfs.stats
3550
}
3651

3752
func (vfs *VFS) Layer(digest registryv1.Hash) (registryv1.Layer, error) {
@@ -233,10 +248,14 @@ type vfsBuilder struct {
233248
containerRegistryOptions []remote.Option
234249
runfilesRootSymlinksPrefix string
235250
layerHints map[string][]string // digest -> []paths
251+
stats *Stats
236252
}
237253

238254
func Builder(dm api.DeployManifest) *vfsBuilder {
239-
return &vfsBuilder{dm: dm}
255+
return &vfsBuilder{
256+
dm: dm,
257+
stats: &Stats{},
258+
}
240259
}
241260

242261
func (b *vfsBuilder) WithCASReader(br casReader) *vfsBuilder {
@@ -279,6 +298,7 @@ func (b *vfsBuilder) Build() (*VFS, error) {
279298
dm: b.dm,
280299
blobs: blobs,
281300
manifests: manifests,
301+
stats: b.stats,
282302
}, nil
283303
}
284304

@@ -454,10 +474,13 @@ func (b *vfsBuilder) layerFromFile(operationIndex int, manifestIndex int, layerI
454474
return blobEntry{}, false
455475
}
456476
if _, err := os.Stat(fpath); err == nil {
477+
stats := b.stats
457478
return blobEntry{
458479
Descriptor: desc,
459480
Location: "file",
481+
stats: stats,
460482
Opener: func() (io.ReadCloser, error) {
483+
stats.LayersFromLocalDisk.Add(1)
461484
return os.Open(fpath)
462485
},
463486
}, true
@@ -478,9 +501,11 @@ func (b *vfsBuilder) layerFromRegistry(pullInfo api.PullInfo, missingBlobs []str
478501
for _, missing := range missingBlobs {
479502
if missing == sha256Hex {
480503
// the layer is marked as missing, so it must exist in one of the original registries
504+
stats := b.stats
481505
return blobEntry{
482506
Descriptor: desc,
483507
Location: "registry",
508+
stats: stats,
484509
Opener: func() (io.ReadCloser, error) {
485510
pullInfo := pullInfo
486511
for _, registry := range pullInfo.OriginalBaseImageRegistries {
@@ -496,6 +521,7 @@ func (b *vfsBuilder) layerFromRegistry(pullInfo api.PullInfo, missingBlobs []str
496521
if err != nil {
497522
continue
498523
}
524+
stats.LayersFromRegistry.Add(1)
499525
return rc, nil
500526
}
501527
return nil, fmt.Errorf("layer %s not found in any of the original registries", desc.Digest)
@@ -524,14 +550,17 @@ func (b *vfsBuilder) layerFromCAS(desc api.Descriptor) (blobEntry, bool) {
524550
hintPaths = b.layerHints[desc.Digest]
525551
}
526552

553+
stats := b.stats
527554
return blobEntry{
528555
Descriptor: desc,
529556
Location: "remote_cache",
557+
stats: stats,
530558
Opener: func() (io.ReadCloser, error) {
531559
// First, try to open from local paths if we have hints
532560
for _, localPath := range hintPaths {
533561
if file, err := os.Open(localPath); err == nil {
534-
// Successfully opened local file
562+
// Successfully opened local file from layer hints
563+
stats.LayersFromLocalDisk.Add(1)
535564
return file, nil
536565
}
537566
// If open failed, try the next path
@@ -546,6 +575,7 @@ func (b *vfsBuilder) layerFromCAS(desc api.Descriptor) (blobEntry, bool) {
546575
if err != nil {
547576
return nil, err
548577
}
578+
stats.LayersFromRemoteCache.Add(1)
549579
return casReader.ReaderForBlob(context.TODO(), digest)
550580
},
551581
}, true
@@ -565,6 +595,7 @@ type blobEntry struct {
565595
api.Descriptor
566596
Location string // "file", "registry", "remote_cache", "stub"
567597
Opener func() (io.ReadCloser, error)
598+
stats *Stats // reference to VFS stats for tracking
568599
}
569600

570601
func (b *vfsBuilder) localIndex(operationIndex int, desc api.Descriptor) blobEntry {

0 commit comments

Comments
 (0)