Skip to content

ateapi: trace PostgreSQL statements in the store - #1462

Open
Da Huang (git286) wants to merge 1 commit into
agent-substrate:mainfrom
git286:fix-atepg-query-tracing
Open

ateapi: trace PostgreSQL statements in the store#1462
Da Huang (git286) wants to merge 1 commit into
agent-substrate:mainfrom
git286:fix-atepg-query-tracing

Conversation

@git286

@git286 Da Huang (git286) commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

The store's pgx pools carried no tracer, so no span was ever emitted for database work: a traced GetActor arrived as a single gRPC server span with the PostgreSQL time indistinguishable from handler overhead, and even the multi-span suspend/resume traces showed step, atelet and snapshot spans but nothing for the many store round-trips between them. The store blind spot in docs/metrics/substrate.yaml describes the metrics half of this; the trace half is what this change closes.

Attach a QueryTracer to the pool config (the watch pool inherits it via Copy). Each statement becomes a client span named after its leading keyword (db.SELECT, db.UPDATE, ...) with the parameterized statement text as db.query.text — arguments never appear in it. Spans only join an existing trace: statements from background work (outbox polling, lease maintenance) carry no surrounding span, and opening a root span for each would flood the backend with single-span traces. pgx.ErrNoRows leaves the span unmarked, since the store maps it to NotFound as an expected lookup outcome.

Fixes #1455

It's a good idea to open an issue first for discussion.

  • Tests pass
  • Appropriate changes to documentation are included in the PR

The store's pgx pools carried no tracer, so no span was ever emitted for
database work: a traced GetActor arrived as a single gRPC server span
with the PostgreSQL time indistinguishable from handler overhead, and
even the multi-span suspend/resume traces showed step, atelet and
snapshot spans but nothing for the many store round-trips between them.
The store blind spot in docs/metrics/substrate.yaml describes the
metrics half of this; the trace half is what this change closes.

Attach a QueryTracer to the pool config (the watch pool inherits it via
Copy). Each statement becomes a client span named after its leading
keyword (db.SELECT, db.UPDATE, ...) with the parameterized statement
text as db.query.text — arguments never appear in it. Spans only join an
existing trace: statements from background work (outbox polling, lease
maintenance) carry no surrounding span, and opening a root span for each
would flood the backend with single-span traces. pgx.ErrNoRows leaves
the span unmarked, since the store maps it to NotFound as an expected
lookup outcome.
if !trace.SpanContextFromContext(ctx).IsValid() {
return ctx
}
ctx, _ = otel.Tracer("atepg").Start(ctx, querySpanName(data.SQL),

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.

Is an error here an impossible condition? Are we ok swallowing the error?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The discarded value is the span, not an error. Start cannot fail. Its signature is Start(ctx, name, ...) (context.Context, Span). The OpenTelemetry API is built so tracing never breaks the app: if sampling is off you just get an inert span back, never an error.

We discard the span because Start also stores a copy inside the returned context, and that copy is the one we need. The span gets closed in a different function: pgx carries our returned context through the query and passes it to TraceQueryEnd, which retrieves the span with trace.SpanFromContext and ends it. A local span variable would go out of scope as soon as TraceQueryStart returns.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ateapi: postgres store has no trace instrumentation - DB time is invisible in every trace

2 participants