Documentation
¶
Overview ¶
Package redis is a pure-Go (CGO-free) reimplementation of the RESP protocol codec and command layer of Ruby's redis / redis-client gem. It encodes a command (an array of bulk strings) to the RESP wire format and decodes every RESP2 and RESP3 reply type into a small, explicit Ruby value model, then maps each command's reply to the typed value the gem returns (a Hash for HGETALL, a Set for SMEMBERS, an Integer for EXISTS, and so on).
The actual TCP/TLS socket is deliberately not part of this package: it is a host seam, mirroring the go-ruby-net-* pattern. A Client drives its commands over an injected Conn (an io.ReadWriter) or a RoundTripper the host wires to a live connection; this package owns only the deterministic, ruby-free wire grammar and command surface.
Ruby value model ¶
A decoded reply is represented by an [any] drawn from a small, fixed set of Go types so a host (such as go-embedded-ruby) can map replies onto its own object graph:
RESP Go value ---- -------- null ($-1, *-1, _) nil simple string (+) string simple error (-) *CommandError blob error (!) *CommandError integer (:) int64 double (,) float64 big number (() *big.Int boolean (#) bool bulk string ($) string verbatim string ($=/=) *VerbatimString array (*) []any map (%) *Map (ordered) set (~) *Set push (>) *Push attribute (|) carried on the following reply, not a value
The command layer then coerces these primitives into the types the gem's methods return (see reply.go).
Index ¶
- Variables
- func DecodeBytes(b []byte) (any, error)
- func EncodeCommand(args ...any) []byte
- func EncodeInline(args ...any) []byte
- type Batch
- type Client
- func (c *Client) Append(key, value any) (any, error)
- func (c *Client) Auth(args ...any) (any, error)
- func (c *Client) Call(args ...any) (any, error)
- func (c *Client) ConfigGet(pattern any) (any, error)
- func (c *Client) Decr(key any) (any, error)
- func (c *Client) DecrBy(key, n any) (any, error)
- func (c *Client) Del(keys ...any) (any, error)
- func (c *Client) Echo(message any) (any, error)
- func (c *Client) Exists(keys ...any) (any, error)
- func (c *Client) Expire(key, seconds any) (any, error)
- func (c *Client) ExpireAt(key, ts any) (any, error)
- func (c *Client) FlushDB() (any, error)
- func (c *Client) Get(key any) (any, error)
- func (c *Client) GetRange(key, start, stop any) (any, error)
- func (c *Client) GetSet(key, value any) (any, error)
- func (c *Client) HDel(key any, fields ...any) (any, error)
- func (c *Client) HExists(key, field any) (any, error)
- func (c *Client) HGet(key, field any) (any, error)
- func (c *Client) HGetAll(key any) (any, error)
- func (c *Client) HIncrBy(key, field, n any) (any, error)
- func (c *Client) HIncrByFloat(key, field, n any) (any, error)
- func (c *Client) HKeys(key any) (any, error)
- func (c *Client) HLen(key any) (any, error)
- func (c *Client) HMGet(key any, fields ...any) (any, error)
- func (c *Client) HSet(key any, pairs ...any) (any, error)
- func (c *Client) HVals(key any) (any, error)
- func (c *Client) Handshake() error
- func (c *Client) Hello(args ...any) (any, error)
- func (c *Client) Incr(key any) (any, error)
- func (c *Client) IncrBy(key, n any) (any, error)
- func (c *Client) IncrByFloat(key, n any) (any, error)
- func (c *Client) IsRESP3() bool
- func (c *Client) Keys(pattern any) (any, error)
- func (c *Client) LIndex(key, index any) (any, error)
- func (c *Client) LLen(key any) (any, error)
- func (c *Client) LPop(key any) (any, error)
- func (c *Client) LPush(key any, values ...any) (any, error)
- func (c *Client) LRange(key, start, stop any) (any, error)
- func (c *Client) LSet(key, index, value any) (any, error)
- func (c *Client) LTrim(key, start, stop any) (any, error)
- func (c *Client) MGet(keys ...any) (any, error)
- func (c *Client) MSet(pairs ...any) (any, error)
- func (c *Client) Multi(fn func(*Batch)) (any, error)
- func (c *Client) NextMessage() (*Message, error)
- func (c *Client) PSubscribe(patterns ...any) (*Message, error)
- func (c *Client) PTTL(key any) (any, error)
- func (c *Client) PUnsubscribe(patterns ...any) (*Message, error)
- func (c *Client) Persist(key any) (any, error)
- func (c *Client) Ping(message ...any) (any, error)
- func (c *Client) Pipelined(fn func(*Batch)) ([]any, error)
- func (c *Client) Publish(channel, message any) (any, error)
- func (c *Client) RPop(key any) (any, error)
- func (c *Client) RPush(key any, values ...any) (any, error)
- func (c *Client) Rename(key, newkey any) (any, error)
- func (c *Client) SAdd(key any, members ...any) (any, error)
- func (c *Client) SCard(key any) (any, error)
- func (c *Client) SDiff(keys ...any) (any, error)
- func (c *Client) SInter(keys ...any) (any, error)
- func (c *Client) SIsMember(key, member any) (any, error)
- func (c *Client) SMembers(key any) (any, error)
- func (c *Client) SRem(key any, members ...any) (any, error)
- func (c *Client) SUnion(keys ...any) (any, error)
- func (c *Client) Scan(cursor any, opts ...any) (any, error)
- func (c *Client) Select(index any) (any, error)
- func (c *Client) Set(key, value any, opts ...any) (any, error)
- func (c *Client) SetNX(key, value any) (any, error)
- func (c *Client) SetRange(key, offset, value any) (any, error)
- func (c *Client) Strlen(key any) (any, error)
- func (c *Client) Subscribe(channels ...any) (*Message, error)
- func (c *Client) TTL(key any) (any, error)
- func (c *Client) Type(key any) (any, error)
- func (c *Client) Unsubscribe(channels ...any) (*Message, error)
- func (c *Client) ZAdd(key any, scoreMembers ...any) (any, error)
- func (c *Client) ZCard(key any) (any, error)
- func (c *Client) ZIncrBy(key, increment, member any) (any, error)
- func (c *Client) ZRange(key, start, stop any, opts ...any) (any, error)
- func (c *Client) ZRank(key, member any) (any, error)
- func (c *Client) ZRem(key any, members ...any) (any, error)
- func (c *Client) ZScore(key, member any) (any, error)
- type CommandError
- type Conn
- type Decoder
- type Map
- type Message
- type MessageKind
- type Options
- type Push
- type RoundTripper
- type ScanResult
- type Set
- type Stringer
- type Symbol
- type Value
- type VerbatimString
Constants ¶
This section is empty.
Variables ¶
var ErrProtocol = errors.New("redis: protocol error")
ErrProtocol reports a malformed RESP stream — a byte the grammar does not permit, or a truncated frame. The gem raises Redis::ProtocolError in the same situations; a host may map this error onto that class.
Functions ¶
func DecodeBytes ¶
DecodeBytes decodes a single reply from a complete byte slice — the golden- vector path used by the ruby-free tests: hand-built RESP bytes in, a value model out. It errors if the bytes hold no complete reply.
func EncodeCommand ¶
EncodeCommand serialises one command — a name plus its arguments — to the RESP multi-bulk wire format: `*N\r\n$len\r\n<arg>\r\n…`. This is the RESP2 request framing every Redis command uses on the wire (RESP3 requests are identical; only replies differ). Every argument is stringified via [arg].
func EncodeInline ¶
EncodeInline serialises a command in the legacy inline form: the arguments separated by single spaces and terminated by CRLF. Redis accepts inline commands when a request does not begin with `*`; the gem never emits them, but a host may need to (e.g. a bare "PING\r\n"). Arguments must not contain spaces or CRLF; callers that need those must use EncodeCommand.
Types ¶
type Batch ¶
type Batch struct {
// contains filtered or unexported fields
}
Batch accumulates commands to be sent together. redis.pipelined and redis.multi yield an object like this; each queued command records its arguments, and the batch is flushed as one write with the replies read back in order.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a Redis client bound to a host connection seam. It builds RESP commands, sends them over the seam, decodes the replies and coerces each to the typed value the redis / redis-client gem returns. It owns no socket: the caller supplies a Conn (streaming, the default) or a RoundTripper.
A zero Client is not usable; construct one with New, NewFromConn or NewFromRoundTripper.
func New ¶
New builds a Client over a streaming Conn and records opts for a host driver to apply during its handshake. It mirrors Redis.new(**opts) at the protocol layer; the actual AUTH/SELECT/HELLO round-trips are the caller's to issue (see Client.Handshake).
func NewFromConn ¶
NewFromConn builds a Client that speaks over a streaming Conn. This is the primary constructor: it supports pipelining, MULTI/EXEC and pub/sub.
func NewFromRoundTripper ¶
func NewFromRoundTripper(rt RoundTripper) *Client
NewFromRoundTripper builds a Client that speaks over a request/reply RoundTripper. Pipelining and transactions still work: the batch is encoded as one request and its concatenated replies are decoded in order.
func (*Client) Auth ¶
Auth authenticates the connection, returning "OK" (AUTH). Pass one argument for password-only auth or two for username+password (ACL) auth.
func (*Client) Call ¶
Call sends one command — a variadic name plus arguments — and returns the raw decoded reply (the value model, no per-command coercion), mirroring redis.call([...]). A Redis error reply is promoted to a returned error, as the gem raises Redis::CommandError. This is the escape hatch for commands the typed surface does not wrap.
func (*Client) ConfigGet ¶
ConfigGet returns matching configuration parameters as an ordered Map (CONFIG GET).
func (*Client) ExpireAt ¶
ExpireAt sets a key's expiry to a UNIX timestamp, returning a boolean (EXPIREAT).
func (*Client) HIncrByFloat ¶
HIncrByFloat increments a hash field by a float, returning a Float (HINCRBYFLOAT).
func (*Client) HSet ¶
HSet sets one or more field/value pairs on a hash and returns the number of new fields (HSET).
func (*Client) Handshake ¶
Handshake applies the recorded Options to a fresh connection the way the gem does after connecting: HELLO for RESP3 (or AUTH for RESP2), then SELECT. It is a convenience over the individual commands; a host that manages its own handshake need not call it.
func (*Client) Hello ¶
Hello performs the RESP3 handshake, returning the server's HELLO reply as an ordered Map (HELLO). Pass the protocol version (2 or 3) and any AUTH/SETNAME options; requesting 3 flips the connection into RESP3 for subsequent replies.
func (*Client) IncrByFloat ¶
IncrByFloat increments the float at key by n, returning a Float (INCRBYFLOAT).
func (*Client) LRange ¶
LRange returns the elements of a list between start and stop as an Array (LRANGE).
func (*Client) MGet ¶
MGet returns the values of the given keys as an Array with nil for missing keys (MGET).
func (*Client) Multi ¶
Multi runs fn to queue commands, wraps them in a MULTI/EXEC transaction and returns the array of results EXEC yields (redis.multi). The wire framing is MULTI, then every queued command (each answered "+QUEUED"), then EXEC, whose reply is the array of the queued commands' results. A nil EXEC reply means the transaction was aborted (a watched key changed); it is returned as nil with no error, as the gem returns nil from a discarded multi block.
func (*Client) NextMessage ¶
NextMessage reads and classifies the next pub/sub frame from the connection — the read side of a redis.subscribe loop. It only works on a streaming Conn client (pub/sub is inherently multi-reply); on a RoundTripper client it returns an error.
func (*Client) PSubscribe ¶
PSubscribe sends a PSUBSCRIBE for the given patterns (PSUBSCRIBE).
func (*Client) PUnsubscribe ¶
PUnsubscribe sends a PUNSUBSCRIBE (PUNSUBSCRIBE).
func (*Client) Pipelined ¶
Pipelined runs fn to queue commands on a fresh Batch, sends them all in one write, then reads and returns each reply in order (redis.pipelined). A Redis error reply for a queued command is returned as a *CommandError value in the results slice (not a returned error), so one failed command does not mask the others — matching the gem, which collects per-command results. A stream or protocol fault aborts with a non-nil error.
func (*Client) Publish ¶
Publish sends a PUBLISH and returns the number of subscribers that received the message (PUBLISH).
func (*Client) SIsMember ¶
SIsMember reports whether a value is a set member, as a boolean (SISMEMBER).
func (*Client) Scan ¶
Scan performs one SCAN iteration from cursor, returning a ScanResult. Extra options (MATCH/COUNT/TYPE) may be appended as further args.
func (*Client) Set ¶
Set stores value at key and returns the "OK" status (SET). Extra options (EX/PX/NX/XX/…) may be appended as further args.
func (*Client) SetNX ¶
SetNX sets key to value only if it does not exist, returning a boolean (SETNX).
func (*Client) SetRange ¶
SetRange overwrites part of key starting at offset, returning the new length (SETRANGE).
func (*Client) Subscribe ¶
Subscribe sends a SUBSCRIBE for the given channels and returns the request's first reply (the first subscribe confirmation) as a Message. Redis answers one confirmation per channel; a host reads the remaining confirmations and the subsequent deliveries with Client.NextMessage.
func (*Client) TTL ¶
TTL returns a key's remaining time-to-live in seconds (TTL): -2 if the key is gone, -1 if it has no expiry.
func (*Client) Unsubscribe ¶
Unsubscribe sends an UNSUBSCRIBE (UNSUBSCRIBE). With no channels it unsubscribes from all.
func (*Client) ZAdd ¶
ZAdd adds scored members to a sorted set, returning the count added (ZADD). Arguments alternate score, member (plus any leading NX/XX/GT/LT/CH options).
func (*Client) ZIncrBy ¶
ZIncrBy increments a member's score, returning the new score as a Float (ZINCRBY).
func (*Client) ZRange ¶
ZRange returns members in the given rank range as an Array (ZRANGE). Append "WITHSCORES" to interleave scores.
type CommandError ¶
type CommandError struct {
// Message is the full error text, e.g. "WRONGTYPE Operation against a key
// holding the wrong kind of value".
Message string
}
CommandError is a Redis error reply — the RESP2 `-` simple error, or the RESP3 `!` blob error. Ruby's redis gem raises a Redis::CommandError carrying this message; the value model surfaces it as an error so a host can decide whether to raise. It satisfies the Go error interface.
func (*CommandError) Code ¶
func (e *CommandError) Code() string
Code returns the leading upper-case error code (the word before the first space), e.g. "WRONGTYPE" or "ERR". Redis conventionally prefixes error messages with such a code; the gem exposes it likewise. An empty message yields "".
func (*CommandError) Error ¶
func (e *CommandError) Error() string
Error implements the error interface.
type Conn ¶
type Conn = io.ReadWriter
Conn is the streaming host seam: a bidirectional byte pipe (an io.ReadWriter) the Client writes requests to and reads replies from. A live *net.Conn or a TLS connection satisfies it directly. This is the primary seam — it supports pipelining, transactions and pub/sub, which need many replies read from one stream — while RoundTripper suits simple request/reply transports.
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder reads RESP replies from a stream. It handles both RESP2 and RESP3: the two protocols share request framing and every RESP2 reply type, and RESP3 adds the typed replies (null, double, boolean, big number, verbatim string, map, set, push, attribute, blob error). One Decoder may read any mix of the two, because the leading type byte disambiguates every frame.
func (*Decoder) Decode ¶
Decode reads and returns exactly one reply, mapping it to the value model. A Redis error reply (RESP2 `-` or RESP3 `!`) is returned as a value of type *CommandError with a nil error; only stream/protocol faults produce a non-nil error. This mirrors the gem, which turns an error reply into a raised Redis::CommandError the caller handles, distinct from a broken connection.
func (*Decoder) LastAttribute ¶
LastAttribute returns the RESP3 attribute dictionary that preceded the most recently decoded reply, or nil if none did. Attributes are out-of-band metadata (e.g. key popularity, client-side-caching hints); the gem exposes them separately from the value, and so does this decoder.
type Map ¶
type Map struct {
// contains filtered or unexported fields
}
Map is an insertion-ordered key/value collection — the value model's Ruby Hash. RESP3 maps (`%`) decode to a Map, and command coercions that build a Hash reply (HGETALL, CONFIG GET, XPENDING summaries, …) return one so key order is preserved on round-trip, exactly as Ruby's ordered Hash does.
type Message ¶
type Message struct {
Kind MessageKind
Pattern string // set for pmessage / (p)subscribe patterns
Channel string // channel (or pattern for psubscribe confirmations)
Payload string // set for message / pmessage
Count int64 // set for (un)subscribe confirmations
}
Message is a decoded pub/sub frame. Depending on Kind, some fields are unset: a "message" carries Channel and Payload; a "pmessage" also carries Pattern; a subscribe/unsubscribe confirmation carries Channel and Count (the number of channels/patterns still subscribed).
type MessageKind ¶
type MessageKind string
MessageKind classifies a pub/sub frame.
const ( // KindSubscribe is a "subscribe" confirmation (channel subscribed). KindSubscribe MessageKind = "subscribe" // KindUnsubscribe is an "unsubscribe" confirmation. KindUnsubscribe MessageKind = "unsubscribe" // KindPSubscribe is a "psubscribe" pattern-subscription confirmation. KindPSubscribe MessageKind = "psubscribe" // KindPUnsubscribe is a "punsubscribe" confirmation. KindPUnsubscribe MessageKind = "punsubscribe" // KindMessage is a "message" delivery on a subscribed channel. KindMessage MessageKind = "message" // KindPMessage is a "pmessage" delivery matching a subscribed pattern. KindPMessage MessageKind = "pmessage" )
type Options ¶
type Options struct {
// Username and Password are sent via AUTH (or HELLO … AUTH) when non-empty,
// matching Redis.new(username:, password:). This package does not itself
// perform the handshake — call [Client.Auth] / [Client.Hello] — but the
// options are recorded so a host driver can.
Username string
Password string
// DB is the logical database index selected with SELECT after connecting
// (Redis.new(db:)). Zero means the default database.
DB int
// Protocol is 2 or 3; 3 requests the RESP3 handshake via HELLO 3. Zero
// defaults to 2, matching the gem's default.
Protocol int
}
Options configures a Client. The fields mirror the gem's Redis.new keywords that affect the protocol layer; transport keywords (host/port/ssl/timeouts) belong to the host that owns the socket, not to this codec.
type Push ¶
type Push struct {
Values []any
}
Push is a RESP3 out-of-band push message (`>`): the frame pub/sub and other server-initiated notifications arrive on. Its Values are the decoded elements (the first is the kind, e.g. "message", "subscribe", "pmessage").
type RoundTripper ¶
type RoundTripper interface {
// RoundTrip sends one request frame and returns its reply frame(s). For a
// single command the reply is one RESP value; the Client parses it.
RoundTrip(request []byte) (reply []byte, err error)
}
RoundTripper is the host seam for a single request/response exchange: given the encoded RESP bytes of a command, it returns the raw RESP bytes of the reply. A host wires this to a live TCP/TLS connection (the socket lives outside this package, mirroring the go-ruby-net-* design). A Client built on a RoundTripper drives one command at a time; pipelines and transactions send a batch and read a matching run of replies, so a RoundTripper-backed Client serialises those onto a Conn-style stream internally.
type ScanResult ¶
ScanResult is the value model of a cursored scan reply. Cursor is the opaque continuation token ("0" when the scan is complete) and Elements are the keys / members / field-value pairs returned by this iteration.
func (*ScanResult) Done ¶
func (r *ScanResult) Done() bool
Done reports whether the scan has completed (the cursor returned to "0").
type Set ¶
type Set struct {
// contains filtered or unexported fields
}
Set is the value model's Ruby Set: an insertion-ordered collection of unique members. RESP3 sets (`~`) decode to a Set, and SMEMBERS / SUNION / SDIFF / SINTER coerce their array reply into one, matching the gem.
func (*Set) Add ¶
Add inserts member if it is not already present, preserving insertion order. It reports whether the member was newly added.
type Stringer ¶
type Stringer interface{ String() string }
Stringer mirrors fmt.Stringer without pulling fmt into the arg fast path.
type Symbol ¶
type Symbol string
Symbol is a Ruby Symbol (`:name`). A host may pass a Symbol as a command argument (the gem accepts symbols and stringifies them by name); the encoder serialises it as its bare name.
type Value ¶
type Value = any
Value is the interface satisfied by every Ruby value this package produces. It is purely documentary — the public API uses any — but a host may use it to constrain its own adapters.
type VerbatimString ¶
type VerbatimString struct {
// Format is the three-character type hint (e.g. "txt", "mkd").
Format string
// Text is the string payload (without the "fmt:" prefix).
Text string
}
VerbatimString is a RESP3 verbatim string (`=`): a bulk string carrying a three-byte format hint (e.g. "txt" or "mkd"). The gem treats it as a plain String; this model keeps the format so a host may inspect it.
func (*VerbatimString) String ¶
func (v *VerbatimString) String() string
String returns the payload, so a VerbatimString stringifies like the plain String the gem exposes.
