add the viewable trait to our gazillion blockstores.

This commit is contained in:
Raúl Kripalani 2020-11-10 12:40:32 +00:00
parent 3577300aee
commit 54bf7c99d7
5 changed files with 105 additions and 2 deletions

View File

@ -70,6 +70,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
}
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
var _ blockstore.Viewer = (*gasChargingBlocks)(nil)
type gasChargingBlocks struct {
chargeGas func(GasCharge)
@ -77,6 +78,24 @@ type gasChargingBlocks struct {
under cbor.IpldBlockstore
}
func (bs *gasChargingBlocks) View(c cid.Cid, cb func([]byte) error) error {
if v, ok := bs.under.(blockstore.Viewer); ok {
bs.chargeGas(bs.pricelist.OnIpldGet())
return v.View(c, func(b []byte) error {
// we have successfully retrieved the value; charge for it, even if the user-provided function fails.
bs.chargeGas(newGasCharge("OnIpldViewEnd", 0, 0).WithExtra(len(b)))
bs.chargeGas(gasOnActorExec)
return cb(b)
})
}
// the underlying blockstore doesn't implement the viewer interface, fall back to normal Get behaviour.
blk, err := bs.Get(c)
if err != nil && blk != nil {
return cb(blk.RawData())
}
return err
}
func (bs *gasChargingBlocks) Get(c cid.Cid) (block.Block, error) {
bs.chargeGas(bs.pricelist.OnIpldGet())
blk, err := bs.under.Get(c)

View File

@ -0,0 +1,29 @@
package blockstore
import (
"sync"
"testing"
)
func BenchmarkCast(b *testing.B) {
temp := Blockstore(NewTemporary())
for i := 0; i < b.N; i++ {
if v, ok := temp.(Viewer); ok {
_ = v
}
}
}
func BenchmarkOnce(b *testing.B) {
var viewer Viewer
var once sync.Once
temp := Blockstore(NewTemporary())
for i := 0; i < b.N; i++ {
once.Do(func() {
if v, ok := temp.(Viewer); ok {
viewer = v
}
})
_ = viewer
}
}

View File

@ -8,16 +8,27 @@ import (
blockstore "github.com/ipfs/go-ipfs-blockstore"
)
// MemStore is a terminal blockstore that keeps blocks in memory.
type MemStore map[cid.Cid]blocks.Block
func (m MemStore) DeleteBlock(k cid.Cid) error {
delete(m, k)
return nil
}
func (m MemStore) Has(k cid.Cid) (bool, error) {
_, ok := m[k]
return ok, nil
}
func (m MemStore) View(k cid.Cid, callback func([]byte) error) error {
b, ok := m[k]
if !ok {
return blockstore.ErrNotFound
}
return callback(b.RawData())
}
func (m MemStore) Get(k cid.Cid) (blocks.Block, error) {
b, ok := m[k]
if !ok {

View File

@ -8,6 +8,8 @@ import (
"github.com/ipfs/go-cid"
)
// SyncStore is a terminal blockstore that is a synchronized version
// of MemStore.
type SyncStore struct {
mu sync.RWMutex
bs MemStore // specifically use a memStore to save indirection overhead.
@ -18,11 +20,20 @@ func (m *SyncStore) DeleteBlock(k cid.Cid) error {
defer m.mu.Unlock()
return m.bs.DeleteBlock(k)
}
func (m *SyncStore) Has(k cid.Cid) (bool, error) {
m.mu.RLock()
defer m.mu.RUnlock()
return m.bs.Has(k)
}
func (m *SyncStore) View(k cid.Cid, callback func([]byte) error) error {
m.mu.RLock()
defer m.mu.RUnlock()
return m.bs.View(k, callback)
}
func (m *SyncStore) Get(k cid.Cid) (blocks.Block, error) {
m.mu.RLock()
defer m.mu.RUnlock()

View File

@ -16,6 +16,9 @@ var log = logging.Logger("bufbs")
type BufferedBS struct {
read bstore.Blockstore
write bstore.Blockstore
readviewer bstore.Viewer
writeviewer bstore.Viewer
}
func NewBufferedBstore(base bstore.Blockstore) *BufferedBS {
@ -27,10 +30,20 @@ func NewBufferedBstore(base bstore.Blockstore) *BufferedBS {
buf = bstore.NewTemporary()
}
return &BufferedBS{
bs := &BufferedBS{
read: base,
write: buf,
}
if v, ok := base.(bstore.Viewer); ok {
bs.readviewer = v
}
if v, ok := buf.(bstore.Viewer); ok {
bs.writeviewer = v
}
if (bs.writeviewer == nil) != (bs.readviewer == nil) {
log.Warnf("one of the stores is not viewable; running less efficiently")
}
return bs
}
func NewTieredBstore(r bstore.Blockstore, w bstore.Blockstore) *BufferedBS {
@ -40,7 +53,8 @@ func NewTieredBstore(r bstore.Blockstore, w bstore.Blockstore) *BufferedBS {
}
}
var _ (bstore.Blockstore) = &BufferedBS{}
var _ bstore.Blockstore = (*BufferedBS)(nil)
var _ bstore.Viewer = (*BufferedBS)(nil)
func (bs *BufferedBS) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) {
a, err := bs.read.AllKeysChan(ctx)
@ -93,6 +107,25 @@ func (bs *BufferedBS) DeleteBlock(c cid.Cid) error {
return bs.write.DeleteBlock(c)
}
func (bs *BufferedBS) View(c cid.Cid, callback func([]byte) error) error {
if bs.writeviewer == nil || bs.readviewer == nil {
// none of the stores supports Viewer, just fall back to pure Get behaviour.
blk, err := bs.Get(c)
if err != nil {
return err
}
return callback(blk.RawData())
}
// both stores are viewable.
if err := bs.writeviewer.View(c, callback); err == bstore.ErrNotFound {
// not found in write blockstore; fall through.
} else {
return err // propagate errors, or nil, i.e. found.
}
return bs.readviewer.View(c, callback)
}
func (bs *BufferedBS) Get(c cid.Cid) (block.Block, error) {
if out, err := bs.write.Get(c); err != nil {
if err != bstore.ErrNotFound {