add the viewable trait to our gazillion blockstores.
This commit is contained in:
parent
3577300aee
commit
54bf7c99d7
@ -70,6 +70,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
|
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
|
||||||
|
var _ blockstore.Viewer = (*gasChargingBlocks)(nil)
|
||||||
|
|
||||||
type gasChargingBlocks struct {
|
type gasChargingBlocks struct {
|
||||||
chargeGas func(GasCharge)
|
chargeGas func(GasCharge)
|
||||||
@ -77,6 +78,24 @@ type gasChargingBlocks struct {
|
|||||||
under cbor.IpldBlockstore
|
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) {
|
func (bs *gasChargingBlocks) Get(c cid.Cid) (block.Block, error) {
|
||||||
bs.chargeGas(bs.pricelist.OnIpldGet())
|
bs.chargeGas(bs.pricelist.OnIpldGet())
|
||||||
blk, err := bs.under.Get(c)
|
blk, err := bs.under.Get(c)
|
||||||
|
29
lib/blockstore/blockstore_bench_test.go
Normal file
29
lib/blockstore/blockstore_bench_test.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -8,16 +8,27 @@ import (
|
|||||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MemStore is a terminal blockstore that keeps blocks in memory.
|
||||||
type MemStore map[cid.Cid]blocks.Block
|
type MemStore map[cid.Cid]blocks.Block
|
||||||
|
|
||||||
func (m MemStore) DeleteBlock(k cid.Cid) error {
|
func (m MemStore) DeleteBlock(k cid.Cid) error {
|
||||||
delete(m, k)
|
delete(m, k)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MemStore) Has(k cid.Cid) (bool, error) {
|
func (m MemStore) Has(k cid.Cid) (bool, error) {
|
||||||
_, ok := m[k]
|
_, ok := m[k]
|
||||||
return ok, nil
|
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) {
|
func (m MemStore) Get(k cid.Cid) (blocks.Block, error) {
|
||||||
b, ok := m[k]
|
b, ok := m[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SyncStore is a terminal blockstore that is a synchronized version
|
||||||
|
// of MemStore.
|
||||||
type SyncStore struct {
|
type SyncStore struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
bs MemStore // specifically use a memStore to save indirection overhead.
|
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()
|
defer m.mu.Unlock()
|
||||||
return m.bs.DeleteBlock(k)
|
return m.bs.DeleteBlock(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SyncStore) Has(k cid.Cid) (bool, error) {
|
func (m *SyncStore) Has(k cid.Cid) (bool, error) {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
return m.bs.Has(k)
|
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) {
|
func (m *SyncStore) Get(k cid.Cid) (blocks.Block, error) {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
|
@ -16,6 +16,9 @@ var log = logging.Logger("bufbs")
|
|||||||
type BufferedBS struct {
|
type BufferedBS struct {
|
||||||
read bstore.Blockstore
|
read bstore.Blockstore
|
||||||
write bstore.Blockstore
|
write bstore.Blockstore
|
||||||
|
|
||||||
|
readviewer bstore.Viewer
|
||||||
|
writeviewer bstore.Viewer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBufferedBstore(base bstore.Blockstore) *BufferedBS {
|
func NewBufferedBstore(base bstore.Blockstore) *BufferedBS {
|
||||||
@ -27,10 +30,20 @@ func NewBufferedBstore(base bstore.Blockstore) *BufferedBS {
|
|||||||
buf = bstore.NewTemporary()
|
buf = bstore.NewTemporary()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BufferedBS{
|
bs := &BufferedBS{
|
||||||
read: base,
|
read: base,
|
||||||
write: buf,
|
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 {
|
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) {
|
func (bs *BufferedBS) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) {
|
||||||
a, err := bs.read.AllKeysChan(ctx)
|
a, err := bs.read.AllKeysChan(ctx)
|
||||||
@ -93,6 +107,25 @@ func (bs *BufferedBS) DeleteBlock(c cid.Cid) error {
|
|||||||
return bs.write.DeleteBlock(c)
|
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) {
|
func (bs *BufferedBS) Get(c cid.Cid) (block.Block, error) {
|
||||||
if out, err := bs.write.Get(c); err != nil {
|
if out, err := bs.write.Get(c); err != nil {
|
||||||
if err != bstore.ErrNotFound {
|
if err != bstore.ErrNotFound {
|
||||||
|
Loading…
Reference in New Issue
Block a user