protocol

package
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package protocol implements the text and binary wire protocols used by the celeris native Memcached driver.

Text protocol

The text protocol is line-oriented ASCII. Storage commands carry a data block on the following line; retrieval commands return zero or more VALUE lines (each with an optional CAS), then the END sentinel. Responses are streamed via TextReader; the reader parses one logical reply at a time and returns ErrIncomplete when more bytes are needed.

Binary protocol

See [binary.go]. Length-prefixed 24-byte header + optional extras/key/value body. Drivers choose text or binary at dial time.

Allocation discipline

Both TextWriter and BinaryWriter expose a caller-supplied byte buffer via Reset — no per-call heap allocation in the steady state. The reader side feeds into an append-only internal buffer; VALUE payloads are sliced into the buffer and valid until the next Feed/Compact cycle.

Index

Constants

View Source
const (
	// BinHeaderLen is the fixed 24-byte binary-protocol header size.
	BinHeaderLen = 24

	// MagicRequest marks a request packet (client → server).
	MagicRequest byte = 0x80
	// MagicResponse marks a response packet (server → client).
	MagicResponse byte = 0x81
)

Binary-protocol constants. See https://github.com/memcached/memcached/wiki/BinaryProtocolRevamped.

View Source
const (
	// OpGet retrieves a value (no extras on request).
	OpGet byte = 0x00
	// OpSet stores a value (4-byte flags + 4-byte exptime extras on request).
	OpSet byte = 0x01
	// OpAdd stores a value only if the key does not exist.
	OpAdd byte = 0x02
	// OpReplace stores a value only if the key already exists.
	OpReplace byte = 0x03
	// OpDelete removes a key.
	OpDelete byte = 0x04
	// OpIncrement atomically adds to a counter.
	OpIncrement byte = 0x05
	// OpDecrement atomically subtracts from a counter.
	OpDecrement byte = 0x06
	// OpQuit closes the connection.
	OpQuit byte = 0x07
	// OpFlush wipes all data (optional 4-byte expiration extras).
	OpFlush byte = 0x08
	// OpGetQ is the quiet variant of GET (no reply on miss).
	OpGetQ byte = 0x09
	// OpNoop is a no-op that forces a reply (used to terminate multi-get
	// pipelines made of OpGetQ packets).
	OpNoop byte = 0x0a
	// OpVersion returns the server version string as the value body.
	OpVersion byte = 0x0b
	// OpGetK is like OpGet but includes the key in the response body.
	OpGetK byte = 0x0c
	// OpGetKQ is the quiet variant of OpGetK.
	OpGetKQ byte = 0x0d
	// OpAppend appends to an existing value.
	OpAppend byte = 0x0e
	// OpPrepend prepends to an existing value.
	OpPrepend byte = 0x0f
	// OpStat requests server statistics (multi-reply terminated by a zero-
	// length key response).
	OpStat byte = 0x10
	// OpTouch updates the expiration of a key without fetching the value.
	OpTouch byte = 0x1c
	// OpGAT gets a value and updates its expiration.
	OpGAT byte = 0x1d
	// OpGATQ is the quiet variant of OpGAT.
	OpGATQ byte = 0x1e
)

Binary opcodes. Only the subset actually used by the driver is enumerated; the parser/encoder is still generic over the full 1-byte opcode range.

View Source
const (
	// StatusOK indicates a successful reply.
	StatusOK uint16 = 0x0000
	// StatusKeyNotFound indicates the key does not exist.
	StatusKeyNotFound uint16 = 0x0001
	// StatusKeyExists indicates the key already exists or CAS mismatch.
	StatusKeyExists uint16 = 0x0002
	// StatusValueTooLarge indicates the value exceeded the server's limit.
	StatusValueTooLarge uint16 = 0x0003
	// StatusInvalidArgs indicates malformed request.
	StatusInvalidArgs uint16 = 0x0004
	// StatusItemNotStored indicates add/replace preconditions failed.
	StatusItemNotStored uint16 = 0x0005
	// StatusNonNumeric indicates incr/decr on a non-numeric value.
	StatusNonNumeric uint16 = 0x0006
	// StatusUnknownCommand indicates an unknown opcode.
	StatusUnknownCommand uint16 = 0x0081
	// StatusOutOfMemory indicates server-side OOM.
	StatusOutOfMemory uint16 = 0x0082
)

