Home > Back-end >  GRPC metadata not updating in Go
GRPC metadata not updating in Go

Time:10-20

I'm trying to write a unary interceptor to add a request ID to incoming requests:

func RequestIDInterceptor(ctx context.Context, req interface{},
    info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (val interface{}, err error) {

    newCtx := ctx
    newCtx = createAndSetMetadata(newCtx, metadata.FromIncomingContext, metadata.NewIncomingContext)
    newCtx = createAndSetMetadata(newCtx, metadata.FromOutgoingContext, metadata.NewOutgoingContext)

    md, ok := metadata.FromOutgoingContext(newCtx)
    fmt.Printf("After update. Metadata: %v, Found: %t\n", md, ok)

    return handler(newCtx, req)
}

func createAndSetMetadata(ctx context.Context, getter func(context.Context) (metadata.MD, bool),
    setter func(context.Context, metadata.MD) context.Context) context.Context {

    meta, ok := getter(ctx)
    fmt.Printf("Found? %t, Metadata: %v\n", ok, meta)
    if !ok {
        meta = metadata.New(map[string]string{})
        ctx = setter(ctx, meta)
    }

    meta.Set("X-Request-Id", "TEST_REQUEST_ID")
    fmt.Printf("Metadata: %v\n", meta)
    return ctx
}

This code works as expected when the request doesn't include metadata (RequestIDInterceptor(context.Background(), nil, nil, handler)) but if the context includes metadata then the ID won't populated. Essentially, the output looks like this:

WITHOUT METADATA
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[x-request-id:[TEST_REQUEST_ID]], Found: true

WITH METADATA
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[], Found: true

To my understanding, the reason this is probably happening is that metadata.MD is being passed by value rather than by reference, so updates are being discarded when createAndSetMetadata returns. How can I fix this code?

CodePudding user response:

The issue is that metadata.FromOutgoingContext effectively returns a deep copy of the metadata.MD found in the context. You can check the sources, it's still true as of google.golang.org/[email protected].

So directly writing to it (the copy) as you would do with a map simply doesn't affect the MD that is stored in the context.

You have to call the setter again to propagate the new values:

func createAndSetMetadata(/* args */) context.Context {
    // ...
    return setter(ctx, meta)
}
  • Related