Skip to content

Commit f4e6116

Browse files
author
Konstantin Nikitin
committed
Fetching statistics from memcached server
1 parent 1952afa commit f4e6116

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

memcache/memcache.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ var (
6262

6363
// ErrNoServers is returned when no servers are configured or available.
6464
ErrNoServers = errors.New("memcache: no servers configured or available")
65+
66+
// ErrBadStatsLine means that the response line was expected to be
67+
// a line with server statistics information, but the parser was not
68+
// able to parse it.
69+
ErrBadStatsLine = errors.New("memcache: bad stats line")
6570
)
6671

6772
const (
@@ -113,6 +118,12 @@ var (
113118
resultTouched = []byte("TOUCHED\r\n")
114119

115120
resultClientErrorPrefix = []byte("CLIENT_ERROR ")
121+
122+
statsGetHits = []byte("STAT get_hits ")
123+
statsGetMisses = []byte("STAT get_misses ")
124+
statsBytesWritten = []byte("STAT bytes_written ")
125+
statsItems = []byte("STAT curr_items ")
126+
statsBytes = []byte("STAT bytes ")
116127
)
117128

118129
// New returns a memcache client using the provided server(s)
@@ -179,6 +190,24 @@ type conn struct {
179190
c *Client
180191
}
181192

193+
// Statistics is a record of a single memcached server usage stats.
194+
type Statistics struct {
195+
// Hits is a counter of cache hits.
196+
Hits uint64
197+
198+
// Misses is a counter of cache misses.
199+
Misses uint64
200+
201+
// ByteHits is amount of bytes transferred for gets.
202+
ByteHits uint64
203+
204+
// Items is amount of keys currently in the cache.
205+
Items uint64
206+
207+
// Bytes is a size of all items currently in the cache.
208+
Bytes uint64
209+
}
210+
182211
// release returns this connection back to the client's free pool
183212
func (cn *conn) release() {
184213
cn.c.putFreeConn(cn.addr, cn)
@@ -682,3 +711,98 @@ func (c *Client) incrDecr(verb, key string, delta uint64) (uint64, error) {
682711
})
683712
return val, err
684713
}
714+
715+
func parseStatsLine(s []byte, expectedPrefix []byte) (uint64, error) {
716+
if !bytes.HasPrefix(s, expectedPrefix) {
717+
return 0, ErrBadStatsLine
718+
}
719+
return strconv.ParseUint(
720+
string(s[len(expectedPrefix):len(s)-2]), 10, 64)
721+
}
722+
723+
func parseStatsResponse(r *bufio.Reader, stats *Statistics) error {
724+
for {
725+
line, err := r.ReadSlice('\n')
726+
if err != nil {
727+
return err
728+
}
729+
if bytes.Equal(line, resultEnd) {
730+
return nil
731+
}
732+
if hits, err := parseStatsLine(line, statsGetHits); err == nil {
733+
stats.Hits = hits
734+
continue
735+
}
736+
if misses, err := parseStatsLine(line, statsGetMisses); err == nil {
737+
stats.Misses = misses
738+
continue
739+
}
740+
if bytesWritten, err := parseStatsLine(line, statsBytesWritten); err == nil {
741+
stats.ByteHits = bytesWritten
742+
continue
743+
}
744+
if items, err := parseStatsLine(line, statsItems); err == nil {
745+
stats.Items = items
746+
continue
747+
}
748+
if bytes_total, err := parseStatsLine(line, statsBytes); err == nil {
749+
stats.Bytes = bytes_total
750+
continue
751+
}
752+
}
753+
}
754+
755+
// Stats returns memcached statistics for each server in the server list.
756+
func (c *Client) Stats() (map[string]*Statistics, error) {
757+
var mlk sync.Mutex
758+
m := make(map[string]*Statistics)
759+
addItemToMap := func(server string, s *Statistics) {
760+
mlk.Lock()
761+
defer mlk.Unlock()
762+
m[server] = s
763+
}
764+
765+
// Eliminate duplicates.
766+
serversMap := make(map[net.Addr]string)
767+
c.selector.Each(func(a net.Addr) error {
768+
serversMap[a] = a.String()
769+
return nil
770+
})
771+
772+
// Query servers.
773+
ch := make(chan error)
774+
for addr, _ := range serversMap {
775+
go func(addr net.Addr) {
776+
cn, err := c.getConn(addr)
777+
if err != nil {
778+
ch <- err
779+
return
780+
}
781+
defer cn.condRelease(&err)
782+
783+
if _, err := fmt.Fprintf(cn.rw, "stats\r\n"); err != nil {
784+
ch <- err
785+
return
786+
}
787+
if err := cn.rw.Flush(); err != nil {
788+
ch <- err
789+
return
790+
}
791+
stats := new(Statistics)
792+
if err := parseStatsResponse(cn.rw.Reader, stats); err != nil {
793+
ch <- err
794+
return
795+
}
796+
addItemToMap(cn.addr.String(), stats)
797+
ch <- nil
798+
}(addr)
799+
}
800+
801+
var err error
802+
for _ = range serversMap {
803+
if ge := <-ch; ge != nil {
804+
err = ge
805+
}
806+
}
807+
return m, err
808+
}

0 commit comments

Comments
 (0)