memcache

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: MIT Imports: 12 Imported by: 0

README

memcache

English | 简体中文

memcache is a concurrent Go client for memcached, speaking the modern meta text protocol. It does not carry a legacy get/set protocol implementation.

The API hides the wire protocol behind verbs named for what you are doing. The meta protocol's CAS tokens and leases never surface in caller code. Instead of reading a version and writing it back, call Update with a transform function and the client runs the read, compare and swap, retry loop internally. Instead of building dogpile protection, call Fetch with a loader and the client makes sure the value is computed once. When you do need the raw protocol, every meta command is still reachable through Meta().

Conventions

  • Every method takes a context.Context as its first parameter.

  • Values are []byte and serialization stays with the caller. Empty values are rejected, because memcached represents lease placeholders as zero byte items.

  • A miss is a normal answer, not an error. Reads return (value, ok, err) where ok reports presence and err reports infrastructure failure. The two never mix.

  • Every method that stores a value takes its TTL as a positional ttl time.Duration parameter. There is no client wide default TTL. Passing 0 stores without expiration, and the constant memcache.Forever spells that choice out at the call site. A negative TTL is an error.

    err = mc.Set(ctx, "config:site", buf, memcache.Forever)
    
  • On verbs that auto create the key (Incr, Decr, Append, Prepend) the TTL applies only when the call creates the key. It never extends an existing key's lifetime.

  • Optional modifiers are typed per verb. Touch is accepted only by Get and GetMany, RefreshAhead only by Fetch, so putting an option on a verb it has no meaning for is a compile error rather than a runtime surprise.

Creating a client

func New(server string, options ...Option) (*Client, error)
func NewServers(servers []string, options ...Option) (*Client, error)
func (c *Client) Close() error
mc, err := memcache.New("127.0.0.1:11211")
if err != nil { /* handle */ }
defer mc.Close()

With multiple servers, keys are distributed by stable rendezvous hashing; WithRouter replaces the routing. Each server has an elastic connection pool. WithMaxIdleConns limits retained idle connections (not active requests) and idle connections are redialed after WithIdleTimeout (90 seconds by default).

Other options: WithTimeout (per request), WithDialTimeout, WithNetwork, WithDialer, WithMaxItemSize, plus the policy options Degrade, OnError, and a client wide RefreshAhead default described below.

Reading

func (c *Client) Get(ctx context.Context, key string, options ...GetOption) (value []byte, ok bool, err error)
func (c *Client) GetMany(ctx context.Context, keys []string, options ...GetOption) (map[string][]byte, error)
func (c *Client) Inspect(ctx context.Context, key string) (info ItemInfo, ok bool, err error)

Get reads one value.

raw, ok, err := mc.Get(ctx, "user:42")
if err != nil { /* infrastructure failure */ }
if !ok { /* miss */ }

GetMany reads a set of keys in one round trip per backend and returns the hits. A miss is expressed by key absence in the returned map.

The Touch(ttl) option makes the same protocol command also slide each hit's expiration to ttl, which turns Get into the read half of session renewal.

session, ok, err := mc.Get(ctx, "session:"+sid, memcache.Touch(30*time.Minute))

The slide is memcached's native touch and is blind: it extends whatever the read hits, including a value kept stale by Invalidate. A revocation that must stick goes through Delete.

Inspect returns an item's metadata (remaining TTL, size, last access, whether it was ever hit) without transferring the value or bumping its LRU position. It is an observability tool.

Writing

func (c *Client) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
func (c *Client) SetMany(ctx context.Context, mapping map[string][]byte, ttl time.Duration) error
func (c *Client) Add(ctx context.Context, key string, value []byte, ttl time.Duration) (ok bool, err error)
func (c *Client) Replace(ctx context.Context, key string, value []byte, ttl time.Duration) (ok bool, err error)
func (c *Client) Touch(ctx context.Context, key string, ttl time.Duration) error
func (c *Client) Delete(ctx context.Context, key string) error
func (c *Client) DeleteMany(ctx context.Context, keys []string) error
func (c *Client) Invalidate(ctx context.Context, key string, grace time.Duration) error

Set unconditionally stores a value for ttl. SetMany stores a batch in one round trip per backend, all sharing the same ttl.

