From 2f8c0d9f9d34706ce6e054d41e287d3feeda6120 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sat, 9 Mar 2024 17:21:20 +0300 Subject: [PATCH] rework metadata Signed-off-by: Vasiliy Tolstov --- broker/memory_test.go | 16 +++---- metadata/context.go | 78 ++++++++------------------------ metadata/context_test.go | 36 ++------------- metadata/metadata.go | 92 ++++++++++++++------------------------ metadata/metadata_test.go | 66 ++++++++++----------------- options/options.go | 21 ++++++--- options/options_test.go | 36 ++++++--------- register/memory/memory.go | 71 ++++++++++++----------------- server/noop.go | 4 +- server/registry.go | 4 +- util/reflect/reflect.go | 3 ++ util/time/duration_test.go | 4 +- 12 files changed, 152 insertions(+), 279 deletions(-) diff --git a/broker/memory_test.go b/broker/memory_test.go index d8bc0a9c..aa8e8211 100644 --- a/broker/memory_test.go +++ b/broker/memory_test.go @@ -37,10 +37,10 @@ func TestMemoryBatchBroker(t *testing.T) { msgs := make([]Message, 0, count) for i := 0; i < count; i++ { message := &memoryMessage{ - header: map[string]string{ - metadata.HeaderTopic: topic, - "foo": "bar", - "id": fmt.Sprintf("%d", i), + header: metadata.Metadata{ + metadata.HeaderTopic: []string{topic}, + "foo": []string{"bar"}, + "id": []string{fmt.Sprintf("%d", i)}, }, body: []byte(`"hello world"`), } @@ -83,10 +83,10 @@ func TestMemoryBroker(t *testing.T) { msgs := make([]Message, 0, count) for i := 0; i < count; i++ { message := &memoryMessage{ - header: map[string]string{ - metadata.HeaderTopic: topic, - "foo": "bar", - "id": fmt.Sprintf("%d", i), + header: metadata.Metadata{ + metadata.HeaderTopic: []string{topic}, + "foo": []string{"bar"}, + "id": []string{fmt.Sprintf("%d", i)}, }, body: []byte(`"hello world"`), } diff --git a/metadata/context.go b/metadata/context.go index 1406b30d..340c1b69 100644 --- a/metadata/context.go +++ b/metadata/context.go @@ -17,11 +17,11 @@ func FromIncomingContext(ctx context.Context) (Metadata, bool) { if ctx == nil { return nil, false } - md, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata) - if !ok || md.md == nil { + md, ok := ctx.Value(mdIncomingKey{}).(Metadata) + if !ok || md == nil { return nil, false } - return md.md, ok + return md, ok } // FromOutgoingContext returns metadata from outgoing ctx @@ -30,11 +30,11 @@ func FromOutgoingContext(ctx context.Context) (Metadata, bool) { if ctx == nil { return nil, false } - md, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata) - if !ok || md.md == nil { + md, ok := ctx.Value(mdOutgoingKey{}).(Metadata) + if !ok || md == nil { return nil, false } - return md.md, ok + return md, ok } // FromContext returns metadata from the given context @@ -43,11 +43,11 @@ func FromContext(ctx context.Context) (Metadata, bool) { if ctx == nil { return nil, false } - md, ok := ctx.Value(mdKey{}).(*rawMetadata) - if !ok || md.md == nil { + md, ok := ctx.Value(mdKey{}).(Metadata) + if !ok || md == nil { return nil, false } - return md.md, ok + return md, ok } // NewContext creates a new context with the given metadata @@ -55,45 +55,16 @@ func NewContext(ctx context.Context, md Metadata) context.Context { if ctx == nil { ctx = context.Background() } - ctx = context.WithValue(ctx, mdKey{}, &rawMetadata{md}) - ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{}) - ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{}) + ctx = context.WithValue(ctx, mdKey{}, md) return ctx } -// SetOutgoingContext modify outgoing context with given metadata -func SetOutgoingContext(ctx context.Context, md Metadata) bool { - if ctx == nil { - return false - } - if omd, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata); ok { - omd.md = md - return true - } - return false -} - -// SetIncomingContext modify incoming context with given metadata -func SetIncomingContext(ctx context.Context, md Metadata) bool { - if ctx == nil { - return false - } - if omd, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata); ok { - omd.md = md - return true - } - return false -} - // NewIncomingContext creates a new context with incoming metadata attached func NewIncomingContext(ctx context.Context, md Metadata) context.Context { if ctx == nil { ctx = context.Background() } - ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{md}) - if v, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata); !ok || v == nil { - ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{}) - } + ctx = context.WithValue(ctx, mdIncomingKey{}, md) return ctx } @@ -102,41 +73,28 @@ func NewOutgoingContext(ctx context.Context, md Metadata) context.Context { if ctx == nil { ctx = context.Background() } - ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{md}) - if v, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata); !ok || v == nil { - ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{}) - } + ctx = context.WithValue(ctx, mdOutgoingKey{}, md) return ctx } // AppendOutgoingContext apends new md to context func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context { - md, ok := Pairs(kv...) - if !ok { - return ctx - } + md := Pairs(kv...) omd, ok := FromOutgoingContext(ctx) if !ok { return NewOutgoingContext(ctx, md) } - for k, v := range md { - omd.Set(k, v) - } - return NewOutgoingContext(ctx, omd) + nmd := Merge(omd, md, true) + return NewOutgoingContext(ctx, nmd) } // AppendIncomingContext apends new md to context func AppendIncomingContext(ctx context.Context, kv ...string) context.Context { - md, ok := Pairs(kv...) - if !ok { - return ctx - } + md := Pairs(kv...) omd, ok := FromIncomingContext(ctx) if !ok { return NewIncomingContext(ctx, md) } - for k, v := range md { - omd.Set(k, v) - } - return NewIncomingContext(ctx, omd) + nmd := Merge(omd, md, true) + return NewIncomingContext(ctx, nmd) } diff --git a/metadata/context_test.go b/metadata/context_test.go index deaa020a..e7df6b06 100644 --- a/metadata/context_test.go +++ b/metadata/context_test.go @@ -24,7 +24,7 @@ func TestNewNilContext(t *testing.T) { } func TestFromContext(t *testing.T) { - ctx := context.WithValue(context.TODO(), mdKey{}, &rawMetadata{New(0)}) + ctx := context.WithValue(context.TODO(), mdKey{}, New(0)) c, ok := FromContext(ctx) if c == nil || !ok { @@ -42,7 +42,7 @@ func TestNewContext(t *testing.T) { } func TestFromIncomingContext(t *testing.T) { - ctx := context.WithValue(context.TODO(), mdIncomingKey{}, &rawMetadata{New(0)}) + ctx := context.WithValue(context.TODO(), mdIncomingKey{}, New(0)) c, ok := FromIncomingContext(ctx) if c == nil || !ok { @@ -51,7 +51,7 @@ func TestFromIncomingContext(t *testing.T) { } func TestFromOutgoingContext(t *testing.T) { - ctx := context.WithValue(context.TODO(), mdOutgoingKey{}, &rawMetadata{New(0)}) + ctx := context.WithValue(context.TODO(), mdOutgoingKey{}, New(0)) c, ok := FromOutgoingContext(ctx) if c == nil || !ok { @@ -59,36 +59,6 @@ func TestFromOutgoingContext(t *testing.T) { } } -func TestSetIncomingContext(t *testing.T) { - md := New(1) - md.Set("key", "val") - ctx := context.WithValue(context.TODO(), mdIncomingKey{}, &rawMetadata{}) - if !SetIncomingContext(ctx, md) { - t.Fatal("SetIncomingContext not works") - } - md, ok := FromIncomingContext(ctx) - if md == nil || !ok { - t.Fatal("SetIncomingContext not works") - } else if v, ok := md.Get("key"); !ok || v != "val" { - t.Fatal("SetIncomingContext not works") - } -} - -func TestSetOutgoingContext(t *testing.T) { - md := New(1) - md.Set("key", "val") - ctx := context.WithValue(context.TODO(), mdOutgoingKey{}, &rawMetadata{}) - if !SetOutgoingContext(ctx, md) { - t.Fatal("SetOutgoingContext not works") - } - md, ok := FromOutgoingContext(ctx) - if md == nil || !ok { - t.Fatal("SetOutgoingContext not works") - } else if v, ok := md.Get("key"); !ok || v != "val" { - t.Fatal("SetOutgoingContext not works") - } -} - func TestNewIncomingContext(t *testing.T) { md := New(1) md.Set("key", "val") diff --git a/metadata/metadata.go b/metadata/metadata.go index 34434f21..13a1c406 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -1,9 +1,9 @@ // Package metadata is a way of defining message headers -package metadata // import "go.unistack.org/micro/v4/metadata" +package metadata import ( "net/textproto" - "sort" + "strings" ) var ( @@ -24,47 +24,7 @@ var ( ) // 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 - -type rawMetadata struct { - md Metadata -} - -// defaultMetadataSize used when need to init new Metadata -var defaultMetadataSize = 2 - -// Iterator used to iterate over metadata with order -type Iterator struct { - md Metadata - keys []string - cur int - cnt int -} - -// Next advance iterator to next element -func (iter *Iterator) Next(k, v *string) bool { - if iter.cur+1 > iter.cnt { - return false - } - - *k = iter.keys[iter.cur] - *v = iter.md[*k] - iter.cur++ - return true -} - -// Iterator returns the itarator for metadata in sorted order -func (md Metadata) Iterator() *Iterator { - iter := &Iterator{md: md, cnt: len(md)} - iter.keys = make([]string, 0, iter.cnt) - for k := range md { - iter.keys = append(iter.keys, k) - } - sort.Strings(iter.keys) - return iter -} +type Metadata map[string][]string // Get returns value from metadata by key func (md Metadata) Get(key string) (string, bool) { @@ -74,7 +34,7 @@ func (md Metadata) Get(key string) (string, bool) { // slow path val, ok = md[textproto.CanonicalMIMEHeaderKey(key)] } - return val, ok + return strings.Join(val, ","), ok } // Set is used to store value in metadata @@ -83,10 +43,19 @@ func (md Metadata) Set(kv ...string) { kv = kv[:len(kv)-1] } for idx := 0; idx < len(kv); idx += 2 { - md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = kv[idx+1] + md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = []string{kv[idx+1]} } } +// Append is used to append value in metadata +func (md Metadata) Append(k string, v ...string) { + if len(v) == 0 { + return + } + k = textproto.CanonicalMIMEHeaderKey(k) + md[k] = append(md[k], v...) +} + // Del is used to remove value from metadata func (md Metadata) Del(keys ...string) { for _, key := range keys { @@ -99,9 +68,9 @@ func (md Metadata) Del(keys ...string) { // Copy makes a copy of the metadata func Copy(md Metadata) Metadata { - nmd := New(len(md)) - for key, val := range md { - nmd.Set(key, val) + nmd := make(Metadata, len(md)) + for k, v := range md { + nmd[k] = v } return nmd } @@ -109,35 +78,40 @@ func Copy(md Metadata) Metadata { // New return new sized metadata func New(size int) Metadata { if size == 0 { - size = defaultMetadataSize + size = 2 } return make(Metadata, size) } // Merge merges metadata to existing metadata, overwriting if specified func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata { - var ok bool nmd := Copy(omd) for key, val := range mmd { - _, ok = nmd[key] + nval, ok := nmd[key] switch { + case ok && overwrite: + nmd[key] = nval + continue case ok && !overwrite: continue - case val != "": - nmd.Set(key, val) - case ok && val == "": - nmd.Del(key) + case !ok: + for _, v := range val { + if v != "" { + nval = append(nval, v) + } + } + nmd[key] = nval } } return nmd } // Pairs from which metadata created -func Pairs(kv ...string) (Metadata, bool) { +func Pairs(kv ...string) Metadata { if len(kv)%2 == 1 { - return nil, false + kv = kv[:len(kv)-1] } - md := New(len(kv) / 2) + md := make(Metadata, len(kv)/2) md.Set(kv...) - return md, true + return md } diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go index eccea7a7..6155e535 100644 --- a/metadata/metadata_test.go +++ b/metadata/metadata_test.go @@ -33,20 +33,25 @@ func TestAppend(t *testing.T) { } func TestPairs(t *testing.T) { - md, ok := Pairs("key1", "val1", "key2", "val2") - if !ok { - t.Fatal("odd number of kv") - } - if _, ok = md.Get("key1"); !ok { + md := Pairs("key1", "val1", "key2", "val2") + + if _, ok := md.Get("key1"); !ok { t.Fatal("key1 not found") } } -func testCtx(ctx context.Context) { - md := New(2) - md.Set("Key1", "Val1_new") - md.Set("Key3", "Val3") - SetOutgoingContext(ctx, md) +func testIncomingCtx(ctx context.Context) { + if md, ok := FromIncomingContext(ctx); ok && md != nil { + md.Set("Key1", "Val1_new") + md.Set("Key3", "Val3") + } +} + +func testOutgoingCtx(ctx context.Context) { + if md, ok := FromOutgoingContext(ctx); ok && md != nil { + md.Set("Key1", "Val1_new") + md.Set("Key3", "Val3") + } } func TestPassing(t *testing.T) { @@ -55,8 +60,8 @@ func TestPassing(t *testing.T) { md1.Set("Key1", "Val1") md1.Set("Key2", "Val2") - ctx = NewIncomingContext(ctx, md1) - testCtx(ctx) + ctx = NewOutgoingContext(ctx, md1) + testOutgoingCtx(ctx) md, ok := FromOutgoingContext(ctx) if !ok { t.Fatalf("missing metadata from outgoing context") @@ -68,10 +73,10 @@ func TestPassing(t *testing.T) { func TestMerge(t *testing.T) { omd := Metadata{ - "key1": "val1", + "key1": []string{"val1"}, } mmd := Metadata{ - "key2": "val2", + "key2": []string{"val2"}, } nmd := Merge(omd, mmd, true) @@ -80,21 +85,6 @@ func TestMerge(t *testing.T) { } } -func TestIterator(t *testing.T) { - md := Metadata{ - "1Last": "last", - "2First": "first", - "3Second": "second", - } - - iter := md.Iterator() - var k, v string - - for iter.Next(&k, &v) { - // fmt.Printf("k: %s, v: %s\n", k, v) - } -} - func TestMedataCanonicalKey(t *testing.T) { md := New(1) md.Set("x-request-id", "12345") @@ -134,10 +124,7 @@ func TestMetadataSet(t *testing.T) { } func TestMetadataDelete(t *testing.T) { - md := Metadata{ - "Foo": "bar", - "Baz": "empty", - } + md := Pairs("Foo", "bar", "Baz", "empty") md.Del("Baz") _, ok := md.Get("Baz") @@ -156,24 +143,19 @@ func TestNilContext(t *testing.T) { } func TestMetadataCopy(t *testing.T) { - md := Metadata{ - "Foo": "bar", - "Bar": "baz", - } + md := Pairs("Foo", "bar", "Bar", "baz") cp := Copy(md) for k, v := range md { - if cv := cp[k]; cv != v { + if cv := cp[k]; len(cv) != len(v) { t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v) } } } func TestMetadataContext(t *testing.T) { - md := Metadata{ - "Foo": "bar", - } + md := Pairs("Foo", "bar") ctx := NewContext(context.TODO(), md) @@ -182,7 +164,7 @@ func TestMetadataContext(t *testing.T) { t.Errorf("Unexpected error retrieving metadata, got %t", ok) } - if emd["Foo"] != md["Foo"] { + if len(emd["Foo"]) != len(md["Foo"]) { t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"]) } diff --git a/options/options.go b/options/options.go index cbcf15fd..2417cff2 100644 --- a/options/options.go +++ b/options/options.go @@ -158,17 +158,28 @@ func Metadata(md ...any) Option { case metadata.Metadata: result = metadata.Copy(vt) case map[string]string: + result = make(metadata.Metadata, len(vt)) + for k, v := range vt { + result.Set(k, v) + } + case map[string][]string: result = metadata.Copy(vt) default: result = metadata.New(0) } } else { result = metadata.New(len(md) / 2) - for idx := 0; idx < len(md)/2; idx += 2 { - k, kok := md[idx].(string) - v, vok := md[idx+1].(string) - if kok && vok { - result.Set(k, v) + for idx := 0; idx <= len(md)/2; idx += 2 { + k, ok := md[idx].(string) + switch vt := md[idx+1].(type) { + case string: + if ok { + result.Set(k, vt) + } + case []string: + if ok { + result.Append(k, vt...) + } } } } diff --git a/options/options_test.go b/options/options_test.go index a2f26e8a..acb64651 100644 --- a/options/options_test.go +++ b/options/options_test.go @@ -100,40 +100,29 @@ func TestMetadataAny(t *testing.T) { }{ { "strings_even", - []any{"key1", "val1", "key2", "val2"}, - metadata.Metadata{ - "Key1": "val1", - "Key2": "val2", - }, + []any{"Strkey1", []string{"val1"}, "Strkey2", []string{"val2"}}, + metadata.Pairs("Strkey1", "val1", "Strkey2", "val2"), }, { "strings_odd", []any{"key1", "val1", "key2"}, - metadata.Metadata{ - "Key1": "val1", - }, + metadata.Pairs("Key1", "val1"), }, { Name: "map", - Data: map[string]string{ - "key1": "val1", - "key2": "val2", + Data: map[string][]string{ + "Mapkey1": {"val1"}, + "Mapkey2": {"val2"}, }, Expected: metadata.Metadata{ - "Key1": "val1", - "Key2": "val2", + "Mapkey1": []string{"val1"}, + "Mapkey2": []string{"val2"}, }, }, { "metadata.Metadata", - metadata.Metadata{ - "key1": "val1", - "key2": "val2", - }, - metadata.Metadata{ - "Key1": "val1", - "Key2": "val2", - }, + metadata.Pairs("key1", "val1", "key2", "val2"), + metadata.Pairs("Key1", "val1", "Key2", "val2"), }, } @@ -143,8 +132,9 @@ func TestMetadataAny(t *testing.T) { var opts []options.Option switch valData := tt.Data.(type) { case []any: + fmt.Printf("%s any %#+v\n", tt.Name, valData) opts = append(opts, options.Metadata(valData...)) - case map[string]string, metadata.Metadata: + case map[string]string, map[string][]string, metadata.Metadata: opts = append(opts, options.Metadata(valData)) } @@ -153,7 +143,7 @@ func TestMetadataAny(t *testing.T) { t.Fatal(err) } if !reflect.Equal(tt.Expected, src.Metadata) { - t.Fatal(fmt.Sprintf("expected: %v, actual: %v", tt.Expected, src.Metadata)) + t.Fatalf("expected: %v, actual: %v", tt.Expected, src.Metadata) } } }) diff --git a/register/memory/memory.go b/register/memory/memory.go index 8be85a0f..e4385597 100644 --- a/register/memory/memory.go +++ b/register/memory/memory.go @@ -2,10 +2,12 @@ package memory import ( "context" - "go.unistack.org/micro/v4/register" "sync" "time" + "go.unistack.org/micro/v4/metadata" + "go.unistack.org/micro/v4/register" + "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/util/id" ) @@ -24,7 +26,7 @@ type node struct { type record struct { Name string Version string - Metadata map[string]string + Metadata metadata.Metadata Nodes map[string]*node Endpoints []*register.Endpoint } @@ -137,11 +139,11 @@ func (m *memory) Register(ctx context.Context, s *register.Service, opts ...regi // domain is set in metadata so it can be passed to watchers if s.Metadata == nil { - s.Metadata = map[string]string{"domain": options.Domain} - } else { - s.Metadata["domain"] = options.Domain + s.Metadata = metadata.New(0) } + s.Metadata.Set("domain", options.Domain) + // ensure the service name exists r := serviceToRecord(s, options.TTL) if _, ok := srvs[s.Name]; !ok { @@ -165,15 +167,8 @@ func (m *memory) Register(ctx context.Context, s *register.Service, opts ...regi continue } - metadata := make(map[string]string, len(n.Metadata)) - - // make copy of metadata - for k, v := range n.Metadata { - metadata[k] = v - } - - // set the domain - metadata["domain"] = options.Domain + metadata := metadata.Copy(n.Metadata) + metadata.Set("domain", options.Domain) // add the node srvs[s.Name][s.Version].Nodes[n.ID] = &node{ @@ -217,10 +212,9 @@ func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...re // domain is set in metadata so it can be passed to watchers if s.Metadata == nil { - s.Metadata = map[string]string{"domain": options.Domain} - } else { - s.Metadata["domain"] = options.Domain + s.Metadata = metadata.New(0) } + s.Metadata.Set("domain", options.Domain) // if the domain doesn't exist, there is nothing to deregister services, ok := m.records[options.Domain] @@ -425,16 +419,24 @@ func (m *watcher) Next() (*register.Result, error) { continue } + if m.wo.Domain == register.WildcardDomain { + return r, nil + } + + if r.Service.Metadata == nil { + continue + } + // extract domain from service metadata var domain string - if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 { - domain = r.Service.Metadata["domain"] + if v, ok := r.Service.Metadata.Get("domain"); ok && v != "" { + domain = v } else { domain = register.DefaultDomain } // only send the event if watching the wildcard or this specific domain - if m.wo.Domain == register.WildcardDomain || m.wo.Domain == domain { + if m.wo.Domain == domain { return r, nil } case <-m.exit: @@ -453,10 +455,7 @@ func (m *watcher) Stop() { } func serviceToRecord(s *register.Service, ttl time.Duration) *record { - metadata := make(map[string]string, len(s.Metadata)) - for k, v := range s.Metadata { - metadata[k] = v - } + metadata := metadata.Copy(s.Metadata) nodes := make(map[string]*node, len(s.Nodes)) for _, n := range s.Nodes { @@ -482,20 +481,11 @@ func serviceToRecord(s *register.Service, ttl time.Duration) *record { } func recordToService(r *record, domain string) *register.Service { - metadata := make(map[string]string, len(r.Metadata)) - for k, v := range r.Metadata { - metadata[k] = v - } - - // set the domain in metadata so it can be determined when a wildcard query is performed - metadata["domain"] = domain - endpoints := make([]*register.Endpoint, len(r.Endpoints)) for i, e := range r.Endpoints { - md := make(map[string]string, len(e.Metadata)) - for k, v := range e.Metadata { - md[k] = v - } + md := metadata.Copy(e.Metadata) + // set the domain in metadata so it can be determined when a wildcard query is performed + md.Set("domain", domain) endpoints[i] = ®ister.Endpoint{ Name: e.Name, @@ -508,15 +498,10 @@ func recordToService(r *record, domain string) *register.Service { nodes := make([]*register.Node, len(r.Nodes)) i := 0 for _, n := range r.Nodes { - md := make(map[string]string, len(n.Metadata)) - for k, v := range n.Metadata { - md[k] = v - } - nodes[i] = ®ister.Node{ ID: n.ID, Address: n.Address, - Metadata: md, + Metadata: metadata.Copy(n.Metadata), } i++ } @@ -524,7 +509,7 @@ func recordToService(r *record, domain string) *register.Service { return ®ister.Service{ Name: r.Name, Version: r.Version, - Metadata: metadata, + Metadata: metadata.Copy(r.Metadata), Endpoints: endpoints, Nodes: nodes, } diff --git a/server/noop.go b/server/noop.go index 9b3355b9..fed12e07 100644 --- a/server/noop.go +++ b/server/noop.go @@ -112,8 +112,8 @@ func (n *noopServer) Register() error { } n.RUnlock() - service.Nodes[0].Metadata["protocol"] = "noop" - service.Nodes[0].Metadata["transport"] = service.Nodes[0].Metadata["protocol"] + service.Nodes[0].Metadata.Set("protocol", "noop") + service.Nodes[0].Metadata.Set("transport", "noop") service.Endpoints = endpoints n.RLock() diff --git a/server/registry.go b/server/registry.go index 0d247dab..17fd01ff 100644 --- a/server/registry.go +++ b/server/registry.go @@ -77,8 +77,8 @@ func NewRegisterService(s Server) (*register.Service, error) { } node.Metadata = metadata.Copy(opts.Metadata) - node.Metadata["server"] = s.String() - node.Metadata["register"] = opts.Register.String() + node.Metadata.Set("server", s.String()) + node.Metadata.Set("register", opts.Register.String()) return ®ister.Service{ Name: opts.Name, diff --git a/util/reflect/reflect.go b/util/reflect/reflect.go index 7d34b068..0950d881 100644 --- a/util/reflect/reflect.go +++ b/util/reflect/reflect.go @@ -532,6 +532,9 @@ func Equal(src interface{}, dst interface{}, excptFields ...string) bool { } s := srcVal.MapIndex(key) d := dstVal.MapIndex(key) + if !s.IsValid() || !d.IsValid() { + return false + } if !Equal(s.Interface(), d.Interface(), excptFields...) { return false } diff --git a/util/time/duration_test.go b/util/time/duration_test.go index 80b7289c..4172e059 100644 --- a/util/time/duration_test.go +++ b/util/time/duration_test.go @@ -35,7 +35,7 @@ func TestUnmarshalJSON(t *testing.T) { err = json.Unmarshal([]byte(`{"ttl":"1y"}`), v) if err != nil { t.Fatal(err) - } else if v.TTL != 31536000000000000 { + } else if v.TTL != 31622400000000000 { t.Fatalf("invalid duration %v != 31536000000000000", v.TTL) } } @@ -55,7 +55,7 @@ func TestParseDuration(t *testing.T) { if err != nil { t.Fatalf("ParseDuration error: %v", err) } - if td.String() != "8760h0m0s" { + if td.String() != "8784h0m0s" { t.Fatalf("ParseDuration 1y != 8760h0m0s : %s", td.String()) } }