How does a value in Go context get marshalled when the context is passed over to a remote service?

469 views Asked by At

Below is the signature of the WithValue func.

func WithValue(parent Context, key, val interface{}) Context

I took a look at Go official documentation but they didn't mention how a value would get marshalled when the context is passed over to a remote service.

For example, I can pass in anything as val such as a complex struct containing exported and un-exported fields. I wonder if my un-exported fields would ever reach the remote service. If they do, how does it work behind the scene? The whole context object gets marshalled into []byte regardless of fields?

One concrete sample of a complex object is the internal implementation of a Span in opentracing.

// Implements the `Span` interface. Created via tracerImpl (see
// `basictracer.New()`).
type spanImpl struct {
    tracer     *tracerImpl
    event      func(SpanEvent)
    sync.Mutex // protects the fields below
    raw        RawSpan
    // The number of logs dropped because of MaxLogsPerSpan.
    numDroppedLogs int
}

The above Span can be passed around from service to service by creating a derived context using the func below.

// ContextWithSpan returns a new `context.Context` that holds a reference to
// the span. If span is nil, a new context without an active span is returned.
func ContextWithSpan(ctx context.Context, span Span) context.Context {
    if span != nil {
        if tracerWithHook, ok := span.Tracer().(TracerContextWithSpanExtension); ok {
            ctx = tracerWithHook.ContextWithSpanHook(ctx, span)
        }
    }
    return context.WithValue(ctx, activeSpanKey, span)
}

What is the high-level protocols/rules that Go uses to pass around complex values stored in Go context? Would the func(SpanEvent) and the mutex really travel over the wire to the next service?

I'd be very grateful if you could explain the expected behavior or point me in the direction of some articles I've not found.

0

There are 0 answers