Add stores only when the key is absent and reports whether this caller won, which makes it a simple distributed lock or once only guard.

won, err := mc.Add(ctx, "job:daily-report", []byte("1"), 10*time.Minute)
if won { /* this process runs the job */ }

Replace stores only when the key still exists and reports whether it did. It is the write half of session renewal, where false means the session already ended.

Touch extends a key's TTL without transferring its value, as one blind protocol command. A missing key is not an error.

Delete removes a key. Deleting an absent key is a success, since the goal state already holds. DeleteMany removes a batch in one round trip per backend.

Invalidate marks a value stale instead of dropping it. For the grace period readers keep serving the old copy while Fetch elects one caller to recompute in the background; afterwards the key decays into a normal miss. Invalidate pairs with keys managed by Fetch: the grace bound holds only while nothing renews the key, since a touch slides it like any other expiration. Use Delete when the old value must not be served for even a second.

Get or compute

func (c *Client) Fetch(ctx context.Context, key string, ttl time.Duration,
    loader func(context.Context) ([]byte, error), options ...FetchOption) ([]byte, error)

Fetch is the highest frequency cache pattern as one verb. It returns the cached value, or runs loader to compute it and stores the result for ttl.

report, err := mc.Fetch(ctx, "report:q3", time.Hour, buildReport)

On a miss, one caller across all processes wins a server side lease and runs the loader. Other goroutines in the same process wait on that result, and other processes wait briefly then compute locally without writing back. So the value is computed once, not once per waiter.

With the RefreshAhead(window) option, a value whose remaining TTL has entered the window is served immediately while one elected caller recomputes in a background goroutine, so no request ever pays the recompute latency.

feed, err := mc.Fetch(ctx, "home:"+uid, 5*time.Minute, buildFeed,
    memcache.RefreshAhead(30*time.Second),
)

The loader runs on a context owned by the client, not the calling request's context, because its result may be shared by other waiters or outlive the caller entirely. All write backs are conditional on the version observed at election, so a key deleted mid recompute is never resurrected.

Atomic modification

func (c *Client) Update(ctx context.Context, key string, ttl time.Duration,
    fn func(current []byte, found bool) ([]byte, error)) ([]byte, error)

Update atomically transforms a value. It reads the current value with its version, applies fn, writes back only if nothing changed in between, and retries on conflict. On a miss fn receives (nil, false). Returning an error from fn aborts without writing. fn may run multiple times, so it must be pure. If the retry loop keeps losing to concurrent writers, Update returns ErrConflict.

cart, err := mc.Update(ctx, "cart:"+uid, 30*time.Minute,
    func(current []byte, found bool) ([]byte, error) {
        var items []Item
        if found {
            if err := json.Unmarshal(current, &items); err != nil {
                return nil, err
            }
        }
        return json.Marshal(append(items, item))
    },
)
func (c *Client) Incr(ctx context.Context, key string, delta uint64, ttl time.Duration) (uint64, error)
func (c *Client) Decr(ctx context.Context, key string, delta uint64, ttl time.Duration) (uint64, error)

Incr adds delta to a decimal counter and returns the new value, creating the counter on a miss so the first request counts as delta. Decr subtracts and saturates at zero. Since the ttl is fixed at creation and later calls never extend it, this is exactly fixed window rate limiting.

n, err := mc.Incr(ctx, "rate:"+ip, 1, time.Minute)
func (c *Client) Append(ctx context.Context, key string, fragment []byte, ttl time.Duration) error
func (c *Client) Prepend(ctx context.Context, key string, fragment []byte, ttl time.Duration) error
func (c *Client) Take(ctx context.Context, key string) ([]byte, error)

Append and Prepend concatenate raw bytes onto a value, creating it on a miss. Take atomically reads a value and deletes it, with no window in which concurrently appended bytes can be lost. Together they make a simple collect then drain pattern, such as buffering events and periodically taking the batch. A nil result from Take means there was nothing to take.

Failure policy

By default every infrastructure failure surfaces as an error. The Degrade(true) client option makes reads report failures as misses and unconditional writes give up silently, because a cache outage should not become a site outage. Every absorbed error still reaches the OnError hook.

mc, err := memcache.NewServers(servers,
    memcache.Degrade(true),
    memcache.OnError(func(err error) { log.Print(err) }),
)

