@@ -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
3752func (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
238254func Builder (dm api.DeployManifest ) * vfsBuilder {
239- return & vfsBuilder {dm : dm }
255+ return & vfsBuilder {
256+ dm : dm ,
257+ stats : & Stats {},
258+ }
240259}
241260
242261func (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
570601func (b * vfsBuilder ) localIndex (operationIndex int , desc api.Descriptor ) blobEntry {
0 commit comments