diff --git a/solver-next/boltdbcachestorage/storage.go b/solver-next/boltdbcachestorage/storage.go index 8a5134d8..c4083043 100644 --- a/solver-next/boltdbcachestorage/storage.go +++ b/solver-next/boltdbcachestorage/storage.go @@ -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 { - ids = append(ids, string(k)) - return nil - }) + 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 { diff --git a/solver-next/cache.go b/solver-next/cache.go index 704e49fd..5e1ae469 100644 --- a/solver-next/cache.go +++ b/solver-next/cache.go @@ -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 + 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, - manager: c, - CacheKey: NewCacheKey("", 0, nil), - deps: deps, + 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 { diff --git a/solver-next/cachestorage.go b/solver-next/cachestorage.go index 84768d1c..59497a04 100644 --- a/solver-next/cachestorage.go +++ b/solver-next/cachestorage.go @@ -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 diff --git a/solver-next/cachestorage_testsuite.go b/solver-next/cachestorage_testsuite.go index 7ef10cb3..0ff09798 100644 --- a/solver-next/cachestorage_testsuite.go +++ b/solver-next/cachestorage_testsuite.go @@ -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 { diff --git a/solver-next/memorycachestorage.go b/solver-next/memorycachestorage.go index 5ea966ca..cce8e090 100644 --- a/solver-next/memorycachestorage.go +++ b/solver-next/memorycachestorage.go @@ -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{ - results: map[string]CacheResult{}, - links: map[CacheInfoLink]map[string]struct{}{}, - backlinks: map[string]struct{}{}, - } - s.byID[info.ID] = k +func newKey(id string) *inMemoryKey { + return &inMemoryKey{ + results: map[string]CacheResult{}, + links: map[CacheInfoLink]map[string]struct{}{}, + backlinks: map[string]struct{}{}, + id: id, } - 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] {