Binary status codes.

View Source
const MaxValueLen = 128 * 1024 * 1024

MaxValueLen caps the advertised length of a single VALUE data block. 128 MiB is above memcached's default 1 MiB per-item limit with plenty of headroom for servers raised via -I.

Variables

View Source
var ErrIncomplete = errors.New("celeris-memcached-protocol: incomplete frame")

ErrIncomplete indicates the buffered input does not yet contain a complete reply. The reader's cursor is not advanced and the caller should feed more bytes and retry.

View Source
var ErrProtocol = errors.New("celeris-memcached-protocol: protocol error")

ErrProtocol is returned when server input cannot be parsed against either dialect (corrupted stream, unknown reply line, bad length header, ...).

Functions

This section is empty.

Types

type BinaryHeader

type BinaryHeader struct {
	Magic       byte
	Opcode      byte
	KeyLen      uint16
	ExtrasLen   uint8
	DataType    uint8
	VBucketOrSt uint16 // vbucket (request) or status (response)
	BodyLen     uint32
	Opaque      uint32
	CAS         uint64
}

BinaryHeader is the fixed 24-byte packet header. Magic + opcode + lengths + status/vbucket + opaque + CAS.

type BinaryPacket

type BinaryPacket struct {
	Header BinaryHeader
	Extras []byte
	Key    []byte
	Value  []byte
}

BinaryPacket is one decoded binary-protocol packet. Extras/Key/Value slices alias the reader's internal buffer; copy before retaining past the next Feed/Compact cycle.

func (*BinaryPacket) Status

func (p *BinaryPacket) Status() uint16

Status returns the response status (response packets only).

type BinaryReader

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

BinaryReader streams binary-protocol packets.

func NewBinaryReader

func NewBinaryReader() *BinaryReader

NewBinaryReader returns a ready-to-use BinaryReader.

func (*BinaryReader) Compact

func (r *BinaryReader) Compact()

Compact discards already-parsed bytes.

func (*BinaryReader) Feed

func (r *BinaryReader) Feed(data []byte)

Feed appends data to the internal buffer.

func (*BinaryReader) Next

func (r *BinaryReader) Next() (BinaryPacket, error)

Next parses one complete packet. Returns ErrIncomplete if more bytes are needed; the read cursor is restored in that case.

func (*BinaryReader) Reset

func (r *BinaryReader) Reset()

Reset clears internal state while retaining the buffer for reuse.

type BinaryWriter

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

BinaryWriter builds binary-protocol requests into a reusable byte buffer.

func NewBinaryWriter

func NewBinaryWriter() *BinaryWriter

NewBinaryWriter returns a BinaryWriter with a small pre-allocated buffer.

func (*BinaryWriter) AppendArith

func (w *BinaryWriter) AppendArith(opcode byte, key string, delta, initial uint64, exptime uint32, opaque uint32) []byte

AppendArith encodes an INCREMENT / DECREMENT packet. Extras are delta(8) + initial(8) + exptime(4) = 20 bytes, big-endian.

func (*BinaryWriter) AppendConcat

func (w *BinaryWriter) AppendConcat(opcode byte, key string, value []byte, cas uint64, opaque uint32) []byte

AppendConcat encodes append/prepend which have no extras.

func (*BinaryWriter) AppendDelete

func (w *BinaryWriter) AppendDelete(key string, cas uint64, opaque uint32) []byte

AppendDelete encodes a DELETE with no extras.

func (*BinaryWriter) AppendFlush

func (w *BinaryWriter) AppendFlush(exptime uint32, opaque uint32) []byte

AppendFlush encodes a FLUSH packet with an optional exptime extra.

