Memcached Driver
Memcached driver for Grove KV with CAS (Compare-And-Swap) support.
The Memcached driver connects Grove KV to Memcached using gomemcache. It provides basic key-value operations and Compare-And-Swap (CAS) for optimistic concurrency control.
Installation
go get github.com/xraph/grove/kv
go get github.com/xraph/grove/kv/drivers/memcacheddriverConnection
import (
"github.com/xraph/grove/kv"
"github.com/xraph/grove/kv/drivers/memcacheddriver"
)
store, err := kv.Open(memcacheddriver.New(), "localhost:11211")
if err != nil {
log.Fatal(err)
}
defer store.Close()The DSN is a comma-separated list of server addresses. For multiple servers:
store, err := kv.Open(memcacheddriver.New(), "mc1:11211,mc2:11211,mc3:11211")Capabilities
| Capability | Supported |
|---|---|
| TTL | Set only (via expiration on write) |
| CAS (SetNX/SetXX) | Yes |
| Scan | No |
| Batch | Yes |
| PubSub | No |
| Transactions | No |
| Streams | No |
Basic Usage
ctx := context.Background()
// Set with TTL
err := store.Set(ctx, "session:abc", sessionData, 30*time.Minute)
// Get
var session Session
err = store.Get(ctx, "session:abc", &session)
// Delete
err = store.Delete(ctx, "session:abc")
// Set only if key does not exist
err = store.Set(ctx, "lock:resource", value, 10*time.Second, kv.NX())
// Set only if key already exists
err = store.Set(ctx, "session:abc", updatedData, 30*time.Minute, kv.XX())CompareAndSwap Operation
The Memcached driver exposes a CompareAndSwap method for atomic read-modify-write operations. This uses Memcached's native CAS protocol to detect concurrent modifications:
mdb := memcacheddriver.Unwrap(store)
err := mdb.CompareAndSwap(ctx, "counter", func(current []byte) ([]byte, error) {
// Parse the current value
count, _ := strconv.Atoi(string(current))
// Modify and return the new value
return []byte(strconv.Itoa(count + 1)), nil
})
if err != nil {
// Error may indicate a CAS conflict if another client modified the value
log.Printf("CAS failed: %v", err)
}If another client modifies the key between the Get and the CompareAndSwap, the operation returns a version conflict error. Retry the operation in a loop for contended keys.
Options
Pass driver options when opening the connection:
import "github.com/xraph/grove/kv/driver"
store, err := kv.Open(memcacheddriver.New(), "localhost:11211",
memcacheddriver.WithTimeout(2 * time.Second),
memcacheddriver.WithMaxIdleConns(10),
)| Option | Description |
|---|---|
memcacheddriver.WithTimeout(d) | Socket read/write timeout for Memcached operations |
memcacheddriver.WithMaxIdleConns(n) | Maximum number of idle connections per server |
Unwrap for Native Client
Access the underlying gomemcache Client for operations not exposed by the Grove KV interface:
// Get the MemcachedDB driver
mdb := memcacheddriver.Unwrap(store)
// Get the raw gomemcache client
client := memcacheddriver.UnwrapClient(store)
// Use native memcache operations
err := client.Increment("page-views", 1)
err = client.Append("log-key", []byte("new entry\n"))Limitations
Memcached is a simple caching layer with several constraints compared to other KV backends:
- No key scanning -- Memcached does not support iterating or listing keys. The
store.Scan()method returnskv.ErrNotSupported. - No TTL query -- You cannot retrieve the remaining TTL of a key. The
store.TTL()method returnskv.ErrNotSupported. - No persistence -- Data is lost when the server restarts. Memcached is a cache, not a durable store.
- No transactions -- There is no multi-key atomic operation beyond CAS on a single key.
- Key size limit -- Keys are limited to 250 bytes.
- Value size limit -- Values are limited to 1 MB by default (configurable on the server).
- No native Pub/Sub -- Use Redis if you need messaging capabilities.
Memcached is best suited for caching hot data where speed is the primary concern and durability is not required.