1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-09-10 09:52:20 +08:00

feat(blockstore) write cache

vendors hashicorp/golang-lru dependency. (Mozilla Public License, version 2.0)

License: MIT
Signed-off-by: Brian Tiger Chow <brian@perfmode.com>
This commit is contained in:
Brian Tiger Chow
2014-11-30 21:25:46 -08:00
parent 670d0244b4
commit c40fe2af17
8 changed files with 729 additions and 0 deletions

View File

@ -0,0 +1,45 @@
package blockstore
import (
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru"
"github.com/jbenet/go-ipfs/blocks"
u "github.com/jbenet/go-ipfs/util"
)
// WriteCached returns a blockstore that caches up to |size| unique writes (bs.Put).
func WriteCached(bs Blockstore, size int) (Blockstore, error) {
c, err := lru.New(size)
if err != nil {
return nil, err
}
return &writecache{blockstore: bs, cache: c}, nil
}
type writecache struct {
cache *lru.Cache // pointer b/c Cache contains a Mutex as value (complicates copying)
blockstore Blockstore
}
func (w *writecache) DeleteBlock(k u.Key) error {
w.cache.Remove(k)
return w.blockstore.DeleteBlock(k)
}
func (w *writecache) Has(k u.Key) (bool, error) {
if _, ok := w.cache.Get(k); ok {
return true, nil
}
return w.blockstore.Has(k)
}
func (w *writecache) Get(k u.Key) (*blocks.Block, error) {
return w.blockstore.Get(k)
}
func (w *writecache) Put(b *blocks.Block) error {
if _, ok := w.cache.Get(b.Key()); ok {
return nil
}
w.cache.Add(b.Key(), struct{}{})
return w.blockstore.Put(b)
}

View File

@ -0,0 +1,88 @@
package blockstore
import (
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
syncds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync"
"github.com/jbenet/go-ipfs/blocks"
"testing"
)
func TestReturnsErrorWhenSizeNegative(t *testing.T) {
bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore()))
_, err := WriteCached(bs, -1)
if err != nil {
return
}
t.Fail()
}
func TestRemoveCacheEntryOnDelete(t *testing.T) {
b := blocks.NewBlock([]byte("foo"))
cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()}
bs := NewBlockstore(syncds.MutexWrap(cd))
cachedbs, err := WriteCached(bs, 1)
if err != nil {
t.Fatal(err)
}
cachedbs.Put(b)
writeHitTheDatastore := false
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 := WriteCached(bs, 1)
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)
}
type callbackDatastore struct {
f func()
ds ds.Datastore
}
func (c *callbackDatastore) SetFunc(f func()) { c.f = f }
func (c *callbackDatastore) Put(key ds.Key, value interface{}) (err error) {
c.f()
return c.ds.Put(key, value)
}
func (c *callbackDatastore) Get(key ds.Key) (value interface{}, err error) {
c.f()
return c.ds.Get(key)
}
func (c *callbackDatastore) Has(key ds.Key) (exists bool, err error) {
c.f()
return c.ds.Has(key)
}
func (c *callbackDatastore) Delete(key ds.Key) (err error) {
c.f()
return c.ds.Delete(key)
}
func (c *callbackDatastore) KeyList() ([]ds.Key, error) {
c.f()
return c.ds.KeyList()
}