From 0e51a79bb6921d67435d9d4d09bb9e6aa471deb2 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Tue, 9 Feb 2021 01:08:45 +0300 Subject: [PATCH] metadata: split context to incoming and outgoing Signed-off-by: Vasiliy Tolstov --- client/noop.go | 2 +- go.mod | 6 ++-- go.sum | 13 ++++---- metadata/context.go | 57 ++++++++++++++++++++++++-------- metadata/metadata.go | 47 ++++---------------------- metadata/metadata_test.go | 69 +++++++-------------------------------- tracer/context.go | 17 +++++----- util/ctx/ctx.go | 7 ++-- util/ctx/ctx_test.go | 2 +- 9 files changed, 84 insertions(+), 136 deletions(-) diff --git a/client/noop.go b/client/noop.go index ceffdf46..e24d216a 100644 --- a/client/noop.go +++ b/client/noop.go @@ -187,7 +187,7 @@ func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOpti options := NewPublishOptions(opts...) - md, ok := metadata.FromContext(ctx) + md, ok := metadata.FromOutgoingContext(ctx) if !ok { md = metadata.New(0) } diff --git a/go.mod b/go.mod index d9c056ec..4ab34bbe 100644 --- a/go.mod +++ b/go.mod @@ -7,15 +7,15 @@ require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/ef-ds/deque v1.0.4 github.com/golang/protobuf v1.4.3 - github.com/google/uuid v1.1.5 + github.com/google/uuid v1.2.0 github.com/imdario/mergo v0.3.11 github.com/kr/text v0.2.0 // indirect - github.com/miekg/dns v1.1.35 + github.com/miekg/dns v1.1.38 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect - golang.org/x/net v0.0.0-20201224014010-6772e930b67b + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect google.golang.org/protobuf v1.25.0 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect diff --git a/go.sum b/go.sum index 5e0eb15e..516770f7 100644 --- a/go.sum +++ b/go.sum @@ -31,16 +31,16 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= -github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.38 h1:MtIY+fmHUVVgv1AXzmKMWcwdCYxTRPG1EDjpqF4RCEw= +github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= @@ -52,7 +52,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/unistack-org/micro v1.18.0 h1:EbFiII0bKV0Xcua7o6J30MFmm4/g0Hv3ECOKzsUBihU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= @@ -69,8 +68,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/metadata/context.go b/metadata/context.go index c6bc32ae..c8000fbf 100644 --- a/metadata/context.go +++ b/metadata/context.go @@ -5,35 +5,64 @@ import ( "context" ) +type mdIncomingKey struct{} +type mdOutgoingKey struct{} +type mdKey struct{} + +// FromIncomingContext returns metadata from incoming ctx +// returned metadata shoud not be modified or race condition happens +func FromIncomingContext(ctx context.Context) (Metadata, bool) { + if ctx == nil { + return nil, false + } + md, ok := ctx.Value(mdIncomingKey{}).(Metadata) + return md, ok +} + +// FromOutgoingContext returns metadata from outgoing ctx +// returned metadata shoud not be modified or race condition happens +func FromOutgoingContext(ctx context.Context) (Metadata, bool) { + if ctx == nil { + return nil, false + } + md, ok := ctx.Value(mdOutgoingKey{}).(Metadata) + return md, ok +} + // FromContext returns metadata from the given context +// returned metadata shoud not be modified or race condition happens +// +// Deprecated: use FromIncomingContext or FromOutgoingContext func FromContext(ctx context.Context) (Metadata, bool) { if ctx == nil { return nil, false } - md, ok := ctx.Value(metadataKey{}).(Metadata) - if !ok { - return nil, ok - } - nmd := Copy(md) - return nmd, ok + md, ok := ctx.Value(mdKey{}).(Metadata) + return md, ok } // NewContext creates a new context with the given metadata +// +// Deprecated: use NewIncomingContext or NewOutgoingContext func NewContext(ctx context.Context, md Metadata) context.Context { if ctx == nil { ctx = context.Background() } - return context.WithValue(ctx, metadataKey{}, Copy(md)) + return context.WithValue(ctx, mdKey{}, md) } -// MergeContext merges metadata to existing metadata, overwriting if specified -func MergeContext(ctx context.Context, pmd Metadata, overwrite bool) context.Context { +// NewIncomingContext creates a new context with incoming metadata attached +func NewIncomingContext(ctx context.Context, md Metadata) context.Context { if ctx == nil { ctx = context.Background() } - md, ok := FromContext(ctx) - if !ok { - return context.WithValue(ctx, metadataKey{}, Copy(pmd)) - } - return context.WithValue(ctx, metadataKey{}, Merge(md, pmd, overwrite)) + return context.WithValue(ctx, mdIncomingKey{}, md) +} + +// NewOutgoingContext creates a new context with outcoming metadata attached +func NewOutgoingContext(ctx context.Context, md Metadata) context.Context { + if ctx == nil { + ctx = context.Background() + } + return context.WithValue(ctx, mdOutgoingKey{}, md) } diff --git a/metadata/metadata.go b/metadata/metadata.go index 028fa4ba..635e6707 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -2,7 +2,6 @@ package metadata import ( - "context" "net/textproto" "sort" ) @@ -12,16 +11,14 @@ var ( HeaderPrefix = "Micro-" ) -type metadataKey struct{} - // Metadata is our way of representing request headers internally. // They're used at the RPC level and translate back and forth // from Transport headers. type Metadata map[string]string var ( - // DefaultMetadataSize used when need to init new Metadata - DefaultMetadataSize = 6 + // defaultMetadataSize used when need to init new Metadata + defaultMetadataSize = 2 ) type Iterator struct { @@ -72,12 +69,9 @@ func (md Metadata) Set(key, val string) { // Del is used to remove value from metadata func (md Metadata) Del(key string) { // fast path - if _, ok := md[key]; ok { - delete(md, key) - } else { - // slow path - delete(md, textproto.CanonicalMIMEHeaderKey(key)) - } + delete(md, key) + // slow path + delete(md, textproto.CanonicalMIMEHeaderKey(key)) } // Copy makes a copy of the metadata @@ -89,39 +83,10 @@ func Copy(md Metadata) Metadata { return nmd } -// Del deletes key from metadata -func Del(ctx context.Context, key string) context.Context { - md, ok := FromContext(ctx) - if !ok { - md = New(0) - } - md.Del(key) - return context.WithValue(ctx, metadataKey{}, md) -} - -// Set add key with val to metadata -func Set(ctx context.Context, key, val string) context.Context { - md, ok := FromContext(ctx) - if !ok { - md = New(0) - } - md.Set(key, val) - return context.WithValue(ctx, metadataKey{}, md) -} - -// Get returns a single value from metadata in the context -func Get(ctx context.Context, key string) (string, bool) { - md, ok := FromContext(ctx) - if !ok { - return "", ok - } - return md.Get(key) -} - // New return new sized metadata func New(size int) Metadata { if size == 0 { - size = DefaultMetadataSize + size = defaultMetadataSize } return make(Metadata, size) } diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go index 64db88e1..34d077fe 100644 --- a/metadata/metadata_test.go +++ b/metadata/metadata_test.go @@ -2,8 +2,6 @@ package metadata import ( "context" - "fmt" - "reflect" "testing" ) @@ -32,26 +30,27 @@ func TestIterator(t *testing.T) { var k, v string for iter.Next(&k, &v) { - fmt.Printf("k: %s, v: %s\n", k, v) + //fmt.Printf("k: %s, v: %s\n", k, v) } } func TestMedataCanonicalKey(t *testing.T) { - ctx := Set(context.TODO(), "x-request-id", "12345") - v, ok := Get(ctx, "x-request-id") + md := New(1) + md.Set("x-request-id", "12345") + v, ok := md.Get("x-request-id") if !ok { t.Fatalf("failed to get x-request-id") } else if v != "12345" { t.Fatalf("invalid metadata value: %s != %s", "12345", v) } - v, ok = Get(ctx, "X-Request-Id") + v, ok = md.Get("X-Request-Id") if !ok { t.Fatalf("failed to get x-request-id") } else if v != "12345" { t.Fatalf("invalid metadata value: %s != %s", "12345", v) } - v, ok = Get(ctx, "X-Request-ID") + v, ok = md.Get("X-Request-ID") if !ok { t.Fatalf("failed to get x-request-id") } else if v != "12345" { @@ -61,9 +60,11 @@ func TestMedataCanonicalKey(t *testing.T) { } func TestMetadataSet(t *testing.T) { - ctx := Set(context.TODO(), "Key", "val") + md := New(1) - val, ok := Get(ctx, "Key") + md.Set("Key", "val") + + val, ok := md.Get("Key") if !ok { t.Fatal("key Key not found") } @@ -78,15 +79,8 @@ func TestMetadataDelete(t *testing.T) { "Baz": "empty", } - ctx := NewContext(context.TODO(), md) - ctx = Del(ctx, "Baz") - - emd, ok := FromContext(ctx) - if !ok { - t.Fatal("key Key not found") - } - - _, ok = emd["Baz"] + md.Del("Baz") + _, ok := md.Get("Baz") if ok { t.Fatal("key Baz not deleted") } @@ -137,42 +131,3 @@ func TestMetadataContext(t *testing.T) { t.Errorf("Expected metadata length 1 got %d", i) } } - -func TestMergeContext(t *testing.T) { - type args struct { - existing Metadata - append Metadata - overwrite bool - } - tests := []struct { - name string - args args - want Metadata - }{ - { - name: "matching key, overwrite false", - args: args{ - existing: Metadata{"Foo": "bar", "Sumo": "demo"}, - append: Metadata{"Sumo": "demo2"}, - overwrite: false, - }, - want: Metadata{"Foo": "bar", "Sumo": "demo"}, - }, - { - name: "matching key, overwrite true", - args: args{ - existing: Metadata{"Foo": "bar", "Sumo": "demo"}, - append: Metadata{"Sumo": "demo2"}, - overwrite: true, - }, - want: Metadata{"Foo": "bar", "Sumo": "demo2"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got, _ := FromContext(MergeContext(NewContext(context.TODO(), tt.args.existing), tt.args.append, tt.args.overwrite)); !reflect.DeepEqual(got, tt.want) { - t.Errorf("MergeContext() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/tracer/context.go b/tracer/context.go index 5617c098..34d88be9 100644 --- a/tracer/context.go +++ b/tracer/context.go @@ -14,11 +14,12 @@ const ( // FromContext returns a span from context func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) { - if ctx == nil { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { return "", "", false } - traceID, traceOk := metadata.Get(ctx, traceIDKey) - microID, microOk := metadata.Get(ctx, "Micro-Id") + traceID, traceOk := md.Get(traceIDKey) + microID, microOk := md.Get("Micro-Id") if !traceOk && !microOk { isFound = false return @@ -26,17 +27,17 @@ func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFo if !traceOk { traceID = microID } - parentSpanID, ok := metadata.Get(ctx, spanIDKey) + parentSpanID, ok = md.Get(spanIDKey) return traceID, parentSpanID, ok } // NewContext saves the trace and span ids in the context func NewContext(ctx context.Context, traceID, parentSpanID string) context.Context { - if ctx == nil { - ctx = context.Background() + md, ok := metadata.FromContext(ctx) + if !ok { + md = metadata.New(2) } - md := metadata.New(2) md.Set(traceIDKey, traceID) md.Set(spanIDKey, parentSpanID) - return metadata.MergeContext(ctx, md, true) + return metadata.NewContext(ctx, md) } diff --git a/util/ctx/ctx.go b/util/ctx/ctx.go index 18728e74..8d673dfe 100644 --- a/util/ctx/ctx.go +++ b/util/ctx/ctx.go @@ -10,10 +10,9 @@ import ( func FromRequest(r *http.Request) context.Context { ctx := r.Context() - md, ok := metadata.FromContext(ctx) + md, ok := metadata.FromIncomingContext(ctx) if !ok { - // create needed map with specific len - md = make(metadata.Metadata, len(r.Header)+2) + md = metadata.New(len(r.Header) + 2) } for key, val := range r.Header { md.Set(key, strings.Join(val, ",")) @@ -22,5 +21,5 @@ func FromRequest(r *http.Request) context.Context { md["Host"] = r.Host // pass http method md["Method"] = r.Method - return metadata.NewContext(ctx, md) + return metadata.NewIncomingContext(ctx, md) } diff --git a/util/ctx/ctx_test.go b/util/ctx/ctx_test.go index 10e62200..da067e61 100644 --- a/util/ctx/ctx_test.go +++ b/util/ctx/ctx_test.go @@ -28,7 +28,7 @@ func TestRequestToContext(t *testing.T) { for _, d := range testData { ctx := FromRequest(d.request) - md, ok := metadata.FromContext(ctx) + md, ok := metadata.FromIncomingContext(ctx) if !ok { t.Fatalf("Expected metadata for request %+v", d.request) }