Verbs whose answer feeds a business decision (Add, Replace, Update, Incr, Decr, Take) keep failing loudly even under Degrade, and an AmbiguousWriteError (the write may have landed) always surfaces. The client never automatically retries a command after writing begins, since blindly retrying arithmetic or append could apply the mutation twice.

Protocol access

Everything the Client's verbs do not cover lives behind Meta(), a 1:1 mapping of the meta protocol that returns typed results instead of collapsing protocol states into errors.

func (m *MetaClient) Get(ctx context.Context, key string, options MetaGetOptions) (GetResult, error)
func (m *MetaClient) Set(ctx context.Context, key string, value []byte, options MetaSetOptions) (MutationResult, error)
func (m *MetaClient) Delete(ctx context.Context, key string, options MetaDeleteOptions) (MutationResult, error)
func (m *MetaClient) Arithmetic(ctx context.Context, key string, options MetaArithmeticOptions) (ArithmeticResult, error)
func (m *MetaClient) Execute(ctx context.Context, command MetaCommand) (RawResponse, error)
func (m *MetaClient) Batch(ctx context.Context, operations []Operation) ([]OperationResult, error)
func (m *MetaClient) Debug(ctx context.Context, key string) (map[string]string, error)
func (m *MetaClient) Noop(ctx context.Context) error
result, err := mc.Meta().Get(ctx, key, memcache.MetaGetOptions{ReturnCAS: true, ReturnTTL: true})
raw, err := mc.Meta().Execute(ctx, memcache.MetaCommand{Command: "mg", Key: key, Flags: []string{"v", "t"}})

Batch validates every operation before writing, groups operations by server, and pipelines them with quiet commands. Results stay in input order and a backend failure is recorded on only that backend's results. Keys containing whitespace or control bytes are automatically base64 encoded with the meta b flag.

License

MIT

Documentation

Overview

Package memcache implements a modern memcached client using only the meta text protocol (mg, ms, md, ma, me, and mn).

The Client's methods are one verb per operation (Get, Fetch, Update, Incr, Take, ...), each returning business values. A miss is a normal answer, never an error; concurrency coordination (leases, compare-and-swap loops, request merging) is the library's job and never appears in caller code; failure behavior is an explicit policy (Degrade, OnError). Values are []byte in this release; the typed layer arrives with generic methods in go1.27.

The 1:1 protocol layer remains fully available behind Client.Meta. Client is safe for concurrent use.

Index

Constants

View Source
const Forever time.Duration = 0

Forever stores without expiration. TTL is a positional parameter on every verb that needs one, so "never expire" is always a visible choice at the call site, never a silent fallback.

Variables

View Source
var (
	// ErrCacheMiss is a protocol-layer sentinel returned by MetaClient.Debug
	// when the key is absent. Client verbs never return it: a miss is a
	// normal answer there, expressed by the ok result or key absence.
	ErrCacheMiss = errors.New("memcache: cache miss")
	// ErrClosed is returned after a client has been closed.
	ErrClosed = errors.New("memcache: client is closed")
	// ErrNotStored means a conditional mutation was not applied.
	ErrNotStored = errors.New("memcache: value not stored")
	// ErrConflict is returned by Update and Take when their optimistic
	// retry loop keeps losing to concurrent writers.
	ErrConflict = errors.New("memcache: too many conflicting concurrent writes")
)

Functions

func RefreshAhead

func RefreshAhead(d time.Duration) interface {
	PolicyOption
	FetchOption
}

RefreshAhead makes Fetch refresh a value in the background once its remaining TTL enters the window, so no reader ever pays the recompute latency. Meaningful only on Fetch, or as a client-wide default.

Types

type AmbiguousWriteError

type AmbiguousWriteError struct {
	Operation string
	Key       string
	Cause     error
}

AmbiguousWriteError means a side-effecting request reached the connection, but its result could not be observed. Retrying it may duplicate the effect. It is the one error class Degrade never absorbs: degrading covers "the cache is unavailable", not "the write may or may not have landed".

func (*AmbiguousWriteError) Error

func (e *AmbiguousWriteError) Error() string

func (*AmbiguousWriteError) Unwrap

func (e *AmbiguousWriteError) Unwrap() error

