Commit 68d0beb8 authored by Brian Tiger Chow's avatar Brian Tiger Chow

feat(blockstore) write cache

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

License: MIT
Signed-off-by: default avatarBrian Tiger Chow <brian@perfmode.com>
parent 75f682be
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)
}
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()
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment