Skip to content

Commit

Permalink
Merge pull request #36 from fastly/master
Browse files Browse the repository at this point in the history
Performance enhancements
  • Loading branch information
bradfitz authored Feb 8, 2017
2 parents 2fafb84 + 1004392 commit 1952afa
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 8 deletions.
30 changes: 24 additions & 6 deletions memcache/memcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,17 @@ var (
ErrNoServers = errors.New("memcache: no servers configured or available")
)

// DefaultTimeout is the default socket read/write timeout.
const DefaultTimeout = 100 * time.Millisecond

const (
buffered = 8 // arbitrary buffered channel size, for readability
maxIdleConnsPerAddr = 2 // TODO(bradfitz): make this configurable?
// DefaultTimeout is the default socket read/write timeout.
DefaultTimeout = 100 * time.Millisecond

// DefaultMaxIdleConns is the default maximum number of idle connections
// kept for any single address.
DefaultMaxIdleConns = 2
)

const buffered = 8 // arbitrary buffered channel size, for readability

// resumableError returns true if err is only a protocol-level cache error.
// This is used to determine whether or not a server connection should
// be re-used or not. If an error occurs, by default we don't reuse the
Expand Down Expand Up @@ -133,6 +136,14 @@ type Client struct {
// If zero, DefaultTimeout is used.
Timeout time.Duration

// MaxIdleConns specifies the maximum number of idle connections that will
// be maintained per address. If less than one, DefaultMaxIdleConns will be
// used.
//
// Consider your expected traffic rates and latency carefully. This should
// be set to a number higher than your peak parallel requests.
MaxIdleConns int

selector ServerSelector

lk sync.Mutex
Expand Down Expand Up @@ -196,7 +207,7 @@ func (c *Client) putFreeConn(addr net.Addr, cn *conn) {
c.freeconn = make(map[string][]*conn)
}
freelist := c.freeconn[addr.String()]
if len(freelist) >= maxIdleConnsPerAddr {
if len(freelist) >= c.maxIdleConns() {
cn.nc.Close()
return
}
Expand Down Expand Up @@ -225,6 +236,13 @@ func (c *Client) netTimeout() time.Duration {
return DefaultTimeout
}

func (c *Client) maxIdleConns() int {
if c.MaxIdleConns > 0 {
return c.MaxIdleConns
}
return DefaultMaxIdleConns
}

// ConnectTimeoutError is the error type used when it takes
// too long to connect to the desired host. This level of
// detail can generally be ignored.
Expand Down
33 changes: 33 additions & 0 deletions memcache/memcache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ limitations under the License.
package memcache

import (
"bufio"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"os/exec"
Expand Down Expand Up @@ -254,3 +257,33 @@ func testTouchWithClient(t *testing.T, c *Client) {
}
}
}

func BenchmarkOnItem(b *testing.B) {
fakeServer, err := net.Listen("tcp", "localhost:0")
if err != nil {
b.Fatal("Could not open fake server: ", err)
}
defer fakeServer.Close()
go func() {
for {
if c, err := fakeServer.Accept(); err == nil {
go func() { io.Copy(ioutil.Discard, c) }()
} else {
return
}
}
}()

addr := fakeServer.Addr()
c := New(addr.String())
if _, err := c.getConn(addr); err != nil {
b.Fatal("failed to initialize connection to fake server")
}

item := Item{Key: "foo"}
dummyFn := func(_ *Client, _ *bufio.ReadWriter, _ *Item) error { return nil }
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.onItem(&item, dummyFn)
}
}
19 changes: 17 additions & 2 deletions memcache/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ type ServerList struct {
addrs []net.Addr
}

// staticAddr caches the Network() and String() values from any net.Addr.
type staticAddr struct {
ntw, str string
}

func newStaticAddr(a net.Addr) net.Addr {
return &staticAddr{
ntw: a.Network(),
str: a.String(),
}
}

func (s *staticAddr) Network() string { return s.ntw }
func (s *staticAddr) String() string { return s.str }

// SetServers changes a ServerList's set of servers at runtime and is
// safe for concurrent use by multiple goroutines.
//
Expand All @@ -58,13 +73,13 @@ func (ss *ServerList) SetServers(servers ...string) error {
if err != nil {
return err
}
naddr[i] = addr
naddr[i] = newStaticAddr(addr)
} else {
tcpaddr, err := net.ResolveTCPAddr("tcp", server)
if err != nil {
return err
}
naddr[i] = tcpaddr
naddr[i] = newStaticAddr(tcpaddr)
}
}

Expand Down

0 comments on commit 1952afa

Please sign in to comment.