feat: chain: make fetching tipset by height 1000x faster

This commit is contained in:
Aayush 2023-03-08 11:36:17 -05:00
parent faedc12531
commit 90c89288b6

View File

@ -4,8 +4,8 @@ import (
"context" "context"
"os" "os"
"strconv" "strconv"
"sync"
lru "github.com/hashicorp/golang-lru"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
@ -13,7 +13,7 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
var DefaultChainIndexCacheSize = 32 << 10 var DefaultChainIndexCacheSize = 32 << 15
func init() { func init() {
if s := os.Getenv("LOTUS_CHAIN_INDEX_CACHE"); s != "" { if s := os.Getenv("LOTUS_CHAIN_INDEX_CACHE"); s != "" {
@ -27,7 +27,8 @@ func init() {
} }
type ChainIndex struct { type ChainIndex struct {
skipCache *lru.ARCCache indexCacheLk sync.Mutex
indexCache map[types.TipSetKey]*lbEntry
loadTipSet loadTipSetFunc loadTipSet loadTipSetFunc
@ -36,17 +37,14 @@ type ChainIndex struct {
type loadTipSetFunc func(context.Context, types.TipSetKey) (*types.TipSet, error) type loadTipSetFunc func(context.Context, types.TipSetKey) (*types.TipSet, error)
func NewChainIndex(lts loadTipSetFunc) *ChainIndex { func NewChainIndex(lts loadTipSetFunc) *ChainIndex {
sc, _ := lru.NewARC(DefaultChainIndexCacheSize)
return &ChainIndex{ return &ChainIndex{
skipCache: sc, indexCache: make(map[types.TipSetKey]*lbEntry, DefaultChainIndexCacheSize),
loadTipSet: lts, loadTipSet: lts,
skipLength: 20, skipLength: 20,
} }
} }
type lbEntry struct { type lbEntry struct {
ts *types.TipSet
parentHeight abi.ChainEpoch
targetHeight abi.ChainEpoch targetHeight abi.ChainEpoch
target types.TipSetKey target types.TipSetKey
} }
@ -58,25 +56,36 @@ func (ci *ChainIndex) GetTipsetByHeight(ctx context.Context, from *types.TipSet,
rounded, err := ci.roundDown(ctx, from) rounded, err := ci.roundDown(ctx, from)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to round down: %w", err)
} }
ci.indexCacheLk.Lock()
defer ci.indexCacheLk.Unlock()
cur := rounded.Key() cur := rounded.Key()
for { for {
cval, ok := ci.skipCache.Get(cur) lbe, ok := ci.indexCache[cur]
if !ok { if !ok {
fc, err := ci.fillCache(ctx, cur) fc, err := ci.fillCache(ctx, cur)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to fill cache: %w", err)
} }
cval = fc lbe = fc
} }
lbe := cval.(*lbEntry) if to == lbe.targetHeight {
if lbe.ts.Height() == to || lbe.parentHeight < to { ts, err := ci.loadTipSet(ctx, lbe.target)
return lbe.ts, nil if err != nil {
} else if to > lbe.targetHeight { return nil, xerrors.Errorf("failed to load tipset: %w", err)
return ci.walkBack(ctx, lbe.ts, to) }
return ts, nil
}
if to > lbe.targetHeight {
ts, err := ci.loadTipSet(ctx, cur)
if err != nil {
return nil, xerrors.Errorf("failed to load tipset: %w", err)
}
return ci.walkBack(ctx, ts, to)
} }
cur = lbe.target cur = lbe.target
@ -87,16 +96,17 @@ func (ci *ChainIndex) GetTipsetByHeightWithoutCache(ctx context.Context, from *t
return ci.walkBack(ctx, from, to) return ci.walkBack(ctx, from, to)
} }
// Caller must hold indexCacheLk
func (ci *ChainIndex) fillCache(ctx context.Context, tsk types.TipSetKey) (*lbEntry, error) { func (ci *ChainIndex) fillCache(ctx context.Context, tsk types.TipSetKey) (*lbEntry, error) {
ts, err := ci.loadTipSet(ctx, tsk) ts, err := ci.loadTipSet(ctx, tsk)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to load tipset: %w", err)
} }
if ts.Height() == 0 { if ts.Height() == 0 {
return &lbEntry{ return &lbEntry{
ts: ts, targetHeight: 0,
parentHeight: 0, target: tsk,
}, nil }, nil
} }
@ -124,12 +134,10 @@ func (ci *ChainIndex) fillCache(ctx context.Context, tsk types.TipSetKey) (*lbEn
} }
lbe := &lbEntry{ lbe := &lbEntry{
ts: ts,
parentHeight: parent.Height(),
targetHeight: skipTarget.Height(), targetHeight: skipTarget.Height(),
target: skipTarget.Key(), target: skipTarget.Key(),
} }
ci.skipCache.Add(tsk, lbe) ci.indexCache[tsk] = lbe
return lbe, nil return lbe, nil
} }
@ -144,7 +152,7 @@ func (ci *ChainIndex) roundDown(ctx context.Context, ts *types.TipSet) (*types.T
rounded, err := ci.walkBack(ctx, ts, target) rounded, err := ci.walkBack(ctx, ts, target)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to walk back: %w", err)
} }
return rounded, nil return rounded, nil
@ -164,7 +172,7 @@ func (ci *ChainIndex) walkBack(ctx context.Context, from *types.TipSet, to abi.C
for { for {
pts, err := ci.loadTipSet(ctx, ts.Parents()) pts, err := ci.loadTipSet(ctx, ts.Parents())
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to load tipset: %w", err)
} }
if to > pts.Height() { if to > pts.Height() {