Go Memcached client library #golang

About

This is a memcache client library for the Go programming language (http://golang.org/).

Installing

Using go get

$ go get github.com/bradfitz/gomemcache/memcache

After this command gomemcache is ready to use. Its source will be in:

$GOPATH/src/github.com/bradfitz/gomemcache/memcache

Example

import (
        "github.com/bradfitz/gomemcache/memcache"
)

func main() {
     mc := memcache.New("10.0.0.1:11211", "10.0.0.2:11211", "10.0.0.3:11212")
     mc.Set(&memcache.Item{Key: "foo", Value: []byte("my value")})

     it, err := mc.Get("foo")
     ...
}

Full docs, see:

See https://godoc.org/github.com/bradfitz/gomemcache/memcache

Or run:

$ godoc github.com/bradfitz/gomemcache/memcache
Owner
Brad Fitzpatrick
Xoogler. Ex @golang team (2010-2020). Currently making WireGuard easier and more magical @Tailscale.
Brad Fitzpatrick
Comments
  • Speed up scanning memcache responses, increase throughput.

    Speed up scanning memcache responses, increase throughput.

    The Sscanf was revealed as being CPU intensive by the -cpuprofile option (It has to use reflection to know what to parse?)

    export GOMAXPROCS=4;
    go test -run='^$' -bench SetGet -benchtime 5s
    

    Before this change: BenchmarkSetGet-4 200000 50299 ns/op After this change: BenchmarkSetGet-4 200000 45301 ns/op

  • Use ReadFull instead of ioutil.ReadAll to read objects

    Use ReadFull instead of ioutil.ReadAll to read objects

    ioutil.ReadAll uses a bytes.Buffer, which allocates memory by doubling. Since we know exactly how muh data we expect to get, we can allocate it in advance. This reduces the total amount of allocation, and ensures that the slice stored in the item won't have excess capacity (which can affect memory usage if the item is held for a long time, for instance in a secondary cache).

  • App engine standard

    App engine standard

    Brad,

    Not sure if you would be interested, but I made your client work on App Engine Standard Environment -- using a memcached instance on Compute Engine. Just had to add the appengine context.

    I'm a newb at this GitHub stuff, so hopefully I did this right...

    Brian

  • fix problems with the dns resolving cache

    fix problems with the dns resolving cache

    There are problems with connecting to the memcash server by DNS name when changing the IP address of the server.

    Due to the fact that the IP address is cached and there is no mechanism for resolving a new address (updating the cache), we get constant errors connecting to the server.

    I have added some solution.

  • Add facebook/mcrouter support

    Add facebook/mcrouter support

    Problem

    We're using mcrouter (v0.40) as a scalable memcache solution, however it doesn't seem to support gets command which this library uses by default even for fetching single key values.

    Basically we get this kind of error:

    memcache: unexpected line in get response: "SERVER_ERROR unexpected result mc_res_unknown (0) for get\r\n"
    

    Probably related issue - https://github.com/bradfitz/gomemcache/issues/37#issuecomment-133867638

    Changes

    This PR

    • adds private getOneFromAddr() which uses get command under the hood & is used for fetching single key values now from Get()
    • adds private getMultiFromAddr() which uses gets from GetMulti()
    • adds public setting GetMultiSupported to the client (set to true by default)
    • changes GetMulti- if GetMultiSupported is set to false, then for mcrouter compatibility, it reads keys in sequence using getOneFromAddr
    • changes integration tests to have more visible hierarchy with t.Run inside (would be nice to make them independent though, looks like they are coupled)
    • changes integration tests to be mandatory and not be skipped if memcached server is not found
    • adds integration test for GetMulti for mcrouter case
    • fixes Set malformed keys unit test, because in go 1.15+ string(0x7f) fails to convert a string conversion from untyped int to string yields a string of one rune, not a string of digits
  • Use buffer pool to read from parseGetResponse bufio.Reader instead of using ReadAll everytime

    Use buffer pool to read from parseGetResponse bufio.Reader instead of using ReadAll everytime

    My main concern is using ioutil.ReadAll everytime the bufio.Reader is read in parseGetResponse since it will then trigger bytes.makeSlice and allocate new space each and every time the function is called.

    Instead I think using a buffer pool like fmt does would be a fairly better solution.

    I've added a benchmark in memcache_test.go that Set 4 []byte of different sizes in memcache, and then randomly Get them. Here's the result before using the buffer pool:

    $ go test ./memcache -v -run=^$ -bench=BenchmarkGet$ -benchtime=10s -parallel=4 -memprofile=prof.mem
    PASS
    BenchmarkGet-4    200000         53816 ns/op       74349 B/op         36 allocs/op
    $ go tool pprof --alloc_space memcache.test prof.mem
    (pprof) list parseGetResponse
    Total: 15.85GB
    ROUTINE ======================== github.com/asticode/gomemcache/memcache.parseGetResponse in /home/asticode/projects/go/src/github.com/asticode/gomemcache/memcache/memcache.go
       51.50MB    15.41GB (flat, cum) 97.23% of Total
             .          .    453:           return err
             .          .    454:       }
             .          .    455:       if bytes.Equal(line, resultEnd) {
             .          .    456:           return nil
             .          .    457:       }
          46MB       46MB    458:       it := new(Item)
             .       66MB    459:       size, err := scanGetResponseLine(line, it)
             .          .    460:       if err != nil {
             .          .    461:           return err
             .          .    462:       }
        5.50MB    15.30GB    463:       it.Value, err = ioutil.ReadAll(io.LimitReader(r, int64(size)+2))
             .          .    464:       if err != nil {
             .          .    465:           return err
             .          .    466:       }
             .          .    467:       if !bytes.HasSuffix(it.Value, crlf) {
             .          .    468:           return fmt.Errorf("memcache: corrupt get result read")
    

    And here's the result after using the buffer pool:

    $ go test ./memcache -v -run=^$ -bench=BenchmarkGet$ -benchtime=10s -parallel=4 -memprofile=prof.mem
    PASS
    BenchmarkGet-4    500000         27828 ns/op        1082 B/op         31 allocs/op
    $ go tool pprof --alloc_space memcache.test prof.mem
    (pprof) list parseGetResponse
    Total: 549.90MB
    ROUTINE ======================== github.com/asticode/gomemcache/memcache.parseGetResponse in /home/asticode/projects/go/src/github.com/asticode/gomemcache/memcache/memcache.go
          49MB   341.24MB (flat, cum) 62.06% of Total
             .          .    466:           return err
             .          .    467:       }
             .          .    468:       if bytes.Equal(line, resultEnd) {
             .          .    469:           return nil
             .          .    470:       }
       33.50MB    33.50MB    471:       it := new(Item)
             .   115.50MB    472:       size, err := scanGetResponseLine(line, it)
             .          .    473:       if err != nil {
             .          .    474:           return err
             .          .    475:       }
             .          .    476:       buf := bufferPool.Get().(*bytes.Buffer)
       15.50MB   192.23MB    477:       _, err = buf.ReadFrom(io.LimitReader(r, int64(size)+2))
             .          .    478:       if err != nil {
             .          .    479:           freeBuffer(buf)
             .          .    480:           return err
             .          .    481:       }
             .          .    482:       it.Value = buf.Bytes()
    
    
  • How to use Codecs?

    How to use Codecs?

    Hi,

    I'm trying to store a struct, but I can find no examples of doing so with this library.

    Googling, I see examples for using memcache.Gob.set, but these advice seem to refer to Appengine/memcache.

    At https://github.com/bradfitz/gomemcache/blob/master/memcache/memcache.go#L150 the comments refer to using a codec to set an Item, but I can't find any reference to codecs, Gob, etc in the source.

    Is this a mistake?

  • Make the maxIdleConnsPerAddr configurable

    Make the maxIdleConnsPerAddr configurable

    Write a new ‘NewWithConnThreshold’ func to configure maxIdleConnsPerAddr. Decide not to change the original 'New' func in consideration of backward compatibility.

  • Exported Metrics Views and Tags

    Exported Metrics Views and Tags

    Moved the views to their own exported variables so that they can be accessed and registered individually. Changed the tags to be exported as well to be able to add or adjust the tags assigned to the views.

  • encounter fault,

    encounter fault, "Get failed memcache: unexpected line in get response: "ERROR\r\n""

    memcached version: 1.5.1 code: // Set foo := &memcache.Item{Key: "home", Value: []byte("fooval")} err := mc.Add(foo) // work 0k here if err != nil { fmt.Println("Set failed once", err.Error()) } err = mc.Add(foo) // work 0k here if err != nil { fmt.Println("Set failed secondly", err.Error()) }

    // Get it, err := mc.Get("home") if err != nil { fmt.Println("Get failed", err.Error()) // failed here } else { if string(it.Key) == "foo" { fmt.Println("Add value is ", string(it.Value)) } else { fmt.Println("Get failed") } }

  • Add Ping method.

    Add Ping method.

    Checks if instances are alive.

    errorCollector := make(chan error)
    m := memcache.New("/var/run/memcached.sock")
    
    go func() {
    	tick := time.NewTicker(time.Second)
    	for range tick.C {
    		if err := m.Ping(); err != nil {
    			errorCollector <- err
    		}
    	}
    }()
    
    go func() {
    	for err := range errorCollector {
    		fmt.Println(err)
    	}
    }()
    
  • Idea: TCP Request pipelining support

    Idea: TCP Request pipelining support

    Currently, gomemcache

    1. acquires a connection
    2. sends a request
    3. receives a response
    4. Releases a connection back to the connection pool

    https://github.com/bradfitz/gomemcache/blob/f7880d5bcff45511fd1679c4c42e611c349e954a/memcache/memcache.go#L290-L304

    It's possible in the memcache protocol to send requests without waiting for the response (https://en.wikipedia.org/wiki/Protocol_pipelining) and this is done in various clients/proxies. This allows for lower resource usage (one connection instead of multiple connection, fewer syscalls, etc), and higher throughput when there is higher latency (e.g. memcached in a different datacenter)

    Is this something you'd be interested in a PR for?

    This is something I'd implemented in https://github.com/TysonAndre/golemproxy (a hackathon project to implement something easier to customize than twemproxy, e.g. for custom business logic to replicate commands or compare results. The source was combined to make the development cycle easier since I was the only one working on that, but the client remains as an independent component with a similar api. golemproxy worked in unit/integration tests)

    golemproxy is a local memcache proxy written in Golang. Like twemproxy, it supports pipelining and shards requests to multiple servers.

    (Another difference is that that repo accepts bytes instead of str, but that was because it was a proxy for clients that send/receive non-utf8 data, such as serialized data)

    (The gomemcache project appeared inactive when I'd worked on that support, so I hadn't created a ticket then)

  • feat: method close connections

    feat: method close connections

    It was implemented a new method to close all connections.

    Code to test

    package main
    
    import (
    	"fmt"
    
    	"github.com/bradfitz/gomemcache/memcache"
    )
    
    func main() {
    	memcached := memcache.New("127.0.0.1:11211")
    	if err := memcached.Ping(); err != nil {
    		fmt.Println("ERRR TO PING", err.Error())
    	}
    
    	err := memcached.Set(&memcache.Item{Key: "renan", Value: []byte("bastos")})
    	if err != nil {
    		fmt.Println("ERROR TO SET DATA", err.Error())
    		return
    	}
    
    	item, err := memcached.Get("renan")
    	if err != nil {
    		fmt.Println("ERROR TO GET DATA", err.Error())
    		return
    	}
    
    	fmt.Println(item)
    
    	err = memcached.Close()
    	if err != nil {
    		fmt.Println("ERROR TO CLOSE", err.Error())
    		return
    	}
    
    	item, err = memcached.Get("renan")
    	if err != nil {
    		fmt.Println("ERROR TO GET DATA", err.Error())
    	}
    
    }
    

    Evidence:

    &{renan [98 97 115 116 111 115] 0 0 184880334}
    ERROR TO GET DATA write tcp 10.8.0.41:55504->172.16.0.6:11211: use of closed network connection
    
  • Faster scanning of memcache responses

    Faster scanning of memcache responses

    Motivation for this change is at https://github.com/grafana/mimir/issues/285#issuecomment-1029143100, where a large program spends 12% of its CPU in this one function.

    Re-coded scanGetResponseLine() using IndexByte() instead of SScanf(). Also added a benchmark.

    name                   old time/op    new time/op    delta
    ScanGetResponseLine-4    4.06µs ± 1%    0.13µs ± 4%  -96.89%  (p=0.008 n=5+5)
    
    name                   old alloc/op   new alloc/op   delta
    ScanGetResponseLine-4      128B ± 0%       24B ± 0%  -81.25%  (p=0.008 n=5+5)
    
    name                   old allocs/op  new allocs/op  delta
    ScanGetResponseLine-4      7.00 ± 0%      1.00 ± 0%  -85.71%  (p=0.008 n=5+5)
    

    This PR is broadly similar to #74, but about twice as fast on my benchmark. The benchmark in #74 spends <2% of its time in scanGetResponseLine() so doesn't show the effect so well.

    The partial copy of strings.Cut() in this PR allows the code to still build with pre-1.18 Go.

  • Adding Support For Meta & Stats Commands

    Adding Support For Meta & Stats Commands

    Adds new functionality for Meta commands, which provides more flexibility with the use of in-line flags, and the stats commands which returns information for each connection. There are also tests for each of these commands.

Redisx: a library of Go utilities built on the redigo redis client library

redisx redisx is a library of Go utilities built on the redigo redis client libr

Dec 24, 2021
Go client library for Pilosa

Go Client for Pilosa Go client for Pilosa high performance distributed index. What's New? See: CHANGELOG Requirements Go 1.12 and higher. Install Down

Dec 3, 2022
Redis client library for Go

go-redis go-redis is a Redis client library for the Go programming language. It's built on the skeleton of gomemcache. It is safe to use by multiple g

Nov 8, 2022
Golang client for redislabs' ReJSON module with support for multilple redis clients (redigo, go-redis)

Go-ReJSON - a golang client for ReJSON (a JSON data type for Redis) Go-ReJSON is a Go client for ReJSON Redis Module. ReJSON is a Redis module that im

Dec 25, 2022
redis client implement by golang, inspired by jedis.

godis redis client implement by golang, refers to jedis. this library implements most of redis command, include normal redis command, cluster command,

Dec 6, 2022
Neo4j REST Client in golang

DEPRECATED! Consider these instead: https://github.com/johnnadratowski/golang-neo4j-bolt-driver https://github.com/go-cq/cq Install: If you don't ha

Nov 9, 2022
Neo4j client for Golang

neoism - Neo4j client for Go Package neoism is a Go client library providing access to the Neo4j graph database via its REST API. Status System Status