type ArithmeticOperation

type ArithmeticOperation struct {
	Key     string
	Options MetaArithmeticOptions
}

ArithmeticOperation is a batch meta arithmetic command.

type ArithmeticResult

type ArithmeticResult struct {
	Key         string
	Status      MutationStatus
	Value       uint64
	HasValue    bool
	Metadata    Metadata
	ReturnedKey []byte
	Opaque      string
}

ArithmeticResult describes an increment or decrement outcome.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a concurrent meta-protocol memcached client. Its methods are one verb per operation, returning business values. The 1:1 protocol layer stays available behind Meta.

func New

func New(server string, options ...Option) (*Client, error)

New creates a lazy client. No connection is opened until the first command.

func NewServers

func NewServers(servers []string, options ...Option) (*Client, error)

NewServers creates a client for one or more backends. It is the preferred multi-server constructor; keys are routed with RendezvousRouter by default.

func (*Client) Add

func (c *Client) Add(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, error)

Add stores only when the key is absent and reports whether this caller won. The bool is the caller's whole answer, so Add keeps it; it is also why Degrade never fakes a result here.

func (*Client) Append

func (c *Client) Append(ctx context.Context, key string, fragment []byte, ttl time.Duration) error

Append adds a fragment to the end of a raw bytes value, creating the value with ttl on a miss. The ttl applies only at creation; later appends never extend an existing value's lifetime. Fragments bypass any value encoding; how the accumulated bytes are structured is the caller's business.

func (*Client) Close

func (c *Client) Close() error

Close cancels background refreshes and any in-flight Fetch loaders, releases idle connections, and prevents new work. Requests already on the wire finish their exchange. Close is idempotent.

func (*Client) Decr

func (c *Client) Decr(ctx context.Context, key string, delta uint64, ttl time.Duration) (uint64, error)

Decr subtracts delta from a decimal counter, saturating at zero. A miss creates the counter at zero with ttl; as with Incr, the ttl applies only at creation.

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, key string) error

Delete removes a key. Deleting an absent key is a success: the goal state already holds.

func (*Client) DeleteMany

func (c *Client) DeleteMany(ctx context.Context, keys []string) error

DeleteMany removes a set of keys in one round trip per backend.

func (*Client) Fetch

func (c *Client) Fetch(ctx context.Context, key string, ttl time.Duration, loader func(context.Context) ([]byte, error), options ...FetchOption) ([]byte, error)

Fetch returns the cached value or computes it exactly once, storing the computed value for ttl. On a miss it takes a server-side lease so that across processes and goroutines a single loader runs while everyone else waits for its result; near expiry (inside RefreshAhead) or during an Invalidate grace period it returns the current value immediately and recomputes in the background. Fetch never fails because coordination failed: every path ends in a value, the loader's own error, or the caller's context error, and write-back failures only reach OnError. Paths that compute without a lease (Degrade, a lease held elsewhere) still merge through the in-process flight map, so a process runs at most one loader per key.

