diff --git a/internal/gps/source_cache_multi.go b/internal/gps/source_cache_multi.go new file mode 100644 index 0000000000..5ce9108d0f --- /dev/null +++ b/internal/gps/source_cache_multi.go @@ -0,0 +1,120 @@ +// Copyright 2017 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. + +package gps + +import ( + "github.com/golang/dep/internal/gps/pkgtree" +) + +// A multiCache manages two cache levels, ephemeral in-memory and persistent on-disk. +// +// The in-memory cache is always checked first, with the on-disk used as a fallback. +// Values read from disk are set in-memory when an appropriate method exists. +// +// Set values are cached both in-memory and on-disk. +type multiCache struct { + mem, disk singleSourceCache +} + +func (c *multiCache) setManifestAndLock(r Revision, ai ProjectAnalyzerInfo, m Manifest, l Lock) { + c.mem.setManifestAndLock(r, ai, m, l) + c.disk.setManifestAndLock(r, ai, m, l) +} + +func (c *multiCache) getManifestAndLock(r Revision, ai ProjectAnalyzerInfo) (Manifest, Lock, bool) { + m, l, ok := c.mem.getManifestAndLock(r, ai) + if ok { + return m, l, true + } + + m, l, ok = c.disk.getManifestAndLock(r, ai) + if ok { + c.mem.setManifestAndLock(r, ai, m, l) + return m, l, true + } + + return nil, nil, false +} + +func (c *multiCache) setPackageTree(r Revision, ptree pkgtree.PackageTree) { + c.mem.setPackageTree(r, ptree) + c.disk.setPackageTree(r, ptree) +} + +func (c *multiCache) getPackageTree(r Revision) (pkgtree.PackageTree, bool) { + ptree, ok := c.mem.getPackageTree(r) + if ok { + return ptree, true + } + + ptree, ok = c.disk.getPackageTree(r) + if ok { + c.mem.setPackageTree(r, ptree) + return ptree, true + } + + return pkgtree.PackageTree{}, false +} + +func (c *multiCache) markRevisionExists(r Revision) { + c.mem.markRevisionExists(r) + c.disk.markRevisionExists(r) +} + +func (c *multiCache) setVersionMap(pvs []PairedVersion) { + c.mem.setVersionMap(pvs) + c.disk.setVersionMap(pvs) +} + +func (c *multiCache) getVersionsFor(rev Revision) ([]UnpairedVersion, bool) { + uvs, ok := c.mem.getVersionsFor(rev) + if ok { + return uvs, true + } + + return c.disk.getVersionsFor(rev) +} + +func (c *multiCache) getAllVersions() []PairedVersion { + pvs := c.mem.getAllVersions() + if pvs != nil { + return pvs + } + + pvs = c.disk.getAllVersions() + if pvs != nil { + c.mem.setVersionMap(pvs) + return pvs + } + + return nil +} + +func (c *multiCache) getRevisionFor(uv UnpairedVersion) (Revision, bool) { + rev, ok := c.mem.getRevisionFor(uv) + if ok { + return rev, true + } + + return c.disk.getRevisionFor(uv) +} + +func (c *multiCache) toRevision(v Version) (Revision, bool) { + rev, ok := c.mem.toRevision(v) + if ok { + return rev, true + } + + return c.disk.toRevision(v) +} + +func (c *multiCache) toUnpaired(v Version) (UnpairedVersion, bool) { + uv, ok := c.mem.toUnpaired(v) + if ok { + return uv, true + } + + return c.disk.toUnpaired(v) +} diff --git a/internal/gps/source_cache_test.go b/internal/gps/source_cache_test.go index ed9260087b..5e12ad19db 100644 --- a/internal/gps/source_cache_test.go +++ b/internal/gps/source_cache_test.go @@ -31,8 +31,29 @@ func Test_singleSourceCache(t *testing.T) { } return bc.newSingleSourceCache(pi), bc.close } - t.Run("bolt/open", singleSourceCacheTest{newCache: newBolt}.run) - t.Run("bolt/refresh", singleSourceCacheTest{newCache: newBolt, persistent: true}.run) + t.Run("bolt/keepOpen", singleSourceCacheTest{newCache: newBolt}.run) + t.Run("bolt/reOpen", singleSourceCacheTest{newCache: newBolt, persistent: true}.run) + + newMulti := func(t *testing.T, cachedir, root string) (singleSourceCache, func() error) { + disk, close := newBolt(t, cachedir, root) + return &multiCache{mem: newMemoryCache(), disk: disk}, close + } + t.Run("multi/keepOpen", singleSourceCacheTest{newCache: newMulti}.run) + t.Run("multi/reOpen", singleSourceCacheTest{persistent: true, newCache: newMulti}.run) + + t.Run("multi/keepOpen/noDisk", singleSourceCacheTest{ + newCache: func(*testing.T, string, string) (singleSourceCache, func() error) { + return &multiCache{mem: newMemoryCache(), disk: discardCache{}}, func() error { return nil } + }, + }.run) + + t.Run("multi/reOpen/noMem", singleSourceCacheTest{ + persistent: true, + newCache: func(t *testing.T, cachedir, root string) (singleSourceCache, func() error) { + disk, close := newBolt(t, cachedir, root) + return &multiCache{mem: discardCache{}, disk: disk}, close + }, + }.run) } var testAnalyzerInfo = ProjectAnalyzerInfo{ @@ -521,3 +542,42 @@ func packageOrErrEqual(a, b pkgtree.PackageOrErr) bool { return true } + +// discardCache discards set values and returns nothing. +type discardCache struct{} + +func (discardCache) setManifestAndLock(Revision, ProjectAnalyzerInfo, Manifest, Lock) {} + +func (discardCache) getManifestAndLock(Revision, ProjectAnalyzerInfo) (Manifest, Lock, bool) { + return nil, nil, false +} + +func (discardCache) setPackageTree(Revision, pkgtree.PackageTree) {} + +func (discardCache) getPackageTree(Revision) (pkgtree.PackageTree, bool) { + return pkgtree.PackageTree{}, false +} + +func (discardCache) markRevisionExists(r Revision) {} + +func (discardCache) setVersionMap(versionList []PairedVersion) {} + +func (discardCache) getVersionsFor(Revision) ([]UnpairedVersion, bool) { + return nil, false +} + +func (discardCache) getAllVersions() []PairedVersion { + return nil +} + +func (discardCache) getRevisionFor(UnpairedVersion) (Revision, bool) { + return "", false +} + +func (discardCache) toRevision(v Version) (Revision, bool) { + return "", false +} + +func (discardCache) toUnpaired(v Version) (UnpairedVersion, bool) { + return nil, false +}