diff --git a/memcache/memcache.go b/memcache/memcache.go index 6f48caa..29f2802 100644 --- a/memcache/memcache.go +++ b/memcache/memcache.go @@ -419,9 +419,10 @@ 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 { +// 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 { rw := conn.rw if _, err := fmt.Fprintf(rw, "version\r\n"); err != nil { return err @@ -433,15 +434,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 +790,35 @@ func (c *Client) Ping() error { return c.selector.Each(c.ping) } +// 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 { + version = "" + } + versionMap[addr.String()] = version + return nil + }) + 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 diff --git a/memcache/memcache_test.go b/memcache/memcache_test.go index a0fa746..11ca821 100644 --- a/memcache/memcache_test.go +++ b/memcache/memcache_test.go @@ -343,6 +343,33 @@ func testWithClient(t *testing.T, c *Client) { t.Errorf("post-DeleteAll want ErrCacheMiss, got %v", err) } + // Test Version + 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() checkErr(err, "error ping: %s", err) diff --git a/memcache/selector.go b/memcache/selector.go index 964dbdb..112bb05 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() @@ -127,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