The loader does not run on the calling context: a background refresh outlives its caller, and a miss-path result is shared by every waiter in the process, so one short-deadline caller must not cancel it for everyone. The loader receives a context owned by the client (carrying the winning caller's deadline on the miss path) and must not rely on request-scoped values.

func (*Client) Get

func (c *Client) Get(ctx context.Context, key string, options ...GetOption) ([]byte, bool, error)

Get reads a value. A miss is a normal answer, not an error: ok reports presence, err reports infrastructure failure, and the two never mix. A value kept stale by Invalidate is returned as an ordinary hit. The Touch option makes the same command also slide the hit's expiration.

func (*Client) GetMany

func (c *Client) GetMany(ctx context.Context, keys []string, options ...GetOption) (map[string][]byte, error)

GetMany reads a set of keys in one round trip per backend and returns the hits; a miss is expressed by key absence. With Degrade enabled a failing backend only removes its own keys from the result.

func (*Client) Incr

func (c *Client) Incr(ctx context.Context, key string, delta uint64, ttl time.Duration) (uint64, error)

Incr adds delta to a decimal counter, creating it with ttl on a miss so the first request counts as delta. The ttl applies only at creation; later increments never extend an existing counter's lifetime, which is exactly what fixed-window counting needs. The result feeds business decisions, so Degrade never fakes one: infrastructure failures surface.

func (*Client) Inspect

func (c *Client) Inspect(ctx context.Context, key string) (ItemInfo, bool, error)

Inspect returns an item's metadata without transferring its value or bumping its LRU position. It is an observability tool; branching business logic on metadata is not a supported pattern.

func (*Client) Invalidate

func (c *Client) Invalidate(ctx context.Context, key string, grace time.Duration) error

Invalidate marks a value stale instead of dropping it. For the grace period, readers keep the old copy while Fetch elects one caller to recompute in the background; afterwards the key decays into a normal miss. The grace bound also guarantees recovery when an elected recomputer crashes without writing: the entry dies on schedule and the next Fetch re-elects. It is an upper bound only while nothing renews the key, since Touch slides it like any other expiration. Invalidate pairs with Fetch-managed keys; use Delete when the old value must not be served for even a second.

func (*Client) Meta

func (c *Client) Meta() *MetaClient

Meta returns the protocol-layer escape hatch. The returned client shares this client's connection pools and configuration.

func (*Client) Prepend

func (c *Client) Prepend(ctx context.Context, key string, fragment []byte, ttl time.Duration) error

Prepend adds a fragment to the front of a raw bytes value, creating the value with ttl on a miss; as with Append, the ttl applies only at creation.

func (*Client) Replace

func (c *Client) Replace(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, error)

Replace stores only when the key still exists and reports whether it did. It is the write half of session renewal: false means the session ended mid-request and there is nothing to write back to.

func (*Client) Set

func (c *Client) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error

Set unconditionally stores a value for ttl. Storing without expiration is the explicit choice Forever.

func (*Client) SetMany

func (c *Client) SetMany(ctx context.Context, mapping map[string][]byte, ttl time.Duration) error

SetMany stores a set of values in one round trip per backend, all sharing the same ttl.

func (*Client) Take

func (c *Client) Take(ctx context.Context, key string) ([]byte, error)

Take atomically reads a value and deletes it: read with version, delete only if unchanged, retry when a concurrent write slipped in between. Bytes appended between the read and the delete are never lost. A nil result means there was nothing to take; a miss and an empty value are deliberately the same answer. The result feeds the caller's next step, so Degrade never absorbs failures here.

func (*Client) Touch

func (c *Client) Touch(ctx context.Context, key string, ttl time.Duration) error

Touch extends a key's TTL without transferring its value, as one blind protocol command. A missing key is not an error; there is simply nothing left to extend. The touch is memcached's native one and applies to whatever it hits, including an entry kept stale by Invalidate, so a revocation that must stick goes through Delete.

func (*Client) Update

func (c *Client) Update(ctx context.Context, key string, ttl time.Duration, fn func(current []byte, found bool) ([]byte, error)) ([]byte, error)

Update atomically transforms a value and stores the result for ttl: read with version, apply fn, write back only if unchanged, retry on conflict. Version tokens never appear in user code. On a miss fn receives (nil, false); returning an error from fn aborts the whole operation without writing. fn may run multiple times and must be pure. A value kept stale by Invalidate is treated as a miss: fn transforms rather than recomputes, and transforming invalidated data would silently launder it back to fresh.

type DeleteOperation

type DeleteOperation struct {
	Key     string
	Options MetaDeleteOptions
}

DeleteOperation is a batch meta delete.

type DialContextFunc

type DialContextFunc func(context.Context, string, string) (net.Conn, error)

DialContextFunc opens a server connection and must be safe for concurrent use.

type Expiration

type Expiration int64

Expiration is sent using memcached's TTL rules. ExpiresIn uses relative seconds through 30 days and automatically converts longer durations to an absolute timestamp; ExpiresAt can be used explicitly.

const (
	NoExpiration Expiration = 0
)

func ExpiresAt

func ExpiresAt(t time.Time) Expiration

ExpiresAt converts a timestamp to memcached's absolute Unix-time form.

func ExpiresIn

func ExpiresIn(d time.Duration) Expiration

ExpiresIn converts a relative duration to whole seconds, rounding a positive sub-second duration up to one second.

type FetchOption

type FetchOption interface {
	// contains filtered or unexported methods
}

FetchOption modifies a Fetch call.

type GetOperation

type GetOperation struct {
	Key     string
	Options MetaGetOptions
}

GetOperation is a batch meta get.

type GetOption

type GetOption interface {
	// contains filtered or unexported methods
}

GetOption modifies a single Get or GetMany call.

func Touch

func Touch(ttl time.Duration) GetOption

Touch makes a read slide each hit's expiration to ttl in the same protocol command, which turns Get into the read half of session renewal. The slide is memcached's native touch and is blind: it extends whatever the read hits, including an entry kept stale by Invalidate, so a revocation that must stick goes through Delete. Touch(Forever) removes the expiration.

type GetResult

type GetResult struct {
	Key         string
	Status      GetStatus
	Value       []byte
	Metadata    Metadata
	ValueState  ValueState
	Lease       LeaseState
	ReturnedKey []byte
	Opaque      string
}

GetResult describes a meta get without collapsing protocol states into an error. Value is present only for a value-bearing hit.

func (GetResult) Hit

func (r GetResult) Hit() bool

type GetStatus

type GetStatus uint8

GetStatus describes whether and how a meta get resolved.

const (
	GetUnknown GetStatus = iota
	GetHit
	GetMiss
	GetPending
	GetUnchanged
)

type ItemInfo

type ItemInfo struct {
	// TTL is the remaining lifetime. A negative value means the item never
	// expires.
	TTL time.Duration
	// Size is the stored value's size in bytes.
	Size int
	// LastAccess is the time since the item was last read or written.
	LastAccess time.Duration
	// HitBefore reports whether the item was ever hit since it was stored.
	HitBefore bool
}

ItemInfo is the read-only metadata returned by Inspect.

type LeaseState

type LeaseState uint8

LeaseState reports ownership of a vivify or early-refresh lease.

const (
	LeaseNone LeaseState = iota
	LeaseGranted
	LeaseBusy
)

type MetaArithmeticOptions added in v0.1.2

type MetaArithmeticOptions struct {
	Delta        uint64
	Decrement    bool
	Initial      *uint64
	InitialTTL   *Expiration
	Touch        *Expiration
	CompareCAS   *uint64
	SetCAS       *uint64
	MetadataOnly bool
	ReturnTTL    bool
	ReturnCAS    bool
	ReturnKey    bool
	Opaque       string
}

MetaArithmeticOptions exposes meta arithmetic behavior.

type MetaClient

type MetaClient struct {
	// contains filtered or unexported fields
}

MetaClient is the 1:1 protocol layer behind Client.Meta. Its methods map directly onto the meta commands (mg, ms, md, ma, me, mn) and return typed results without collapsing protocol states into errors. Anything the Client's verbs do not cover is expressible here.

func (*MetaClient) Arithmetic

func (m *MetaClient) Arithmetic(ctx context.Context, key string, options MetaArithmeticOptions) (ArithmeticResult, error)

Arithmetic performs configurable unsigned 64-bit meta arithmetic.

func (*MetaClient) Batch

func (m *MetaClient) Batch(ctx context.Context, operations []Operation) ([]OperationResult, error)

Batch executes operations in pipelines grouped by backend and restores input order. All operations are validated before any network write. Per-operation transport failures are returned in OperationResult.Err so a failure on one backend does not erase successful results from another.

func (*MetaClient) Debug

func (m *MetaClient) Debug(ctx context.Context, key string) (map[string]string, error)

Debug returns the me command's internal key/value metadata. A miss is reported as ErrCacheMiss.

func (*MetaClient) Delete

func (m *MetaClient) Delete(ctx context.Context, key string, options MetaDeleteOptions) (MutationResult, error)

Delete performs a configurable meta delete or stale invalidation.

func (*MetaClient) Execute

func (m *MetaClient) Execute(ctx context.Context, command MetaCommand) (RawResponse, error)

Execute runs a raw command against the server selected by its key.

func (*MetaClient) Get

func (m *MetaClient) Get(ctx context.Context, key string, options MetaGetOptions) (GetResult, error)

Get performs a configurable meta get.

func (*MetaClient) Noop

func (m *MetaClient) Noop(ctx context.Context) error

Noop checks every configured backend with the meta no-op command.

func (*MetaClient) Set

func (m *MetaClient) Set(ctx context.Context, key string, value []byte, options MetaSetOptions) (MutationResult, error)

Set performs a configurable meta set in any of its five modes.

type MetaCommand

type MetaCommand struct {
	Command  string
	Key      string
	Flags    []string
	Value    []byte
	HasValue bool
}

MetaCommand is the raw escape hatch for protocol extensions. Command must be a supported two-byte meta command. Flags omit their separating spaces.

type MetaDeleteOptions added in v0.1.2

type MetaDeleteOptions struct {
	CompareCAS *uint64
	SetCAS     *uint64
	Invalidate bool
	StaleFor   *Expiration
	DropValue  bool
	ReturnKey  bool
	Opaque     string
}

MetaDeleteOptions exposes meta delete behavior.

type MetaGetOptions added in v0.1.2

type MetaGetOptions struct {
	MetadataOnly      bool
	ReturnCAS         bool
	ReturnTTL         bool
	ReturnSize        bool
	ReturnLastAccess  bool
	ReturnHitBefore   bool
	ReturnClientFlags bool
	ReturnKey         bool
	Touch             *Expiration
	VivifyTTL         *Expiration
	RefreshBefore     *Expiration
	UnlessCAS         *uint64
	SetCAS            *uint64
	NoLRUBump         bool
	Opaque            string
}

MetaGetOptions exposes meta get behavior. Zero values perform a normal value read. Set MetadataOnly for an explicit metadata-only operation. When VivifyTTL and RefreshBefore are combined, the wire protocol cannot distinguish a real empty value won for early refresh from a newly vivified empty placeholder. Value-bearing reads use the reference clients' empty value heuristic; metadata-only reads reject that combination.

type MetaSetOptions added in v0.1.2

type MetaSetOptions struct {
	TTL         Expiration
	ClientFlags uint32
	Mode        StoreMode
	CompareCAS  *uint64
	SetCAS      *uint64
	Invalidate  bool
	VivifyTTL   *Expiration
	ReturnCAS   bool
	ReturnSize  bool
	ReturnKey   bool
	Opaque      string
}

MetaSetOptions exposes meta set behavior.

type Metadata

type Metadata struct {
	CAS         *uint64
	TTL         *int64
	Size        *uint64
	ClientFlags *uint32
	LastAccess  *uint64
	HitBefore   *bool
}

Metadata contains values requested from a meta get/arithmetic response. Pointer fields distinguish a returned zero from a field not requested.

type MutationResult

type MutationResult struct {
	Key         string
	Status      MutationStatus
	CAS         *uint64
	Size        *uint64
	ReturnedKey []byte
	Opaque      string
}

MutationResult describes a set or delete outcome.

func (MutationResult) Applied

func (r MutationResult) Applied() bool

type MutationStatus

type MutationStatus uint8

MutationStatus describes a conditional set, delete, or arithmetic outcome.

const (
	MutationUnknown MutationStatus = iota
	MutationApplied
	MutationNotFound
	MutationAlreadyExists
	MutationCASMismatch
)

type Operation

type Operation interface {
	// contains filtered or unexported methods
}

Operation is one item accepted by Client.Batch. The concrete operation structs below are the only implementations.

type OperationResult

type OperationResult struct {
	Get        *GetResult
	Mutation   *MutationResult
	Arithmetic *ArithmeticResult
	Err        error
	Ambiguous  bool
}

OperationResult contains exactly one typed result when Err is nil. Ambiguous is true when a side effect may have happened but no response or barrier proved its outcome.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option configures the client at construction time. Engine options such as WithTimeout implement only this interface; policy options additionally implement PolicyOption.

func WithDialTimeout

func WithDialTimeout(timeout time.Duration) Option

WithDialTimeout sets the connection establishment timeout.

func WithDialer

func WithDialer(dial DialContextFunc) Option

WithDialer supplies a custom dialer, useful for TLS and tests.

func WithIdleTimeout

func WithIdleTimeout(timeout time.Duration) Option

WithIdleTimeout bounds how long a pooled connection may sit idle before it is discarded and replaced by a fresh dial. Connections silently dropped by a restarted server or an intermediary while idle would otherwise surface as a spurious error, or as an AmbiguousWriteError on a mutation. Zero disables the limit. The default is 90 seconds.

func WithMaxIdleConns

func WithMaxIdleConns(max int) Option

WithMaxIdleConns sets the number of idle connections retained per server. Active connections are not capped. Zero disables pooling.

func WithMaxItemSize

func WithMaxItemSize(max int) Option

WithMaxItemSize rejects larger outgoing and incoming values. Zero disables the limit. The default is 1 MiB, matching a default memcached server.

func WithNetwork

func WithNetwork(network string) Option

WithNetwork changes the network passed to the dialer (normally "tcp" or "unix").

func WithRouter

func WithRouter(router Router) Option

WithRouter supplies the multi-server routing policy.

func WithServers

func WithServers(servers ...string) Option

WithServers configures all backends. Keys are routed consistently across the list. At least one non-empty address is required.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the total deadline for one command or one backend's batch exchange. Context deadlines take precedence when sooner. A zero duration disables this client-side timeout.

type PolicyOption

type PolicyOption interface {
	Option
	// contains filtered or unexported methods
}

PolicyOption is an Option that declares a client-wide policy default, such as RefreshAhead, Degrade, or OnError. Engine options are not PolicyOptions.

func Degrade

func Degrade(on bool) PolicyOption

Degrade selects the failure policy for the whole client. When enabled, reads report an infrastructure failure as a miss and unconditional writes give up silently; every absorbed error still reaches OnError. Verbs whose answer feeds a business decision (Add, Replace, Update, Incr, Decr, Take) keep returning errors, and an AmbiguousWriteError is never absorbed. The default is to return every error.

func OnError

func OnError(hook func(error)) PolicyOption

OnError installs the observability hook for failures that never reach a caller: errors absorbed by Degrade, background loader failures, and Fetch write-back failures. The hook must be safe for concurrent use and must not block.

type ProtocolError

type ProtocolError struct{ Message string }

ProtocolError reports malformed or unexpected data from a server.

func (*ProtocolError) Error

func (e *ProtocolError) Error() string

type RawResponse

type RawResponse struct {
	Code     ResponseCode
	Value    []byte
	Metadata Metadata
	Key      []byte
	Opaque   string
	Won      bool
	Busy     bool
	Stale    bool
	Flags    []string
	Debug    map[string]string
	// contains filtered or unexported fields
}

RawResponse is a lightly parsed response from ExecuteMeta.

type RendezvousRouter

type RendezvousRouter struct{}

RendezvousRouter implements highest-random-weight hashing. Routing uses the original key bytes and is stable across processes.

func (RendezvousRouter) Pick

func (RendezvousRouter) Pick(key string, servers []string) int

type ResponseCode

type ResponseCode string

ResponseCode is the two-byte meta protocol result code.

const (
	ResponseHeader    ResponseCode = "HD"
	ResponseValue     ResponseCode = "VA"
	ResponseMiss      ResponseCode = "EN"
	ResponseNotStored ResponseCode = "NS"
	ResponseExists    ResponseCode = "EX"
	ResponseNotFound  ResponseCode = "NF"
	ResponseNoop      ResponseCode = "MN"
	ResponseDebug     ResponseCode = "ME"
)

type Router

type Router interface {
	Pick(key string, servers []string) int
}

Router selects a server index for a key. Implementations must be safe for concurrent use and return an index in [0, len(servers)). Routers supplied with WithRouter receive a slice private to each call and may reorder it before Pick returns; the selected address is mapped back to its connection pool. Implementations must not retain or mutate the slice after Pick returns.

type ServerError

type ServerError struct {
	Kind    string
	Message string
}

ServerError represents an ERROR, CLIENT_ERROR, or SERVER_ERROR response.

func (*ServerError) Error

func (e *ServerError) Error() string

type SetOperation

type SetOperation struct {
	Key     string
	Value   []byte
	Options MetaSetOptions
}

SetOperation is a batch meta set.

type StoreMode

type StoreMode uint8

StoreMode selects meta set's M flag.

const (
	ModeSet StoreMode = iota
	ModeAdd
	ModeReplace
	ModeAppend
	ModePrepend
)

type ValueState

type ValueState uint8

ValueState distinguishes fresh, stale, and lease-placeholder values.

const (
	ValueUnknown ValueState = iota
	ValueFresh
	ValueStale
	ValueMissing
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL