diff --git a/.changelog/693.bugfix.md b/.changelog/693.bugfix.md new file mode 100644 index 000000000..cde4561fd --- /dev/null +++ b/.changelog/693.bugfix.md @@ -0,0 +1 @@ +analyzer: make node stats analyzer more robust to unsupported runtimes diff --git a/analyzer/node_stats/node_stats.go b/analyzer/node_stats/node_stats.go index 480345b2b..33f312504 100644 --- a/analyzer/node_stats/node_stats.go +++ b/analyzer/node_stats/node_stats.go @@ -36,9 +36,7 @@ func NewAnalyzer( cfg config.ItemBasedAnalyzerConfig, layers []common.Layer, consensusClient nodeapi.ConsensusApiLite, - emeraldClient nodeapi.RuntimeApiLite, - sapphireClient nodeapi.RuntimeApiLite, - pontusxClient nodeapi.RuntimeApiLite, + runtimeClients map[common.Runtime]nodeapi.RuntimeApiLite, target storage.TargetStorage, logger *log.Logger, ) (analyzer.Analyzer, error) { @@ -53,13 +51,9 @@ func NewAnalyzer( p := &processor{ layers: layers, consensusSource: consensusClient, - runtimeSources: map[common.Runtime]nodeapi.RuntimeApiLite{ - common.RuntimeEmerald: emeraldClient, - common.RuntimeSapphire: sapphireClient, - common.RuntimePontusx: pontusxClient, - }, - target: target, - logger: logger.With("analyzer", nodeStatsAnalyzerName), + runtimeSources: runtimeClients, + target: target, + logger: logger.With("analyzer", nodeStatsAnalyzerName), } return item.NewAnalyzer[common.Layer]( diff --git a/cmd/analyzer/analyzer.go b/cmd/analyzer/analyzer.go index e9fe13f96..b92ee77fe 100644 --- a/cmd/analyzer/analyzer.go +++ b/cmd/analyzer/analyzer.go @@ -547,19 +547,20 @@ func NewService(cfg *config.AnalysisConfig) (*Service, error) { //nolint:gocyclo if err1 != nil { return nil, err1 } - emeraldClient, err1 := sources.Runtime(ctx, common.RuntimeEmerald) - if err1 != nil { - return nil, err1 - } - sapphireClient, err1 := sources.Runtime(ctx, common.RuntimeSapphire) - if err1 != nil { - return nil, err1 - } - pontusxClient, err1 := sources.Runtime(ctx, common.RuntimePontusx) - if err1 != nil { - return nil, err1 + runtimeClients := map[common.Runtime]nodeapi.RuntimeApiLite{} + // We attempt to provide the analyzer with all runtime clients. This may + // fail if the node does not support the runtime, which is valid. If + // the analyzer expects the node to support the runtime but it does not, + // the analyzer will log an error. + for _, runtime := range []common.Runtime{common.RuntimeEmerald, common.RuntimeCipher, common.RuntimeSapphire, common.RuntimePontusx} { + client, err2 := sources.Runtime(ctx, runtime) + if err2 != nil { + logger.Warn("unable to instantiate runtime client for node stats analyzer", "runtime", runtime) + continue + } + runtimeClients[runtime] = client } - return nodestats.NewAnalyzer(cfg.Analyzers.NodeStats.ItemBasedAnalyzerConfig, cfg.Analyzers.NodeStats.Layers, sourceClient, emeraldClient, sapphireClient, pontusxClient, dbClient, logger) + return nodestats.NewAnalyzer(cfg.Analyzers.NodeStats.ItemBasedAnalyzerConfig, cfg.Analyzers.NodeStats.Layers, sourceClient, runtimeClients, dbClient, logger) }) } if cfg.Analyzers.AggregateStats != nil { diff --git a/storage/oasis/nodeapi/history/runtime.go b/storage/oasis/nodeapi/history/runtime.go index 8b815bef9..ca636e0e9 100644 --- a/storage/oasis/nodeapi/history/runtime.go +++ b/storage/oasis/nodeapi/history/runtime.go @@ -25,6 +25,10 @@ func NewHistoryRuntimeApiLite(ctx context.Context, history *config.History, sdkP apis := map[string]nodeapi.RuntimeApiLite{} for _, record := range history.Records { if archiveConfig, ok := nodes[record.ArchiveName]; ok { + // If sdkPT is nil, the subsequent sdkConn.Runtime() call will panic + if sdkPT == nil { + return nil, fmt.Errorf("no paratime specified") + } sdkConn, err := connections.SDKConnect(ctx, record.ChainContext, archiveConfig.ResolvedRuntimeNode(runtime), fastStartup) if err != nil { return nil, err