func (*BinaryWriter) AppendGAT

func (w *BinaryWriter) AppendGAT(opcode byte, key string, exptime uint32, opaque uint32) []byte

AppendGAT encodes a Get-And-Touch (or GATQ) packet. Extras carry exptime(4).

func (*BinaryWriter) AppendGet

func (w *BinaryWriter) AppendGet(opcode byte, key string, opaque uint32) []byte

AppendGet encodes a simple GET (or GetK) with no extras.

func (*BinaryWriter) AppendRequest

func (w *BinaryWriter) AppendRequest(opcode byte, extras, key, value []byte, cas uint64, opaque uint32) []byte

AppendRequest encodes a request packet with the given opcode, extras, key, value, cas, and opaque. The buffer is appended to — call BinaryWriter.Reset to start a fresh packet.

func (*BinaryWriter) AppendSimple

func (w *BinaryWriter) AppendSimple(opcode byte, opaque uint32) []byte

AppendSimple encodes a packet with no extras/key/value (e.g. version, noop, quit, stats-without-arg).

func (*BinaryWriter) AppendStats

func (w *BinaryWriter) AppendStats(arg string, opaque uint32) []byte

AppendStats encodes STATS with an optional sub-statistic key.

func (*BinaryWriter) AppendStorage

func (w *BinaryWriter) AppendStorage(opcode byte, key string, value []byte, flags uint32, exptime uint32, cas uint64, opaque uint32) []byte

AppendStorage encodes a storage-class request (set/add/replace). The 8-byte extras carry flags (4) + exptime (4), big-endian.

func (*BinaryWriter) AppendTouch

func (w *BinaryWriter) AppendTouch(key string, exptime uint32, opaque uint32) []byte

AppendTouch encodes a TOUCH packet. Extras carry exptime(4).

func (*BinaryWriter) Bytes

func (w *BinaryWriter) Bytes() []byte

Bytes returns the serialized buffer.

func (*BinaryWriter) Reset

func (w *BinaryWriter) Reset()

Reset empties the internal buffer without freeing it.

type Kind

type Kind uint8

Kind tags the flavor of a parsed text-protocol reply.

const (
	// KindStored corresponds to "STORED\r\n".
	KindStored Kind = iota
	// KindNotStored corresponds to "NOT_STORED\r\n".
	KindNotStored
	// KindExists corresponds to "EXISTS\r\n" (CAS mismatch).
	KindExists
	// KindNotFound corresponds to "NOT_FOUND\r\n".
	KindNotFound
	// KindDeleted corresponds to "DELETED\r\n".
	KindDeleted
	// KindTouched corresponds to "TOUCHED\r\n".
	KindTouched
	// KindOK corresponds to "OK\r\n".
	KindOK
	// KindEnd corresponds to "END\r\n" and terminates a get/gets/stats reply.
	KindEnd
	// KindValue is a "VALUE <key> <flags> <bytes> [<cas>]\r\n<data>\r\n" block.
	KindValue
	// KindStat is a "STAT <name> <value>\r\n" line within a stats reply.
	KindStat
	// KindNumber is a decimal integer reply from incr/decr.
	KindNumber
	// KindVersion is a "VERSION <version>\r\n" reply.
	KindVersion
	// KindError is a generic "ERROR\r\n" reply (unknown command).
	KindError
	// KindClientError is a "CLIENT_ERROR <msg>\r\n" reply (malformed input).
	KindClientError
	// KindServerError is a "SERVER_ERROR <msg>\r\n" reply (server-side fault).
	KindServerError
)

func (Kind) String

func (k Kind) String() string

String returns a short name for diagnostics.

type TextReader

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

TextReader streams text-protocol replies. The zero value is not usable; call NewTextReader.

func NewTextReader

func NewTextReader() *TextReader

NewTextReader returns a ready-to-use TextReader.

func (*TextReader) Compact

func (r *TextReader) Compact()

Compact discards already-parsed bytes. Aliased slices from previously returned replies become invalid after Compact.

