Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions memcache/memcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions memcache/memcache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions memcache/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
)