Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ExecutionTrace cache #6000

Merged
merged 1 commit into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/submodule/syncer/syncer_submodule.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,11 @@ func NewSyncerSubmodule(ctx context.Context,
config.Repo().Config().FevmConfig.EnableEthRPC,
)

stmgr := statemanger.NewStateManger(chn.ChainReader, chn.MessageStore, nodeConsensus, rnd,
stmgr, err := statemanger.NewStateManger(chn.ChainReader, chn.MessageStore, nodeConsensus, rnd,
chn.Fork, gasPriceSchedule, chn.SystemCall, config.Repo().Config().NetworkParams.ActorDebugging)
if err != nil {
return nil, err
}

blkValid.Stmgr = stmgr
chn.Stmgr = stmgr
Expand Down
3 changes: 2 additions & 1 deletion pkg/chainsync/syncer/syncer_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ func TestLoadFork(t *testing.T) {
sel := &chain.FakeChainSelector{}

blockValidator := builder.FakeStateEvaluator()
stmgr := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), blockValidator, nil, nil, nil, nil, false)
stmgr, err := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), blockValidator, nil, nil, nil, nil, false)
require.NoError(t, err)

s, err := syncer.NewSyncer(stmgr, blockValidator, sel, builder.Store(),
builder.Mstore(), builder.BlockStore(), builder, clock.NewFake(time.Unix(1234567890, 0)), nil)
Expand Down
13 changes: 9 additions & 4 deletions pkg/chainsync/syncer/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@ func TestNoUncessesaryFetch(t *testing.T) {
// A new syncer unable to fetch blocks from the network can handle a tipset that's already
// in the bsstore and linked to genesis.
eval := builder.FakeStateEvaluator()
stmgr := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, nil, nil, nil, false)
stmgr, err := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, nil, nil, nil, false)
assert.NoError(t, err)

newSyncer, err := syncer.NewSyncer(stmgr,
eval,
&chain.FakeChainSelector{},
Expand Down Expand Up @@ -498,7 +500,9 @@ func TestSemanticallyBadTipSetFails(t *testing.T) {
eval := newPoisonValidator(t, 98, 99)
builder := chain.NewBuilder(t, address.Undef)

stmgr := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, nil, nil, nil, false)
stmgr, err := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, nil, nil, nil, false)
require.NoError(t, err)

builder, syncer := setupWithValidator(ctx, t, builder, stmgr, eval)

genesis := builder.Store().GetHead()
Expand Down Expand Up @@ -528,7 +532,7 @@ func TestSemanticallyBadTipSetFails(t *testing.T) {
Err: nil,
ChainInfo: *types.NewChainInfo("", "", link1),
}
err := syncer.HandleNewTipSet(ctx, target1)
err = syncer.HandleNewTipSet(ctx, target1)
require.Error(t, err)
assert.Contains(t, err.Error(), "val semantic fails")
}
Expand Down Expand Up @@ -577,7 +581,8 @@ func setup(ctx context.Context, t *testing.T) (*chain.Builder, *syncer.Syncer) {
builder := chain.NewBuilder(t, address.Undef)
eval := builder.FakeStateEvaluator()

stmgr := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, nil, nil, nil, false)
stmgr, err := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, nil, nil, nil, false)
require.NoError(t, err)

return setupWithValidator(ctx, t, builder, stmgr, eval)
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/messagepool/messagepool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,10 @@ func newWalletAndMpool(t *testing.T, tma *testMpoolAPI) (*wallet.Wallet, *Messag

builder := chain.NewBuilder(t, address.Undef)
eval := builder.FakeStateEvaluator()
stmgr := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, fork.NewMockFork(), nil, nil, false)
stmgr, err := statemanger.NewStateManger(builder.Store(), builder.MessageStore(), eval, nil, fork.NewMockFork(), nil, nil, false)
if err != nil {
t.Fatal(err)
}

