Skip to main content

Node lifecycle and administration

Select the physical node explicitly before issuing lifecycle commands:

RespireServerNode node = redis.Server.OnNode(new RespireEndpoint("redis-primary", 6379));
string password = await node.AclGeneratePasswordAsync();
byte[][] users = await node.AclUsersAsync();
RespireCommandKeyFlags[] keys = await node.CommandGetKeysAndFlagsAsync("MGET", ["key1", "key2"]);

OnNode does not connect or discover topology. Each call opens an independent connection to that endpoint and disposes it when the operation finishes. Authentication, renewable credentials, TLS, timeouts, protocol, and the selected database come from the client. No command is redirected, replayed, or sent to all nodes. The handle follows client disposal. These methods are immediate; they are not queueable in a batch or transaction.

All key arguments and returned keys are physical server keys. WithKeyPrefix does not modify them. Binary inputs are copied before asynchronous I/O; binary results own their storage. OnNode extends Respire's implementation of IServerCommands without adding requirements to third-party interface implementations. Calling it on a third-party implementation throws NotSupportedException.

Read commands​

MethodRedis commandMinimum Redis version
AclGeneratePasswordAsyncACL GENPASS [bits]6.0
AclUsersAsyncACL USERS6.0
MemoryMallocStatsAsyncMEMORY MALLOC-STATS4.0
LatencyGraphAsyncLATENCY GRAPH event2.8.13
CommandGetKeysAndFlagsAsyncCOMMAND GETKEYSANDFLAGS7.0
BackupStatusAsync, BackupListAsyncBACKUP STATUS, BACKUP LIST8.10
KeysAsyncKEYS pattern1.0

ACL GENPASS defaults to 256 bits; explicit values must be 1 through 1024. Redis rounds up to whole hexadecimal digits. COMMAND GETKEYSANDFLAGS preserves unknown flags. Latency graphs require a recorded event; Redis reports an error for an unknown event. Allocator reports depend on the server's allocator.

BACKUP STATUS exposes state, error text, and nullable Unix timestamps. Zero timestamps mean no recorded time. Unknown states and fields remain available for newer servers. BACKUP LIST returns immutable file paths on the server, not file contents.

Use KeysAsync only for debugging. KEYS scans the entire database and blocks the server. Use redis.Keys.ScanAsync for production iteration instead.

Mutations​

Set RespireOptions.AllowAdmin = true to enable every operation below. Redis ACL checks still apply. An unsupported command or unavailable feature produces the original server error. Local client-side caching is conservatively invalidated before and after a mutation, including a failed or cancelled mutation.

MethodsRedis command
AclLoadAsync, AclSaveAsyncACL LOAD, ACL SAVE
FailoverAsync, AbortFailoverAsyncFAILOVER [TO host port [FORCE]] [TIMEOUT ms], FAILOVER ABORT
ReplicaOfAsync, PromoteToPrimaryAsyncREPLICAOF host port, REPLICAOF NO ONE
SwapDatabasesAsyncSWAPDB first second
ModuleLoadAsync, ModuleLoadExtendedAsync, ModuleUnloadAsyncMODULE LOAD, MODULE LOADEX, MODULE UNLOAD
BackupStartAsync, BackupSealAsync, BackupAbortAsync, BackupCleanupAsyncBACKUP START, SEAL, ABORT, CLEANUP
ScriptKillAsync, FunctionKillAsyncSCRIPT KILL, FUNCTION KILL
SendShutdownAsync, AbortShutdownAsyncSHUTDOWN, SHUTDOWN ABORT
MigrateAsyncMIGRATE with KEYS

FAILOVER requires Redis 6.2; forced failover requires both a target and positive timeout. Aborting failover can leave inconsistent replication state. Module paths and configuration refer to the server's filesystem and installed modules. MODULE LOADEX requires Redis 7.0; configuration pairs each emit their own CONFIG token. Backup operations require Redis 8.10.

Cancelling BUSY scripts and functions​

await redis.Server.OnNode(new RespireEndpoint("busy-node", 6379)).ScriptKillAsync();

