diff options
Diffstat (limited to 'gopls/internal/govulncheck/vulncache.go')
-rw-r--r-- | gopls/internal/govulncheck/vulncache.go | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/gopls/internal/govulncheck/vulncache.go b/gopls/internal/govulncheck/vulncache.go new file mode 100644 index 000000000..a259f0273 --- /dev/null +++ b/gopls/internal/govulncheck/vulncache.go @@ -0,0 +1,105 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package govulncheck + +import ( + "sync" + "time" + + vulnc "golang.org/x/vuln/client" + "golang.org/x/vuln/osv" +) + +// inMemoryCache is an implementation of the [client.Cache] interface +// that "decorates" another instance of that interface to provide +// an additional layer of (memory-based) caching. +type inMemoryCache struct { + mu sync.Mutex + underlying vulnc.Cache + db map[string]*db +} + +var _ vulnc.Cache = &inMemoryCache{} + +type db struct { + retrieved time.Time + index vulnc.DBIndex + entry map[string][]*osv.Entry +} + +// NewInMemoryCache returns a new memory-based cache that decorates +// the provided cache (file-based, perhaps). +func NewInMemoryCache(underlying vulnc.Cache) *inMemoryCache { + return &inMemoryCache{ + underlying: underlying, + db: make(map[string]*db), + } +} + +func (c *inMemoryCache) lookupDBLocked(dbName string) *db { + cached := c.db[dbName] + if cached == nil { + cached = &db{entry: make(map[string][]*osv.Entry)} + c.db[dbName] = cached + } + return cached +} + +// ReadIndex returns the index for dbName from the cache, or returns zero values +// if it is not present. +func (c *inMemoryCache) ReadIndex(dbName string) (vulnc.DBIndex, time.Time, error) { + c.mu.Lock() + defer c.mu.Unlock() + cached := c.lookupDBLocked(dbName) + + if cached.retrieved.IsZero() { + // First time ReadIndex is called. + index, retrieved, err := c.underlying.ReadIndex(dbName) + if err != nil { + return index, retrieved, err + } + cached.index, cached.retrieved = index, retrieved + } + return cached.index, cached.retrieved, nil +} + +// WriteIndex puts the index and retrieved time into the cache. +func (c *inMemoryCache) WriteIndex(dbName string, index vulnc.DBIndex, retrieved time.Time) error { + c.mu.Lock() + defer c.mu.Unlock() + cached := c.lookupDBLocked(dbName) + cached.index, cached.retrieved = index, retrieved + // TODO(hyangah): shouldn't we invalidate all cached entries? + return c.underlying.WriteIndex(dbName, index, retrieved) +} + +// ReadEntries returns the vulndb entries for path from the cache. +func (c *inMemoryCache) ReadEntries(dbName, path string) ([]*osv.Entry, error) { + c.mu.Lock() + defer c.mu.Unlock() + cached := c.lookupDBLocked(dbName) + entries, ok := cached.entry[path] + if !ok { + // cache miss + entries, err := c.underlying.ReadEntries(dbName, path) + if err != nil { + return entries, err + } + cached.entry[path] = entries + } + return entries, nil +} + +// WriteEntries puts the entries for path into the cache. +func (c *inMemoryCache) WriteEntries(dbName, path string, entries []*osv.Entry) error { + c.mu.Lock() + defer c.mu.Unlock() + cached := c.lookupDBLocked(dbName) + cached.entry[path] = entries + return c.underlying.WriteEntries(dbName, path, entries) +} |