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}