A Go client for Memcached's text and meta text protocols. It provides context-aware operations, resolver-driven node discovery, configurable multi-node routing and connection pools, value codecs, and optional OpenTelemetry instrumentation. Requires Go 1.26 or newer.
Both clients support basic text commands, CAS, multi-server routing, connection reuse, TCP, and Unix sockets. Here is what this package adds over bradfitz/gomemcache's core API:
| Capability | bradfitz/gomemcache | This package |
|---|---|---|
| Per-operation context and deadlines | Get(key) / Set(item) have no call context |
Get(ctx, key) / Set(ctx, ...); context deadlines and separate dial, read, and write timeouts |
| Meta text protocol | No meta command API | MetaGet, MetaSet, MetaDelete, MetaArithmetic, MetaDebug, MetaNoOp; CAS and TTL options, but not all recache response markers are exposed |
| Connection limits | Reuses connections per address; configurable MaxIdleConns |
Pools per node with MaxConns, lifetime, and idle timeout; concurrent dials reserve capacity before connecting |
| Value compression | No built-in codec | Pluggable Codec; MC-COMPRESS-compatible Deflate, LZ4, Snappy, and Zstd |
| OpenTelemetry | No built-in instrumentation | Opt-in tracing, operation metrics, and discovery/topology metrics |
| Built-in key routing | CRC32 ServerList or a custom selector |
CRC32, Murmur3, rendezvous hashing, or a custom picker |
| Node discovery | Configure addresses through a selector | The resolver package provides static addresses and AWS / Google discovery; custom resolvers decide the next refresh time |
| Tools | Client library | Interactive CLI and Wails GUI |
Comparison checked against gomemcache revision 4d751bb; later upstream changes may differ.
The API also provides GetAndTouch, GetAndTouches, Stats, Version, and the usual storage, retrieval, deletion, and counter commands. UDP is available as an opt-in transport. See the usage guide for the full command list and operational limits.
Start a Memcached server at localhost:11211, then install the client:
go get github.com/yeqown/memcached@latestpackage main
import (
"context"
"fmt"
"time"
"github.com/yeqown/memcached"
)
func main() {
client, err := memcached.New("localhost:11211",
memcached.WithDialTimeout(2*time.Second),
memcached.WithReadTimeout(3*time.Second),
memcached.WithWriteTimeout(3*time.Second),
memcached.WithMaxConns(64),
memcached.WithMaxIdleConns(16),
memcached.WithMaxLifetime(time.Hour),
memcached.WithMaxIdleTimeout(5*time.Minute),
)
if err != nil {
panic(err)
}
defer client.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := client.Set(ctx, "greeting", []byte("hello"), 0, time.Minute); err != nil {
panic(err)
}
item, err := client.Get(ctx, "greeting")
if err != nil {
panic(err)
}
fmt.Println(string(item.Value)) // hello
}The values above are examples to tune for your workload. New also accepts comma-separated server addresses and composable options:
| To configure | Address or option |
|---|---|
| Multiple nodes and key placement | "cache-1:11211,cache-2:11211" plus memcached.WithPicker(picker.NewRendezvousHashPicker(0)) |
| Custom address resolution and discovery | memcached.WithResolver(resolver) and memcached.WithResolveTimeout(5*time.Second) |
| Compression or another value codec | memcached.WithCodec(codec) |
| Traces and metrics | memcached.WithTelemetry(telemetry.WithTracerProvider(tp), telemetry.WithMeterProvider(mp)) |
| Writes without server acknowledgments | memcached.WithNoReply() |
| Legacy UDP transport | A udp:// address plus memcached.WithUDPEnabled() |
See the usage guide for codec and telemetry setup. With multiple servers, Gets and GetAndTouches send all requested keys to one node; use per-key reads unless the keys are known to reside together.
AWS ElastiCache Memcached and Google Memorystore Memcached use the same built-in resolver: pass the configuration / discovery endpoint, including its port, to New with memcached.WithResolver(resolver.NewAutoDiscovery(time.Minute)). It uses only config get cluster, reuses its discovery connection, rejects older generations, and returns its cached topology on timeout. Non-positive intervals default to one minute. See the automatic discovery guide.
Resolver and picker APIs and built-ins now live in github.com/yeqown/memcached/resolver and github.com/yeqown/memcached/picker. Node addresses are defined by resolver.Addr{Network, Address}, a comparable value created with resolver.NewAddr(network, address). Custom resolvers implement Resolve(ctx context.Context, target string) (resolver.ResolveResult, *time.Time, error) and Close() error. The client owns their lifetime and closes them on shutdown or failed initialization. Return the full node list in ResolveResult.Addrs and a separate next resolve time. A nil time stops refreshing, including on error; transient errors should return a retry time. The default resolver.NewStatic() resolves once.
Custom pickers implement picker.Picker and are passed directly through memcached.WithPicker(p). The Builder interface has been removed; built-in constructors such as picker.NewCRC32HashPicker() return a ready-to-use Picker. Hash functions are internal to picker; the standalone hash package has been removed. The client reuses the supplied picker across topology changes and supplies the current immutable address snapshot to each Pick call. Pickers must support concurrent calls and must not retain or modify the supplied addresses. Resolvers control address order: the static resolver preserves input order, while automatic discovery sorts by network/address. Topology preserves that order. The new rendezvous scoring changes existing key placement when upgrading. Changing membership or switching hash strategies can cause cache misses, so applications need an origin fallback or cache warming.
Discovery and node lifetimes are managed by an internal topology; Client selects nodes through topology and borrows connections through node. Removed nodes drain already borrowed connections; waiting or dialing requests may fail. See the topology design.