Skip to content

OEV-1648 Emit whitelisted EVM chain config metric - #548

Open
mostlyconsistent wants to merge 4 commits into
developfrom
mkysel/metrics
Open

OEV-1648 Emit whitelisted EVM chain config metric#548
mostlyconsistent wants to merge 4 commits into
developfrom
mkysel/metrics

Conversation

@mostlyconsistent

@mostlyconsistent mostlyconsistent commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

What

Emits SVR-relevant EVM chain config as an OpenTelemetry info gauge from each chain's startup path:

evm_chain_config_info{chain_id, transaction_v2_enabled, dual_broadcast} = 1 

Value is always 1; all state is in the labels. The label whitelist is exhaustive and contains no URL.

Why

Since ~2026-07-20 the INCIDENT-2541 mitigation (drop filters in chip-ingress and beholder-gateway-logs) drops the whole NodeConfig beholder event, because that event carried the entire node TOML — including RPC/OFA URLs with embedded credentials. Collateral damage:

  • svr_beholder.node_configs (BigQuery) stopped ingesting.
  • node_config_boot_time, which OFE derived from that event, went absent.
  • DF-SVR-018 ("NOP node config DualBroadcast/TxV2 disabled") depends on that metric and has been evaluating blind since.

@mostlyconsistent

Copy link
Copy Markdown
Contributor Author

@cll-gg let's look at this after the weekend. No code should land on a Friday :)

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✅ API Diff Results - github.com/smartcontractkit/chainlink-evm

✅ Compatible Changes (2)

pkg/chains/legacyevm.ChainOpts (1)
  • Meter — ➕ Added
pkg/chains/legacyevm.ChainRelayOpts (1)
  • Meter — ➕ Added

📄 View full apidiff report

@mostlyconsistent mostlyconsistent changed the title Chain Config Metrics OEV-1648 Emit whitelisted EVM chain config metric Sep 8, 2026
@mostlyconsistent
mostlyconsistent marked this pull request as ready for review September 8, 2026 13:52
@mostlyconsistent
mostlyconsistent requested review from a team as code owners September 8, 2026 13:52
Comment on lines +39 to +48
gauge, err := meter.Int64Gauge(
evmChainConfigInfoMetricName,
metric.WithDescription("Whitelisted EVM chain configuration; value is always 1, state is in the labels"),
metric.WithUnit("{info}"),
)
if err != nil {
return fmt.Errorf("failed to create %s gauge: %w", evmChainConfigInfoMetricName, err)
}

gauge.Record(ctx, 1, metric.WithAttributes(chainConfigAttributes(chainID, txV2Enabled, dualBroadcast)...))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructing and emitting together like this seems problematic. Can we construct the gauge once ahead of time?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you go into more details on why it is problematic?

// hiccup must never block chain startup.
func (c *chain) emitChainConfigInfo(ctx context.Context, meter metric.Meter) {
if meter == nil {
meter = beholder.GetMeter()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically want to push these global beholder calls up/out to a higher level so that we have clean injection everywhere internal. It also keeps high level packages from being imported if we reference just the otel types.

}

txV2 := c.cfg.EVM().Transactions().TransactionManagerV2()
if err := recordChainConfigInfo(ctx, meter, c.id.String(), txV2.Enabled(), derefBool(txV2.DualBroadcast())); err != nil {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO the indirection makes this harder to read compared to inlining:

Suggested change
if err := recordChainConfigInfo(ctx, meter, c.id.String(), txV2.Enabled(), derefBool(txV2.DualBroadcast())); err != nil {
var dualBroadcast bool
if db := txV2.DualBroadcast(); db != nil {
dualBroadcast = *db
}
if err := recordChainConfigInfo(ctx, meter, c.id.String(), txV2.Enabled(), dualBroadcast); err != nil {

But maybe a different name would be fine?

Suggested change
if err := recordChainConfigInfo(ctx, meter, c.id.String(), txV2.Enabled(), derefBool(txV2.DualBroadcast())); err != nil {
if err := recordChainConfigInfo(ctx, meter, c.id.String(), txV2.Enabled(), isTrue(txV2.DualBroadcast())); err != nil {


// Meter is used to report chain metrics. Defaults to the global beholder
// meter when unset.
// TODO: make this required once chainlink core passes it explicitly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO will still be part of the current ticket?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its a chicken and egg problem. Once this lands, I can land a version of this smartcontractkit/chainlink#23726, and then undo the TODO :)

Up to you how many tickets you wanna have for the work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants