solver: simplify cache backend interface
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>docker-18.09
parent
5593bb9f48
commit
4df3b2e000
|
@ -10,7 +10,6 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
mainBucket = "_main"
|
||||
resultBucket = "_result"
|
||||
linksBucket = "_links"
|
||||
byResultBucket = "_byresult"
|
||||
|
@ -27,7 +26,7 @@ func NewStore(dbPath string) (*Store, error) {
|
|||
return nil, errors.Wrapf(err, "failed to open database file %s", dbPath)
|
||||
}
|
||||
if err := db.Update(func(tx *bolt.Tx) error {
|
||||
for _, b := range []string{mainBucket, resultBucket, linksBucket, byResultBucket, backlinksBucket} {
|
||||
for _, b := range []string{resultBucket, linksBucket, byResultBucket, backlinksBucket} {
|
||||
if _, err := tx.CreateBucketIfNotExists([]byte(b)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -39,47 +38,30 @@ func NewStore(dbPath string) (*Store, error) {
|
|||
return &Store{db: db}, nil
|
||||
}
|
||||
|
||||
func (s *Store) Get(id string) (solver.CacheKeyInfo, error) {
|
||||
var cki solver.CacheKeyInfo
|
||||
func (s *Store) Exists(id string) bool {
|
||||
exists := false
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(mainBucket))
|
||||
if b == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
v := b.Get([]byte(id))
|
||||
if v == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
return json.Unmarshal(v, &cki)
|
||||
b := tx.Bucket([]byte(linksBucket)).Bucket([]byte(id))
|
||||
exists = b != nil
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return solver.CacheKeyInfo{}, err
|
||||
return false
|
||||
}
|
||||
return cki, nil
|
||||
}
|
||||
|
||||
func (s *Store) Set(info solver.CacheKeyInfo) error {
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(mainBucket))
|
||||
if b == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
dt, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Put([]byte(info.ID), dt)
|
||||
})
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s *Store) Walk(fn func(id string) error) error {
|
||||
ids := make([]string, 0)
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(mainBucket))
|
||||
return b.ForEach(func(k, v []byte) error {
|
||||
b := tx.Bucket([]byte(linksBucket))
|
||||
c := b.Cursor()
|
||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||
if v == nil {
|
||||
ids = append(ids, string(k))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -272,7 +254,7 @@ func (s *Store) emptyBranchWithParents(tx *bolt.Tx, id []byte) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
return tx.Bucket([]byte(mainBucket)).Delete(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) AddLink(id string, link solver.CacheInfoLink, target string) error {
|
||||
|
|
|
@ -43,10 +43,12 @@ func NewCacheManager(id string, storage CacheKeyStorage, results CacheResultStor
|
|||
}
|
||||
|
||||
type inMemoryCacheKey struct {
|
||||
CacheKeyInfo CacheKeyInfo
|
||||
manager *inMemoryCacheManager
|
||||
cacheResult CacheResult
|
||||
deps []CacheKeyWithSelector // only []*inMemoryCacheKey
|
||||
digest digest.Digest
|
||||
output Index
|
||||
id string
|
||||
CacheKey
|
||||
}
|
||||
|
||||
|
@ -55,10 +57,10 @@ func (ck *inMemoryCacheKey) Deps() []CacheKeyWithSelector {
|
|||
}
|
||||
|
||||
func (ck *inMemoryCacheKey) Digest() digest.Digest {
|
||||
return ck.CacheKeyInfo.Base
|
||||
return ck.digest
|
||||
}
|
||||
func (ck *inMemoryCacheKey) Output() Index {
|
||||
return Index(ck.CacheKeyInfo.Output)
|
||||
return ck.output
|
||||
}
|
||||
|
||||
func withExporter(ck *inMemoryCacheKey, cacheResult *CacheResult, deps []CacheKeyWithSelector) ExportableCacheKey {
|
||||
|
@ -78,7 +80,7 @@ type cacheExporter struct {
|
|||
func (ce *cacheExporter) Export(ctx context.Context, m map[digest.Digest]*ExportRecord, converter func(context.Context, Result) (*Remote, error)) (*ExportRecord, error) {
|
||||
var res Result
|
||||
if ce.cacheResult == nil {
|
||||
cr, err := ce.inMemoryCacheKey.manager.getBestResult(ce.inMemoryCacheKey.CacheKeyInfo)
|
||||
cr, err := ce.inMemoryCacheKey.manager.getBestResult(ce.inMemoryCacheKey.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -109,7 +111,7 @@ func (ce *cacheExporter) Export(ctx context.Context, m map[digest.Digest]*Export
|
|||
}
|
||||
}
|
||||
|
||||
cacheID := digest.FromBytes([]byte(ce.inMemoryCacheKey.CacheKeyInfo.ID))
|
||||
cacheID := digest.FromBytes([]byte(ce.inMemoryCacheKey.id))
|
||||
if remote != nil && len(remote.Descriptors) > 0 && remote.Descriptors[0].Digest != "" {
|
||||
cacheID = remote.Descriptors[0].Digest
|
||||
}
|
||||
|
@ -164,20 +166,22 @@ func (c *inMemoryCacheManager) ID() string {
|
|||
return c.id
|
||||
}
|
||||
|
||||
func (c *inMemoryCacheManager) toInMemoryCacheKey(cki CacheKeyInfo, deps []CacheKeyWithSelector) *inMemoryCacheKey {
|
||||
func (c *inMemoryCacheManager) toInMemoryCacheKey(id string, dgst digest.Digest, output Index, deps []CacheKeyWithSelector) *inMemoryCacheKey {
|
||||
ck := &inMemoryCacheKey{
|
||||
CacheKeyInfo: cki,
|
||||
id: id,
|
||||
output: output,
|
||||
digest: dgst,
|
||||
manager: c,
|
||||
CacheKey: NewCacheKey("", 0, nil),
|
||||
deps: deps,
|
||||
}
|
||||
ck.SetValue(internalMemoryKey, cki.ID)
|
||||
ck.SetValue(internalMemoryKey, id)
|
||||
return ck
|
||||
}
|
||||
|
||||
func (c *inMemoryCacheManager) getBestResult(cki CacheKeyInfo) (*CacheResult, error) {
|
||||
func (c *inMemoryCacheManager) getBestResult(id string) (*CacheResult, error) {
|
||||
var results []*CacheResult
|
||||
if err := c.backend.WalkResults(cki.ID, func(res CacheResult) error {
|
||||
if err := c.backend.WalkResults(id, func(res CacheResult) error {
|
||||
results = append(results, &res)
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
@ -241,7 +245,7 @@ func (c *inMemoryCacheManager) Query(deps []CacheKeyWithSelector, input Index, d
|
|||
allRes := map[string]struct{}{}
|
||||
for _, d := range allDeps {
|
||||
if d.internalKey != nil {
|
||||
if err := c.backend.WalkLinks(d.internalKey.CacheKeyInfo.ID, CacheInfoLink{input, output, dgst, d.key.Selector}, func(id string) error {
|
||||
if err := c.backend.WalkLinks(d.internalKey.id, CacheInfoLink{input, output, dgst, d.key.Selector}, func(id string) error {
|
||||
d.results[id] = struct{}{}
|
||||
allRes[id] = struct{}{}
|
||||
return nil
|
||||
|
@ -267,7 +271,7 @@ func (c *inMemoryCacheManager) Query(deps []CacheKeyWithSelector, input Index, d
|
|||
d.internalKey = internalKey
|
||||
}
|
||||
if _, ok := d.results[res]; !ok {
|
||||
if err := c.backend.AddLink(d.internalKey.CacheKeyInfo.ID, CacheInfoLink{
|
||||
if err := c.backend.AddLink(d.internalKey.id, CacheInfoLink{
|
||||
Input: input,
|
||||
Output: output,
|
||||
Digest: dgst,
|
||||
|
@ -279,21 +283,14 @@ func (c *inMemoryCacheManager) Query(deps []CacheKeyWithSelector, input Index, d
|
|||
}
|
||||
hadResults := false
|
||||
|
||||
cki, err := c.backend.Get(res)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == ErrNotFound {
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed lookup by internal ID %s", res)
|
||||
}
|
||||
deps := formatDeps(allDeps, int(input))
|
||||
k := c.toInMemoryCacheKey(cki, deps)
|
||||
fdeps := formatDeps(allDeps, int(input))
|
||||
k := c.toInMemoryCacheKey(res, dgst, output, fdeps)
|
||||
// TODO: invoke this only once per input
|
||||
if err := c.backend.WalkResults(res, func(r CacheResult) error {
|
||||
if c.results.Exists(r.ID) {
|
||||
outs = append(outs, &CacheRecord{
|
||||
ID: res + "@" + r.ID,
|
||||
CacheKey: withExporter(k, &r, deps),
|
||||
CacheKey: withExporter(k, &r, fdeps),
|
||||
CacheManager: c,
|
||||
Loadable: true,
|
||||
CreatedAt: r.CreatedAt,
|
||||
|
@ -308,18 +305,14 @@ func (c *inMemoryCacheManager) Query(deps []CacheKeyWithSelector, input Index, d
|
|||
}
|
||||
|
||||
if !hadResults {
|
||||
cki, err := c.backend.Get(res)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == ErrNotFound {
|
||||
if len(deps) == 0 {
|
||||
if !c.backend.Exists(res) {
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed lookup by internal ID %s", res)
|
||||
}
|
||||
deps := formatDeps(allDeps, int(input))
|
||||
k := c.toInMemoryCacheKey(cki, deps)
|
||||
outs = append(outs, &CacheRecord{
|
||||
ID: res,
|
||||
CacheKey: withExporter(k, nil, deps),
|
||||
CacheKey: withExporter(k, nil, fdeps),
|
||||
CacheManager: c,
|
||||
Loadable: false,
|
||||
})
|
||||
|
@ -342,7 +335,7 @@ func (c *inMemoryCacheManager) Load(ctx context.Context, rec *CacheRecord) (Resu
|
|||
return nil, err
|
||||
}
|
||||
|
||||
res, err := c.backend.Load(ck.CacheKeyInfo.ID, keyParts[1])
|
||||
res, err := c.backend.Load(ck.id, keyParts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -366,7 +359,7 @@ func (c *inMemoryCacheManager) Save(k CacheKey, r Result) (ExportableCacheKey, e
|
|||
return empty, err
|
||||
}
|
||||
|
||||
if err := c.backend.AddResult(ck.CacheKeyInfo.ID, res); err != nil {
|
||||
if err := c.backend.AddResult(ck.id, res); err != nil {
|
||||
return empty, err
|
||||
}
|
||||
|
||||
|
@ -417,11 +410,7 @@ func (c *inMemoryCacheManager) getInternalKey(k CacheKey, createIfNotExist bool)
|
|||
}
|
||||
internalV := k.GetValue(internalMemoryKey)
|
||||
if internalV != nil {
|
||||
cki, err := c.backend.Get(internalV.(string))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed lookup by internal ID %s", internalV.(string))
|
||||
}
|
||||
return c.toInMemoryCacheKey(cki, k.Deps()), nil
|
||||
return c.toInMemoryCacheKey(internalV.(string), k.Digest(), k.Output(), k.Deps()), nil
|
||||
}
|
||||
|
||||
matches := make(map[string]struct{})
|
||||
|
@ -444,7 +433,7 @@ func (c *inMemoryCacheManager) getInternalKey(k CacheKey, createIfNotExist bool)
|
|||
for _, ck := range cks {
|
||||
internalCk := ck.CacheKey.CacheKey.(*inMemoryCacheKey)
|
||||
m2 := make(map[string]struct{})
|
||||
if err := c.backend.WalkLinks(internalCk.CacheKeyInfo.ID, CacheInfoLink{
|
||||
if err := c.backend.WalkLinks(internalCk.id, CacheInfoLink{
|
||||
Input: Index(i),
|
||||
Output: Index(k.Output()),
|
||||
Digest: k.Digest(),
|
||||
|
@ -486,46 +475,25 @@ func (c *inMemoryCacheManager) getInternalKey(k CacheKey, createIfNotExist bool)
|
|||
if len(k.Deps()) == 0 {
|
||||
internalKey = digest.FromBytes([]byte(fmt.Sprintf("%s@%d", k.Digest(), k.Output()))).String()
|
||||
}
|
||||
cki, err := c.backend.Get(internalKey)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == ErrNotFound {
|
||||
if !createIfNotExist {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return c.toInMemoryCacheKey(cki, k.Deps()), nil
|
||||
}
|
||||
}
|
||||
|
||||
cki := CacheKeyInfo{
|
||||
ID: internalKey,
|
||||
Base: k.Digest(),
|
||||
Output: int(k.Output()),
|
||||
}
|
||||
|
||||
if err := c.backend.Set(cki); err != nil {
|
||||
return nil, err
|
||||
return c.toInMemoryCacheKey(internalKey, k.Digest(), k.Output(), k.Deps()), nil
|
||||
}
|
||||
|
||||
for i, dep := range deps {
|
||||
for _, ck := range dep {
|
||||
internalCk := ck.CacheKey.CacheKey.(*inMemoryCacheKey)
|
||||
err := c.backend.AddLink(internalCk.CacheKeyInfo.ID, CacheInfoLink{
|
||||
err := c.backend.AddLink(internalCk.id, CacheInfoLink{
|
||||
Input: Index(i),
|
||||
Output: Index(cki.Output),
|
||||
Digest: cki.Base,
|
||||
Output: k.Output(),
|
||||
Digest: k.Digest(),
|
||||
Selector: ck.Selector,
|
||||
}, cki.ID)
|
||||
}, internalKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.toInMemoryCacheKey(cki, k.Deps()), nil
|
||||
return c.toInMemoryCacheKey(internalKey, k.Digest(), k.Output(), k.Deps()), nil
|
||||
}
|
||||
|
||||
func newCombinedCacheManager(cms []CacheManager, main CacheManager) CacheManager {
|
||||
|
|
|
@ -12,9 +12,8 @@ var ErrNotFound = errors.Errorf("not found")
|
|||
|
||||
// CacheKeyStorage is interface for persisting cache metadata
|
||||
type CacheKeyStorage interface {
|
||||
Get(id string) (CacheKeyInfo, error)
|
||||
Exists(id string) bool
|
||||
Walk(fn func(id string) error) error
|
||||
Set(info CacheKeyInfo) error
|
||||
|
||||
WalkResults(id string, fn func(CacheResult) error) error
|
||||
Load(id string, resultID string) (CacheResult, error)
|
||||
|
@ -25,20 +24,6 @@ type CacheKeyStorage interface {
|
|||
WalkLinks(id string, link CacheInfoLink, fn func(id string) error) error
|
||||
}
|
||||
|
||||
// CacheKeyInfo is storable metadata about single cache key
|
||||
type CacheKeyInfo struct {
|
||||
ID string
|
||||
Base digest.Digest
|
||||
Output int
|
||||
// Deps []CacheKeyInfoWithSelector
|
||||
}
|
||||
|
||||
// CacheKeyInfoWithSelector is CacheKeyInfo combined with a selector
|
||||
type CacheKeyInfoWithSelector struct {
|
||||
ID string
|
||||
Selector digest.Digest
|
||||
}
|
||||
|
||||
// CacheResult is a record for a single solve result
|
||||
type CacheResult struct {
|
||||
// Payload []byte
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
func RunCacheStorageTests(t *testing.T, st func() (CacheKeyStorage, func())) {
|
||||
for _, tc := range []func(*testing.T, CacheKeyStorage){
|
||||
testGetSet,
|
||||
testResults,
|
||||
testLinks,
|
||||
testResultReleaseSingleLevel,
|
||||
|
@ -32,53 +31,21 @@ func runStorageTest(t *testing.T, fn func(t *testing.T, st CacheKeyStorage), st
|
|||
}))
|
||||
}
|
||||
|
||||
func testGetSet(t *testing.T, st CacheKeyStorage) {
|
||||
t.Parallel()
|
||||
cki := CacheKeyInfo{
|
||||
ID: "foo",
|
||||
Base: digest.FromBytes([]byte("foo")),
|
||||
}
|
||||
err := st.Set(cki)
|
||||
require.NoError(t, err)
|
||||
|
||||
cki2, err := st.Get(cki.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, cki, cki2)
|
||||
|
||||
_, err = st.Get("bar")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, errors.Cause(err), ErrNotFound)
|
||||
}
|
||||
|
||||
func testResults(t *testing.T, st CacheKeyStorage) {
|
||||
t.Parallel()
|
||||
cki := CacheKeyInfo{
|
||||
ID: "foo",
|
||||
Base: digest.FromBytes([]byte("foo")),
|
||||
}
|
||||
err := st.Set(cki)
|
||||
require.NoError(t, err)
|
||||
|
||||
cki2 := CacheKeyInfo{
|
||||
ID: "bar",
|
||||
Base: digest.FromBytes([]byte("bar")),
|
||||
}
|
||||
err = st.Set(cki2)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(cki.ID, CacheResult{
|
||||
err := st.AddResult("foo", CacheResult{
|
||||
ID: "foo0",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(cki.ID, CacheResult{
|
||||
err = st.AddResult("foo", CacheResult{
|
||||
ID: "foo1",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(cki2.ID, CacheResult{
|
||||
err = st.AddResult("bar", CacheResult{
|
||||
ID: "bar0",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
@ -132,36 +99,18 @@ func testResults(t *testing.T, st CacheKeyStorage) {
|
|||
|
||||
func testLinks(t *testing.T, st CacheKeyStorage) {
|
||||
t.Parallel()
|
||||
cki := CacheKeyInfo{
|
||||
ID: "foo",
|
||||
Base: digest.FromBytes([]byte("foo")),
|
||||
}
|
||||
err := st.Set(cki)
|
||||
require.NoError(t, err)
|
||||
|
||||
cki2 := CacheKeyInfo{
|
||||
ID: "bar",
|
||||
Base: digest.FromBytes([]byte("bar")),
|
||||
}
|
||||
err = st.Set(cki2)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, st.Set(CacheKeyInfo{ID: "target0"}))
|
||||
require.NoError(t, st.Set(CacheKeyInfo{ID: "target0-second"}))
|
||||
require.NoError(t, st.Set(CacheKeyInfo{ID: "target1"}))
|
||||
require.NoError(t, st.Set(CacheKeyInfo{ID: "target1-second"}))
|
||||
|
||||
l0 := CacheInfoLink{
|
||||
Input: 0, Output: 1, Digest: digest.FromBytes([]byte(">target0")),
|
||||
}
|
||||
err = st.AddLink(cki.ID, l0, "target0")
|
||||
err := st.AddLink("foo", l0, "target0")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddLink(cki2.ID, l0, "target0-second")
|
||||
err = st.AddLink("bar", l0, "target0-second")
|
||||
require.NoError(t, err)
|
||||
|
||||
m := map[string]struct{}{}
|
||||
err = st.WalkLinks(cki.ID, l0, func(id string) error {
|
||||
err = st.WalkLinks("foo", l0, func(id string) error {
|
||||
m[id] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
|
@ -175,18 +124,18 @@ func testLinks(t *testing.T, st CacheKeyStorage) {
|
|||
Input: 0, Output: 1, Digest: digest.FromBytes([]byte(">target1")),
|
||||
}
|
||||
m = map[string]struct{}{}
|
||||
err = st.WalkLinks(cki.ID, l1, func(id string) error {
|
||||
err = st.WalkLinks("foo", l1, func(id string) error {
|
||||
m[id] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(m), 0)
|
||||
|
||||
err = st.AddLink(cki.ID, l1, "target1")
|
||||
err = st.AddLink("foo", l1, "target1")
|
||||
require.NoError(t, err)
|
||||
|
||||
m = map[string]struct{}{}
|
||||
err = st.WalkLinks(cki.ID, l1, func(id string) error {
|
||||
err = st.WalkLinks("foo", l1, func(id string) error {
|
||||
m[id] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
|
@ -196,11 +145,11 @@ func testLinks(t *testing.T, st CacheKeyStorage) {
|
|||
_, ok = m["target1"]
|
||||
require.True(t, ok)
|
||||
|
||||
err = st.AddLink(cki.ID, l1, "target1-second")
|
||||
err = st.AddLink("foo", l1, "target1-second")
|
||||
require.NoError(t, err)
|
||||
|
||||
m = map[string]struct{}{}
|
||||
err = st.WalkLinks(cki.ID, l1, func(id string) error {
|
||||
err = st.WalkLinks("foo", l1, func(id string) error {
|
||||
m[id] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
|
@ -214,20 +163,14 @@ func testLinks(t *testing.T, st CacheKeyStorage) {
|
|||
|
||||
func testResultReleaseSingleLevel(t *testing.T, st CacheKeyStorage) {
|
||||
t.Parallel()
|
||||
cki := CacheKeyInfo{
|
||||
ID: "foo",
|
||||
Base: digest.FromBytes([]byte("foo")),
|
||||
}
|
||||
err := st.Set(cki)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(cki.ID, CacheResult{
|
||||
err := st.AddResult("foo", CacheResult{
|
||||
ID: "foo0",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(cki.ID, CacheResult{
|
||||
err = st.AddResult("foo", CacheResult{
|
||||
ID: "foo1",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
@ -256,35 +199,18 @@ func testResultReleaseSingleLevel(t *testing.T, st CacheKeyStorage) {
|
|||
})
|
||||
|
||||
require.Equal(t, len(m), 0)
|
||||
|
||||
_, err = st.Get("foo")
|
||||
require.Error(t, err)
|
||||
require.Error(t, errors.Cause(err), ErrNotFound)
|
||||
}
|
||||
|
||||
func testResultReleaseMultiLevel(t *testing.T, st CacheKeyStorage) {
|
||||
t.Parallel()
|
||||
cki := CacheKeyInfo{
|
||||
ID: "foo",
|
||||
Base: digest.FromBytes([]byte("foo")),
|
||||
}
|
||||
err := st.Set(cki)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(cki.ID, CacheResult{
|
||||
err := st.AddResult("foo", CacheResult{
|
||||
ID: "foo-result",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
sub0 := CacheKeyInfo{
|
||||
ID: "sub0",
|
||||
Base: digest.FromBytes([]byte("sub0")),
|
||||
}
|
||||
err = st.Set(sub0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(sub0.ID, CacheResult{
|
||||
err = st.AddResult("sub0", CacheResult{
|
||||
ID: "sub0-result",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
@ -293,23 +219,16 @@ func testResultReleaseMultiLevel(t *testing.T, st CacheKeyStorage) {
|
|||
l0 := CacheInfoLink{
|
||||
Input: 0, Output: 1, Digest: digest.FromBytes([]byte("to-sub0")),
|
||||
}
|
||||
err = st.AddLink(cki.ID, l0, "sub0")
|
||||
err = st.AddLink("foo", l0, "sub0")
|
||||
require.NoError(t, err)
|
||||
|
||||
sub1 := CacheKeyInfo{
|
||||
ID: "sub1",
|
||||
Base: digest.FromBytes([]byte("sub1")),
|
||||
}
|
||||
err = st.Set(sub1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddResult(sub1.ID, CacheResult{
|
||||
err = st.AddResult("sub1", CacheResult{
|
||||
ID: "sub1-result",
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.AddLink(cki.ID, l0, "sub1")
|
||||
err = st.AddLink("foo", l0, "sub1")
|
||||
require.NoError(t, err)
|
||||
|
||||
// delete one sub doesn't delete parent
|
||||
|
@ -328,9 +247,7 @@ func testResultReleaseMultiLevel(t *testing.T, st CacheKeyStorage) {
|
|||
_, ok := m["foo-result"]
|
||||
require.True(t, ok)
|
||||
|
||||
_, err = st.Get("sub0")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, errors.Cause(err), ErrNotFound)
|
||||
require.False(t, st.Exists("sub0"))
|
||||
|
||||
m = map[string]struct{}{}
|
||||
err = st.WalkLinks("foo", l0, func(id string) error {
|
||||
|
@ -348,8 +265,7 @@ func testResultReleaseMultiLevel(t *testing.T, st CacheKeyStorage) {
|
|||
err = st.Release("foo-result")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = st.Get("foo")
|
||||
require.NoError(t, err)
|
||||
require.True(t, st.Exists("foo"))
|
||||
|
||||
m = map[string]struct{}{}
|
||||
err = st.WalkResults("foo", func(res CacheResult) error {
|
||||
|
@ -373,13 +289,8 @@ func testResultReleaseMultiLevel(t *testing.T, st CacheKeyStorage) {
|
|||
err = st.Release("sub1-result")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = st.Get("sub1")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, errors.Cause(err), ErrNotFound)
|
||||
|
||||
_, err = st.Get("foo")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, errors.Cause(err), ErrNotFound)
|
||||
require.False(t, st.Exists("sub1"))
|
||||
require.False(t, st.Exists("foo"))
|
||||
}
|
||||
|
||||
func getFunctionName(i interface{}) string {
|
||||
|
|
|
@ -22,37 +22,28 @@ type inMemoryStore struct {
|
|||
}
|
||||
|
||||
type inMemoryKey struct {
|
||||
CacheKeyInfo
|
||||
|
||||
id string
|
||||
results map[string]CacheResult
|
||||
links map[CacheInfoLink]map[string]struct{}
|
||||
backlinks map[string]struct{}
|
||||
}
|
||||
|
||||
func (s *inMemoryStore) Get(id string) (CacheKeyInfo, error) {
|
||||
func (s *inMemoryStore) Exists(id string) bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
k, ok := s.byID[id]
|
||||
if !ok {
|
||||
return CacheKeyInfo{}, errors.WithStack(ErrNotFound)
|
||||
if k, ok := s.byID[id]; ok {
|
||||
return len(k.links) > 0
|
||||
}
|
||||
return k.CacheKeyInfo, nil
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *inMemoryStore) Set(info CacheKeyInfo) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
k, ok := s.byID[info.ID]
|
||||
if !ok {
|
||||
k = &inMemoryKey{
|
||||
func newKey(id string) *inMemoryKey {
|
||||
return &inMemoryKey{
|
||||
results: map[string]CacheResult{},
|
||||
links: map[CacheInfoLink]map[string]struct{}{},
|
||||
backlinks: map[string]struct{}{},
|
||||
id: id,
|
||||
}
|
||||
s.byID[info.ID] = k
|
||||
}
|
||||
k.CacheKeyInfo = info
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *inMemoryStore) Walk(fn func(string) error) error {
|
||||
|
@ -112,7 +103,8 @@ func (s *inMemoryStore) AddResult(id string, res CacheResult) error {
|
|||
defer s.mu.Unlock()
|
||||
k, ok := s.byID[id]
|
||||
if !ok {
|
||||
return errors.Wrapf(ErrNotFound, "no such key %s", id)
|
||||
k = newKey(id)
|
||||
s.byID[id] = k
|
||||
}
|
||||
k.results[res.ID] = res
|
||||
m, ok := s.byResult[res.ID]
|
||||
|
@ -161,7 +153,7 @@ func (s *inMemoryStore) emptyBranchWithParents(k *inMemoryKey) {
|
|||
continue
|
||||
}
|
||||
for l := range p.links {
|
||||
delete(p.links[l], k.ID)
|
||||
delete(p.links[l], k.id)
|
||||
if len(p.links[l]) == 0 {
|
||||
delete(p.links, l)
|
||||
}
|
||||
|
@ -169,7 +161,7 @@ func (s *inMemoryStore) emptyBranchWithParents(k *inMemoryKey) {
|
|||
s.emptyBranchWithParents(p)
|
||||
}
|
||||
|
||||
delete(s.byID, k.ID)
|
||||
delete(s.byID, k.id)
|
||||
}
|
||||
|
||||
func (s *inMemoryStore) AddLink(id string, link CacheInfoLink, target string) error {
|
||||
|
@ -177,11 +169,13 @@ func (s *inMemoryStore) AddLink(id string, link CacheInfoLink, target string) er
|
|||
defer s.mu.Unlock()
|
||||
k, ok := s.byID[id]
|
||||
if !ok {
|
||||
return errors.Wrapf(ErrNotFound, "no such key %s", id)
|
||||
k = newKey(id)
|
||||
s.byID[id] = k
|
||||
}
|
||||
k2, ok := s.byID[target]
|
||||
if !ok {
|
||||
return errors.Wrapf(ErrNotFound, "no such key %s", target)
|
||||
k2 = newKey(target)
|
||||
s.byID[target] = k2
|
||||
}
|
||||
m, ok := k.links[link]
|
||||
if !ok {
|
||||
|
@ -199,7 +193,7 @@ func (s *inMemoryStore) WalkLinks(id string, link CacheInfoLink, fn func(id stri
|
|||
k, ok := s.byID[id]
|
||||
if !ok {
|
||||
s.mu.RUnlock()
|
||||
return errors.Wrapf(ErrNotFound, "no such key %s", id)
|
||||
return nil
|
||||
}
|
||||
var links []string
|
||||
for target := range k.links[link] {
|
||||
|
|
Loading…
Reference in New Issue