From 0ab16db728c1c72c44ec17f267a9f8c8e1f4eeca Mon Sep 17 00:00:00 2001 From: Yicheng Qin Date: Thu, 6 Aug 2015 16:20:53 -0700 Subject: [PATCH] client: Sync() pin the endpoint when member list doesn't change This helps client to pin the same endpoint as long as cluster doesn't change. --- client/client.go | 14 ++++++++++++++ client/client_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/client/client.go b/client/client.go index 054b14c87..a931fd8be 100644 --- a/client/client.go +++ b/client/client.go @@ -22,6 +22,8 @@ import ( "net" "net/http" "net/url" + "reflect" + "sort" "sync" "time" @@ -313,6 +315,18 @@ func (c *httpClusterClient) Sync(ctx context.Context) error { for _, m := range ms { eps = append(eps, m.ClientURLs...) } + sort.Sort(sort.StringSlice(eps)) + + ceps := make([]string, len(c.endpoints)) + for i, cep := range c.endpoints { + ceps[i] = cep.String() + } + sort.Sort(sort.StringSlice(ceps)) + // fast path if no change happens + // this helps client to pin the endpoint when no cluster change + if reflect.DeepEqual(eps, ceps) { + return nil + } return c.reset(eps) } diff --git a/client/client_test.go b/client/client_test.go index de9184ce9..3a020d46f 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -802,6 +802,46 @@ func TestHTTPClusterClientAutoSyncFail(t *testing.T) { } } +// TestHTTPClusterClientSyncPinEndpoint tests that Sync() pins the endpoint when +// it gets the exactly same member list as before. +func TestHTTPClusterClientSyncPinEndpoint(t *testing.T) { + cf := newStaticHTTPClientFactory([]staticHTTPResponse{ + staticHTTPResponse{ + resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}}, + body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`), + }, + staticHTTPResponse{ + resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}}, + body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`), + }, + staticHTTPResponse{ + resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}}, + body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`), + }, + }) + + hc := &httpClusterClient{ + clientFactory: cf, + rand: rand.New(rand.NewSource(0)), + } + err := hc.reset([]string{"http://127.0.0.1:4003", "http://127.0.0.1:2379", "http://127.0.0.1:4001", "http://127.0.0.1:4002"}) + if err != nil { + t.Fatalf("unexpected error during setup: %#v", err) + } + pinnedEndpoint := hc.endpoints[hc.pinned] + + for i := 0; i < 3; i++ { + err = hc.Sync(context.Background()) + if err != nil { + t.Fatalf("#%d: unexpected error during Sync: %#v", i, err) + } + + if g := hc.endpoints[hc.pinned]; g != pinnedEndpoint { + t.Errorf("#%d: pinned endpoint = %s, want %s", i, g, pinnedEndpoint) + } + } +} + func TestHTTPClusterClientResetFail(t *testing.T) { tests := [][]string{ // need at least one endpoint