@@ -23,6 +23,7 @@ import (
23
23
"time"
24
24
25
25
"github.com/golangci/golangci-lint/internal/mmap"
26
+ "github.com/golangci/golangci-lint/internal/robustio"
26
27
"github.com/rogpeppe/go-internal/lockedfile"
27
28
)
28
29
@@ -34,8 +35,50 @@ type ActionID [HashSize]byte
34
35
// An OutputID is a cache output key, the hash of an output of a computation.
35
36
type OutputID [HashSize ]byte
36
37
38
+ // Cache is the interface as used by the cmd/go.
39
+ type Cache interface {
40
+ // Get returns the cache entry for the provided ActionID.
41
+ // On miss, the error type should be of type *entryNotFoundError.
42
+ //
43
+ // After a success call to Get, OutputFile(Entry.OutputID) must
44
+ // exist on disk for until Close is called (at the end of the process).
45
+ Get (ActionID ) (Entry , error )
46
+
47
+ // Put adds an item to the cache.
48
+ //
49
+ // The seeker is only used to seek to the beginning. After a call to Put,
50
+ // the seek position is not guaranteed to be in any particular state.
51
+ //
52
+ // As a special case, if the ReadSeeker is of type noVerifyReadSeeker,
53
+ // the verification from GODEBUG=goverifycache=1 is skipped.
54
+ //
55
+ // After a success call to Get, OutputFile(Entry.OutputID) must
56
+ // exist on disk for until Close is called (at the end of the process).
57
+ Put (ActionID , io.ReadSeeker ) (_ OutputID , size int64 , _ error )
58
+
59
+ // Close is called at the end of the go process. Implementations can do
60
+ // cache cleanup work at this phase, or wait for and report any errors from
61
+ // background cleanup work started earlier. Any cache trimming should in one
62
+ // process should not violate cause the invariants of this interface to be
63
+ // violated in another process. Namely, a cache trim from one process should
64
+ // not delete an ObjectID from disk that was recently Get or Put from
65
+ // another process. As a rule of thumb, don't trim things used in the last
66
+ // day.
67
+ Close () error
68
+
69
+ // OutputFile returns the path on disk where OutputID is stored.
70
+ //
71
+ // It's only called after a successful get or put call so it doesn't need
72
+ // to return an error; it's assumed that if the previous get or put succeeded,
73
+ // it's already on disk.
74
+ OutputFile (OutputID ) string
75
+
76
+ // FuzzDir returns where fuzz files are stored.
77
+ FuzzDir () string
78
+ }
79
+
37
80
// A Cache is a package cache, backed by a file system directory tree.
38
- type Cache struct {
81
+ type DiskCache struct {
39
82
dir string
40
83
now func () time.Time
41
84
}
@@ -51,7 +94,7 @@ type Cache struct {
51
94
// to share a cache directory (for example, if the directory were stored
52
95
// in a network file system). File locking is notoriously unreliable in
53
96
// network file systems and may not suffice to protect the cache.
54
- func Open (dir string ) (* Cache , error ) {
97
+ func Open (dir string ) (* DiskCache , error ) {
55
98
info , err := os .Stat (dir )
56
99
if err != nil {
57
100
return nil , err
@@ -65,15 +108,15 @@ func Open(dir string) (*Cache, error) {
65
108
return nil , err
66
109
}
67
110
}
68
- c := & Cache {
111
+ c := & DiskCache {
69
112
dir : dir ,
70
113
now : time .Now ,
71
114
}
72
115
return c , nil
73
116
}
74
117
75
118
// fileName returns the name of the file corresponding to the given id.
76
- func (c * Cache ) fileName (id [HashSize ]byte , key string ) string {
119
+ func (c * DiskCache ) fileName (id [HashSize ]byte , key string ) string {
77
120
return filepath .Join (c .dir , fmt .Sprintf ("%02x" , id [0 ]), fmt .Sprintf ("%x" , id )+ "-" + key )
78
121
}
79
122
@@ -116,30 +159,34 @@ var errVerifyMode = errors.New("gocacheverify=1")
116
159
// DebugTest is set when GODEBUG=gocachetest=1 is in the environment.
117
160
var DebugTest = false
118
161
119
- func init () { initEnv () }
120
-
121
- func initEnv () {
122
- verify = false
123
- debugHash = false
124
- debug := strings .Split (os .Getenv ("GODEBUG" ), "," )
125
- for _ , f := range debug {
126
- if f == "gocacheverify=1" {
127
- verify = true
128
- }
129
- if f == "gocachehash=1" {
130
- debugHash = true
131
- }
132
- if f == "gocachetest=1" {
133
- DebugTest = true
134
- }
135
- }
136
- }
162
+ // func init() { initEnv() }
163
+
164
+ // var (
165
+ // goCacheVerify = godebug.New("gocacheverify")
166
+ // goDebugHash = godebug.New("gocachehash")
167
+ // goCacheTest = godebug.New("gocachetest")
168
+ // )
169
+
170
+ // func initEnv() {
171
+ // if goCacheVerify.Value() == "1" {
172
+ // goCacheVerify.IncNonDefault()
173
+ // verify = true
174
+ // }
175
+ // if goDebugHash.Value() == "1" {
176
+ // goDebugHash.IncNonDefault()
177
+ // debugHash = true
178
+ // }
179
+ // if goCacheTest.Value() == "1" {
180
+ // goCacheTest.IncNonDefault()
181
+ // DebugTest = true
182
+ // }
183
+ // }
137
184
138
185
// Get looks up the action ID in the cache,
139
186
// returning the corresponding output ID and file size, if any.
140
187
// Note that finding an output ID does not guarantee that the
141
188
// saved file for that output ID is still available.
142
- func (c * Cache ) Get (id ActionID ) (Entry , error ) {
189
+ func (c * DiskCache ) Get (id ActionID ) (Entry , error ) {
143
190
if verify {
144
191
return Entry {}, & entryNotFoundError {Err : errVerifyMode }
145
192
}
@@ -149,11 +196,11 @@ func (c *Cache) Get(id ActionID) (Entry, error) {
149
196
type Entry struct {
150
197
OutputID OutputID
151
198
Size int64
152
- Time time.Time
199
+ Time time.Time // when added to cache
153
200
}
154
201
155
202
// get is Get but does not respect verify mode, so that Put can use it.
156
- func (c * Cache ) get (id ActionID ) (Entry , error ) {
203
+ func (c * DiskCache ) get (id ActionID ) (Entry , error ) {
157
204
missing := func (reason error ) (Entry , error ) {
158
205
return Entry {}, & entryNotFoundError {Err : reason }
159
206
}
@@ -220,17 +267,12 @@ func (c *Cache) get(id ActionID) (Entry, error) {
220
267
221
268
// GetFile looks up the action ID in the cache and returns
222
269
// the name of the corresponding data file.
223
- func (c * Cache ) GetFile ( id ActionID ) (file string , entry Entry , err error ) {
270
+ func GetFile (c Cache , id ActionID ) (file string , entry Entry , err error ) {
224
271
entry , err = c .Get (id )
225
272
if err != nil {
226
273
return "" , Entry {}, err
227
274
}
228
-
229
- file , err = c .OutputFile (entry .OutputID )
230
- if err != nil {
231
- return "" , Entry {}, err
232
- }
233
-
275
+ file = c .OutputFile (entry .OutputID )
234
276
info , err := os .Stat (file )
235
277
if err != nil {
236
278
return "" , Entry {}, & entryNotFoundError {Err : err }
@@ -244,12 +286,12 @@ func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) {
244
286
// GetBytes looks up the action ID in the cache and returns
245
287
// the corresponding output bytes.
246
288
// GetBytes should only be used for data that can be expected to fit in memory.
247
- func (c * Cache ) GetBytes ( id ActionID ) ([]byte , Entry , error ) {
289
+ func GetBytes (c Cache , id ActionID ) ([]byte , Entry , error ) {
248
290
entry , err := c .Get (id )
249
291
if err != nil {
250
292
return nil , entry , err
251
293
}
252
- data , err := c . readFileCGIL (c .OutputFile (entry .OutputID ))
294
+ data , err := robustio . ReadFile (c .OutputFile (entry .OutputID ))
253
295
if err != nil {
254
296
return nil , entry , err
255
297
}
@@ -262,16 +304,12 @@ func (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) {
262
304
// GetMmap looks up the action ID in the cache and returns
263
305
// the corresponding output bytes.
264
306
// GetMmap should only be used for data that can be expected to fit in memory.
265
- func (c * Cache ) GetMmap ( id ActionID ) ([]byte , Entry , error ) {
307
+ func GetMmap (c Cache , id ActionID ) ([]byte , Entry , error ) {
266
308
entry , err := c .Get (id )
267
309
if err != nil {
268
310
return nil , entry , err
269
311
}
270
- outputFile , err := c .OutputFile (entry .OutputID )
271
- if err != nil {
272
- return nil , entry , err
273
- }
274
- md , err := mmap .Mmap (outputFile )
312
+ md , err := mmap .Mmap (c .OutputFile (entry .OutputID ))
275
313
if err != nil {
276
314
return nil , Entry {}, err
277
315
}
@@ -282,13 +320,10 @@ func (c *Cache) GetMmap(id ActionID) ([]byte, Entry, error) {
282
320
}
283
321
284
322
// OutputFile returns the name of the cache file storing output with the given OutputID.
285
- func (c * Cache ) OutputFile (out OutputID ) ( string , error ) {
323
+ func (c * DiskCache ) OutputFile (out OutputID ) string {
286
324
file := c .fileName (out , "d" )
287
- err := c .used (file )
288
- if err != nil {
289
- return "" , err
290
- }
291
- return file , nil
325
+ c .used (file )
326
+ return file
292
327
}
293
328
294
329
// Time constants for cache expiration.
@@ -318,7 +353,7 @@ const (
318
353
// mtime is more than an hour old. This heuristic eliminates
319
354
// nearly all of the mtime updates that would otherwise happen,
320
355
// while still keeping the mtimes useful for cache trimming.
321
- func (c * Cache ) used (file string ) error {
356
+ func (c * DiskCache ) used (file string ) error {
322
357
info , err := os .Stat (file )
323
358
if err == nil && c .now ().Sub (info .ModTime ()) < mtimeInterval {
324
359
return nil
@@ -339,8 +374,10 @@ func (c *Cache) used(file string) error {
339
374
return nil
340
375
}
341
376
377
+ func (c * DiskCache ) Close () error { return c .Trim () }
378
+
342
379
// Trim removes old cache entries that are likely not to be reused.
343
- func (c * Cache ) Trim () {
380
+ func (c * DiskCache ) Trim () error {
344
381
now := c .now ()
345
382
346
383
// We maintain in dir/trim.txt the time of the last completed cache trim.
@@ -354,7 +391,7 @@ func (c *Cache) Trim() {
354
391
if t , err := strconv .ParseInt (strings .TrimSpace (string (data )), 10 , 64 ); err == nil {
355
392
lastTrim := time .Unix (t , 0 )
356
393
if d := now .Sub (lastTrim ); d < trimInterval && d > - mtimeInterval {
357
- return
394
+ return nil
358
395
}
359
396
}
360
397
}
@@ -373,12 +410,14 @@ func (c *Cache) Trim() {
373
410
var b bytes.Buffer
374
411
fmt .Fprintf (& b , "%d" , now .Unix ())
375
412
if err := lockedfile .Write (filepath .Join (c .dir , "trim.txt" ), & b , 0666 ); err != nil {
376
- return
413
+ return err
377
414
}
415
+
416
+ return nil
378
417
}
379
418
380
419
// trimSubdir trims a single cache subdirectory.
381
- func (c * Cache ) trimSubdir (subdir string , cutoff time.Time ) {
420
+ func (c * DiskCache ) trimSubdir (subdir string , cutoff time.Time ) {
382
421
// Read all directory entries from subdir before removing
383
422
// any files, in case removing files invalidates the file offset
384
423
// in the directory scan. Also, ignore error from f.Readdirnames,
@@ -406,7 +445,7 @@ func (c *Cache) trimSubdir(subdir string, cutoff time.Time) {
406
445
407
446
// putIndexEntry adds an entry to the cache recording that executing the action
408
447
// with the given id produces an output with the given output id (hash) and size.
409
- func (c * Cache ) putIndexEntry (id ActionID , out OutputID , size int64 , allowVerify bool ) error {
448
+ func (c * DiskCache ) putIndexEntry (id ActionID , out OutputID , size int64 , allowVerify bool ) error {
410
449
// Note: We expect that for one reason or another it may happen
411
450
// that repeating an action produces a different output hash
412
451
// (for example, if the output contains a time stamp or temp dir name).
@@ -463,21 +502,32 @@ func (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify
463
502
return nil
464
503
}
465
504
505
+ // noVerifyReadSeeker is a io.ReadSeeker wrapper sentinel type
506
+ // that says that Cache.Put should skip the verify check
507
+ // (from GODEBUG=goverifycache=1).
508
+ type noVerifyReadSeeker struct {
509
+ io.ReadSeeker
510
+ }
511
+
466
512
// Put stores the given output in the cache as the output for the action ID.
467
513
// It may read file twice. The content of file must not change between the two passes.
468
- func (c * Cache ) Put (id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
469
- return c .put (id , file , true )
514
+ func (c * DiskCache ) Put (id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
515
+ wrapper , isNoVerify := file .(noVerifyReadSeeker )
516
+ if isNoVerify {
517
+ file = wrapper .ReadSeeker
518
+ }
519
+ return c .put (id , file , ! isNoVerify )
470
520
}
471
521
472
522
// PutNoVerify is like Put but disables the verify check
473
523
// when GODEBUG=goverifycache=1 is set.
474
524
// It is meant for data that is OK to cache but that we expect to vary slightly from run to run,
475
525
// like test output containing times and the like.
476
- func (c * Cache ) PutNoVerify ( id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
477
- return c .put (id , file , false )
526
+ func PutNoVerify (c Cache , id ActionID , file io.ReadSeeker ) (OutputID , int64 , error ) {
527
+ return c .Put (id , noVerifyReadSeeker { file } )
478
528
}
479
529
480
- func (c * Cache ) put (id ActionID , file io.ReadSeeker , allowVerify bool ) (OutputID , int64 , error ) {
530
+ func (c * DiskCache ) put (id ActionID , file io.ReadSeeker , allowVerify bool ) (OutputID , int64 , error ) {
481
531
// Compute output ID.
482
532
h := sha256 .New ()
483
533
if _ , err := file .Seek (0 , 0 ); err != nil {
@@ -500,14 +550,14 @@ func (c *Cache) put(id ActionID, file io.ReadSeeker, allowVerify bool) (OutputID
500
550
}
501
551
502
552
// PutBytes stores the given bytes in the cache as the output for the action ID.
503
- func (c * Cache ) PutBytes ( id ActionID , data []byte ) error {
553
+ func PutBytes (c Cache , id ActionID , data []byte ) error {
504
554
_ , _ , err := c .Put (id , bytes .NewReader (data ))
505
555
return err
506
556
}
507
557
508
558
// copyFile copies file into the cache, expecting it to have the given
509
559
// output ID and size, if that file is not present already.
510
- func (c * Cache ) copyFile (file io.ReadSeeker , out OutputID , size int64 ) error {
560
+ func (c * DiskCache ) copyFile (file io.ReadSeeker , out OutputID , size int64 ) error {
511
561
name := c .fileName (out , "d" )
512
562
info , err := os .Stat (name )
513
563
if err == nil && info .Size () == size {
@@ -608,6 +658,6 @@ func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error {
608
658
// They may be removed with 'go clean -fuzzcache'.
609
659
//
610
660
// TODO(#48526): make Trim remove unused files from this directory.
611
- func (c * Cache ) FuzzDir () string {
661
+ func (c * DiskCache ) FuzzDir () string {
612
662
return filepath .Join (c .dir , "fuzz" )
613
663
}
0 commit comments