RESP request/reply over a pair of value streams, independent of the transport carrying them.
A transport and a connection are wired together with their streams: take the replies the
transport receives, establish the connection with them, hand the requests the connection
returns to the transport to send, then await the connection. See tcp.connect for a transport
that does this for its caller.
The connection is the mechanics of talking to a server: sending commands and pairing replies
with them, subscriptions, push messages, and transactions. Higher level, typed operations are
functions in the commands interface that borrow the connection.
Types
use record connect-optssince 0.3.0-dev
from componentized:valkey/types@0.3.0-dev
Options for establishing a connection.
auth:option<tuple<string, string>>The username and password to authenticate with.
client-name:option<string>The name of the connection, see
CLIENT SETNAME.keep-alive-interval:option<duration-milliseconds>Enables keep-alive pings at this interval, see
connection.set-keep-alive.
use record duration-millisecondssince 0.3.0-dev
from componentized:valkey/types@0.3.0-dev
A duration in whole milliseconds, e.g. PX milliseconds.
px:u64
use variant error-codesince 0.3.0-dev
from componentized:valkey/types@0.3.0-dev
connection The errors of a connection and of every command interface.
closed:option<string>The transport closed while a reply was outstanding, or before the command was sent. Holds the transport's error when it did not close gracefully.
client:stringClient errors are semantic issues with input/output values, e.g. a reply that does not have the shape the command expects.
resp:resp-errorRESP errors are encoding issues converting values to/from bytes.
server:string-or-bytesServer errors are Error or BulkError RESP values returned directly from the server. An Error is a string, a BulkError a string when it is UTF-8, bytes otherwise.
laggedThe reader of a stream fell too far behind and its stream was closed, rather than holding up the connection. What the stream missed is lost.
use variant resp-valuesince 0.3.0-dev
from componentized:valkey/types@0.3.0-dev
string:stringerror:stringinteger:integerbulk-string:bytesarray:list<resp-nested-value>nullboolean:booldouble:doublebig-number:stringbulk-error:bytesverbatim-string:tuple<string, string>map:list<tuple<resp-nested-value, resp-nested-value>>set:list<resp-nested-value>push:list<resp-nested-value>
use variant string-or-bytessince 0.3.0-dev
from componentized:valkey/types@0.3.0-dev
string:stringutf-8 decoded bytes.
bytes:bytesbytes that cannot be represented as a utf-8 string.
use variant subscription-eventsince 0.3.0-dev
from componentized:valkey/types@0.3.0-dev
What a subscription's stream carries, in the order it happened.
subscribed:subscription-targetThe server confirmed the target, its messages may arrive from now on.
unsubscribed:tuple<subscription-target, unsubscribe-reason>The target is no longer subscribed to, none of its messages arrive after this.
message:subscription-message
use variant subscription-targetsince 0.3.0-dev
from componentized:valkey/types@0.3.0-dev
What a subscription is subscribed to, see connection.subscription.
channel:stringA channel, see SUBSCRIBE.
pattern:stringA pattern of channels, see PSUBSCRIBE.
shardchannel:stringA shard channel, see SSUBSCRIBE.
resource connectionsince 0.3.0-dev
new: static async func( replies: stream<resp-value>, closed: future<result<_, error-code>>, opts: option<connect-opts>, ) -> tuple<stream<resp-value>, future<result<connection, error-code>>>since 0.3.0-devEstablishes a connection over a transport.
repliesare the values the transport receives from the server, andclosedresolves when the transport closes, tookfor a graceful close or to the error otherwise, seetcp.transport.receive. Returns the requests for the transport to write to the server, in order, and the connection once it is established.The connection sends
HELLO 3, with theauthandclient-nameofopts, as its first command. This pins the connection to RESP3 and confirms the server can be reached. The future resolves to the connection once HELLO succeeds, or to the error that stopped it. The rest ofoptsis applied before the connection is returned.It then reports the library to the server,
CLIENT SETINFO LIB-NAME componentized:valkeyandLIB-VERwith the version of the package, ignoring an error from a server that does not know CLIENT SETINFO. They survivereset,client-setinforeplaces them.Async so the connection keeps working between commands after it returns, e.g. to read push messages and to send keep-alive pings.
server-properties: func() -> list<tuple<string, resp-value>>since 0.3.0-devThe server's properties from the reply to HELLO, e.g.
server,versionandmode.exchange: async func( command: list<string-or-bytes>, ) -> result<resp-value, error-code>since 0.3.0-devSends a command and waits for its reply.
Several commands may be in flight at once, replies are paired with commands in the order the commands were sent.
For SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE and their UNSUBSCRIBE counterparts, prefer a
subscription, which owns its targets' messages. Once the connection has one, it alone sends these commands, and they fail with a client error before anything is sent, here, withexchange-all,emitandtransaction.RESET, HELLO and CLIENT REPLY always fail with a client error. See
resetandserver-properties, CLIENT REPLY would stop replies from pairing with commands, seeemit.A command has at least one word, an empty command fails with a client error.
exchange-all: async func( commands: list<list<string-or-bytes>>, ) -> list<result<resp-value, error-code>>since 0.3.0-devSends the commands one after another, with no other command sent between them, and waits for every reply.
Each command has its own result, in order, as if each was sent with
exchange. Commands sharing a connection may otherwise be interleaved with other callers' commands, which matters for a sequence that must not be interrupted, e.g. MULTI, its commands and EXEC.emit: async func( command: list<string-or-bytes>, ) -> result<_, error-code>since 0.3.0-devSends a command without waiting for its reply, resolving once the command is written.
The server still replies, the reply is discarded when it arrives and an error it reports is not returned. An implementation may instead ask the server not to reply, e.g. with
CLIENT REPLY SKIP.A command has at least one word, an empty command fails with a client error.
watch: async func(keys: list<string>) -> result<_, error-code>since 0.3.0-devMarks the keys to watch, EXEC aborts the next transaction when any of them changed.
Watched keys belong to the connection, and EXEC, DISCARD or UNWATCH from any caller sharing it clears them. Use a connection of your own for WATCH, the reads it guards, and the transaction.
WATCH key [ key ... ] https://valkey.io/commands/watch/
At least one key, no keys fails with a client error.
unwatch: async func() -> result<_, error-code>since 0.3.0-devForgets all the watched keys.
UNWATCH https://valkey.io/commands/unwatch/
transaction: async func( commands: list<list<string-or-bytes>>, ) -> result<option<list<resp-value>>, error-code>since 0.3.0-devRuns the commands as a transaction, MULTI, each command, then EXEC, with no other command sent between them, see
exchange-all.Returns none when a watched key changed and the transaction was aborted, otherwise the reply of each command in order. A command that fails while running is an error value in its place, the others still ran. A command rejected while queuing, e.g. an unknown command, fails the transaction with that error, none of the commands ran.
MULTI and EXEC are only sent as part of a transaction, the commands between them reply QUEUED rather than their reply.
MULTI, EXEC https://valkey.io/commands/multi/ https://valkey.io/commands/exec/
subscription: func() -> result<subscription, error-code>since 0.3.0-devA new subscription, subscribed to nothing yet, see the subscription's
subscribe.From the first subscription, the connection owns its subscriptions: SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE and their UNSUBSCRIBE counterparts sent with
exchangeand the like fail with a client error, so the server's subscriptions stay in step with the connection's. Subscriptions made withexchangebefore are unsupported. They may keep delivering todead-letters, the exact behavior is undocumented.Fails with a client error when the connection component was built without pub/sub, e.g.
connection-minimal. Its push messages are then all dead letters, and SUBSCRIBE and the like may still be sent withexchange.dead-letters: func() -> stream<resp-value>since 0.3.0-devThe values nothing else claims, kept apart from replies.
Push messages that no subscription owns, e.g. client tracking invalidations, messages and confirmations of SUBSCRIBE sent with
exchange, or kinds of push messages from a newer server. Values the server sends unprompted, when no command is waiting for a reply, are included, e.g. the feed of MONITOR, which are not RESP push values.Each call returns a new stream that receives every value arriving after the call, values arriving while no stream is open are dropped. A stream that falls too far behind is closed rather than holding up replies, the values it missed are lost.
reset: async func() -> result<_, error-code>since 0.3.0-devResets the connection to the state of a new one, then repeats the handshake of
new.RESET discards a transaction in progress, forgets the watched keys, unsubscribes from every channel, turns off client tracking and the like, selects database 0, and forgets the client name and the authenticated user. It also switches the connection to RESP2, so it is followed at once by
HELLO 3with theauthandclient-namethe connection was established with, with no other command sent between them.server-propertiesthen holds the new reply to HELLO.The subscriptions are unsubscribed from every target, each with the reason
reset, and stay open for new targets.No other command is sent until the handshake succeeds, so none runs on the connection RESET leaves. When the handshake fails, e.g. the credentials expired since the connection was established, the connection closes with its error, and the commands queued after the reset fail with it without being sent. Establish a new connection.
RESET https://valkey.io/commands/reset/
set-keep-alive: func(interval: option<duration-milliseconds>)since 0.3.0-devSends PING when no command has been sent and no reply is outstanding for
interval,nonedisables it. A PING that fails closes the connection. Disabled by default.Unlike TCP keep-alive, a PING counts as activity for the server's idle client timeout, and works over any transport.
resource subscriptionsince 0.3.0-dev
The messages of a set of channels, patterns of channels and shard channels, that changes
as targets are subscribed to and unsubscribed from, see connection.subscription.
Subscriptions sharing a connection may overlap, the server stays subscribed to a target while any subscription has it. The subscription stays open until it is dropped, with or without targets or streams, then it unsubscribes from its targets. It keeps its connection open.
subscribe: async func( targets: list<subscription-target>, ) -> result<_, error-code>since 0.3.0-devSubscribes to the targets, resolving once the server confirms each of them, see
subscription-event.subscribed. Targets already subscribed to are left as they are. The server unsubscribes from a shard channel on its own when its slot moves to another node.Each kind of target is subscribed to with its own command, queued with no other command between them. Every command is sent, even when another fails, and those that succeed take effect, see
subscription-event.subscribedandtargets. The error then holds the error of each command that failed, e.g.PSUBSCRIBE: NOPERM .... A command is accepted or refused as a whole, targets of one kind are subscribed to all or none.SUBSCRIBE channel [ channel ... ] PSUBSCRIBE pattern [ pattern ... ] SSUBSCRIBE shardchannel [ shardchannel ... ] https://valkey.io/commands/subscribe/ https://valkey.io/commands/psubscribe/ https://valkey.io/commands/ssubscribe/
At least one target, no targets fails with a client error.
events: func() -> tuple<stream<subscription-event>, future<result<_, error-code>>>since 0.3.0-devThe events arriving after the call: targets subscribed to and unsubscribed from, and messages, in the order they happened.
Each call returns a new stream, every open stream receives every event. Events arriving while no stream is open are kept for the next stream, up to 256, later ones are dropped. Dropping a stream does not unsubscribe, call
eventsagain for another.Pub/sub delivers each message at most once, to all listeners. A stream receives no events from before it opened, beyond those kept while no stream was open, and does not start with the current targets, see
targets. Messages published while nothing listens are lost, e.g. before a target is subscribed to, after the connection closed, or past what a lagging stream holds. For delivery that is guaranteed, use a Valkey stream, see XADD and XREADGROUP.The stream closes once the subscription is dropped, the future then resolves to
ok, or to the error that closed the connection. A stream that falls too far behind is closed rather than holding up the connection, its future resolves tolagged, and the events it missed, messages and changes of targets alike, are lost. The future resolves after the stream delivered what it holds, read the stream to its end first.targets: func() -> list<subscription-target>since 0.3.0-devThe targets the server confirmed, in the order they were subscribed to, until they are unsubscribed from.
Once the connection closes, the targets confirmed until then are still listed, e.g. to subscribe to them on a new connection.
subscribeandunsubscribethen fail with the error that closed the connection, and leave the targets as they are.unsubscribe: async func( targets: option<list<subscription-target>>, ) -> result<_, error-code>since 0.3.0-devUnsubscribes from the targets, none for every target, resolving once each is unsubscribed from, see
subscription-event.unsubscribed. Targets not subscribed to are left as they are. The server stays subscribed to targets other subscriptions have.Each kind of target is unsubscribed from with its own command, as for
subscribe, e.g. thetargetsmay be given as they are.UNSUBSCRIBE [ channel [ channel ... ] ] PUNSUBSCRIBE [ pattern [ pattern ... ] ] SUNSUBSCRIBE [ shardchannel [ shardchannel ... ] ] https://valkey.io/commands/unsubscribe/ https://valkey.io/commands/punsubscribe/ https://valkey.io/commands/sunsubscribe/
A list of targets has at least one target, otherwise fails with a client error. None unsubscribes from every target.
WIT
/// RESP request/reply over a pair of value streams, independent of the transport carrying them.
///
/// A transport and a connection are wired together with their streams: take the replies the
/// transport receives, establish the connection with them, hand the requests the connection
/// returns to the transport to send, then await the connection. See `tcp.connect` for a transport
/// that does this for its caller.
///
/// The connection is the mechanics of talking to a server: sending commands and pairing replies
/// with them, subscriptions, push messages, and transactions. Higher level, typed operations are
/// functions in the `commands` interface that borrow the connection.
interface connection {
@since(version = 0.3.0-dev)
use types.{connect-opts, duration-milliseconds, error-code, resp-value, string-or-bytes, subscription-event, subscription-target};
@since(version = 0.3.0-dev)
resource connection {
/// Establishes a connection over a transport.
///
/// `replies` are the values the transport receives from the server, and `closed` resolves
/// when the transport closes, to `ok` for a graceful close or to the error otherwise, see
/// `tcp.transport.receive`. Returns the requests for the transport to write to the server,
/// in order, and the connection once it is established.
///
/// The connection sends `HELLO 3`, with the `auth` and `client-name` of `opts`, as its first
/// command. This pins the connection to RESP3 and confirms the server can be reached. The
/// future resolves to the connection once HELLO succeeds, or to the error that stopped it.
/// The rest of `opts` is applied before the connection is returned.
///
/// It then reports the library to the server, `CLIENT SETINFO LIB-NAME componentized:valkey`
/// and `LIB-VER` with the version of the package, ignoring an error from a server that does
/// not know CLIENT SETINFO. They survive `reset`, `client-setinfo` replaces them.
///
/// Async so the connection keeps working between commands after it returns, e.g. to read
/// push messages and to send keep-alive pings.
@since(version = 0.3.0-dev)
new: static async func(replies: stream<resp-value>, closed: future<result<_, error-code>>, opts: option<connect-opts>) -> tuple<stream<resp-value>, future<result<connection, error-code>>>;
/// The server's properties from the reply to HELLO, e.g. `server`, `version` and `mode`.
@since(version = 0.3.0-dev)
server-properties: func() -> list<tuple<string, resp-value>>;
/// Sends a command and waits for its reply.
///
/// Several commands may be in flight at once, replies are paired with commands in the order
/// the commands were sent.
///
/// For SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE and their UNSUBSCRIBE counterparts, prefer a
/// `subscription`, which owns its targets' messages. Once the connection has one, it alone
/// sends these commands, and they fail with a client error before anything is sent, here,
/// with `exchange-all`, `emit` and `transaction`.
///
/// RESET, HELLO and CLIENT REPLY always fail with a client error. See `reset` and
/// `server-properties`, CLIENT REPLY would stop replies from pairing with commands, see
/// `emit`.
///
/// A command has at least one word, an empty command fails with a client error.
@since(version = 0.3.0-dev)
exchange: async func(command: list<string-or-bytes>) -> result<resp-value, error-code>;
/// Sends the commands one after another, with no other command sent between them, and waits
/// for every reply.
///
/// Each command has its own result, in order, as if each was sent with `exchange`. Commands
/// sharing a connection may otherwise be interleaved with other callers' commands, which
/// matters for a sequence that must not be interrupted, e.g. MULTI, its commands and EXEC.
@since(version = 0.3.0-dev)
exchange-all: async func(commands: list<list<string-or-bytes>>) -> list<result<resp-value, error-code>>;
/// Sends a command without waiting for its reply, resolving once the command is written.
///
/// The server still replies, the reply is discarded when it arrives and an error it reports
/// is not returned. An implementation may instead ask the server not to reply, e.g. with
/// `CLIENT REPLY SKIP`.
///
/// A command has at least one word, an empty command fails with a client error.
@since(version = 0.3.0-dev)
emit: async func(command: list<string-or-bytes>) -> result<_, error-code>;
/// Marks the keys to watch, EXEC aborts the next transaction when any of them changed.
///
/// Watched keys belong to the connection, and EXEC, DISCARD or UNWATCH from any caller sharing
/// it clears them. Use a connection of your own for WATCH, the reads it guards, and the
/// transaction.
///
/// WATCH key [ key ... ]
/// https://valkey.io/commands/watch/
///
/// At least one key, no keys fails with a client error.
@since(version = 0.3.0-dev)
watch: async func(keys: list<string>) -> result<_, error-code>;
/// Forgets all the watched keys.
///
/// UNWATCH
/// https://valkey.io/commands/unwatch/
@since(version = 0.3.0-dev)
unwatch: async func() -> result<_, error-code>;
/// Runs the commands as a transaction, MULTI, each command, then EXEC, with no other command
/// sent between them, see `exchange-all`.
///
/// Returns none when a watched key changed and the transaction was aborted, otherwise the
/// reply of each command in order. A command that fails while running is an error value in
/// its place, the others still ran. A command rejected while queuing, e.g. an unknown
/// command, fails the transaction with that error, none of the commands ran.
///
/// MULTI and EXEC are only sent as part of a transaction, the commands between them reply
/// QUEUED rather than their reply.
///
/// MULTI, EXEC
/// https://valkey.io/commands/multi/
/// https://valkey.io/commands/exec/
@since(version = 0.3.0-dev)
transaction: async func(commands: list<list<string-or-bytes>>) -> result<option<list<resp-value>>, error-code>;
/// A new subscription, subscribed to nothing yet, see the subscription's `subscribe`.
///
/// From the first subscription, the connection owns its subscriptions: SUBSCRIBE,
/// PSUBSCRIBE, SSUBSCRIBE and their UNSUBSCRIBE counterparts sent with `exchange` and the like
/// fail with a client error, so the server's subscriptions stay in step with the
/// connection's. Subscriptions made with `exchange` before are unsupported. They may keep
/// delivering to `dead-letters`, the exact behavior is undocumented.
///
/// Fails with a client error when the connection component was built without pub/sub,
/// e.g. `connection-minimal`. Its push messages are then all dead letters, and SUBSCRIBE and
/// the like may still be sent with `exchange`.
@since(version = 0.3.0-dev)
subscription: func() -> result<subscription, error-code>;
/// The values nothing else claims, kept apart from replies.
///
/// Push messages that no subscription owns, e.g. client tracking invalidations, messages
/// and confirmations of SUBSCRIBE sent with `exchange`, or kinds of push messages from a
/// newer server. Values the server sends unprompted, when no command is waiting for a
/// reply, are included, e.g. the feed of MONITOR, which are not RESP push values.
///
/// Each call returns a new stream that receives every value arriving after the call,
/// values arriving while no stream is open are dropped. A stream that falls too far behind
/// is closed rather than holding up replies, the values it missed are lost.
@since(version = 0.3.0-dev)
dead-letters: func() -> stream<resp-value>;
/// Resets the connection to the state of a new one, then repeats the handshake of `new`.
///
/// RESET discards a transaction in progress, forgets the watched keys, unsubscribes from
/// every channel, turns off client tracking and the like, selects database 0, and forgets
/// the client name and the authenticated user. It also switches the connection to RESP2,
/// so it is followed at once by `HELLO 3` with the `auth` and `client-name` the connection
/// was established with, with no other command sent between them. `server-properties`
/// then holds the new reply to HELLO.
///
/// The subscriptions are unsubscribed from every target, each with the reason `reset`, and
/// stay open for new targets.
///
/// No other command is sent until the handshake succeeds, so none runs on the connection
/// RESET leaves. When the handshake fails, e.g. the credentials expired since the connection
/// was established, the connection closes with its error, and the commands queued after
/// the reset fail with it without being sent. Establish a new connection.
///
/// RESET
/// https://valkey.io/commands/reset/
@since(version = 0.3.0-dev)
reset: async func() -> result<_, error-code>;
/// Sends PING when no command has been sent and no reply is outstanding for `interval`,
/// `none` disables it. A PING that fails closes the connection. Disabled by default.
///
/// Unlike TCP keep-alive, a PING counts as activity for the server's idle client timeout,
/// and works over any transport.
@since(version = 0.3.0-dev)
set-keep-alive: func(interval: option<duration-milliseconds>);
}
/// The messages of a set of channels, patterns of channels and shard channels, that changes
/// as targets are subscribed to and unsubscribed from, see `connection.subscription`.
///
/// Subscriptions sharing a connection may overlap, the server stays subscribed to a target while
/// any subscription has it. The subscription stays open until it is dropped, with or without
/// targets or streams, then it unsubscribes from its targets. It keeps its connection open.
@since(version = 0.3.0-dev)
resource subscription {
/// Subscribes to the targets, resolving once the server confirms each of them, see
/// `subscription-event.subscribed`. Targets already subscribed to are left as they are. The
/// server unsubscribes from a shard channel on its own when its slot moves to another node.
///
/// Each kind of target is subscribed to with its own command, queued with no other command
/// between them. Every command is sent, even when another fails, and those that succeed
/// take effect, see `subscription-event.subscribed` and `targets`. The error then holds the
/// error of each command that failed, e.g. `PSUBSCRIBE: NOPERM ...`. A command is accepted
/// or refused as a whole, targets of one kind are subscribed to all or none.
///
/// SUBSCRIBE channel [ channel ... ]
/// PSUBSCRIBE pattern [ pattern ... ]
/// SSUBSCRIBE shardchannel [ shardchannel ... ]
/// https://valkey.io/commands/subscribe/
/// https://valkey.io/commands/psubscribe/
/// https://valkey.io/commands/ssubscribe/
///
/// At least one target, no targets fails with a client error.
@since(version = 0.3.0-dev)
subscribe: async func(targets: list<subscription-target>) -> result<_, error-code>;
/// The events arriving after the call: targets subscribed to and unsubscribed from, and
/// messages, in the order they happened.
///
/// Each call returns a new stream, every open stream receives every event. Events arriving
/// while no stream is open are kept for the next stream, up to 256, later ones are dropped.
/// Dropping a stream does not unsubscribe, call `events` again for another.
///
/// Pub/sub delivers each message at most once, to all listeners. A stream receives
/// no events from before it opened, beyond those kept while no stream was open, and does
/// not start with the current targets, see `targets`. Messages published while nothing
/// listens are lost, e.g. before a target is subscribed to, after the connection closed, or
/// past what a lagging stream holds. For delivery that is guaranteed, use a Valkey stream,
/// see XADD and XREADGROUP.
///
/// The stream closes once the subscription is dropped, the future then resolves to `ok`,
/// or to the error that closed the connection. A stream that falls too far behind is
/// closed rather than holding up the connection, its future resolves to `lagged`, and the
/// events it missed, messages and changes of targets alike, are lost. The future resolves
/// after the stream delivered what it holds, read the stream to its end first.
@since(version = 0.3.0-dev)
events: func() -> tuple<stream<subscription-event>, future<result<_, error-code>>>;
/// The targets the server confirmed, in the order they were subscribed to, until they are
/// unsubscribed from.
///
/// Once the connection closes, the targets confirmed until then are still listed, e.g. to
/// subscribe to them on a new connection. `subscribe` and `unsubscribe` then fail with the
/// error that closed the connection, and leave the targets as they are.
@since(version = 0.3.0-dev)
targets: func() -> list<subscription-target>;
/// Unsubscribes from the targets, none for every target, resolving once each is
/// unsubscribed from, see `subscription-event.unsubscribed`. Targets not subscribed to are
/// left as they are. The server stays subscribed to targets other subscriptions have.
///
/// Each kind of target is unsubscribed from with its own command, as for `subscribe`, e.g.
/// the `targets` may be given as they are.
///
/// UNSUBSCRIBE [ channel [ channel ... ] ]
/// PUNSUBSCRIBE [ pattern [ pattern ... ] ]
/// SUNSUBSCRIBE [ shardchannel [ shardchannel ... ] ]
/// https://valkey.io/commands/unsubscribe/
/// https://valkey.io/commands/punsubscribe/
/// https://valkey.io/commands/sunsubscribe/
///
/// A list of targets has at least one target, otherwise fails with a client error. None
/// unsubscribes from every target.
@since(version = 0.3.0-dev)
unsubscribe: async func(targets: option<list<subscription-target>>) -> result<_, error-code>;
}
}