Skip to main content

Deferred raw commands

Batches, transactions, and watched transactions expose Execute(command, args), also available through IRespireCommandQueue. It returns a RespirePending<RespireResult> immediately. Execution and cancellation belong to batch.ExecuteAsync() or transaction.CommitAsync().

await using var client = await RespireClient.ConnectAsync("localhost:6379");
var view = client.WithKeyPrefix("tenant:");
using var batch = view.CreateBatch();
var write = batch.Execute(RespireCommands.String.SET, "key", "value");
var read = batch.Execute("GET", "key");
await batch.ExecuteAsync();
using var writeResult = write.Result;
using var readResult = read.Result;
Console.WriteLine(readResult.AsString());

The arguments are snapshotted when queued, including binary keys and values. Later edits to the argument array or its binary buffers do not change the command. Keys receive the view's prefix; values, fields, script source, and script arguments do not. Queueing never opens a connection or sends the command.

Prefer typed facets when they expose the operation you need. Raw enqueueing parses the command name, allocates a token array, and snapshots binary arguments; replies are also copied into owned storage. This escape hatch does not promise allocation-free execution.

Results and failures​

Deferred raw results copy replies into GC-managed storage. Unread results do not retain pooled receive buffers. Disposal is optional, but disposing a root invalidates all struct copies and nested views; repeated disposal is safe. Results remain valid after the queue is disposed. Immediate ExecuteAsync results remain pooled leases and must be disposed. Both expose the same RespireResult conversions and RESP2/RESP3 aggregate representation, including nulls and nested errors.

Top-level server errors fault the corresponding pending. A batch finishes all pendings before ExecuteAsync throws its first error; TryExecuteAsync returns the failure summary instead. Transactions preserve their existing contract: errors inside EXEC fault individual pendings, while transaction-level failures fault commit. Successful neighboring results remain readable. Nested aggregate errors are data: inspect IsError and ErrorMessage. Reading a pending before execution throws RespirePendingNotReadyException. Discarding an unsent queue faults its pendings.

Names and supported layouts​

Use a catalog descriptor or a case-insensitive verb such as "get". A known subcommand uses one space, for example "OBJECT ENCODING". All actual arguments belong in the argument array. Inline arguments ("GET key"), interpolated commands, leading/trailing/repeated spaces, control characters, and non-ASCII names are not accepted. Null arguments cannot be serialized. The client validates the key layout; Redis validates remaining arity, options, types, and version requirements.

Supported forms are deliberately explicit. Unknown or module layouts throw NotSupportedException before enqueueing, including on standalone unprefixed clients. The client does not infer key positions from arbitrary argument contents. There is no metadata override that can bypass these checks. Use a typed facet or immediate raw execution for unsupported forms, subject to that API's own prefix/routing restrictions.

Key layoutCommands
No keysPING, ECHO, TIME
First argumentGET, SET, GETSET, SETNX, SETEX, PSETEX, GETDEL, GETEX, APPEND, STRLEN, GETRANGE, SETRANGE, INCR, INCRBY, INCRBYFLOAT, DECR, DECRBY
First argumentTYPE, TTL, PTTL, EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT, EXPIRETIME, PEXPIRETIME, PERSIST, DUMP, RESTORE
First argumentHGET, HSET, HSETNX, HMGET, HMSET, HGETALL, HDEL, HEXISTS, HLEN, HKEYS, HVALS, HSTRLEN, HINCRBY, HINCRBYFLOAT, HRANDFIELD
First argumentLPUSH, RPUSH, LPUSHX, RPUSHX, LPOP, RPOP, LLEN, LRANGE, LINDEX, LSET, LINSERT, LREM, LTRIM, LPOS
First argumentSADD, SREM, SCARD, SMEMBERS, SISMEMBER, SMISMEMBER, SPOP, SRANDMEMBER
First argumentZADD, ZREM, ZCARD, ZSCORE, ZMSCORE, ZINCRBY, ZCOUNT, ZLEXCOUNT, ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZRANGEBYLEX, ZREVRANGEBYLEX, ZRANK, ZREVRANK, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZREMRANGEBYLEX, ZPOPMIN, ZPOPMAX, ZRANDMEMBER
First argumentGETBIT, SETBIT, BITCOUNT, BITPOS, BITFIELD, BITFIELD_RO, PFADD, GEOADD, GEODIST, GEOHASH, GEOPOS, GEOSEARCH
First argumentXADD, XACK, XDEL, XTRIM, XLEN, XRANGE, XREVRANGE, XPENDING, XCLAIM, XAUTOCLAIM
First argument after the named subcommandOBJECT ENCODING/FREQ/IDLETIME/REFCOUNT; MEMORY USAGE; XINFO STREAM/GROUPS/CONSUMERS; XGROUP CREATE/SETID/DESTROY/CREATECONSUMER/DELCONSUMER
First two argumentsRENAME, RENAMENX, COPY, LCS, SMOVE, LMOVE, RPOPLPUSH, ZRANGESTORE, GEOSEARCHSTORE
Every argumentDEL, UNLINK, EXISTS, TOUCH, MGET, SDIFF, SINTER, SUNION, SDIFFSTORE, SINTERSTORE, SUNIONSTORE, PFCOUNT, PFMERGE
Alternating keys and valuesMSET, MSETNX
All arguments after the bit operationBITOP
Source/name, key count, keys, other argumentsEVAL, EVALSHA, EVAL_RO, EVALSHA_RO, FCALL, FCALL_RO
Key count, keys, other argumentsLMPOP, ZMPOP, SINTERCARD, ZDIFF, ZINTER, ZUNION, ZINTERCARD
Destination, key count, source keys, other argumentsZDIFFSTORE, ZINTERSTORE, ZUNIONSTORE

Script/function key counts may be zero. Other counted layouts require a positive count, and all counts must fit the supplied arguments. Script/function execution never loads, reloads, or replays after NOSCRIPT or a missing-function error. Scripts and functions must access keys through the declared key arguments; source is not rewritten.

Cluster and queue restrictions​

Every key within one raw command must share a slot after prefixing. A mismatch throws RespireServerException with code CROSSSLOT locally, before enqueueing. This matches the existing typed multi-key command contract; the exception type does not imply a server round trip. Rejected commands do not pin a transaction to a slot. A Cluster transaction also requires all its commands to share one slot. Cluster batches group commands by slot; order is preserved within a group, while different groups may execute independently. Keyless commands use an execution node and never fan out across primaries.

Blocking and connection-state commands are rejected: blocking list/sorted-set pops, subscriptions, WATCH, transaction control, connection setup, CLIENT commands, WAIT/WAITAOF, and similar session operations. XREAD/XREADGROUP are currently rejected even without BLOCK because their layouts are not supported here. Administrative, unknown, and module forms are rejected rather than trusted to preserve queue state. SORT and the GEORADIUS family are also excluded because their optional external keys need their own parsing rules; use the typed sorting/geospatial APIs where available.

No per-command flags, fire-and-forget mode, or implicit cluster-wide administration are available. Deferred execution keeps the queue's existing cancellation, redirect, and conservative client-cache invalidation behavior. Custom queue adapters must implement the added IRespireCommandQueue.Execute member.