redis_driver/commands/cluster_commands.rs
1use std::collections::HashMap;
2
3use crate::{
4 prepare_command,
5 resp::{
6 cmd, CommandArg, CommandArgs, FromSingleValueArray, FromValue, HashMapExt, IntoArgs,
7 KeyValueArgOrCollection, SingleArgOrCollection, Value,
8 },
9 Error, PreparedCommand, Result,
10};
11
12/// A group of Redis commands related to [`Cluster Management`](https://redis.io/docs/management/scaling/)
13/// # See Also
14/// [Redis Cluster Management commands](https://redis.io/commands/?group=cluster)
15/// [Redis cluster specification](https://redis.io/docs/reference/cluster-spec/)
16pub trait ClusterCommands {
17 /// When a cluster client receives an -ASK redirect,
18 /// the ASKING command is sent to the target node followed by the command which was redirected.
19 /// This is normally done automatically by cluster clients.
20 ///
21 /// # See Also
22 /// [<https://redis.io/commands/asking/>](https://redis.io/commands/asking/)
23 #[must_use]
24 fn asking(&mut self) -> PreparedCommand<Self, ()>
25 where
26 Self: Sized,
27 {
28 prepare_command(self, cmd("ASKING"))
29 }
30
31 /// This command is useful in order to modify a node's view of the cluster configuration.
32 ///
33 /// Specifically it assigns a set of hash slots to the node receiving the command.
34 /// If the command is successful, the node will map the specified hash slots to itself,
35 /// and will start broadcasting the new configuration.
36 ///
37 /// # See Also
38 /// [<https://redis.io/commands/cluster-addslots/>](https://redis.io/commands/cluster-addslots/)
39 #[must_use]
40 fn cluster_addslots<S>(&mut self, slots: S) -> PreparedCommand<Self, ()>
41 where
42 Self: Sized,
43 S: SingleArgOrCollection<u16>,
44 {
45 prepare_command(self, cmd("CLUSTER").arg("ADDSLOTS").arg(slots))
46 }
47
48 /// This command is similar to the [`cluster_addslots`](crate::ClusterCommands::cluster_addslots)
49 /// command in that they both assign hash slots to nodes.
50 ///
51 /// The difference between the two commands is that [`cluster_addslots`](crate::ClusterCommands::cluster_addslots)
52 /// takes a list of slots to assign to the node, while this command takes a list of slot ranges
53 /// (specified by a tuple containing start and end slots) to assign to the node.
54 ///
55 /// # See Also
56 /// [<https://redis.io/commands/cluster-addslotsrange/>](https://redis.io/commands/cluster-addslotsrange/)
57 #[must_use]
58 fn cluster_addslotsrange<S>(&mut self, slots: S) -> PreparedCommand<Self, ()>
59 where
60 Self: Sized,
61 S: KeyValueArgOrCollection<u16, u16>,
62 {
63 prepare_command(self, cmd("CLUSTER").arg("ADDSLOTSRANGE").arg(slots))
64 }
65
66 /// Advances the cluster config epoch.
67 ///
68 /// # Return
69 /// * `Bumped` if the epoch was incremented, or
70 /// * `Still` if the node already has the greatest config epoch in the cluster.
71 ///
72 /// # See Also
73 /// [<https://redis.io/commands/cluster-bumpepoch/>](https://redis.io/commands/cluster-bumpepoch/)
74 #[must_use]
75 fn cluster_bumpepoch(&mut self) -> PreparedCommand<Self, ClusterBumpEpochResult>
76 where
77 Self: Sized,
78 {
79 prepare_command(self, cmd("CLUSTER").arg("BUMPEPOCH"))
80 }
81
82 /// The command returns the number of failure reports for the specified node.
83 ///
84 /// # Return
85 /// The number of active failure reports for the node.
86 ///
87 /// # See Also
88 /// [<https://redis.io/commands/cluster-count-failure-reports/>](https://redis.io/commands/cluster-count-failure-reports/)
89 #[must_use]
90 fn cluster_count_failure_reports<I>(&mut self, node_id: I) -> PreparedCommand<Self, usize>
91 where
92 Self: Sized,
93 I: Into<CommandArg>,
94 {
95 prepare_command(
96 self,
97 cmd("CLUSTER").arg("COUNT-FAILURE-REPORTS").arg(node_id),
98 )
99 }
100
101 /// Returns the number of keys in the specified Redis Cluster hash slot.
102 ///
103 /// # Return
104 /// The number of keys in the specified hash slot, or an error if the hash slot is invalid.
105 ///
106 /// # See Also
107 /// [<https://redis.io/commands/cluster-countkeysinslot/>](https://redis.io/commands/cluster-countkeysinslot/)
108 #[must_use]
109 fn cluster_countkeysinslot(&mut self, slot: usize) -> PreparedCommand<Self, usize>
110 where
111 Self: Sized,
112 {
113 prepare_command(self, cmd("CLUSTER").arg("COUNTKEYSINSLOT").arg(slot))
114 }
115
116 /// In Redis Cluster, each node keeps track of which master is serving a particular hash slot.
117 /// This command asks a particular Redis Cluster node to forget which master
118 /// is serving the hash slots specified as arguments.
119
120 /// # See Also
121 /// [<https://redis.io/commands/cluster-delslots/>](https://redis.io/commands/cluster-delslots/)
122 #[must_use]
123 fn cluster_delslots<S>(&mut self, slots: S) -> PreparedCommand<Self, ()>
124 where
125 Self: Sized,
126 S: SingleArgOrCollection<u16>,
127 {
128 prepare_command(self, cmd("CLUSTER").arg("DELSLOTS").arg(slots))
129 }
130
131 /// This command is similar to the [`cluster_delslotsrange`](crate::ClusterCommands::cluster_delslotsrange)
132 /// command in that they both remove hash slots from the node.
133 ///
134 /// The difference is that [`cluster_delslotsrange`](crate::ClusterCommands::cluster_delslotsrange)
135 /// takes a list of hash slots to remove from the node,
136 /// while this command takes a list of slot ranges (specified by a tuple containing start and end slots) to remove from the node.
137 /// # See Also
138 /// [<https://redis.io/commands/cluster-delslotsrange/>](https://redis.io/commands/cluster-delslotsrange/)
139 #[must_use]
140 fn cluster_delslotsrange<S>(&mut self, slots: S) -> PreparedCommand<Self, ()>
141 where
142 Self: Sized,
143 S: KeyValueArgOrCollection<u16, u16>,
144 {
145 prepare_command(self, cmd("CLUSTER").arg("DELSLOTSRANGE").arg(slots))
146 }
147
148 /// This command, that can only be sent to a Redis Cluster replica node,
149 /// forces the replica to start a manual failover of its master instance.
150 ///
151 /// # Errors
152 /// An error cann occured if the operation cannot be executed,
153 /// for example if we are talking with a node which is already a master.
154 ///
155 /// # See Also
156 /// [<https://redis.io/commands/cluster-failover/>](https://redis.io/commands/cluster-failover/)
157 #[must_use]
158 fn cluster_failover(&mut self, option: ClusterFailoverOption) -> PreparedCommand<Self, ()>
159 where
160 Self: Sized,
161 {
162 prepare_command(self, cmd("CLUSTER").arg("FAILOVER").arg(option))
163 }
164
165 /// Deletes all slots from a node.
166 ///
167 /// # See Also
168 /// [<https://redis.io/commands/cluster-flushslots/>](https://redis.io/commands/cluster-flushslots/)
169 #[must_use]
170 fn cluster_flushslots(&mut self) -> PreparedCommand<Self, ()>
171 where
172 Self: Sized,
173 {
174 prepare_command(self, cmd("CLUSTER").arg("FLUSHSLOTS"))
175 }
176
177 /// The command is used in order to remove a node, specified via its node ID,
178 /// from the set of known nodes of the Redis Cluster node receiving the command.
179 /// In other words the specified node is removed from the nodes table of the node receiving the command.
180 ///
181 /// # See Also
182 /// [<https://redis.io/commands/cluster-forget/>](https://redis.io/commands/cluster-forget/)
183 #[must_use]
184 fn cluster_forget<I>(&mut self, node_id: I) -> PreparedCommand<Self, ()>
185 where
186 Self: Sized,
187 I: Into<CommandArg>,
188 {
189 prepare_command(self, cmd("CLUSTER").arg("FORGET").arg(node_id))
190 }
191
192 /// The command returns an array of keys names stored in
193 /// the contacted node and hashing to the specified hash slot.
194 ///
195 /// The maximum number of keys to return is specified via the count argument,
196 /// so that it is possible for the user of this API to batch-processing keys.
197 ///
198 /// # See Also
199 /// [<https://redis.io/commands/cluster-getkeysinslot/>](https://redis.io/commands/cluster-getkeysinslot/)
200 #[must_use]
201 fn cluster_getkeysinslot(&mut self, slot: u16, count: usize) -> PreparedCommand<Self, ()>
202 where
203 Self: Sized,
204 {
205 prepare_command(
206 self,
207 cmd("CLUSTER").arg("GETKEYSINSLOT").arg(slot).arg(count),
208 )
209 }
210
211 /// This command provides [`info`](crate::ServerCommands::info) style information about Redis Cluster vital parameters.
212 ///
213 /// # Return
214 /// The Cluster information
215 ///
216 /// # See Also
217 /// [<https://redis.io/commands/cluster-info/>](https://redis.io/commands/cluster-info/)
218 #[must_use]
219 fn cluster_info(&mut self, slot: u16, count: usize) -> PreparedCommand<Self, ClusterInfo>
220 where
221 Self: Sized,
222 {
223 prepare_command(self, cmd("CLUSTER").arg("INFO").arg(slot).arg(count))
224 }
225
226 /// Returns an integer identifying the hash slot the specified key hashes to.
227 ///
228 /// # Return
229 /// The hash slot number.
230 ///
231 /// # See Also
232 /// [<https://redis.io/commands/cluster-keyslot/>](https://redis.io/commands/cluster-keyslot/)
233 #[must_use]
234 fn cluster_keyslot<K>(&mut self, key: K) -> PreparedCommand<Self, u16>
235 where
236 Self: Sized,
237 K: Into<CommandArg>,
238 {
239 prepare_command(self, cmd("CLUSTER").arg("KEYSLOT").arg(key))
240 }
241
242 /// Each node in a Redis Cluster maintains a pair of long-lived TCP link with each peer in the cluster:
243 /// - One for sending outbound messages towards the peer
244 /// - and one for receiving inbound messages from the peer.
245 ///
246 /// This command outputs information of all such peer links as an array,
247 /// where each array element is a struct that contains attributes and their values for an individual link.
248 ///
249 /// # Return
250 /// An array of structs where each struct contains various attributes and their values of a cluster link.
251 ///
252 /// # See Also
253 /// [<https://redis.io/commands/cluster-links/>](https://redis.io/commands/cluster-links/)
254 #[must_use]
255 fn cluster_links<I>(&mut self) -> PreparedCommand<Self, Vec<I>>
256 where
257 Self: Sized,
258 I: FromSingleValueArray<ClusterLinkInfo>,
259 {
260 prepare_command(self, cmd("CLUSTER").arg("LINKS"))
261 }
262
263 /// This command is used in order to connect different Redis nodes with cluster support enabled, into a working cluster.
264 ///
265 /// # Return
266 /// An array of structs where each struct contains various attributes and their values of a cluster link.
267 ///
268 /// # See Also
269 /// [<https://redis.io/commands/cluster-meet/>](https://redis.io/commands/cluster-meet/)
270 #[must_use]
271 fn cluster_meet<IP>(
272 &mut self,
273 ip: IP,
274 port: u16,
275 cluster_bus_port: Option<u16>,
276 ) -> PreparedCommand<Self, ()>
277 where
278 Self: Sized,
279 IP: Into<CommandArg>,
280 {
281 prepare_command(
282 self,
283 cmd("CLUSTER")
284 .arg("MEET")
285 .arg(ip)
286 .arg(port)
287 .arg(cluster_bus_port),
288 )
289 }
290
291 /// This command returns the unique, auto-generated identifier that is associated with the connected cluster node.
292 ///
293 /// # Return
294 /// The node id.
295 ///
296 /// # See Also
297 /// [<https://redis.io/commands/cluster-myid/>](https://redis.io/commands/cluster-myid/)
298 #[must_use]
299 fn cluster_myid<N>(&mut self) -> PreparedCommand<Self, N>
300 where
301 Self: Sized,
302 N: FromValue,
303 {
304 prepare_command(self, cmd("CLUSTER").arg("MYID"))
305 }
306
307 /// Each node in a Redis Cluster has its view of the current cluster configuration,
308 /// given by the set of known nodes, the state of the connection we have with such nodes,
309 /// their flags, properties and assigned slots, and so forth.
310 ///
311 /// This command provides all this information, that is, the current cluster configuration of the node we are contacting,
312 /// in a serialization format which happens to be exactly the same as the one used by Redis Cluster itself
313 /// in order to store on disk the cluster state (however the on disk cluster state has a few additional info appended at the end).
314 ///
315 /// # Return
316 /// The serialized cluster configuration.
317 /// The output of the command is just a space-separated CSV string, where each line represents a node in the cluster.
318 ///
319 /// # See Also
320 /// [<https://redis.io/commands/cluster-nodes/>](https://redis.io/commands/cluster-nodes/)
321 #[must_use]
322 fn cluster_nodes<R>(&mut self) -> PreparedCommand<Self, R>
323 where
324 Self: Sized,
325 R: FromValue,
326 {
327 prepare_command(self, cmd("CLUSTER").arg("NODES"))
328 }
329
330 /// The command provides a list of replica nodes replicating from the specified master node.
331 ///
332 /// # Return
333 /// The command returns data in the same format as [`cluster_nodes`](crate::ClusterCommands::cluster_nodes).
334 ///
335 /// # See Also
336 /// [<https://redis.io/commands/cluster-replicas/>](https://redis.io/commands/cluster-replicas/)
337 #[must_use]
338 fn cluster_replicas<I, R>(&mut self, node_id: I) -> PreparedCommand<Self, R>
339 where
340 Self: Sized,
341 I: Into<CommandArg>,
342 R: FromValue,
343 {
344 prepare_command(self, cmd("CLUSTER").arg("REPLICAS").arg(node_id))
345 }
346
347 /// The command reconfigures a node as a replica of the specified master.
348 /// If the node receiving the command is an empty master, as a side effect of the command, the node role is changed from master to replica.
349 ///
350 /// # See Also
351 /// [<https://redis.io/commands/cluster-replicate/>](https://redis.io/commands/cluster-replicate/)
352 #[must_use]
353 fn cluster_replicate<I>(&mut self, node_id: I) -> PreparedCommand<Self, ()>
354 where
355 Self: Sized,
356 I: Into<CommandArg>,
357 {
358 prepare_command(self, cmd("CLUSTER").arg("REPLICATE").arg(node_id))
359 }
360
361 /// Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft.
362 ///
363 /// # See Also
364 /// [<https://redis.io/commands/cluster-reset/>](https://redis.io/commands/cluster-reset/)
365 #[must_use]
366 fn cluster_reset(&mut self, reset_type: ClusterResetType) -> PreparedCommand<Self, ()>
367 where
368 Self: Sized,
369 {
370 prepare_command(self, cmd("CLUSTER").arg("RESET").arg(reset_type))
371 }
372
373 /// Forces a node to save the nodes.conf configuration on disk.
374 /// Before to return the command calls `fsync(2)` in order to make sure the configuration is flushed on the computer disk.
375 ///
376 /// # See Also
377 /// [<https://redis.io/commands/cluster-saveconfig/>](https://redis.io/commands/cluster-saveconfig/)
378 #[must_use]
379 fn cluster_saveconfig(&mut self) -> PreparedCommand<Self, ()>
380 where
381 Self: Sized,
382 {
383 prepare_command(self, cmd("CLUSTER").arg("SAVECONFIG"))
384 }
385
386 /// This command sets a specific config epoch in a fresh node.
387 ///
388 /// # See Also
389 /// [<https://redis.io/commands/cluster-set-config-epoch/>](https://redis.io/commands/cluster-set-config-epoch/)
390 #[must_use]
391 fn cluster_set_config_epoch(&mut self, config_epoch: u64) -> PreparedCommand<Self, ()>
392 where
393 Self: Sized,
394 {
395 prepare_command(
396 self,
397 cmd("CLUSTER").arg("SET-CONFIG-EPOCH").arg(config_epoch),
398 )
399 }
400
401 /// This command is responsible of changing the state of a hash slot in the receiving node in different ways.
402 ///
403 /// # See Also
404 /// [<https://redis.io/commands/cluster-setslot/>](https://redis.io/commands/cluster-setslot/)
405 #[must_use]
406 fn cluster_setslot(
407 &mut self,
408 slot: u16,
409 subcommand: ClusterSetSlotSubCommand,
410 ) -> PreparedCommand<Self, ()>
411 where
412 Self: Sized,
413 {
414 prepare_command(
415 self,
416 cmd("CLUSTER").arg("SETSLOT").arg(slot).arg(subcommand),
417 )
418 }
419
420 /// This command returns details about the shards of the cluster.
421 ///
422 /// # Return
423 /// A list of shard information for each shard (slot ranges & shard nodes)
424 ///
425 /// # See Also
426 /// [<https://redis.io/commands/cluster-shards/>](https://redis.io/commands/cluster-shards/)
427 #[must_use]
428 fn cluster_shards<S>(&mut self) -> PreparedCommand<Self, S>
429 where
430 Self: Sized,
431 S: FromSingleValueArray<ClusterShardResult>
432 {
433 prepare_command(self, cmd("CLUSTER").arg("SHARDS"))
434 }
435
436 /// Enables read queries for a connection to a Redis Cluster replica node.
437 ///
438 /// # See Also
439 /// [<https://redis.io/commands/readonly/>](https://redis.io/commands/readonly/)
440 #[must_use]
441 fn readonly(&mut self) -> PreparedCommand<Self, ()>
442 where
443 Self: Sized,
444 {
445 prepare_command(self, cmd("READONLY"))
446 }
447
448 /// Disables read queries for a connection to a Redis Cluster replica node.
449 ///
450 /// # See Also
451 /// [<https://redis.io/commands/readwrite/>](https://redis.io/commands/readwrite/)
452 #[must_use]
453 fn readwrite(&mut self) -> PreparedCommand<Self, ()>
454 where
455 Self: Sized,
456 {
457 prepare_command(self, cmd("READWRITE"))
458 }
459}
460
461/// Result for the [`cluster_bumpepoch`](crate::ClusterCommands::cluster_bumpepoch) command
462pub enum ClusterBumpEpochResult {
463 /// if the epoch was incremented
464 Bumped,
465 /// if the node already has the greatest config epoch in the cluster.
466 Still,
467}
468
469impl FromValue for ClusterBumpEpochResult {
470 fn from_value(value: Value) -> Result<Self> {
471 let result: String = value.into()?;
472 match result.as_str() {
473 "BUMPED" => Ok(Self::Bumped),
474 "STILL" => Ok(Self::Still),
475 _ => Err(Error::Client(
476 "Unexpected result for command 'CLUSTER BUMPEPOCH'".to_owned(),
477 )),
478 }
479 }
480}
481
482/// Options for the [`cluster_failover`](crate::ClusterCommands::cluster_failover) command
483pub enum ClusterFailoverOption {
484 /// No option
485 Default,
486 /// FORCE option: manual failover when the master is down
487 Force,
488 /// TAKEOVER option: manual failover without cluster consensus
489 Takeover,
490}
491
492impl Default for ClusterFailoverOption {
493 fn default() -> Self {
494 Self::Default
495 }
496}
497
498impl IntoArgs for ClusterFailoverOption {
499 fn into_args(self, args: CommandArgs) -> CommandArgs {
500 match self {
501 ClusterFailoverOption::Default => args,
502 ClusterFailoverOption::Force => args.arg("FORCE"),
503 ClusterFailoverOption::Takeover => args.arg("TAKEOVER"),
504 }
505 }
506}
507
508/// Cluster state used in the `cluster_state` field of [`ClusterInfo`](crate::ClusterInfo)
509pub enum ClusterState {
510 /// State is `ok` if the node is able to receive queries.
511 Ok,
512 /// `fail` if there is at least one hash slot which is unbound (no node associated),
513 /// in error state (node serving it is flagged with FAIL flag),
514 /// or if the majority of masters can't be reached by this node.
515 Fail,
516}
517
518impl FromValue for ClusterState {
519 fn from_value(value: Value) -> Result<Self> {
520 match value.into::<String>()?.as_str() {
521 "ok" => Ok(ClusterState::Ok),
522 "fail" => Ok(ClusterState::Fail),
523 _ => Err(Error::Client("Unexpected ClusterState result".to_owned())),
524 }
525 }
526}
527
528/// Result for the [`cluster_info`](crate::ClusterCommands::cluster_info) command
529pub struct ClusterInfo {
530 /// State is ok if the node is able to receive queries.
531 /// fail if there is at least one hash slot which is unbound (no node associated),
532 /// in error state (node serving it is flagged with FAIL flag),
533 /// or if the majority of masters can't be reached by this node.
534 pub cluster_state: ClusterState,
535
536 /// Number of slots which are associated to some node (not unbound).
537 /// This number should be 16384 for the node to work properly,
538 /// which means that each hash slot should be mapped to a node.
539 pub cluster_slots_assigned: usize,
540
541 /// Number of hash slots mapping to a node not in FAIL or PFAIL state.
542 pub cluster_slots_ok: usize,
543
544 /// Number of hash slots mapping to a node in PFAIL state.
545 /// Note that those hash slots still work correctly,
546 /// as long as the PFAIL state is not promoted to FAIL by the failure detection algorithm.
547 /// PFAIL only means that we are currently not able to talk with the node,
548 /// but may be just a transient error.
549 pub cluster_slots_pfail: usize,
550
551 /// Number of hash slots mapping to a node in FAIL state.
552 /// If this number is not zero the node is not able to serve queries
553 /// unless cluster-require-full-coverage is set to no in the configuration.
554 pub cluster_slots_fail: usize,
555
556 /// The total number of known nodes in the cluster,
557 /// including nodes in HANDSHAKE state that may not currently be proper members of the cluster.
558 pub cluster_known_nodes: usize,
559
560 /// The number of master nodes serving at least one hash slot in the cluster.
561 pub cluster_size: usize,
562
563 /// The local Current Epoch variable.
564 /// This is used in order to create unique increasing version numbers during fail overs.
565 pub cluster_current_epoch: usize,
566
567 /// The Config Epoch of the node we are talking with.
568 /// This is the current configuration version assigned to this node.
569 pub cluster_my_epoch: u64,
570
571 /// Number of messages sent via the cluster node-to-node binary bus.
572 pub cluster_stats_messages_sent: usize,
573
574 /// Number of messages received via the cluster node-to-node binary bus.
575 pub cluster_stats_messages_received: usize,
576
577 /// Accumulated count of cluster links freed due to exceeding the `cluster-link-sendbuf-limit` configuration.
578 pub total_cluster_links_buffer_limit_exceeded: usize,
579
580 /// Cluster bus PING sent (not to be confused with the client command [`ping`](crate::ConnectionCommands::ping)).
581 pub cluster_stats_messages_ping_sent: usize,
582
583 /// Cluster bus PING received (not to be confused with the client command [`ping`](crate::ConnectionCommands::ping)).
584 pub cluster_stats_messages_ping_received: usize,
585
586 /// PONG sent (reply to PING).
587 pub cluster_stats_messages_pong_sent: usize,
588
589 /// PONG received (reply to PING).
590 pub cluster_stats_messages_pong_received: usize,
591
592 /// Handshake message sent to a new node, either through gossip or [`cluster_meet`](crate::ClusterCommands::cluster_meet).
593 pub cluster_stats_messages_meet_sent: usize,
594
595 /// Handshake message sent to a new node, either through gossip or [`cluster_meet`](crate::ClusterCommands::cluster_meet).
596 pub cluster_stats_messages_meet_received: usize,
597
598 /// Mark node xxx as failing.
599 pub cluster_stats_messages_fail_sent: usize,
600
601 /// Mark node xxx as failing.
602 pub cluster_stats_messages_fail_received: usize,
603
604 /// Pub/Sub Publish propagation, see [`Pubsub`](https://redis.io/topics/pubsub#pubsub).
605 pub cluster_stats_messages_publish_sent: usize,
606
607 /// Pub/Sub Publish propagation, see [`Pubsub`](https://redis.io/topics/pubsub#pubsub).
608 pub cluster_stats_messages_publish_received: usize,
609
610 /// Replica initiated leader election to replace its master.
611 pub cluster_stats_messages_auth_req_sent: usize,
612
613 /// Replica initiated leader election to replace its master.
614 pub cluster_stats_messages_auth_req_received: usize,
615
616 /// Message indicating a vote during leader election.
617 pub cluster_stats_messages_auth_ack_sent: usize,
618
619 /// Message indicating a vote during leader election.
620 pub cluster_stats_messages_auth_ack_received: usize,
621
622 /// Another node slots configuration.
623 pub cluster_stats_messages_update_sent: usize,
624
625 /// Another node slots configuration.
626 pub cluster_stats_messages_update_received: usize,
627
628 /// Pause clients for manual failover.
629 pub cluster_stats_messages_mfstart_sent: usize,
630
631 /// Pause clients for manual failover.
632 pub cluster_stats_messages_mfstart_received: usize,
633
634 /// Module cluster API message.
635 pub cluster_stats_messages_module_sent: usize,
636
637 /// Module cluster API message.
638 pub cluster_stats_messages_module_received: usize,
639
640 /// Pub/Sub Publish shard propagation, see [`Sharded Pubsub`](https://redis.io/topics/pubsub#sharded-pubsub).
641 pub cluster_stats_messages_publishshard_sent: usize,
642
643 /// Pub/Sub Publish shard propagation, see [`Sharded Pubsub`](https://redis.io/topics/pubsub#sharded-pubsub).
644 pub cluster_stats_messages_publishshard_received: usize,
645}
646
647impl FromValue for ClusterInfo {
648 fn from_value(value: Value) -> Result<Self> {
649 let lines: String = value.into()?;
650 let mut values = lines
651 .split("\r\n")
652 .filter(|line| line.is_empty() || line.starts_with('#'))
653 .map(|line| {
654 let mut parts = line.split(':');
655 match (parts.next(), parts.next(), parts.next()) {
656 (Some(key), Some(value), None) => Ok((
657 key.to_owned(),
658 Value::BulkString(Some(value.as_bytes().to_vec())),
659 )),
660 _ => Err(Error::Client(
661 "Unexpected result for cluster_info".to_owned(),
662 )),
663 }
664 })
665 .collect::<Result<HashMap<String, Value>>>()?;
666
667 Ok(Self {
668 cluster_state: values.remove_or_default("cluster_state").into()?,
669 cluster_slots_assigned: values.remove_or_default("cluster_slots_assigned").into()?,
670 cluster_slots_ok: values.remove_or_default("cluster_slots_ok").into()?,
671 cluster_slots_pfail: values.remove_or_default("cluster_slots_pfail").into()?,
672 cluster_slots_fail: values.remove_or_default("cluster_slots_fail").into()?,
673 cluster_known_nodes: values.remove_or_default("cluster_known_nodes").into()?,
674 cluster_size: values.remove_or_default("cluster_size").into()?,
675 cluster_current_epoch: values.remove_or_default("cluster_current_epoch").into()?,
676 cluster_my_epoch: values.remove_or_default("cluster_my_epoch").into()?,
677 cluster_stats_messages_sent: values
678 .remove_or_default("cluster_stats_messages_sent")
679 .into()?,
680 cluster_stats_messages_received: values
681 .remove_or_default("cluster_stats_messages_received")
682 .into()?,
683 total_cluster_links_buffer_limit_exceeded: values
684 .remove_or_default("total_cluster_links_buffer_limit_exceeded")
685 .into()?,
686 cluster_stats_messages_ping_sent: values
687 .remove_or_default("cluster_stats_messages_ping_sent")
688 .into()?,
689 cluster_stats_messages_ping_received: values
690 .remove_or_default("cluster_stats_messages_ping_received")
691 .into()?,
692 cluster_stats_messages_pong_sent: values
693 .remove_or_default("cluster_stats_messages_pong_sent")
694 .into()?,
695 cluster_stats_messages_pong_received: values
696 .remove_or_default("cluster_stats_messages_pong_received")
697 .into()?,
698 cluster_stats_messages_meet_sent: values
699 .remove_or_default("cluster_stats_messages_meet_sent")
700 .into()?,
701 cluster_stats_messages_meet_received: values
702 .remove_or_default("cluster_stats_messages_meet_received")
703 .into()?,
704 cluster_stats_messages_fail_sent: values
705 .remove_or_default("cluster_stats_messages_fail_sent")
706 .into()?,
707 cluster_stats_messages_fail_received: values
708 .remove_or_default("cluster_stats_messages_fail_received")
709 .into()?,
710 cluster_stats_messages_publish_sent: values
711 .remove_or_default("cluster_stats_messages_publish_sent")
712 .into()?,
713 cluster_stats_messages_publish_received: values
714 .remove_or_default("cluster_stats_messages_publish_received")
715 .into()?,
716 cluster_stats_messages_auth_req_sent: values
717 .remove_or_default("cluster_stats_messages_auth-req_sent")
718 .into()?,
719 cluster_stats_messages_auth_req_received: values
720 .remove_or_default("cluster_stats_messages_auth-req_received")
721 .into()?,
722 cluster_stats_messages_auth_ack_sent: values
723 .remove_or_default("cluster_stats_messages_auth-ack_sent")
724 .into()?,
725 cluster_stats_messages_auth_ack_received: values
726 .remove_or_default("cluster_stats_messages_auth-ack_received")
727 .into()?,
728 cluster_stats_messages_update_sent: values
729 .remove_or_default("cluster_stats_messages_update_sent")
730 .into()?,
731 cluster_stats_messages_update_received: values
732 .remove_or_default("cluster_stats_messages_update_received")
733 .into()?,
734 cluster_stats_messages_mfstart_sent: values
735 .remove_or_default("cluster_stats_messages_mfstart_sent")
736 .into()?,
737 cluster_stats_messages_mfstart_received: values
738 .remove_or_default("cluster_stats_messages_mfstart_received")
739 .into()?,
740 cluster_stats_messages_module_sent: values
741 .remove_or_default("cluster_stats_messages_mfstart_received")
742 .into()?,
743 cluster_stats_messages_module_received: values
744 .remove_or_default("cluster_stats_messages_module_received")
745 .into()?,
746 cluster_stats_messages_publishshard_sent: values
747 .remove_or_default("cluster_stats_messages_publishshard_sent")
748 .into()?,
749 cluster_stats_messages_publishshard_received: values
750 .remove_or_default("cluster_stats_messages_publishshard_received")
751 .into()?,
752 })
753 }
754}
755
756/// This link is established by the local node to the peer, or accepted by the local node from the peer.
757pub enum ClusterLinkDirection {
758 To,
759 From,
760}
761
762impl FromValue for ClusterLinkDirection {
763 fn from_value(value: Value) -> Result<Self> {
764 match value.into::<String>()?.as_str() {
765 "to" => Ok(ClusterLinkDirection::To),
766 "from" => Ok(ClusterLinkDirection::From),
767 _ => Err(Error::Client(
768 "Unexpected ClusterLinkDirection result".to_owned(),
769 )),
770 }
771 }
772}
773
774/// Result for the [`cluster_links`](crate::ClusterCommands::cluster_links) command
775pub struct ClusterLinkInfo {
776 /// This link is established by the local node to the peer,
777 /// or accepted by the local node from the peer.
778 pub direction: ClusterLinkDirection,
779 /// The node id of the peer.
780 pub node: String,
781 /// Creation time of the link. (In the case of a to link,
782 /// this is the time when the TCP link is created by the local node,
783 /// not the time when it is actually established.)
784 pub create_time: u64,
785 /// Events currently registered for the link. `r` means readable event, `w` means writable event.
786 pub events: String,
787 /// Allocated size of the link's send buffer,
788 /// which is used to buffer outgoing messages toward the peer.
789 pub send_buffer_allocated: usize,
790 /// Size of the portion of the link's send buffer that is currently holding data(messages).
791 pub send_buffer_used: usize,
792}
793
794impl FromValue for ClusterLinkInfo {
795 fn from_value(value: Value) -> Result<Self> {
796 let mut values: HashMap<String, Value> = value.into()?;
797
798 Ok(Self {
799 direction: values.remove_or_default("direction").into()?,
800 node: values.remove_or_default("node").into()?,
801 create_time: values.remove_or_default("create-time").into()?,
802 events: values.remove_or_default("events").into()?,
803 send_buffer_allocated: values.remove_or_default("send-buffer-allocated").into()?,
804 send_buffer_used: values.remove_or_default("send-buffer-used").into()?,
805 })
806 }
807}
808
809/// Type of [`cluster reset`](crate::ClusterCommands::cluster_reset)
810pub enum ClusterResetType {
811 Hard,
812 Soft,
813}
814
815impl IntoArgs for ClusterResetType {
816 fn into_args(self, args: CommandArgs) -> CommandArgs {
817 match self {
818 ClusterResetType::Hard => args.arg("HARD"),
819 ClusterResetType::Soft => args.arg("SOFT"),
820 }
821 }
822}
823
824/// Subcommand for the [`cluster_setslot`](crate::ClusterCommands::cluster_setslot) command.
825pub enum ClusterSetSlotSubCommand {
826 /// Set a hash slot in importing state.
827 Importing { node_id: String },
828 /// Set a hash slot in migrating state.
829 Migrating { node_id: String },
830 /// Bind the hash slot to a different node.
831 Node { node_id: String },
832 /// Clear any importing / migrating state from hash slot.
833 Stable,
834}
835
836impl IntoArgs for ClusterSetSlotSubCommand {
837 fn into_args(self, args: CommandArgs) -> CommandArgs {
838 match self {
839 ClusterSetSlotSubCommand::Importing { node_id } => args.arg("IMPORTING").arg(node_id),
840 ClusterSetSlotSubCommand::Migrating { node_id } => args.arg("MIGRATING").arg(node_id),
841 ClusterSetSlotSubCommand::Node { node_id } => args.arg("NODE").arg(node_id),
842 ClusterSetSlotSubCommand::Stable => args.arg("STABLE"),
843 }
844 }
845}
846
847/// Result for the [`cluster_shards`](crate::ClusterCommands::cluster_shards) command.
848#[derive(Debug)]
849pub struct ClusterShardResult {
850 pub slots: Vec<(u16, u16)>,
851 pub nodes: Vec<ClusterNodeResult>,
852}
853
854impl FromValue for ClusterShardResult {
855 fn from_value(value: Value) -> Result<Self> {
856 let mut values: HashMap<String, Value> = value.into()?;
857
858 Ok(Self {
859 slots: values.remove_with_result("slots")?.into()?,
860 nodes: values.remove_with_result("nodes")?.into()?,
861 })
862 }
863}
864
865/// Cluster node result for the [`cluster_shards`](crate::ClusterCommands::cluster_shards) command.
866#[derive(Debug)]
867pub struct ClusterNodeResult {
868 /// The unique node id for this particular node.
869 pub id: String,
870
871 /// The preferred endpoint to reach the node
872 pub endpoint: String,
873
874 /// The IP address to send requests to for this node.
875 pub ip: String,
876
877 /// The TCP (non-TLS) port of the node. At least one of port or tls-port will be present.
878 pub port: Option<u16>,
879
880 /// The announced hostname to send requests to for this node.
881 pub hostname: Option<String>,
882
883 /// The TLS port of the node. At least one of port or tls-port will be present.
884 pub tls_port: Option<u16>,
885
886 /// The replication role of this node.
887 pub role: String,
888
889 /// The replication offset of this node.
890 /// This information can be used to send commands to the most up to date replicas.
891 pub replication_offset: usize,
892
893 /// Either `online`, `failed`, or `loading`.
894 /// This information should be used to determine which nodes should be sent traffic.
895 /// The loading health state should be used to know that a node is not currently eligible to serve traffic,
896 /// but may be eligible in the future.
897 pub health: ClusterHealthStatus,
898}
899
900impl FromValue for ClusterNodeResult {
901 fn from_value(value: Value) -> Result<Self> {
902 let mut values: HashMap<String, Value> = value.into()?;
903
904 Ok(Self {
905 id: values.remove_with_result("id")?.into()?,
906 endpoint: values.remove_with_result("endpoint")?.into()?,
907 ip: values.remove_with_result("ip")?.into()?,
908 port: values.remove_or_default("port").into()?,
909 hostname: values.remove_or_default("hostname").into()?,
910 tls_port: values.remove_or_default("tls-port").into()?,
911 role: values.remove_with_result("role")?.into()?,
912 replication_offset: values.remove_with_result("replication-offset")?.into()?,
913 health: values.remove_with_result("health")?.into()?,
914 })
915 }
916}
917
918/// Cluster health status for the [`cluster_shards`](crate::ClusterCommands::cluster_shards) command.
919#[derive(Debug)]
920pub enum ClusterHealthStatus {
921 Online,
922 Failed,
923 Loading,
924}
925
926impl FromValue for ClusterHealthStatus {
927 fn from_value(value: Value) -> Result<Self> {
928 match value.into::<String>()?.as_str() {
929 "online" => Ok(Self::Online),
930 "failed" => Ok(Self::Failed),
931 "loading" => Ok(Self::Loading),
932 _ => Err(Error::Client(
933 "Unexpected result for ClusterHealthStatus".to_owned(),
934 )),
935 }
936 }
937}