mp, err := New(context.Background(), tma, stmgr, ds, config.NewDefaultConfig().NetworkParams, config.DefaultMessagePoolParam, "mptest", nil)
if err != nil {
Expand Down
58 changes: 56 additions & 2 deletions pkg/statemanger/state_manger.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ import (
"github.com/filecoin-project/venus/venus-shared/actors/builtin/paych"
blockstoreutil "github.com/filecoin-project/venus/venus-shared/blockstore"
"github.com/filecoin-project/venus/venus-shared/types"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
"go.opencensus.io/trace"
)

const execTraceCacheSize = 16

// stateManagerAPI defines the methods needed from StateManager
// todo remove this code and add private interface in market and paychanel package
type IStateManager interface {
Expand All @@ -42,6 +45,11 @@ type stateComputeResult struct {
stateRoot, receipt cid.Cid
}

type tipSetCacheEntry struct {
postStateRoot cid.Cid
invocTrace []*types.InvocResult
}

var _ IStateManager = &Stmgr{}

type Stmgr struct {
Expand All @@ -65,6 +73,13 @@ type Stmgr struct {
fStopLk sync.Mutex

log *logging.ZapEventLogger

// We keep a small cache for calls to ExecutionTrace which helps improve
// performance for node operators like exchanges and block explorers
execTraceCache *lru.ARCCache[types.TipSetKey, tipSetCacheEntry]
// We need a lock while making the copy as to prevent other callers
// overwrite the cache while making the copy
execTraceCacheLock sync.Mutex
}

func NewStateManger(cs *chain.Store,
Expand All @@ -75,7 +90,12 @@ func NewStateManger(cs *chain.Store,
gasSchedule *gas.PricesSchedule,
syscallsImpl vm.SyscallsImpl,
actorDebugging bool,
) *Stmgr {
) (*Stmgr, error) {
execTraceCache, err := lru.NewARC[types.TipSetKey, tipSetCacheEntry](execTraceCacheSize)
if err != nil {
return nil, err
}

return &Stmgr{
cs: cs,
ms: ms,
Expand All @@ -88,7 +108,8 @@ func NewStateManger(cs *chain.Store,
stCache: make(map[types.TipSetKey]stateComputeResult),
chsWorkingOn: make(map[types.TipSetKey]chan struct{}, 1),
actorDebugging: actorDebugging,
}
execTraceCache: execTraceCache,
}, nil
}

func (s *Stmgr) ResolveToDeterministicAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
Expand Down Expand Up @@ -447,6 +468,20 @@ func (s *Stmgr) Replay(ctx context.Context, ts *types.TipSet, msgCID cid.Cid) (*
}

func (s *Stmgr) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*types.InvocResult, error) {

tsKey := ts.Key()

// check if we have the trace for this tipset in the cache
s.execTraceCacheLock.Lock()
if entry, ok := s.execTraceCache.Get(tsKey); ok {
// we have to make a deep copy since caller can modify the invocTrace
// and we don't want that to change what we store in cache
invocTraceCopy := makeDeepCopy(entry.invocTrace)
s.execTraceCacheLock.Unlock()
return entry.postStateRoot, invocTraceCopy, nil
}
s.execTraceCacheLock.Unlock()

var invocTrace []*types.InvocResult

cb := func(mcid cid.Cid, msg *types.Message, ret *vm.Ret) error {
Expand Down Expand Up @@ -474,9 +509,28 @@ func (s *Stmgr) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid,
return cid.Undef, nil, err
}

invocTraceCopy := makeDeepCopy(invocTrace)

s.execTraceCacheLock.Lock()
s.execTraceCache.Add(tsKey, tipSetCacheEntry{st, invocTraceCopy})
s.execTraceCacheLock.Unlock()

return st, invocTrace, nil
}

func makeDeepCopy(invocTrace []*types.InvocResult) []*types.InvocResult {
c := make([]*types.InvocResult, len(invocTrace))
for i, ir := range invocTrace {
if ir == nil {
continue
}
tmp := *ir
c[i] = &tmp
}

return c
}

func MakeMsgGasCost(msg *types.Message, ret *vm.Ret) types.MsgGasCost {
return types.MsgGasCost{
Message: msg.Cid(),
Expand Down