mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
feat(webhook): link async worker span to the originating request span
This commit is contained in:
@@ -3,24 +3,38 @@ package webhook
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
|
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
|
||||||
"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
|
"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// detachAsyncContext detaches ctx from the inbound request lifecycle so the
|
// detachAsyncContext detaches ctx from the inbound request lifecycle so the
|
||||||
// webhook goroutine survives echo recycling the request, while preserving the
|
// webhook goroutine survives echo recycling the request, while preserving the
|
||||||
// conversion deadline.
|
// conversion deadline and the caller's trace.
|
||||||
//
|
//
|
||||||
// Echo cancels the request context as soon as the synchronous handler returns
|
// Echo cancels the request context as soon as the synchronous handler returns
|
||||||
// [api.ErrAsyncProcess], which would abort the asynchronous work. Detaching via
|
// [api.ErrAsyncProcess], which would abort the asynchronous work. Detaching via
|
||||||
// [context.WithoutCancel] severs that cancellation while keeping the context
|
// [context.WithoutCancel] severs that cancellation while keeping the context
|
||||||
// values, most importantly the active trace span, so downstream conversion and
|
// values. [context.WithoutCancel] also drops the deadline, so it is re-layered.
|
||||||
// webhook spans stay in the caller's trace instead of starting a new one.
|
//
|
||||||
// [context.WithoutCancel] also drops the deadline, so it is re-layered. The
|
// The server span ends as soon as that handler returns, so its span context is
|
||||||
// returned cancel function cleans up both the detached context and the original
|
// re-seated as a remote, non-recording parent: the asynchronous worker keeps
|
||||||
// working directory.
|
// the same trace without recording into a span that is about to end. A
|
||||||
|
// worker-root [webhook.Async] span is then opened, linked back to the
|
||||||
|
// originating request span, and stays open for the whole delivery so downstream
|
||||||
|
// conversion and webhook spans have a live parent in the caller's trace.
|
||||||
|
//
|
||||||
|
// The returned cancel function ends the worker span and cleans up both the
|
||||||
|
// detached context and the original working directory.
|
||||||
func detachAsyncContext(ctx *api.Context, cancel context.CancelFunc) context.CancelFunc {
|
func detachAsyncContext(ctx *api.Context, cancel context.CancelFunc) context.CancelFunc {
|
||||||
deadline, hasDeadline := ctx.Deadline()
|
deadline, hasDeadline := ctx.Deadline()
|
||||||
|
|
||||||
|
serverSpanCtx := trace.SpanContextFromContext(ctx.Context)
|
||||||
base := context.WithoutCancel(ctx.Context)
|
base := context.WithoutCancel(ctx.Context)
|
||||||
|
if serverSpanCtx.IsValid() {
|
||||||
|
base = trace.ContextWithRemoteSpanContext(base, serverSpanCtx)
|
||||||
|
}
|
||||||
|
|
||||||
var detachedCtx context.Context
|
var detachedCtx context.Context
|
||||||
var detachedCancel context.CancelFunc
|
var detachedCancel context.CancelFunc
|
||||||
@@ -30,10 +44,18 @@ func detachAsyncContext(ctx *api.Context, cancel context.CancelFunc) context.Can
|
|||||||
// Fallback if no deadline was set (rare, as newContext enforces it).
|
// Fallback if no deadline was set (rare, as newContext enforces it).
|
||||||
detachedCtx, detachedCancel = context.WithCancel(base)
|
detachedCtx, detachedCancel = context.WithCancel(base)
|
||||||
}
|
}
|
||||||
ctx.Context = detachedCtx
|
|
||||||
|
startOpts := []trace.SpanStartOption{trace.WithSpanKind(trace.SpanKindInternal)}
|
||||||
|
if serverSpanCtx.IsValid() {
|
||||||
|
startOpts = append(startOpts, trace.WithLinks(trace.Link{SpanContext: serverSpanCtx}))
|
||||||
|
}
|
||||||
|
|
||||||
|
workerCtx, workerSpan := gotenberg.Tracer().Start(detachedCtx, "webhook.Async", startOpts...)
|
||||||
|
ctx.Context = workerCtx
|
||||||
|
|
||||||
originalCancel := cancel
|
originalCancel := cancel
|
||||||
return func() {
|
return func() {
|
||||||
|
workerSpan.End()
|
||||||
detachedCancel()
|
detachedCancel()
|
||||||
originalCancel()
|
originalCancel()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user