diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index f85ff7c04..3c2644550 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -128,10 +128,17 @@ func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types } func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { + if entry, ok := sm.execTraceCache.Get(ts); ok { + return entry.cid, entry.invocTrace, nil + } + var invocTrace []*api.InvocResult st, err := sm.ExecutionTraceWithMonitor(ctx, ts, &InvocationTracer{trace: &invocTrace}) if err != nil { return cid.Undef, nil, err } + + sm.execTraceCache.Add(ts, tipSetCacheEntry{st, invocTrace}) + return st, invocTrace, nil } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 2d528c91b..5f0ad33fe 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -5,6 +5,7 @@ import ( "fmt" "sync" + lru "github.com/hashicorp/golang-lru/v2" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" cbor "github.com/ipfs/go-ipld-cbor" @@ -39,6 +40,8 @@ import ( const LookbackNoLimit = api.LookbackNoLimit const ReceiptAmtBitwidth = 3 +const execTraceCacheSize = 16 + var log = logging.Logger("statemgr") type StateManagerAPI interface { @@ -138,6 +141,10 @@ type StateManager struct { beacon beacon.Schedule msgIndex index.MsgIndex + + // 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.TipSet, tipSetCacheEntry] } // Caches a single state tree @@ -146,6 +153,11 @@ type treeCache struct { tree *state.StateTree } +type tipSetCacheEntry struct { + cid cid.Cid + invocTrace []*api.InvocResult +} + func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, us UpgradeSchedule, beacon beacon.Schedule, metadataDs dstore.Batching, msgIndex index.MsgIndex) (*StateManager, error) { // If we have upgrades, make sure they're in-order and make sense. if err := us.Validate(); err != nil { @@ -185,6 +197,11 @@ func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, } } + execTraceCache, err := lru.NewARC[*types.TipSet, tipSetCacheEntry](execTraceCacheSize) + if err != nil { + return nil, err + } + return &StateManager{ networkVersions: networkVersions, latestVersion: lastVersion, @@ -200,8 +217,9 @@ func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, root: cid.Undef, tree: nil, }, - compWait: make(map[string]chan struct{}), - msgIndex: msgIndex, + compWait: make(map[string]chan struct{}), + msgIndex: msgIndex, + execTraceCache: execTraceCache, }, nil }