diff --git a/memcache/memcache.go b/memcache/memcache.go index 25e88ca2..545a3e79 100644 --- a/memcache/memcache.go +++ b/memcache/memcache.go @@ -112,6 +112,7 @@ var ( resultTouched = []byte("TOUCHED\r\n") resultClientErrorPrefix = []byte("CLIENT_ERROR ") + versionPrefix = []byte("VERSION") ) // New returns a memcache client using the provided server(s) @@ -398,6 +399,30 @@ 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(rw *bufio.ReadWriter) error { + if _, err := fmt.Fprintf(rw, "version\r\n"); err != nil { + return err + } + if err := rw.Flush(); err != nil { + return err + } + line, err := rw.ReadSlice('\n') + 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)) + } + return nil + }) +} + func (c *Client) touchFromAddr(addr net.Addr, keys []string, expiration int32) error { return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error { for _, key := range keys { @@ -644,6 +669,12 @@ func (c *Client) DeleteAll() error { }) } +// Ping checks all instances if they are alive. Returns error if any +// of them is down. +func (c *Client) Ping() error { + return c.selector.Each(c.ping) +} + // 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 4b52a911..70d47026 100644 --- a/memcache/memcache_test.go +++ b/memcache/memcache_test.go @@ -209,6 +209,9 @@ func testWithClient(t *testing.T, c *Client) { t.Errorf("post-DeleteAll want ErrCacheMiss, got %v", err) } + // Test Ping + err = c.Ping() + checkErr(err, "error ping: %s", err) } func testTouchWithClient(t *testing.T, c *Client) {