ipld-eth-server/vendor/github.com/ipfs/go-ipfs-blockstore/arc_cache.go
Elizabeth Engelman 36533f7c3f Update vendor directory and make necessary code changes
Fixes for new geth version
2019-09-25 16:32:27 -05:00

183 lines
4.2 KiB
Go

package blockstore
import (
"context"
lru "github.com/hashicorp/golang-lru"
blocks "github.com/ipfs/go-block-format"
cid "github.com/ipfs/go-cid"
metrics "github.com/ipfs/go-metrics-interface"
)
type cacheHave bool
type cacheSize int
// arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for
// block Cids. This provides block access-time improvements, allowing
// to short-cut many searches without query-ing the underlying datastore.
type arccache struct {
arc *lru.ARCCache
blockstore Blockstore
hits metrics.Counter
total metrics.Counter
}
func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, error) {
arc, err := lru.NewARC(lruSize)
if err != nil {
return nil, err
}
c := &arccache{arc: arc, blockstore: bs}
c.hits = metrics.NewCtx(ctx, "arc.hits_total", "Number of ARC cache hits").Counter()
c.total = metrics.NewCtx(ctx, "arc_total", "Total number of ARC cache requests").Counter()
return c, nil
}
func (b *arccache) DeleteBlock(k cid.Cid) 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, ErrNotFound:
b.cacheHave(k, false)
return err
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 cid.Cid) (has bool, size int, ok bool) {
b.total.Inc()
if !k.Defined() {
log.Error("undefined cid in arccache")
// Return cache invalid so the call to blockstore happens
// in case of invalid key and correct error is created.
return false, -1, false
}
h, ok := b.arc.Get(k.KeyString())
if ok {
b.hits.Inc()
switch h := h.(type) {
case cacheHave:
return bool(h), -1, true
case cacheSize:
return true, int(h), true
}
}
return false, -1, false
}
func (b *arccache) Has(k cid.Cid) (bool, error) {
if has, _, ok := b.hasCached(k); ok {
return has, nil
}
has, err := b.blockstore.Has(k)
if err != nil {
return false, err
}
b.cacheHave(k, has)
return has, nil
}
func (b *arccache) GetSize(k cid.Cid) (int, error) {
if has, blockSize, ok := b.hasCached(k); ok {
if has {
return blockSize, nil
}
return -1, ErrNotFound
}
blockSize, err := b.blockstore.GetSize(k)
if err == ErrNotFound {
b.cacheHave(k, false)
} else if err == nil {
b.cacheSize(k, blockSize)
}
return blockSize, err
}
func (b *arccache) Get(k cid.Cid) (blocks.Block, error) {
if !k.Defined() {
log.Error("undefined cid in arc cache")
return nil, ErrNotFound
}
if has, _, ok := b.hasCached(k); ok && !has {
return nil, ErrNotFound
}
bl, err := b.blockstore.Get(k)
if bl == nil && err == ErrNotFound {
b.cacheHave(k, false)
} else if bl != nil {
b.cacheSize(k, len(bl.RawData()))
}
return bl, err
}
func (b *arccache) Put(bl blocks.Block) error {
if has, _, ok := b.hasCached(bl.Cid()); ok && has {
return nil
}
err := b.blockstore.Put(bl)
if err == nil {
b.cacheSize(bl.Cid(), len(bl.RawData()))
}
return err
}
func (b *arccache) PutMany(bs []blocks.Block) error {
var good []blocks.Block
for _, block := range bs {
// call put on block if result is inconclusive or we are sure that
// the block isn't in storage
if has, _, ok := b.hasCached(block.Cid()); !ok || (ok && !has) {
good = append(good, block)
}
}
err := b.blockstore.PutMany(good)
if err != nil {
return err
}
for _, block := range good {
b.cacheSize(block.Cid(), len(block.RawData()))
}
return nil
}
func (b *arccache) HashOnRead(enabled bool) {
b.blockstore.HashOnRead(enabled)
}
func (b *arccache) cacheHave(c cid.Cid, have bool) {
b.arc.Add(c.KeyString(), cacheHave(have))
}
func (b *arccache) cacheSize(c cid.Cid, blockSize int) {
b.arc.Add(c.KeyString(), cacheSize(blockSize))
}
func (b *arccache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, 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()
}