-
Notifications
You must be signed in to change notification settings - Fork 155
Description
Describe the bug
In opentelemetry-ecto:
Because Ecto emits telemetry events only after queries have finished, OpentelemetryEcto estimates the start time of the span by subtracting the reported total duration from the current timestamp.
opentelemetry-sqlcommenter attaches SQLCommenter-style annotations to generated queries, resembling this:
SELECT
f0."id", f0."user_id", f0."filter_id", f0."hide", f0."whole_word", f0."phrase", f0."context",
f0."expires_at", f0."inserted_at", f0."updated_at"
FROM
"filters" AS f0
WHERE
((f0."expires_at" IS NULL) OR (f0."expires_at" > $1)) AND (f0."hide")
AND
(f0."user_id" = $2)
ORDER BY
f0."id" DESC/*traceparent='00-adcd837ab305e206fc7bf5fc3395ca95-d503af1be7dc8fbc-01'*/Per opentelemetry-sqlcommenter's instructions, it's hooked into a Ecto in lib/pleroma/repo.ex inside defmodule Pleroma.Repo do ... end block as follows:
defdelegate prepare_query(operation, query, opts), to: OpentelemetrySqlcommenterprepare_query (or prepare_query_sampled) set comment: comment, but also prepare: :unnamed, the latter seemingly a trick to disable prepared statements, so that the preparation will happen every time, resulting in the delegate being called.
Neat trick, but it has a flaw: it happens while Ecto is making the query, meaning when the delegated prepare_query invokes OpenTelemetry.Tracer.current_span_ctx(), it's receiving the parent context -- or frequently none at all. (I'm not an Elixir or Erlang developer, so this is a lot of tapping in the dark. Please correct me if I am wrong.)
This seems to be the cause of queries sent by pg_tracing being detached from spans created by opentelemetry-ecto (sometimes with no parent span -- with pg_tracing just using part of the trace ID instead -- and sometimes with parent-of-ecto-span as parent).
I previously thought it was a pg_tracing bug: DataDog/pg_tracing#84 but I think the issues I saw there are all coming from opentelemetry-ecto faking the start event.
opentelemetry-sqlcommenter seems to be doing the right thing: getting the span context, and using it to build the comment, so the issue with the wrong parent seems to be elsewhere.
(Possibly related: #425)
Expected behavior
Correct span ID should be known at the time the query is being executed, so the correct parent span ID can be sent into the database.
Some thoughts?
- Would Ecto /
ecto-sqlitself have to be patched to correctly start and end spans? Or can the fix live fully outside of that core? - Am I setting things up incorrectly or missing something? Should I be using something else to create the spans?
- Would some integration between the
opentelemetry-sqlcommenter's hack andopentelemetry-ectomake sense, e.g. starting the span during query prep (giving up on "end time, minus telemetry-observed total duration" trick to compute an accurate start_time) make sense (with fallback to that kind ofstart_timeif need be)? - If it does make sense, what would be the best way in Elixir to store the fact that a span has already been started? A thread-local variable, presumably? (A global variable with mutex guarding it is, I expect, not a very Erlang-like approach.)
Additional context
I do not actually know Elixir or Erlang, neither the languages nor the wider ecosystem. I am not familiar with Ecto, ecto-sql, or OpenTelemetry implementation in Elixir or Erlang. I am very unfamiliar with how threading or global vars (such as a tracing context) work in Erlang and Elixir.
Honestly, I was mainly trying to figure out the causes of performance issues I have with Pleroma (a fediverse server, implementing a federated social network over HTTP) and with queries it's sending into PostgreSQL.
In my setup, for each request I propagate a single trace:
- starting in nginx (creates trace ID and a span),
- getting into Pleroma in Phoenix (receives the trace ID and parent span ID, creates a span) which
- during the handling of a route causes many Ecto queries to PostgreSQL
- these get annotated with
traceparentcomment as an SQLCommenter-style comment - this is used by the
pg_tracingextension installed into PostgreSQL - both Pleroma and PostgreSQL have been configured to talk to local otelcol-contrib, which currently forwards this to Google Cloud Trace
I haven't set up Oban telemetry or anything else I might be missing. Some processing happens without HTTP requests as well.
I'm also running this on top of whatever's in my Debian-based install -- apparently, "Elixir 1.14.0 (compiled with Erlang/OTP 24)". This is mostly Pleroma v2.9.1 with these OpenTelemetry-related changes installed.