mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
fix(webhook): preserve trace context across async detach via WithoutCancel
This commit is contained in:
@@ -11,22 +11,30 @@ import (
|
|||||||
// conversion deadline.
|
// conversion deadline.
|
||||||
//
|
//
|
||||||
// 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. Replacing the
|
// [api.ErrAsyncProcess], which would abort the asynchronous work. Detaching via
|
||||||
// embedded context severs that cancellation. The returned cancel function
|
// [context.WithoutCancel] severs that cancellation while keeping the context
|
||||||
// cleans up both the detached context and the original working directory.
|
// values, most importantly the active trace span, so downstream conversion and
|
||||||
|
// 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
|
||||||
|
// returned cancel function 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 {
|
||||||
if deadline, ok := ctx.Deadline(); ok {
|
deadline, hasDeadline := ctx.Deadline()
|
||||||
detachedCtx, detachedCancel := context.WithDeadline(context.Background(), deadline)
|
base := context.WithoutCancel(ctx.Context)
|
||||||
ctx.Context = detachedCtx
|
|
||||||
|
|
||||||
originalCancel := cancel
|
var detachedCtx context.Context
|
||||||
return func() {
|
var detachedCancel context.CancelFunc
|
||||||
detachedCancel()
|
if hasDeadline {
|
||||||
originalCancel()
|
detachedCtx, detachedCancel = context.WithDeadline(base, deadline)
|
||||||
}
|
} else {
|
||||||
|
// Fallback if no deadline was set (rare, as newContext enforces it).
|
||||||
|
detachedCtx, detachedCancel = context.WithCancel(base)
|
||||||
}
|
}
|
||||||
|
ctx.Context = detachedCtx
|
||||||
|
|
||||||
// Fallback if no deadline was set (rare, as newContext enforces it).
|
originalCancel := cancel
|
||||||
ctx.Context = context.Background()
|
return func() {
|
||||||
return cancel
|
detachedCancel()
|
||||||
|
originalCancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,37 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
|
"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestDetachAsyncContext_PreservesTraceContext(t *testing.T) {
|
||||||
|
traceID, _ := trace.TraceIDFromHex("0123456789abcdef0123456789abcdef")
|
||||||
|
spanID, _ := trace.SpanIDFromHex("0123456789abcdef")
|
||||||
|
sc := trace.NewSpanContext(trace.SpanContextConfig{
|
||||||
|
TraceID: traceID,
|
||||||
|
SpanID: spanID,
|
||||||
|
TraceFlags: trace.FlagsSampled,
|
||||||
|
Remote: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
reqCtx, reqCancel := context.WithDeadline(
|
||||||
|
trace.ContextWithSpanContext(context.Background(), sc),
|
||||||
|
time.Now().Add(2*time.Hour),
|
||||||
|
)
|
||||||
|
defer reqCancel()
|
||||||
|
|
||||||
|
ctx := &api.Context{Context: reqCtx}
|
||||||
|
cancel := detachAsyncContext(ctx, func() {})
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
got := trace.SpanContextFromContext(ctx.Context)
|
||||||
|
if got.TraceID() != sc.TraceID() {
|
||||||
|
t.Errorf("expected the detached context to keep trace id %s, got %s", sc.TraceID(), got.TraceID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDetachAsyncContext_PreservesDeadline(t *testing.T) {
|
func TestDetachAsyncContext_PreservesDeadline(t *testing.T) {
|
||||||
deadline := time.Now().Add(2 * time.Hour)
|
deadline := time.Now().Add(2 * time.Hour)
|
||||||
reqCtx, reqCancel := context.WithDeadline(context.Background(), deadline)
|
reqCtx, reqCancel := context.WithDeadline(context.Background(), deadline)
|
||||||
|
|||||||
Reference in New Issue
Block a user