-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
blockstore: extract ARC cache from Bloom cache #3026
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package blockstore | ||
|
||
import ( | ||
"github.com/ipfs/go-ipfs/blocks" | ||
key "github.com/ipfs/go-ipfs/blocks/key" | ||
ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" | ||
lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" | ||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" | ||
) | ||
|
||
type arccache struct { | ||
arc *lru.ARCCache | ||
blockstore Blockstore | ||
} | ||
|
||
func arcCached(bs Blockstore, lruSize int) (*arccache, error) { | ||
arc, err := lru.NewARC(lruSize) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &arccache{arc: arc, blockstore: bs}, nil | ||
} | ||
|
||
func (b *arccache) DeleteBlock(k key.Key) error { | ||
if has, ok := b.hasCached(k); ok && !has { | ||
return ErrNotFound | ||
} | ||
|
||
b.arc.Remove(k) // Invalidate cache before deleting. | ||
err := b.blockstore.DeleteBlock(k) | ||
switch err { | ||
case nil, ds.ErrNotFound, ErrNotFound: | ||
b.arc.Add(k, false) | ||
return nil | ||
default: | ||
return err | ||
} | ||
} | ||
|
||
// if ok == false has is inconclusive | ||
// if ok == true then has respons to question: is it contained | ||
func (b *arccache) hasCached(k key.Key) (has bool, ok bool) { | ||
if k == "" { | ||
// Return cache invalid so the call to blockstore happens | ||
// in case of invalid key and correct error is created. | ||
return false, false | ||
} | ||
|
||
h, ok := b.arc.Get(k) | ||
if ok { | ||
return h.(bool), true | ||
} | ||
return false, false | ||
} | ||
|
||
func (b *arccache) Has(k key.Key) (bool, error) { | ||
if has, ok := b.hasCached(k); ok { | ||
return has, nil | ||
} | ||
|
||
res, err := b.blockstore.Has(k) | ||
if err == nil { | ||
b.arc.Add(k, res) | ||
} | ||
return res, err | ||
} | ||
|
||
func (b *arccache) Get(k key.Key) (blocks.Block, error) { | ||
if has, ok := b.hasCached(k); ok && !has { | ||
return nil, ErrNotFound | ||
} | ||
|
||
bl, err := b.blockstore.Get(k) | ||
if bl == nil && err == ErrNotFound { | ||
b.arc.Add(k, false) | ||
} else if bl != nil { | ||
b.arc.Add(k, true) | ||
} | ||
return bl, err | ||
} | ||
|
||
func (b *arccache) Put(bl blocks.Block) error { | ||
if has, ok := b.hasCached(bl.Key()); ok && has { | ||
return nil | ||
} | ||
|
||
err := b.blockstore.Put(bl) | ||
if err == nil { | ||
b.arc.Add(bl.Key(), true) | ||
} | ||
return err | ||
} | ||
|
||
func (b *arccache) PutMany(bs []blocks.Block) error { | ||
var good []blocks.Block | ||
for _, block := range bs { | ||
if has, ok := b.hasCached(block.Key()); !ok || (ok && !has) { | ||
good = append(good, block) | ||
} | ||
} | ||
err := b.blockstore.PutMany(bs) | ||
if err != nil { | ||
return err | ||
} | ||
for _, block := range bs { | ||
b.arc.Add(block.Key(), true) | ||
} | ||
return nil | ||
} | ||
|
||
func (b *arccache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { | ||
return b.blockstore.AllKeysChan(ctx) | ||
} | ||
|
||
func (b *arccache) GCLock() Unlocker { | ||
return b.blockstore.(GCBlockstore).GCLock() | ||
} | ||
|
||
func (b *arccache) PinLock() Unlocker { | ||
return b.blockstore.(GCBlockstore).PinLock() | ||
} | ||
|
||
func (b *arccache) GCRequested() bool { | ||
return b.blockstore.(GCBlockstore).GCRequested() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package blockstore | ||
|
||
import ( | ||
"github.com/ipfs/go-ipfs/blocks" | ||
"testing" | ||
|
||
ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" | ||
syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" | ||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" | ||
) | ||
|
||
func testArcCached(bs GCBlockstore, ctx context.Context) (*arccache, error) { | ||
if ctx == nil { | ||
ctx = context.TODO() | ||
} | ||
opts := DefaultCacheOpts() | ||
opts.HasBloomFilterSize = 0 | ||
opts.HasBloomFilterHashes = 0 | ||
bbs, err := CachedBlockstore(bs, ctx, opts) | ||
if err == nil { | ||
return bbs.(*arccache), nil | ||
} else { | ||
return nil, err | ||
} | ||
} | ||
|
||
func TestRemoveCacheEntryOnDelete(t *testing.T) { | ||
b := blocks.NewBlock([]byte("foo")) | ||
cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} | ||
bs := NewBlockstore(syncds.MutexWrap(cd)) | ||
cachedbs, err := testArcCached(bs, nil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
cachedbs.Put(b) | ||
|
||
cd.Lock() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whats with locking here? you dont need to lock around a variable instantiation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah why is this lock here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The race detector was giving me an error otherwise for some reason, I can tinker with it more to find out the exact reason. Maybe line 47 should be locked instead. |
||
writeHitTheDatastore := false | ||
cd.Unlock() | ||
|
||
cd.SetFunc(func() { | ||
writeHitTheDatastore = true | ||
}) | ||
|
||
cachedbs.DeleteBlock(b.Key()) | ||
cachedbs.Put(b) | ||
if !writeHitTheDatastore { | ||
t.Fail() | ||
} | ||
} | ||
|
||
func TestElideDuplicateWrite(t *testing.T) { | ||
cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} | ||
bs := NewBlockstore(syncds.MutexWrap(cd)) | ||
cachedbs, err := testArcCached(bs, nil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
b1 := blocks.NewBlock([]byte("foo")) | ||
|
||
cachedbs.Put(b1) | ||
cd.SetFunc(func() { | ||
t.Fatal("write hit the datastore") | ||
}) | ||
cachedbs.Put(b1) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newArcCacheDS