Skip to content

Enable Trace Propagation#65

Open
AnthonySuper wants to merge 1 commit into
riverqueue:masterfrom
AnthonySuper:enable-trace-propagation
Open

Enable Trace Propagation#65
AnthonySuper wants to merge 1 commit into
riverqueue:masterfrom
AnthonySuper:enable-trace-propagation

Conversation

@AnthonySuper

Copy link
Copy Markdown

When the configuration parameter EnableTracePropagation is enabled, the traces generated by river workers will be linked to the traces that enqueued them. This is done by use of metadata parameters, following the example in River's documentation for the middleware functionality (https://riverqueue.com/docs/middleware).

This uses a link instead of directly making a child span which is semantically more correct—if a parent span is used, you get weird situations where a POST request that returns in 50ms somehow contains a trace that starts two hours later and takes 10 minutes. River jobs are async, after all.

Fixes #41 (I believe, at least).

When the configuration parameter `EnableTracePropagation` is enabled,
the traces generated by river workers will be *linked* to the traces
that enqueued them. This is done by use of metadata parameters,
following the example in River's documentation for the middleware
functionality (https://riverqueue.com/docs/middleware).

This uses a *link* instead of directly making a child span which is
semantically more correct.

Fixes riverqueue#41 (I believe, at least).
@brandur

brandur commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

@codex review

@AnthonySuper

Copy link
Copy Markdown
Author

Just an FYI: we've rolled this code out in production at my company and it has been successful (traces from jobs are properly linking to whatever kicked them off).

@brandur brandur left a comment

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.

Good stuff! Want to add a changelog entry?

Comment thread otelriver/middleware.go
for k, v := range carrier {
meta[k] = v
}
injected, err := json.Marshal(meta)

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.

What do you think about using sjson here instead of unmarshaling and then re-marshaling? sjon's already in the dependency graph, so it's not too big of a deal.

  func injectTraceContext(ctx context.Context, metadata []byte) []byte {
        carrier := make(propagation.MapCarrier)
        propagation.TraceContext{}.Inject(ctx, carrier)
        if len(carrier) == 0 {
                return metadata
        }

        original := metadata
        for k, v := range carrier {
                var err error
                metadata, err = sjson.SetBytes(metadata, k, v)
                if err != nil {
                        return original
                }
        }
        return metadata
  }

Comment thread otelriver/middleware.go
}
}
extracted := propagation.TraceContext{}.Extract(ctx, carrier)
return trace.SpanFromContext(extracted).SpanContext()

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.

I think this piece is subtly not-quite-right in that the purpose of this function is always to pull a span context out of the given metadata and we should never be looking to get out of active context. What do you think about removing the ctx parameter from extractSpanContext in favor of using context.Background() here? If no span context is founded in metadata, then nothing should be extracted.

Comment thread otelriver/middleware.go

// EnableTracePropagation injects W3C trace context (traceparent/tracestate)
// into job metadata on insert and extracts it on work, adding a span link
// from the work span back to the span that enqueued the job. A link is used

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.

Do you want to maybe move the sentence starting from "A link is used ..." from here down to this line?

startOpts = append(startOpts, trace.WithLinks(trace.Link{SpanContext: sc}))

It feels like rationalizing the use of a link here is a little bit TMI for the docblock and belongs more in an internal comment.

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.

Proposal: Persist trace context in job metadata

2 participants