From 91beb61027b133c7459b50823cdd02fe59019b75 Mon Sep 17 00:00:00 2001 From: Marcin Date: Sat, 6 Apr 2024 16:20:42 +0100 Subject: [PATCH 1/6] implementation --- memcache/memcache.go | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/memcache/memcache.go b/memcache/memcache.go index 6f48caa..26e65eb 100644 --- a/memcache/memcache.go +++ b/memcache/memcache.go @@ -419,9 +419,9 @@ func (c *Client) flushAllFromAddr(addr net.Addr) error { }) } -// ping sends the version command to the given addr -func (c *Client) ping(addr net.Addr) error { - return c.withAddrRw(addr, func(conn *conn) error { +func (c *Client) version(addr net.Addr) (string, error) { + var version string + err := c.withAddrRw(addr, func(conn *conn) error { rw := conn.rw if _, err := fmt.Fprintf(rw, "version\r\n"); err != nil { return err @@ -433,15 +433,20 @@ func (c *Client) ping(addr net.Addr) error { if err != nil { return err } - - switch { - case bytes.HasPrefix(line, versionPrefix): - break - default: - return fmt.Errorf("memcache: unexpected response line from ping: %q", string(line)) + if !bytes.HasPrefix(line, versionPrefix) { + return fmt.Errorf("memcache: unexpected response line from version: %q", string(line)) } + // Then we expect a space and the version string + version = string(bytes.TrimSpace(line[len(versionPrefix):])) return nil }) + return version, err +} + +// ping sends the version command to the given addr +func (c *Client) ping(addr net.Addr) error { + _, err := c.version(addr) + return err } func (c *Client) touchFromAddr(addr net.Addr, keys []string, expiration int32) error { @@ -784,6 +789,19 @@ func (c *Client) Ping() error { return c.selector.Each(c.ping) } +func (c *Client) Version() (map[string]string, error) { + versionMap := make(map[string]string) + err := c.selector.Each(func(addr net.Addr) error { + version, err := c.version(addr) + if err != nil { + return err + } + versionMap[addr.String()] = version + return nil + }) + return versionMap, err +} + // Increment atomically increments key by delta. The return value is // the new value after being incremented or an error. If the value // didn't exist in memcached the error is ErrCacheMiss. The value in From 972868ffc387a94769b939db7af8df1aaf198f7b Mon Sep 17 00:00:00 2001 From: Marcin Date: Sat, 6 Apr 2024 16:21:33 +0100 Subject: [PATCH 2/6] test --- memcache/memcache_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/memcache/memcache_test.go b/memcache/memcache_test.go index a0fa746..c33fdba 100644 --- a/memcache/memcache_test.go +++ b/memcache/memcache_test.go @@ -343,6 +343,10 @@ func testWithClient(t *testing.T, c *Client) { t.Errorf("post-DeleteAll want ErrCacheMiss, got %v", err) } + // Test Version + _, err = c.Version() + checkErr(err, "Version: %v", err) + // Test Ping err = c.Ping() checkErr(err, "error ping: %s", err) From 7cac148c1e149168d583b8c02e4775c22a90aef8 Mon Sep 17 00:00:00 2001 From: Marcin Date: Fri, 4 Apr 2025 18:25:57 +0100 Subject: [PATCH 3/6] VersionAll --- memcache/memcache.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/memcache/memcache.go b/memcache/memcache.go index 26e65eb..29f2802 100644 --- a/memcache/memcache.go +++ b/memcache/memcache.go @@ -419,6 +419,7 @@ func (c *Client) flushAllFromAddr(addr net.Addr) error { }) } +// version sends the version command to the given addr. returns the version string func (c *Client) version(addr net.Addr) (string, error) { var version string err := c.withAddrRw(addr, func(conn *conn) error { @@ -789,12 +790,15 @@ func (c *Client) Ping() error { return c.selector.Each(c.ping) } -func (c *Client) Version() (map[string]string, error) { +// VersionAll returns map of the version strings of all instances, keyed by +// their address string. If any instance is down, its version string is +// returned as an empty string. +func (c *Client) VersionAll() (map[string]string, error) { versionMap := make(map[string]string) err := c.selector.Each(func(addr net.Addr) error { version, err := c.version(addr) if err != nil { - return err + version = "" } versionMap[addr.String()] = version return nil @@ -802,6 +806,19 @@ func (c *Client) Version() (map[string]string, error) { return versionMap, err } +// Version returns the version string of the instance at the given key. +func (c *Client) Version(key string) (string, error) { + addr, err := c.selector.PickServer(key) + if err != nil { + return "", err + } + version, err := c.version(addr) + if err != nil { + return "", err + } + return version, nil +} + // Increment atomically increments key by delta. The return value is // the new value after being incremented or an error. If the value // didn't exist in memcached the error is ErrCacheMiss. The value in From ce686e5963b5b0fbdc065158093e6b99c416a0a0 Mon Sep 17 00:00:00 2001 From: Marcin Date: Fri, 4 Apr 2025 18:26:04 +0100 Subject: [PATCH 4/6] more version tests --- memcache/memcache_test.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/memcache/memcache_test.go b/memcache/memcache_test.go index c33fdba..11ca821 100644 --- a/memcache/memcache_test.go +++ b/memcache/memcache_test.go @@ -344,8 +344,31 @@ func testWithClient(t *testing.T, c *Client) { } // Test Version - _, err = c.Version() - checkErr(err, "Version: %v", err) + versions, err := c.VersionAll() + if err != nil { + t.Fatalf("VersionAll: %v", err) + } + if len(versions) != 1 { + t.Fatalf("VersionAll returned %d addresses, expected 1", len(versions)) + } + + expected_key := c.selector.(*ServerList).addrs[0].String() + _, ok := versions[expected_key] + if !ok { + var found_key string = "" + for k := range versions { + found_key = k + } + t.Fatalf("VersionAll key %q not found in %v. Found %q instead", expected_key, versions, found_key) + } + + version, err := c.Version(expected_key) + if err != nil { + t.Fatalf("Version: %v", err) + } + if version != versions[expected_key] { + t.Fatalf("Version: expected %q, got %q", versions[expected_key], version) + } // Test Ping err = c.Ping() From bec957a3dc82f6261d6bb4e0d265e3d17b9ecc66 Mon Sep 17 00:00:00 2001 From: Marcin Date: Fri, 4 Apr 2025 18:26:25 +0100 Subject: [PATCH 5/6] Each doc --- memcache/selector.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/memcache/selector.go b/memcache/selector.go index 964dbdb..5d72a40 100644 --- a/memcache/selector.go +++ b/memcache/selector.go @@ -90,6 +90,8 @@ func (ss *ServerList) SetServers(servers ...string) error { } // Each iterates over each server calling the given function +// with the server address. If the function returns an error, +// the iteration stops immediately and the error is returned. func (ss *ServerList) Each(f func(net.Addr) error) error { ss.mu.RLock() defer ss.mu.RUnlock() From 4d7fb7b64c441f395458340cacb83e0e2d4431e0 Mon Sep 17 00:00:00 2001 From: Marcin Date: Fri, 4 Apr 2025 18:26:36 +0100 Subject: [PATCH 6/6] type assertions --- memcache/selector.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/memcache/selector.go b/memcache/selector.go index 5d72a40..112bb05 100644 --- a/memcache/selector.go +++ b/memcache/selector.go @@ -129,3 +129,8 @@ func (ss *ServerList) PickServer(key string) (net.Addr, error) { return ss.addrs[cs%uint32(len(ss.addrs))], nil } + +var ( + _ ServerSelector = (*ServerList)(nil) + _ net.Addr = (*staticAddr)(nil) +) \ No newline at end of file