func (*TextReader) Feed

func (r *TextReader) Feed(data []byte)

Feed appends data to the internal buffer.

func (*TextReader) Next

func (r *TextReader) Next() (TextReply, error)

Next parses one complete text-protocol reply. Returns ErrIncomplete if more bytes are needed; the read cursor is restored in that case.

func (*TextReader) Reset

func (r *TextReader) Reset()

Reset clears internal state while retaining the buffer for reuse.

type TextReply

type TextReply struct {
	Kind  Kind
	Key   []byte
	Flags uint32
	// CAS is the compare-and-swap token for VALUE blocks returned by gets/gats
	// replies. Zero when the server did not advertise one.
	CAS  uint64
	Int  uint64
	Data []byte
}

TextReply is one decoded text-protocol reply line (or VALUE block).

Depending on Kind, different fields are populated:

  • KindValue: Key, Flags, CAS, Data.
  • KindStat: Key (name), Data (value).
  • KindNumber: Int.
  • KindVersion: Data (version string).
  • KindError / KindClientError / KindServerError: Data (message, may be empty).
  • Scalars (Stored, Deleted, Touched, OK, End, ...): no extra fields.

type TextWriter

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

TextWriter builds text-protocol requests into a reusable byte buffer. The internal buffer is reset on every TextWriter.Reset call; callers that batch multiple commands should call Reset at the start of a batch and then use the Append* methods.

func NewTextWriter

func NewTextWriter() *TextWriter

NewTextWriter returns a TextWriter with a small pre-allocated buffer.

func (*TextWriter) AppendArith

func (w *TextWriter) AppendArith(cmd, key string, delta uint64) []byte

AppendArith encodes "incr <key> <delta>\r\n" or "decr <key> <delta>\r\n".

func (*TextWriter) AppendDelete

func (w *TextWriter) AppendDelete(key string) []byte

AppendDelete encodes "delete <key>\r\n".

func (*TextWriter) AppendFlushAll

func (w *TextWriter) AppendFlushAll(delay int64) []byte

AppendFlushAll encodes "flush_all[ <delay>]\r\n". Pass a negative delay to omit the argument.

func (*TextWriter) AppendRetrieval

func (w *TextWriter) AppendRetrieval(cmd string, keys ...string) []byte

AppendRetrieval encodes "<cmd> <key1> <key2> ...\r\n". cmd is one of get / gets. gat/gats carry a leading exptime — see [AppendRetrievalTouch].

func (*TextWriter) AppendRetrievalTouch

func (w *TextWriter) AppendRetrievalTouch(cmd string, exptime int64, keys ...string) []byte

AppendRetrievalTouch encodes "<cmd> <exptime> <key1> <key2> ...\r\n". cmd is one of gat / gats.

func (*TextWriter) AppendSimple

func (w *TextWriter) AppendSimple(cmd string) []byte

AppendSimple encodes a single-word command like "version\r\n", "stats\r\n", or "quit\r\n".

func (*TextWriter) AppendStats

func (w *TextWriter) AppendStats(arg string) []byte

AppendStats encodes "stats[ <arg>]\r\n".

func (*TextWriter) AppendStorage

func (w *TextWriter) AppendStorage(cmd, key string, flags uint32, exptime int64, data []byte, casID uint64, noreply bool) []byte

AppendStorage encodes "<cmd> <key> <flags> <exptime> <bytes>[ <cas>][ noreply]\r\n<data>\r\n". cmd is one of: set, add, replace, append, prepend, cas. For cas commands, casID must be non-zero.

func (*TextWriter) AppendTouch

func (w *TextWriter) AppendTouch(key string, exptime int64) []byte

AppendTouch encodes "touch <key> <exptime>\r\n".

func (*TextWriter) Bytes

func (w *TextWriter) Bytes() []byte

Bytes returns the serialized buffer.

func (*TextWriter) Reset

func (w *TextWriter) Reset()

Reset empties the internal buffer without freeing it.

Jump to

Keyboard shortcuts

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