diff --git a/cache/memcached_client.go b/cache/memcached_client.go index 12c8ee37a..77e544aa1 100644 --- a/cache/memcached_client.go +++ b/cache/memcached_client.go @@ -488,7 +488,7 @@ func (c *memcachedClient) sortKeysByServer(keys []string) []string { bucketed[addrString] = append(bucketed[addrString], key) } - var out []string + out := make([]string, 0, len(keys)) for srv := range bucketed { out = append(out, bucketed[srv]...) } diff --git a/cache/memcached_client_test.go b/cache/memcached_client_test.go index e45ecd4ac..d5fb896be 100644 --- a/cache/memcached_client_test.go +++ b/cache/memcached_client_test.go @@ -2,7 +2,8 @@ package cache import ( "context" - "errors" + "fmt" + "hash/crc32" "net" "testing" "time" @@ -56,6 +57,50 @@ func TestMemcachedClient_GetMulti(t *testing.T) { }) } +func BenchmarkMemcachedClient_sortKeysByServer(b *testing.B) { + mockSelector := &mockServerSelector{ + servers: []mockServer{ + {addr: "127.0.0.1"}, + {addr: "127.0.0.2"}, + {addr: "127.0.0.3"}, + {addr: "127.0.0.4"}, + {addr: "127.0.0.5"}, + {addr: "127.0.0.6"}, + {addr: "127.0.0.7"}, + {addr: "127.0.0.8"}, + }, + } + + client, err := newMemcachedClient( + log.NewNopLogger(), + newMockMemcachedClientBackend(), + mockSelector, + MemcachedClientConfig{ + Addresses: []string{"localhost"}, + MaxAsyncConcurrency: 1, + MaxAsyncBufferSize: 10, + }, + prometheus.NewPedanticRegistry(), + "test", + ) + + if err != nil { + b.Fatal("unexpected error creating memcachedClient", err) + } + + const numKeys = 10_000 + + keys := make([]string, numKeys) + for i := 0; i < numKeys; i++ { + keys[i] = fmt.Sprintf("some-key:%d", i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + client.sortKeysByServer(keys) + } +} + type mockMemcachedClientBackend struct { allocations int values map[string]*memcache.Item @@ -99,17 +144,36 @@ func (m *mockMemcachedClientBackend) Delete(key string) error { func (m *mockMemcachedClientBackend) Close() {} -type mockServerSelector struct{} +type mockServer struct { + addr string +} -func (m mockServerSelector) SetServers(_ ...string) error { - return nil +func (m mockServer) Network() string { + return "tcp" +} + +func (m mockServer) String() string { + return m.addr +} + +type mockServerSelector struct { + servers []mockServer } -func (m mockServerSelector) PickServer(key string) (net.Addr, error) { - return nil, errors.New("mock server selector") +func (s *mockServerSelector) PickServer(key string) (net.Addr, error) { + cs := crc32.ChecksumIEEE([]byte(key)) + return s.servers[cs%uint32(len(s.servers))], nil } +func (s *mockServerSelector) Each(f func(net.Addr) error) error { + for _, srv := range s.servers { + if err := f(srv); err != nil { + return err + } + } -func (m mockServerSelector) Each(f func(net.Addr) error) error { + return nil +} +func (s *mockServerSelector) SetServers(_ ...string) error { return nil }