Repository navigation
all: Add opt-in OpenTelemetry tracing - #6744
Open
datanexus-vincent wants to merge 10 commits into
Open
datanexus-vincent wants to merge 10 commits into
datanexus-vincent wants to merge 10 commits into
Conversation
Add `graph::telemetry`, a thin wrapper around the opentelemetry crates and the only place that uses them. It provides a `Span` type that is a no-op unless a tracer provider has been installed, a `Context` for parenting across threads, W3C header extraction and injection, and an in-memory exporter for tests. Tracing is switched on with `GRAPH_OTEL_TRACING` and the span groups to emit are chosen with `GRAPH_OTEL_SPANS`.
`telemetry::init` installs an OTLP exporter over http/protobuf as the global tracer provider when tracing is enabled, configured through the standard `OTEL_*` environment variables. The node calls it at startup and, since it has no shutdown sequence of its own, flushes buffered spans on SIGINT and SIGTERM before exiting.
A section started with `start_root_section` begins a trace; sections nested in it become child spans of the nearest enclosing section that has one. Parenting comes from the section stack alone, so it works unchanged across the threads that share a `StopwatchMetrics`. Host function sections are gated by the `host_fn` span group, handler sections by `handler`. With tracing off the only addition is one atomic load per section start.
Each processed block becomes a trace rooted at the `process_block` section, carrying the deployment, block number and hash, network and shard. A block that fails processing marks its span with the error. The `process_triggers` section was scoped to trigger processing when it was added (graphprotocol#4558), but the runner refactor left its guard alive until the end of block processing. It now ends after the triggers ran again.
…racing attributes Handler spans name the handler and, for chain triggers, the transaction and log index. `store.set` spans name the entity type and `eth_call` spans the contract, function and address. Section attributes are now built by closures that the stopwatch only calls when the span is recorded, so an unsampled trace or a disabled group formats nothing. The `store_set` host function started a section whose guard was dropped on the same line, so it only ever recorded an empty counter. It is removed.
Each batch the writer commits is its own trace, linked to the traces of the blocks whose changes it contains; a batch may hold several blocks and is written after they were processed, so nesting it under one of them would be wrong. Entity queries become child spans of the trace attached to the thread that runs them. The SQL text in query timing logs was truncated at a fixed byte offset, which panics inside a multibyte character. It is now cut on a char boundary.
The HTTP server starts a span for each GraphQL request, joining the trace named by a W3C traceparent header when there is one. The span's context travels with the query and is attached to the thread that runs it, so the store's query spans nest under the request.
… synced The runner and the writer disabled their stopwatch once the deployment was synced so that `deployment_sync_secs` only measures the time to sync. With spans anchored in the stopwatch that left a synced deployment with no block and no write spans. `stop_recording` now stops the counter and, only with tracing off, turns the sections into no-ops as before.
Most host functions share the `host_export_other` stopwatch section, so their spans could not be told apart. Every host function span now carries the WASM import name in `host_function`. The section names, and with them the `deployment_sync_secs` label set, are unchanged.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds opt-in OpenTelemetry tracing to graph-node. With
GRAPH_OTEL_TRACING=true, graph-node exports spans over OTLP (http/protobuf) to any collector or vendor agent. Off by default; when off, nothing is installed and the only new cost is one atomic load at the start and end of each instrumented section.Documentation: docs/tracing.md, plus a section in
docs/environment-variables.mdand a NEWS entry.Why
Prometheus metrics say how long block processing, writes and queries take on average. They cannot say where the time went in one slow block or one slow request, or which blocks a slow batch write came from. Traces can, and many enterprise-level operators already run OpenTelemetry collectors and vendor agents that accept OTLP.
Design
StopwatchMetricssections. The runner, the writer and the WASM host already wrap their work in stopwatch sections. Each section now optionally opens a span, so span names are the existingdeployment_sync_secssection names and the tree nests exactly as the sections do. Notracingcrate, no changes to logging; slog stays as it is.process_blockstarts a trace per block,write_batchone per batch written by the write queue,graphql_queryone per request. Nothing relies on the OpenTelemetry thread-local context except the one place where a GraphQL query runs on a dedicated blocking thread, which is where the store'sdb_queryspans find their parent.process_blockspan (capped at 32, overflow counted indropped_links).opentelemetrySDK behind a thingraph::telemetrymodule.Span,Context,extract/injectand the enabled flags are the only surface the rest of the code sees. The SDK reads the standardOTEL_*variables itself; graph-node adds onlyGRAPH_OTEL_TRACINGandGRAPH_OTEL_SPANS.GRAPH_OTEL_SPANSselectsblock,handler,host_fn,store,graphql.host_fnis off by default because a handler can make thousands of store and chain calls.What is traced
A request with a W3C
traceparentheader joins the caller's trace.query_idon the request span is the id the query timing logs and the SQLroutecomment carry, so spans, log lines andpg_stat_activitycan be matched up.sqlis only attached whenGRAPH_LOG_QUERY_TIMINGincludessql, the same gate as the logs.Testing
graph::telemetry::testinglets tests assert on exported span trees. Tests cover the module itself, the stopwatch span derivation, the per-block trace and handler attributes (block_spansrunner test), write batch links anddb_queryparenting (test-store), and the HTTP server's header extraction and error status.graphql_queryunder the header's span withdb_queryunder it, a failing query carried an error status, and spans sent just before SIGTERM arrived during shutdown.Notes for reviewers
Cargo.lockonly gains the new packages; unrelated entries were left as they were.