@@ -31,8 +31,50 @@ type ActionID [HashSize]byte
31
31
// An OutputID is a cache output key, the hash of an output of a computation.
32
32
type OutputID [HashSize ]byte
33
33
34
+ // Cache is the interface as used by the cmd/go.
35
+ type Cache interface {
36
+ // Get returns the cache entry for the provided ActionID.
37
+ // On miss, the error type should be of type *entryNotFoundError.
38
+ //
39
+ // After a success call to Get, OutputFile(Entry.OutputID) must
40
+ // exist on disk for until Close is called (at the end of the process).
41
+ Get (ActionID ) (Entry , error )
42
+
43
+ // Put adds an item to the cache.
44
+ //
45
+ // The seeker is only used to seek to the beginning. After a call to Put,
46
+ // the seek position is not guaranteed to be in any particular state.
47
+ //
48
+ // As a special case, if the ReadSeeker is of type noVerifyReadSeeker,
49
+ // the verification from GODEBUG=goverifycache=1 is skipped.
50
+ //
51
+ // After a success call to Get, OutputFile(Entry.OutputID) must
52
+ // exist on disk for until Close is called (at the end of the process).
53
+ Put (ActionID , io.ReadSeeker ) (_ OutputID , size int64 , _ error )
54
+
55
+ // Close is called at the end of the go process. Implementations can do
56
+ // cache cleanup work at this phase, or wait for and report any errors from
57
+ // background cleanup work started earlier. Any cache trimming should in one
58
+ // process should not violate cause the invariants of this interface to be
59
+ // violated in another process. Namely, a cache trim from one process should
60
+ // not delete an ObjectID from disk that was recently Get or Put from
61
+ // another process. As a rule of thumb, don't trim things used in the last
62
+ // day.
63
+ Close () error
64
+
65
+ // OutputFile returns the path on disk where OutputID is stored.
66
+ //
67
+ // It's only called after a successful get or put call so it doesn't need
68
+ // to return an error; it's assumed that if the previous get or put succeeded,
69
+ // it's already on disk.
70
+ OutputFile (OutputID ) string
71
+
72
+ // FuzzDir returns where fuzz files are stored.
73
+ FuzzDir () string
74
+ }
75
+
34
76
// A Cache is a package cache, backed by a file system directory tree.
35
- type Cache struct {
77
+ type DiskCache struct {
36
78
dir string
37
79
now func () time.Time
38
80
}
@@ -48,7 +90,7 @@ type Cache struct {
48
90
// to share a cache directory (for example, if the directory were stored
49
91
// in a network file system). File locking is notoriously unreliable in
50
92
// network file systems and may not suffice to protect the cache.
51
- func Open (dir string ) (* Cache , error ) {
93
+ func Open (dir string ) (* DiskCache , error ) {
52
94
info , err := os .Stat (dir )
53
95
if err != nil {
54
96
return nil , err
@@ -62,15 +104,15 @@ func Open(dir string) (*Cache, error) {
62
104
return nil , err
63
105
}
64
106
}
65
- c := & Cache {
107
+ c := & DiskCache {
66
108
dir : dir ,
67
109
now : time .Now ,
68
110
}
69
111
return c , nil
70
112
}
71
113
72
114
// fileName returns the name of the file corresponding to the given id.
73
- func (c * Cache ) fileName (id [HashSize ]byte , key string ) string {
115
+ func (c * DiskCache ) fileName (id [HashSize ]byte , key string ) string {
74
116
return filepath .Join (c .dir , fmt .Sprintf ("%02x" , id [0 ]), fmt .Sprintf ("%x" , id )+ "-" + key )
75
117
}
76
118
@@ -136,7 +178,7 @@ func initEnv() {
136
178
// returning the corresponding output ID and file size, if any.
137
179
// Note that finding an output ID does not guarantee that the
138
180
// saved file for that output ID is still available.
139
- func (c * Cache ) Get (id ActionID ) (Entry , error ) {
181
+ func (c * DiskCache ) Get (id ActionID ) (Entry , error ) {
140
182
if verify {
141
183
return Entry {}, & entryNotFoundError {Err : errVerifyMode }
142
184
}
@@ -150,7 +192,7 @@ type Entry struct {
150
192
}
151
193
152
194
// get is Get but does not respect verify mode, so that Put can use it.
153
- func (c * Cache ) get (id ActionID ) (Entry , error ) {
195
+ func (c * DiskCache ) get (id ActionID ) (Entry , error ) {
154
196
missing := func (reason error ) (Entry , error ) {
155
197
return Entry {}, & entryNotFoundError {Err : reason }
156
198
}
@@ -214,7 +256,7 @@ func (c *Cache) get(id ActionID) (Entry, error) {
214
256
215
257
// GetFile looks up the action ID in the cache and returns
216
258
// the name of the corresponding data file.
217
- func (c * Cache ) GetFile ( id ActionID ) (file string , entry Entry , err error ) {
259
+ func GetFile (c Cache , id ActionID ) (file string , entry Entry , err error ) {
218
260
entry , err = c .Get (id )
219
261
if err != nil {
220
262
return "" , Entry {}, err
@@ -233,7 +275,7 @@ func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) {
233
275
// GetBytes looks up the action ID in the cache and returns
234
276
// the corresponding output bytes.
235
277
// GetBytes should only be used for data that can be expected to fit in memory.
236
- func (c * Cache ) GetBytes ( id ActionID ) ([]byte , Entry , error ) {
278
+ func GetBytes (c Cache , id ActionID ) ([]byte , Entry , error ) {
237
279
entry , err := c .Get (id )
238
280
if err != nil {
239
281
return nil , entry , err
@@ -248,7 +290,7 @@ func (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) {
248
290
// GetMmap looks up the action ID in the cache and returns
249
291
// the corresponding output bytes.
250
292
// GetMmap should only be used for data that can be expected to fit in memory.
251
- func (c * Cache ) GetMmap ( id ActionID ) ([]byte , Entry , error ) {
293
+ func GetMmap (c Cache , id ActionID ) ([]byte , Entry , error ) {
252
294
entry , err := c .Get (id )
253
295
if err != nil {
254
296
return nil , entry , err
@@ -264,7 +306,7 @@ func (c *Cache) GetMmap(id ActionID) ([]byte, Entry, error) {
264
306
}
265
307
266
308
// OutputFile returns the name of the cache file storing output with the given OutputID.
267
- func (c * Cache ) OutputFile (out OutputID ) string {
309
+ func (c * DiskCache ) OutputFile (out OutputID ) string {
268
310
file := c .fileName (out , "d" )
269
311
c .used (file )
270
312
return file
@@ -297,16 +339,18 @@ const (
297
339
// mtime is more than an hour old. This heuristic eliminates
298
340
// nearly all of the mtime updates that would otherwise happen,
299
341
// while still keeping the mtimes useful for cache trimming.
300
- func (c * Cache ) used (file string ) {
342
+ func (c * DiskCache ) used (file string ) {
301
343
info , err := os .Stat (file )
302
344
if err == nil && c .now ().Sub (info .ModTime ()) < mtimeInterval {
303
345
return
304
346
}
305
347
os .Chtimes (file , c .now (), c .now ())
306
348
}
307
349
350
+ func (c * DiskCache ) Close () error { return c .Trim () }
351
+
308
352
// Trim removes old cache entries that are likely not to be reused.
309
- func (c * Cache ) Trim () error {
353
+ func (c * DiskCache ) Trim () error {
310
354
now := c .now ()
311
355
312
356
// We maintain in dir/trim.txt the time of the last completed cache trim.
@@ -346,7 +390,7 @@ func (c *Cache) Trim() error {
346
390
}
347
391
348
392
// trimSubdir trims a single cache subdirectory.
349
- func (c * Cache ) trimSubdir (subdir string , cutoff time.Time ) {
393
+ func (c * DiskCache ) trimSubdir (subdir string , cutoff time.Time ) {
350
394
// Read all directory entries from subdir before removing
351
395
// any files, in case removing files invalidates the file offset
352
396
// in the directory scan. Also, ignore error from f.Readdirnames,
@@ -374,7 +418,7 @@ func (c *Cache) trimSubdir(subdir string, cutoff time.Time) {
374
418
375
419
// putIndexEntry adds an entry to the cache recording that executing the action
376
420
// with the given id produces an output with the given output id (hash) and size.
377
- func (c * Cache ) putIndexEntry (id ActionID , out OutputID , size int64 , allowVerify bool ) error {
421
+ func (c * DiskCache ) putIndexEntry (id ActionID , out OutputID , size int64 , allowVerify bool ) error {
378
422
// Note: We expect that for one reason or another it may happen
379
423
// that repeating an action produces a different output hash
380
424
// (for example, if the output contains a time stamp or temp dir name).
@@ -428,21 +472,29 @@ func (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify
428
472
return nil
429
473
}
430
474
475
+ // noVerifyReadSeeker is a io.ReadSeeker wrapper sentinel type
476
+ // that says that Cache.Put should skip the verify check
477
+ // (from GODEBUG=goverifycache=1).
478
+ type noVerifyReadSeeker struct {
479
+ io.ReadSeeker
480
+ }
481
+
431
482
// Put stores the given output in the cache as the output for the action ID.
432
483
// It may read file twice. The content of file must not change between the two passes.
433
- func (c * Cache ) Put (id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
434
- return c .put (id , file , true )
484
+ func (c * DiskCache ) Put (id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
485
+ _ , isNoVerify := file .(noVerifyReadSeeker )
486
+ return c .put (id , file , ! isNoVerify )
435
487
}
436
488
437
489
// PutNoVerify is like Put but disables the verify check
438
490
// when GODEBUG=goverifycache=1 is set.
439
491
// It is meant for data that is OK to cache but that we expect to vary slightly from run to run,
440
492
// like test output containing times and the like.
441
- func (c * Cache ) PutNoVerify ( id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
442
- return c .put (id , file , false )
493
+ func PutNoVerify (c Cache , id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
494
+ return c .Put (id , noVerifyReadSeeker { file } )
443
495
}
444
496
445
- func (c * Cache ) put (id ActionID , file io.ReadSeeker , allowVerify bool ) (OutputID , int64 , error ) {
497
+ func (c * DiskCache ) put (id ActionID , file io.ReadSeeker , allowVerify bool ) (OutputID , int64 , error ) {
446
498
// Compute output ID.
447
499
h := sha256 .New ()
448
500
if _ , err := file .Seek (0 , 0 ); err != nil {
@@ -465,14 +517,14 @@ func (c *Cache) put(id ActionID, file io.ReadSeeker, allowVerify bool) (OutputID
465
517
}
466
518
467
519
// PutBytes stores the given bytes in the cache as the output for the action ID.
468
- func (c * Cache ) PutBytes ( id ActionID , data []byte ) error {
520
+ func PutBytes (c Cache , id ActionID , data []byte ) error {
469
521
_ , _ , err := c .Put (id , bytes .NewReader (data ))
470
522
return err
471
523
}
472
524
473
525
// copyFile copies file into the cache, expecting it to have the given
474
526
// output ID and size, if that file is not present already.
475
- func (c * Cache ) copyFile (file io.ReadSeeker , out OutputID , size int64 ) error {
527
+ func (c * DiskCache ) copyFile (file io.ReadSeeker , out OutputID , size int64 ) error {
476
528
name := c .fileName (out , "d" )
477
529
info , err := os .Stat (name )
478
530
if err == nil && info .Size () == size {
@@ -562,6 +614,6 @@ func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error {
562
614
// They may be removed with 'go clean -fuzzcache'.
563
615
//
564
616
// TODO(#48526): make Trim remove unused files from this directory.
565
- func (c * Cache ) FuzzDir () string {
617
+ func (c * DiskCache ) FuzzDir () string {
566
618
return filepath .Join (c .dir , "fuzz" )
567
619
}
0 commit comments