@@ -16,6 +16,8 @@ import (
1616 "time"
1717
1818 "github.com/bazelbuild/remote-apis-sdks/go/pkg/digest"
19+ "golang.org/x/sync/errgroup"
20+
1921 log "github.com/golang/glog"
2022)
2123
@@ -100,7 +102,7 @@ type DiskCache struct {
100102 testGcTicks chan uint64
101103}
102104
103- func New (ctx context.Context , root string , maxCapacityBytes uint64 ) * DiskCache {
105+ func New (ctx context.Context , root string , maxCapacityBytes uint64 ) ( * DiskCache , error ) {
104106 res := & DiskCache {
105107 root : root ,
106108 maxCapacityBytes : maxCapacityBytes ,
@@ -112,44 +114,45 @@ func New(ctx context.Context, root string, maxCapacityBytes uint64) *DiskCache {
112114 shutdown : make (chan bool ),
113115 }
114116 heap .Init (res .queue )
115- _ = os .MkdirAll (root , os .ModePerm )
117+ if err := os .MkdirAll (root , os .ModePerm ); err != nil {
118+ return nil , err
119+ }
116120 // We use Git's directory/file naming structure as inspiration:
117121 // https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#:~:text=The%20subdirectory%20is%20named%20with%20the%20first%202%20characters%20of%20the%20SHA%2D1%2C%20and%20the%20filename%20is%20the%20remaining%2038%20characters.
118- var wg sync.WaitGroup
119- wg .Add (256 )
122+ eg , eCtx := errgroup .WithContext (ctx )
120123 for i := 0 ; i < 256 ; i ++ {
121124 prefixDir := filepath .Join (root , fmt .Sprintf ("%02x" , i ))
122- go func () {
123- defer wg .Done ()
124- _ = os .MkdirAll (prefixDir , os .ModePerm )
125- _ = filepath .WalkDir (prefixDir , func (path string , d fs.DirEntry , err error ) error {
125+ eg .Go (func () error {
126+ if eCtx .Err () != nil {
127+ return eCtx .Err ()
128+ }
129+ if err := os .MkdirAll (prefixDir , os .ModePerm ); err != nil {
130+ return err
131+ }
132+ return filepath .WalkDir (prefixDir , func (path string , d fs.DirEntry , err error ) error {
126133 // We log and continue on all errors, because cache read errors are not critical.
127134 if err != nil {
128- log .Errorf ("Error reading cache directory: %v" , err )
129- return nil
135+ return fmt .Errorf ("error reading cache directory: %v" , err )
130136 }
131137 if d .IsDir () {
132138 return nil
133139 }
134140 subdir := filepath .Base (filepath .Dir (path ))
135141 k , err := res .getKeyFromFileName (subdir + d .Name ())
136142 if err != nil {
137- log .Errorf ("Error parsing cached file name %s: %v" , path , err )
138- return nil
143+ return fmt .Errorf ("error parsing cached file name %s: %v" , path , err )
139144 }
140- atime , err := GetLastAccessTime (path )
145+ atime , err := getLastAccessTime (path )
141146 if err != nil {
142- log .Errorf ("Error getting last accessed time of %s: %v" , path , err )
143- return nil
147+ return fmt .Errorf ("error getting last accessed time of %s: %v" , path , err )
144148 }
145149 it := & qitem {
146150 key : k ,
147151 lat : atime ,
148152 }
149153 size , err := res .getItemSize (k )
150154 if err != nil {
151- log .Errorf ("Error getting file size of %s: %v" , path , err )
152- return nil
155+ return fmt .Errorf ("error getting file size of %s: %v" , path , err )
153156 }
154157 res .store .Store (k , it )
155158 atomic .AddInt64 (& res .sizeBytes , size )
@@ -158,11 +161,13 @@ func New(ctx context.Context, root string, maxCapacityBytes uint64) *DiskCache {
158161 res .mu .Unlock ()
159162 return nil
160163 })
161- }()
164+ })
165+ }
166+ if err := eg .Wait (); err != nil {
167+ return nil , err
162168 }
163- wg .Wait ()
164169 go res .gc ()
165- return res
170+ return res , nil
166171}
167172
168173func (d * DiskCache ) getItemSize (k key ) (int64 , error ) {
@@ -284,18 +289,13 @@ func copyFile(src, dst string, size int64) error {
284289 return err
285290 }
286291 defer out .Close ()
287- _ , err = io .Copy (out , in )
292+ n , err : = io .Copy (out , in )
288293 if err != nil {
289294 return err
290295 }
291- // Required sanity check: sometimes the copy pretends to succeed, but doesn't, if
292- // the file is being concurrently deleted.
293- dstInfo , err := os .Stat (dst )
294- if err != nil {
295- return err
296- }
297- if dstInfo .Size () != size {
298- return fmt .Errorf ("copy of %s to %s failed: src/dst size mismatch: wanted %d, got %d" , src , dst , size , dstInfo .Size ())
296+ // Required sanity check: if the file is being concurrently deleted, we may not always copy everything.
297+ if n != size {
298+ return fmt .Errorf ("copy of %s to %s failed: src/dst size mismatch: wanted %d, got %d" , src , dst , size , n )
299299 }
300300 return nil
301301}
@@ -309,15 +309,23 @@ func (d *DiskCache) LoadCas(dg digest.Digest, path string) bool {
309309 }
310310 it := iUntyped .(* qitem )
311311 it .mu .RLock ()
312- if err := copyFile (d .getPath (k ), path , dg .Size ); err != nil {
312+ err := copyFile (d .getPath (k ), path , dg .Size )
313+ it .mu .RUnlock ()
314+ if err != nil {
313315 // It is not possible to prevent a race with GC; hence, we return false on copy errors.
314- it .mu .RUnlock ()
315316 return false
316317 }
317- it .mu .RUnlock ()
318318
319319 d .mu .Lock ()
320320 d .queue .Bump (it )
321321 d .mu .Unlock ()
322322 return true
323323}
324+
325+ func getLastAccessTime (path string ) (time.Time , error ) {
326+ info , err := os .Stat (path )
327+ if err != nil {
328+ return time.Time {}, err
329+ }
330+ return FileInfoToAccessTime (info ), nil
331+ }
0 commit comments