Skip to content

feat(token): add metrics and traces for cctp and lombard - #1409

Open
makramkd wants to merge 3 commits into
mainfrom
mk/CCIP-13435
Open

feat(token): add metrics and traces for cctp and lombard#1409
makramkd wants to merge 3 commits into
mainfrom
mk/CCIP-13435

Conversation

@makramkd

@makramkd makramkd commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Description

  • Add per-message attestation fetch counter + duration histogram for cctp/lombard verifiers (outcome: success/not_ready/not_found/error)
  • Add outbound attestation HTTP client metrics: active requests, requests by method/outcome/status, duration, rate-limited counter, and API cooldown gauge
  • Open per-message attestation fetch spans under the task-verifier attempt span so they extend the source reader's base message trace
  • Add span events (fetch started/failed/not_found/not_ready/succeeded) and provider/status/result attributes
  • Add a child span around each outbound attestation HTTP request in the shared token HTTP client

Testing

I tested this out on devenv in the following situations:

  • Default happy path (no retries)
  • Simulated retires (altered the fakes to return an error so that it can be retried by the verifier)

This is what the span looks like for the happy path:

Screenshot 2026-09-04 at 12 20 40 PM

And this is what it looks like with some retries:

Screenshot 2026-09-04 at 1 03 46 PM

Checklist

  • Breaking changes documented in changelog (see changelog directory)
  • Cross link related PRs (in this or other repositories)

- Add per-message attestation fetch counter + duration histogram for
  cctp/lombard verifiers (outcome: success/not_ready/not_found/error)
- Add outbound attestation HTTP client metrics: active requests,
  requests by method/outcome/status, duration, rate-limited counter,
  and API cooldown gauge
- Open per-message attestation fetch spans under the task-verifier
  attempt span so they extend the source reader's base message trace
- Add span events (fetch started/failed/not_found/not_ready/succeeded)
  and provider/status/result attributes
- Add a child span around each outbound attestation HTTP request in the
  shared token HTTP client
@makramkd
makramkd marked this pull request as ready for review September 4, 2026 10:06
@makramkd
makramkd requested review from a team as code owners September 4, 2026 10:06
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

Code coverage report:

Package main mk/CCIP-13435 Diff
github.com/smartcontractkit/chainlink-ccv/aggregator 50.86% 50.85% -0.01%
github.com/smartcontractkit/chainlink-ccv/bootstrap 70.65% 70.65% +0.00%
github.com/smartcontractkit/chainlink-ccv/cli 58.12% 58.12% +0.00%
github.com/smartcontractkit/chainlink-ccv/cmd 35.65% 35.65% +0.00%
github.com/smartcontractkit/chainlink-ccv/common 46.51% 46.51% +0.00%
github.com/smartcontractkit/chainlink-ccv/executor 42.80% 42.80% +0.00%
github.com/smartcontractkit/chainlink-ccv/indexer 35.59% 35.55% -0.04%
github.com/smartcontractkit/chainlink-ccv/integration 56.44% 56.44% +0.00%
github.com/smartcontractkit/chainlink-ccv/internal 0.00% 0.00% +0.00%
github.com/smartcontractkit/chainlink-ccv/migration 78.70% 78.70% +0.00%
github.com/smartcontractkit/chainlink-ccv/pkg 100.00% 100.00% +0.00%
github.com/smartcontractkit/chainlink-ccv/pricer 0.00% 0.00% +0.00%
github.com/smartcontractkit/chainlink-ccv/protocol 63.46% 63.46% +0.00%
github.com/smartcontractkit/chainlink-ccv/tools 43.61% 43.61% +0.00%
github.com/smartcontractkit/chainlink-ccv/verifier 35.59% 36.72% +1.13%
Total 49.90% 49.80% -0.10%

Comment on lines +505 to +509
vm.tokenHTTPRequestDurationSeconds, err = beholder.GetMeter().Float64Histogram(
"verifier_token_http_request_duration_seconds",
metric.WithDescription("Duration of outbound attestation HTTP requests"),
metric.WithUnit("seconds"),
)

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.

Can you check if in VM it is verifier_token_http_request_duration_seconds and not verifier_token_http_request_duration_seconds_seconds or something else

sdkmetric.NewView(
sdkmetric.Instrument{Name: "verifier_token_attestation_fetch_duration_seconds"},
sdkmetric.Stream{Aggregation: sdkmetric.AggregationExplicitBucketHistogram{
Boundaries: []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50},

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.

do we have a default timeout for attestation fetch? I think windows bigger than 5s won't be utilized

Comment on lines +114 to +116
if attemptSC := oteltrace.SpanContextFromContext(task.TraceContext); attemptSC.IsValid() {
parentCtx = oteltrace.ContextWithSpanContext(ctx, attemptSC)
}

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.

I have a helper method
TraceContextForMessage(tCtx, messageID)
In which cases attemptSC can be invalid? don't have this check anywhere

// task.TraceContext is derived from context.WithoutCancel, so inject the attempt
// span context into ctx to keep its deadline/cancellation for the actual fetch.
parentCtx := ctx
if task.TraceContext != 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.

we should always have the TraceContext. Reading from db preserving the parent-child relations, it just loses the span itself (and ability to update existing span)

// must not be silently dropped because the (often short) API timeout fired
// while we were measuring the request.
mCtx := context.WithoutCancel(ctx)
_, span := otel.GetTracerProvider().Tracer("ccv.token.http").Start(ctx, "token_http_request",

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 are using only beholder.GetTracer() (available globally)
Is there some idea to not use beholder one?

// must not be silently dropped because the (often short) API timeout fired
// while we were measuring the request.
mCtx := context.WithoutCancel(ctx)
_, span := otel.GetTracerProvider().Tracer("ccv.token.http").Start(ctx, "token_http_request",

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.

shouldn't be mCtx passed to the span too?

// mCtx is deliberately detached from the request's deadline: metric records
// must not be silently dropped because the (often short) API timeout fired
// while we were measuring the request.
mCtx := context.WithoutCancel(ctx)

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.

I don't really think metrics make some requests using provided context. Is it a best-practice to not pass cancellable context to the methods if you don't want them to be interrupted?

// (which keeps its deadline/cancellation) rather than using task.TraceContext
// directly, since that is derived from context.WithoutCancel.
parentCtx := ctx
if task.TraceContext != 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.

same thoughts as for CCTP

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