Dec 30, 2022
Type-safe Redis client for Golang

Redis client for Golang ❤️ Uptrace.dev - distributed traces, logs, and errors in one place Join Discord to ask questions. Documentation Reference Exam

Jan 1, 2023
Type-safe Redis client for Golang

Redis client for Golang ❤️ Uptrace.dev - distributed traces, logs, and errors in one place Join Discord to ask questions. Documentation Reference Exam

Jan 4, 2023
HDFS for Go - This is a native golang client for hdfs.

HDFS for Go This is a native golang client for hdfs. It connects directly to the namenode using the protocol buffers API. It tries to be idiomatic by

Dec 24, 2022
A CouchDB client in Go(Golang)

pillow pillow is a CouchDB client in Go(Golang). Resources Installation Usage Example Installation Install pillow as you normally would for any Go pac

Nov 9, 2022
Redis client for Golang
Redis client for Golang

Redis client for Golang To ask questions, join Discord or use Discussions. Newsl

Dec 23, 2021
Redis client for Golang
Redis client for Golang

Redis client for Golang Discussions. Newsletter to get latest updates. Documentation Reference Examples RealWorld example app Other projects you may l

Dec 30, 2021
Aerospike Client Go

Aerospike Go Client An Aerospike library for Go. This library is compatible with Go 1.9+ and supports the following operating systems: Linux, Mac OS X

Dec 14, 2022
Couchbase client in Go

A smart client for couchbase in go This is a unoffical version of a Couchbase Golang client. If you are looking for the Offical Couchbase Golang clien

Nov 27, 2022
Neo4j Rest API Client for Go lang

neo4j.go Implementation of client package for communication with Neo4j Rest API. For more information and documentation please read Godoc Neo4j Page s

Nov 9, 2022
Go client for Redis

Redigo Redigo is a Go client for the Redis database. Features A Print-like API with support for all Redis commands. Pipelining, including pipelined tr

Jan 1, 2023
Go Redis Client

xredis Built on top of github.com/garyburd/redigo with the idea to simplify creating a Redis client, provide type safe calls and encapsulate the low l

Sep 26, 2022
godis - an old Redis client for Go

godis Implements a few database clients for Redis. There is a stable client and an experimental client, redis and exp, respectively. To use any of the

Apr 16, 2022