Kill methods create a fresh control connection even when the client's sole multiplexed connection is waiting for the running script. The control handshake uses RESP2, database zero, and authentication only. It skips client naming, database selection, topology queries, tracking, and maintenance negotiation. TLS and credential providers still apply. FUNCTION KILL requires Redis 7.0.

Redis refuses to kill a script or function that has already written data. NOTBUSY, UNKILLABLE, authorization errors, and transport errors propagate to the caller.

Shutdown and ambiguous outcomes​

RespireServerNode node = redis.Server.OnNode(new RespireEndpoint("redis-primary", 6379));
await node.SendShutdownAsync(new RespireShutdownOptions
{
SaveMode = RespireShutdownSaveMode.NoSave,
Now = true,
});

Redis sends no success reply for a normal shutdown. SendShutdownAsync completes after the command is written to the local socket. It does not confirm server acceptance or observe server-side errors. Verify shutdown separately. NOW skips waiting for replicas; FORCE can lose data when persistence fails. Both modifiers and AbortShutdownAsync require Redis 7.0. The abort method waits for OK. Shutdown uses the same independent control handshake as the kill methods; a BUSY script requires NOSAVE.

MigrateAsync accepts one or more physical keys, a TCP destination, destination database, and positive server timeout. RespireMigrateOptions selects COPY, REPLACE, and destination AUTH or AUTH2. The result distinguishes acknowledged OK from NOKEY. The server timeout limits idle time during communication with the destination, not the total transfer duration. Respire's CommandTimeout and caller cancellation still apply independently. Set RespireMigrateOptions.CommandTimeout to override the client response budget for this call and its dedicated connection setup; null inherits the shared client's setting. The override must be at least one millisecond and does not change other operations, ConnectionIdleReadTimeout, or caller cancellation. Respire never extends these budgets automatically. Configure the client timeout for the expected total transfer time, including time spent transferring large values and waiting for replies. A client timeout can expire while Redis is still transferring keys, even when it exceeds the server timeout. For example, choose a five-second server idle timeout and a thirty-second client command budget only when thirty seconds covers the expected whole transfer.

Respire's command telemetry records the operation name without MIGRATE arguments. Destination authentication credentials are not included. Client-generated diagnostics do not format those arguments. Original server errors are preserved; treat their text and server-side command logs according to the server's handling of sensitive data.

Timeouts, errors, cancellation, and disconnects can leave a transfer or other lifecycle mutation partially applied. Inspect the affected servers before retrying. Respire never replays these operations automatically.

Atomic Redis Cluster slot migration​

Redis 8.4 and later support CLUSTER MIGRATION. Use the destination primary's explicit endpoint to start an import. Set AllowAdmin = true for imports and cancellation; status queries do not require the client-side admin flag. Redis ACL permissions still apply to all three operations. This protocol is distinct from Valkey slot migration and classic key-by-key MIGRATE.

var destination = redis.Server.OnNode(new RespireEndpoint("destination-primary", 6379));
string taskId = await destination.ClusterMigrationImportAsync(
[new RespireClusterSlotRange(0, 100), new RespireClusterSlotRange(200, 300)], cancellationToken);
RespireClusterMigrationTask[] tasks = await destination.ClusterMigrationStatusAsync(taskId, cancellationToken);
long cancelled = await destination.ClusterMigrationCancelAsync(taskId, cancellationToken);

Ranges are inclusive, non-overlapping, and within 0–16383; Redis accepts fewer than 16384 ranges. The source is determined by the server's slot ownership. The returned task ID confirms task creation, not migration completion. Poll with your own cancellation/deadline until the task reports completed or a terminal failure, and inspect LastError and Retries. Respire does not orchestrate the migration, wait for cluster convergence, or silently refresh its routing table.

ClusterMigrationStatusAsync() sends STATUS ALL and includes active and archived tasks on this node. The string overload sends STATUS ID task-id; an absent task returns an empty array. RespireClusterMigrationStatusScope.Default explicitly sends bare STATUS, matching the published optional-selector syntax. Redis 8.4 and 8.10 currently reject that form; use All for those versions. Respire preserves the error without substituting another request. The discrepancy is visible in the Redis 8.4 implementation and Redis 8.10 implementation.

