Skip to content

Commit e28ddc1

Browse files
authored
dev: improve readability and maintenance of internal/cache (#5101)
1 parent 34209d7 commit e28ddc1

File tree

4 files changed

+352
-104
lines changed

4 files changed

+352
-104
lines changed

internal/cache/cache.go

Lines changed: 165 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import (
77
"errors"
88
"fmt"
99
"runtime"
10-
"sort"
10+
"slices"
11+
"strings"
1112
"sync"
1213

14+
"golang.org/x/exp/maps"
1315
"golang.org/x/tools/go/packages"
1416

1517
"github.com/golangci/golangci-lint/internal/go/cache"
@@ -25,8 +27,13 @@ const (
2527
HashModeNeedAllDeps
2628
)
2729

28-
// Cache is a per-package data cache. A cached data is invalidated when
29-
// package, or it's dependencies change.
30+
var ErrMissing = errors.New("missing data")
31+
32+
type hashResults map[HashMode]string
33+
34+
// Cache is a per-package data cache.
35+
// A cached data is invalidated when package,
36+
// or it's dependencies change.
3037
type Cache struct {
3138
lowLevelCache cache.Cache
3239
pkgHashes sync.Map
@@ -45,91 +52,62 @@ func NewCache(sw *timeutils.Stopwatch, log logutils.Log) (*Cache, error) {
4552
}
4653

4754
func (c *Cache) Close() {
48-
c.sw.TrackStage("close", func() {
49-
err := c.lowLevelCache.Close()
50-
if err != nil {
51-
c.log.Errorf("cache close: %v", err)
52-
}
53-
})
55+
err := c.sw.TrackStageErr("close", c.lowLevelCache.Close)
56+
if err != nil {
57+
c.log.Errorf("cache close: %v", err)
58+
}
5459
}
5560

5661
func (c *Cache) Put(pkg *packages.Package, mode HashMode, key string, data any) error {
57-
var err error
58-
buf := &bytes.Buffer{}
59-
c.sw.TrackStage("gob", func() {
60-
err = gob.NewEncoder(buf).Encode(data)
61-
})
62+
buf, err := c.encode(data)
6263
if err != nil {
63-
return fmt.Errorf("failed to gob encode: %w", err)
64+
return err
6465
}
6566

66-
var aID cache.ActionID
67-
68-
c.sw.TrackStage("key build", func() {
69-
aID, err = c.pkgActionID(pkg, mode)
70-
if err == nil {
71-
subkey, subkeyErr := cache.Subkey(aID, key)
72-
if subkeyErr != nil {
73-
err = fmt.Errorf("failed to build subkey: %w", subkeyErr)
74-
}
75-
aID = subkey
76-
}
77-
})
67+
actionID, err := c.buildKey(pkg, mode, key)
7868
if err != nil {
7969
return fmt.Errorf("failed to calculate package %s action id: %w", pkg.Name, err)
8070
}
81-
c.ioSem <- struct{}{}
82-
c.sw.TrackStage("cache io", func() {
83-
err = cache.PutBytes(c.lowLevelCache, aID, buf.Bytes())
84-
})
85-
<-c.ioSem
71+
72+
err = c.putBytes(actionID, buf)
8673
if err != nil {
8774
return fmt.Errorf("failed to save data to low-level cache by key %s for package %s: %w", key, pkg.Name, err)
8875
}
8976

9077
return nil
9178
}
9279

93-
var ErrMissing = errors.New("missing data")
94-
9580
func (c *Cache) Get(pkg *packages.Package, mode HashMode, key string, data any) error {
96-
var aID cache.ActionID
97-
var err error
98-
c.sw.TrackStage("key build", func() {
99-
aID, err = c.pkgActionID(pkg, mode)
100-
if err == nil {
101-
subkey, subkeyErr := cache.Subkey(aID, key)
102-
if subkeyErr != nil {
103-
err = fmt.Errorf("failed to build subkey: %w", subkeyErr)
104-
}
105-
aID = subkey
106-
}
107-
})
81+
actionID, err := c.buildKey(pkg, mode, key)
10882
if err != nil {
10983
return fmt.Errorf("failed to calculate package %s action id: %w", pkg.Name, err)
11084
}
11185

112-
var b []byte
113-
c.ioSem <- struct{}{}
114-
c.sw.TrackStage("cache io", func() {
115-
b, _, err = cache.GetBytes(c.lowLevelCache, aID)
116-
})
117-
<-c.ioSem
86+
cachedData, err := c.getBytes(actionID)
11887
if err != nil {
11988
if cache.IsErrMissing(err) {
12089
return ErrMissing
12190
}
12291
return fmt.Errorf("failed to get data from low-level cache by key %s for package %s: %w", key, pkg.Name, err)
12392
}
12493

125-
c.sw.TrackStage("gob", func() {
126-
err = gob.NewDecoder(bytes.NewReader(b)).Decode(data)
127-
})
128-
if err != nil {
129-
return fmt.Errorf("failed to gob decode: %w", err)
130-
}
94+
return c.decode(cachedData, data)
95+
}
13196

132-
return nil
97+
func (c *Cache) buildKey(pkg *packages.Package, mode HashMode, key string) (cache.ActionID, error) {
98+
return timeutils.TrackStage(c.sw, "key build", func() (cache.ActionID, error) {
99+
actionID, err := c.pkgActionID(pkg, mode)
100+
if err != nil {
101+
return actionID, err
102+
}
103+
104+
subkey, subkeyErr := cache.Subkey(actionID, key)
105+
if subkeyErr != nil {
106+
return actionID, fmt.Errorf("failed to build subkey: %w", subkeyErr)
107+
}
108+
109+
return subkey, nil
110+
})
133111
}
134112

135113
func (c *Cache) pkgActionID(pkg *packages.Package, mode HashMode) (cache.ActionID, error) {
@@ -142,89 +120,172 @@ func (c *Cache) pkgActionID(pkg *packages.Package, mode HashMode) (cache.ActionI
142120
if err != nil {
143121
return cache.ActionID{}, fmt.Errorf("failed to make a hash: %w", err)
144122
}
123+
145124
fmt.Fprintf(key, "pkgpath %s\n", pkg.PkgPath)
146125
fmt.Fprintf(key, "pkghash %s\n", hash)
147126

148127
return key.Sum(), nil
149128
}
150129

151-
// packageHash computes a package's hash. The hash is based on all Go
152-
// files that make up the package, as well as the hashes of imported
153-
// packages.
154130
func (c *Cache) packageHash(pkg *packages.Package, mode HashMode) (string, error) {
155-
type hashResults map[HashMode]string
156-
hashResI, ok := c.pkgHashes.Load(pkg)
157-
if ok {
158-
hashRes := hashResI.(hashResults)
159-
if _, ok := hashRes[mode]; !ok {
160-
return "", fmt.Errorf("no mode %d in hash result", mode)
131+
results, found := c.pkgHashes.Load(pkg)
132+
if found {
133+
hashRes := results.(hashResults)
134+
if result, ok := hashRes[mode]; ok {
135+
return result, nil
161136
}
162-
return hashRes[mode], nil
137+
138+
return "", fmt.Errorf("no mode %d in hash result", mode)
163139
}
164140

165-
hashRes := hashResults{}
141+
hashRes, err := c.computePkgHash(pkg)
142+
if err != nil {
143+
return "", err
144+
}
145+
146+
result, found := hashRes[mode]
147+
if !found {
148+
return "", fmt.Errorf("invalid mode %d", mode)
149+
}
166150

151+
c.pkgHashes.Store(pkg, hashRes)
152+
153+
return result, nil
154+
}
155+
156+
// computePkgHash computes a package's hash.
157+
// The hash is based on all Go files that make up the package,
158+
// as well as the hashes of imported packages.
159+
func (c *Cache) computePkgHash(pkg *packages.Package) (hashResults, error) {
167160
key, err := cache.NewHash("package hash")
168161
if err != nil {
169-
return "", fmt.Errorf("failed to make a hash: %w", err)
162+
return nil, fmt.Errorf("failed to make a hash: %w", err)
170163
}
171164

165+
hashRes := hashResults{}
166+
172167
fmt.Fprintf(key, "pkgpath %s\n", pkg.PkgPath)
168+
173169
for _, f := range pkg.CompiledGoFiles {
174-
c.ioSem <- struct{}{}
175-
h, fErr := cache.FileHash(f)
176-
<-c.ioSem
170+
h, fErr := c.fileHash(f)
177171
if fErr != nil {
178-
return "", fmt.Errorf("failed to calculate file %s hash: %w", f, fErr)
172+
return nil, fmt.Errorf("failed to calculate file %s hash: %w", f, fErr)
179173
}
174+
180175
fmt.Fprintf(key, "file %s %x\n", f, h)
181176
}
177+
182178
curSum := key.Sum()
183179
hashRes[HashModeNeedOnlySelf] = hex.EncodeToString(curSum[:])
184180

185-
imps := make([]*packages.Package, 0, len(pkg.Imports))
186-
for _, imp := range pkg.Imports {
187-
imps = append(imps, imp)
188-
}
189-
sort.Slice(imps, func(i, j int) bool {
190-
return imps[i].PkgPath < imps[j].PkgPath
181+
imps := maps.Values(pkg.Imports)
182+
183+
slices.SortFunc(imps, func(a, b *packages.Package) int {
184+
return strings.Compare(a.PkgPath, b.PkgPath)
191185
})
192186

193-
calcDepsHash := func(depMode HashMode) error {
194-
for _, dep := range imps {
195-
if dep.PkgPath == "unsafe" {
196-
continue
197-
}
187+
if err := c.computeDepsHash(HashModeNeedOnlySelf, imps, key); err != nil {
188+
return nil, err
189+
}
190+
191+
curSum = key.Sum()
192+
hashRes[HashModeNeedDirectDeps] = hex.EncodeToString(curSum[:])
193+
194+
if err := c.computeDepsHash(HashModeNeedAllDeps, imps, key); err != nil {
195+
return nil, err
196+
}
197+
198+
curSum = key.Sum()
199+
hashRes[HashModeNeedAllDeps] = hex.EncodeToString(curSum[:])
198200

199-
depHash, depErr := c.packageHash(dep, depMode)
200-
if depErr != nil {
201-
return fmt.Errorf("failed to calculate hash for dependency %s with mode %d: %w", dep.Name, depMode, depErr)
202-
}
201+
return hashRes, nil
202+
}
203203

204-
fmt.Fprintf(key, "import %s %s\n", dep.PkgPath, depHash)
204+
func (c *Cache) computeDepsHash(depMode HashMode, imps []*packages.Package, key *cache.Hash) error {
205+
for _, dep := range imps {
206+
if dep.PkgPath == "unsafe" {
207+
continue
205208
}
206-
return nil
209+
210+
depHash, err := c.packageHash(dep, depMode)
211+
if err != nil {
212+
return fmt.Errorf("failed to calculate hash for dependency %s with mode %d: %w", dep.Name, depMode, err)
213+
}
214+
215+
fmt.Fprintf(key, "import %s %s\n", dep.PkgPath, depHash)
207216
}
208217

209-
if err := calcDepsHash(HashModeNeedOnlySelf); err != nil {
210-
return "", err
218+
return nil
219+
}
220+
221+
func (c *Cache) putBytes(actionID cache.ActionID, buf *bytes.Buffer) error {
222+
c.ioSem <- struct{}{}
223+
224+
err := c.sw.TrackStageErr("cache io", func() error {
225+
return cache.PutBytes(c.lowLevelCache, actionID, buf.Bytes())
226+
})
227+
228+
<-c.ioSem
229+
230+
if err != nil {
231+
return err
211232
}
212233

213-
curSum = key.Sum()
214-
hashRes[HashModeNeedDirectDeps] = hex.EncodeToString(curSum[:])
234+
return nil
235+
}
215236

216-
if err := calcDepsHash(HashModeNeedAllDeps); err != nil {
217-
return "", err
237+
func (c *Cache) getBytes(actionID cache.ActionID) ([]byte, error) {
238+
c.ioSem <- struct{}{}
239+
240+
cachedData, err := timeutils.TrackStage(c.sw, "cache io", func() ([]byte, error) {
241+
b, _, errGB := cache.GetBytes(c.lowLevelCache, actionID)
242+
return b, errGB
243+
})
244+
245+
<-c.ioSem
246+
247+
if err != nil {
248+
return nil, err
218249
}
219-
curSum = key.Sum()
220-
hashRes[HashModeNeedAllDeps] = hex.EncodeToString(curSum[:])
221250

222-
if _, ok := hashRes[mode]; !ok {
223-
return "", fmt.Errorf("invalid mode %d", mode)
251+
return cachedData, nil
252+
}
253+
254+
func (c *Cache) fileHash(f string) ([cache.HashSize]byte, error) {
255+
c.ioSem <- struct{}{}
256+
257+
h, err := cache.FileHash(f)
258+
259+
<-c.ioSem
260+
261+
if err != nil {
262+
return [cache.HashSize]byte{}, err
224263
}
225264

226-
c.pkgHashes.Store(pkg, hashRes)
227-
return hashRes[mode], nil
265+
return h, nil
266+
}
267+
268+
func (c *Cache) encode(data any) (*bytes.Buffer, error) {
269+
buf := &bytes.Buffer{}
270+
err := c.sw.TrackStageErr("gob", func() error {
271+
return gob.NewEncoder(buf).Encode(data)
272+
})
273+
if err != nil {
274+
return nil, fmt.Errorf("failed to gob encode: %w", err)
275+
}
276+
277+
return buf, nil
278+
}
279+
280+
func (c *Cache) decode(b []byte, data any) error {
281+
err := c.sw.TrackStageErr("gob", func() error {
282+
return gob.NewDecoder(bytes.NewReader(b)).Decode(data)
283+
})
284+
if err != nil {
285+
return fmt.Errorf("failed to gob decode: %w", err)
286+
}
287+
288+
return nil
228289
}
229290

230291
func SetSalt(b *bytes.Buffer) {

0 commit comments

Comments
 (0)