Skip to content
31 changes: 29 additions & 2 deletions memcache/memcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import (
"io"
"io/ioutil"
"net"

"strconv"
"strings"
"sync"
"sync/atomic"
"time"
)

Expand Down Expand Up @@ -129,6 +129,12 @@ func NewFromSelector(ss ServerSelector) *Client {
return &Client{selector: ss}
}

// Stats contains statistic about connections being used by client.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

statistics (plural)

type Stats struct {
ActiveConns int
IdleConns int
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document these two also.

}

// Client is a memcache client.
// It is safe for unlocked use by multiple concurrent goroutines.
type Client struct {
Expand All @@ -144,9 +150,12 @@ type Client struct {
// be set to a number higher than your peak parallel requests.
MaxIdleConns int

// number of currently used connections
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add docs that this must be accessed atomically

activeConns int32

selector ServerSelector

lk sync.Mutex
lk sync.RWMutex
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't seem worth it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe more straightforward to just make idleConns an atomic int as well and inc/dec when freeconn changes?

freeconn map[string][]*conn
}

Expand Down Expand Up @@ -193,6 +202,7 @@ func (cn *conn) extendDeadline() {
// cache miss). The purpose is to not recycle TCP connections that
// are bad.
func (cn *conn) condRelease(err *error) {
atomic.AddInt32(&cn.c.activeConns, -1)
if *err == nil || resumableError(*err) {
cn.release()
} else {
Expand Down Expand Up @@ -276,6 +286,7 @@ func (c *Client) getConn(addr net.Addr) (*conn, error) {
cn, ok := c.getFreeConn(addr)
if ok {
cn.extendDeadline()
atomic.AddInt32(&c.activeConns, 1)
return cn, nil
}
nc, err := c.dial(addr)
Expand All @@ -289,6 +300,7 @@ func (c *Client) getConn(addr net.Addr) (*conn, error) {
c: c,
}
cn.extendDeadline()
atomic.AddInt32(&c.activeConns, 1)
return cn, nil
}

Expand Down Expand Up @@ -465,6 +477,21 @@ func (c *Client) GetMulti(keys []string) (map[string]*Item, error) {
return m, err
}

// Stats returns current statistic
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the current statistics. (with trailing period)

func (c *Client) Stats() Stats {
c.lk.RLock()
idleConns := 0
for _, conns := range c.freeconn {
idleConns += len(conns)
}
c.lk.RUnlock()

return Stats{
ActiveConns: int(atomic.LoadInt32(&c.activeConns)),
IdleConns: idleConns,
}
}

// parseGetResponse reads a GET response from r and calls cb for each
// read and allocated Item
func parseGetResponse(r *bufio.Reader, cb func(*Item)) error {
Expand Down