Task results own their data. Slots retains the server's range text; Operation, State, and AdditionalFields preserve future values. Timestamps are decoded from Unix milliseconds; unset start/end values (-1) become null. WritePauseMilliseconds is the server's pause duration.

ClusterMigrationCancelAllAsync() sends CANCEL ALL. Both cancellation methods return the number of tasks cancelled on the selected node. Cancelling on the source does not stop the destination retrying: cancel at the destination too. Cancellation does not undo an already completed migration. Check status and slot ownership across the affected nodes.

Every request uses a short-lived connection to exactly the selected endpoint. Imports and cancellation conservatively fence the local client cache, including error paths. These requests never redirect or replay. A client timeout, cancellation, or disconnect after submission may leave an import running; inspect task status before deciding what to do next.

Valkey slot migration and deletion​

Valkey 9.0 and later support a separate atomic slot migration protocol. These methods target exactly the endpoint selected by Server.OnNode; they do not use Redis's CLUSTER MIGRATION command family and are not supported by Redis.

RespireServerNode source = redis.Server.OnNode(new RespireEndpoint("valkey-source", 6379));
// Replace these placeholders with primary IDs from CLUSTER NODES.
string firstTargetId = "<first-target-node-id>";
string secondTargetId = "<second-target-node-id>";
await source.ClusterMigrateSlotsAsync([
new RespireValkeySlotMigrationGroup(firstTargetId, [new(0, 99), new(200, 299)]),
new RespireValkeySlotMigrationGroup(secondTargetId, [new(100, 199)]),
]);
RespireValkeySlotMigration[] jobs = await source.ClusterGetSlotMigrationsAsync();

ClusterMigrateSlotsAsync sends one SLOTSRANGE ... NODE ... group per destination. Ranges are inclusive, must lie within 0–16383, and cannot overlap within or across groups. All groups must be admitted together, but an OK reply only starts the asynchronous work. Inspect status for completion before relying on changed ownership. Client cancellation stops waiting; it does not cancel server jobs. Ambiguous transport failures are not retried or redirected.

ClusterCancelSlotMigrationsAsync cancels active exports started on the selected source node. Calling it on a target does not cancel that target's imports. Valkey returns ERR No migrations ongoing when that node has no active exports; the method preserves this server error rather than treating cancellation as idempotent. Start, cancellation, and flush require AllowAdmin; status is read-only under the client policy. Server ACL permissions apply independently to every operation.

Status snapshots own their strings and nested AdditionalFields, so they remain usable after connections and the client are disposed. success, failed, and cancelled are terminal states; other states remain active. Operation and state strings preserve future values. Source and target IDs are absent on tracking replicas. Timestamps are Unix seconds, represented as DateTimeOffset. Valkey 9.1 adds RemainingReplicationBytes; it is null when the server omits remaining_repl_size. The server retains only a bounded history of inactive jobs, in memory; snapshots are not a durable audit log. See Valkey's status documentation.

ClusterFlushSlotAsync(slot, ServerFlushMode.Default/Sync/Async) implements the supported Valkey 9+ FLUSHSLOT command. It destructively deletes the selected slot's keys across databases on that node. It does not remove slot ownership and is distinct from FLUSHSLOTS, which clears ownership metadata. Default follows the server's lazyfree-lazy-user-flush setting. Use only on a deliberately selected endpoint. The Valkey 9.0 command schema and implementation establish this supported user-facing boundary.

There is deliberately no typed SYNCSLOTS request API. Valkey documents it as an internal state-machine command. Its implementation uses migration-owned connections, disabled ordinary replies, and pushed protocol messages. A normal one-request/one-reply wrapper cannot implement that protocol. The raw-command API remains available for server-specific ordinary commands, but does not turn SYNCSLOTS into a supported RPC.

Current online MIGRATESLOTS documentation also shows per-group AUTH. The released 9.0 implementation and 9.1 grammar do not accept that option. This typed API targets their shared grammar; configure server-to-server migration authentication on the servers. Client authentication still comes from the client's normal options.