Skip to main content

redis/cluster_handling/sync_connection/
mod.rs

1//! This module extends the library to support Redis Cluster.
2//!
3//! The cluster connection is meant to abstract the fact that a cluster is composed of multiple nodes,
4//! and to provide an API which is as close as possible to that of a single node connection. In order to do that,
5//! the cluster connection maintains connections to each node in the Redis/ Valkey cluster, and can route
6//! requests automatically to the relevant nodes. In cases that the cluster connection receives indications
7//! that the cluster topology has changed, it will query nodes in order to find the current cluster topology.
8//! If it disconnects from some nodes, it will automatically reconnect to those nodes.
9//!
10//! Note that pubsub & push sending functionality is not currently provided by this module.
11//!
12//! # Example
13//! ```rust,no_run
14//! use redis::TypedCommands;
15//! use redis::cluster::ClusterClient;
16//!
17//! let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
18//! let client = ClusterClient::new(nodes).unwrap();
19//! let mut connection = client.get_connection().unwrap();
20//!
21//! connection.set("test", "test_data").unwrap();
22//! let rv = connection.get("test").unwrap().unwrap();
23//!
24//! assert_eq!(rv.as_str(), "test_data");
25//! ```
26//!
27//! # Pipelining
28//! ```rust,no_run
29//! use redis::TypedCommands;
30//! use redis::cluster::{cluster_pipe, ClusterClient};
31//!
32//! let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
33//! let client = ClusterClient::new(nodes).unwrap();
34//! let mut connection = client.get_connection().unwrap();
35//!
36//! let key = "test";
37//!
38//! cluster_pipe()
39//!     .rpush(key, "123").ignore()
40//!     .ltrim(key, -10, -1).ignore()
41//!     .expire(key, 60).ignore()
42//!     .exec(&mut connection).unwrap();
43//! ```
44//!
45//! # Sending request to specific node
46//! In some cases you'd want to send a request to a specific node in the cluster, instead of
47//! letting the cluster connection decide by itself to which node it should send the request.
48//! This can happen, for example, if you want to send SCAN commands to each node in the cluster.
49//!
50//! ```rust,no_run
51//! use redis::Commands;
52//! use redis::cluster::ClusterClient;
53//! use redis::cluster_routing::{ RoutingInfo, SingleNodeRoutingInfo };
54//!
55//! let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
56//! let client = ClusterClient::new(nodes).unwrap();
57//! let mut connection = client.get_connection().unwrap();
58//!
59//! let routing_info = RoutingInfo::SingleNode(SingleNodeRoutingInfo::ByAddress{
60//!     host: "redis://127.0.0.1".to_string(),
61//!     port: 6378
62//! });
63//! let _: redis::Value = connection.route_command(&redis::cmd("PING"), routing_info).unwrap();
64//! ```
65use std::cell::RefCell;
66use std::collections::HashSet;
67use std::thread;
68use std::time::Duration;
69
70mod pipeline;
71
72pub use super::NodeAddress;
73pub use super::client::{ClusterClient, ClusterClientBuilder};
74use super::topology::parse_slots;
75use super::{
76    client::ClusterParams,
77    read_routing::ReadRoutingStrategy,
78    routing::{Redirect, Route, RoutingInfo},
79    slot_map::SlotMap,
80};
81use crate::IntoConnectionInfo;
82pub use crate::TlsMode; // Pub for backwards compatibility
83use crate::cluster_handling::{get_connection_info, slot_cmd};
84use crate::cluster_routing::{
85    MultipleNodeRoutingInfo, ResponsePolicy, Routable, SingleNodeRoutingInfo, Slot, SlotAddr,
86};
87use crate::cmd::{Cmd, cmd};
88use crate::connection::{Connection, ConnectionInfo, ConnectionLike, connect};
89use crate::errors::{ErrorKind, RedisError, RetryMethod};
90use crate::parser::parse_redis_value;
91use crate::types::{HashMap, RedisResult, Value};
92use pipeline::UNROUTABLE_ERROR;
93use rand::{rng, seq::IteratorRandom};
94
95pub use pipeline::{ClusterPipeline, cluster_pipe};
96
97#[derive(Clone)]
98enum Input<'a> {
99    Slice {
100        cmd: &'a [u8],
101        routable: Value,
102    },
103    Cmd(&'a Cmd),
104    Commands {
105        cmd: &'a [u8],
106        offset: usize,
107        count: usize,
108    },
109}
110
111impl<'a> Input<'a> {
112    fn send(&'a self, connection: &mut impl ConnectionLike) -> RedisResult<Output> {
113        match self {
114            Input::Slice { cmd, routable: _ } => connection
115                .req_packed_command(cmd)
116                .and_then(|value| value.extract_error())
117                .map(Output::Single),
118            Input::Cmd(cmd) => connection
119                .req_command(cmd)
120                .and_then(|value| value.extract_error())
121                .map(Output::Single),
122            Input::Commands { cmd, offset, count } => connection
123                .req_packed_commands(cmd, *offset, *count)
124                .and_then(Value::extract_error_vec)
125                .map(Output::Multi),
126        }
127    }
128}
129
130impl Routable for Input<'_> {
131    fn arg_idx(&self, idx: usize) -> Option<&[u8]> {
132        match self {
133            Input::Slice { cmd: _, routable } => routable.arg_idx(idx),
134            Input::Cmd(cmd) => cmd.arg_idx(idx),
135            Input::Commands { .. } => None,
136        }
137    }
138
139    fn position(&self, candidate: &[u8]) -> Option<usize> {
140        match self {
141            Input::Slice { cmd: _, routable } => routable.position(candidate),
142            Input::Cmd(cmd) => cmd.position(candidate),
143            Input::Commands { .. } => None,
144        }
145    }
146}
147
148enum Output {
149    Single(Value),
150    Multi(Vec<Value>),
151}
152
153impl From<Output> for Value {
154    fn from(value: Output) -> Self {
155        match value {
156            Output::Single(value) => value,
157            Output::Multi(values) => Value::Array(values),
158        }
159    }
160}
161
162impl From<Output> for Vec<Value> {
163    fn from(value: Output) -> Self {
164        match value {
165            Output::Single(value) => vec![value],
166            Output::Multi(values) => values,
167        }
168    }
169}
170
171/// Implements the process of connecting to a Redis server
172/// and obtaining and configuring a connection handle.
173pub trait Connect: Sized {
174    /// Connect to a node, returning handle for command execution.
175    fn connect<T>(info: T, timeout: Option<Duration>) -> RedisResult<Self>
176    where
177        T: IntoConnectionInfo;
178
179    /// Sends an already encoded (packed) command into the TCP socket and
180    /// does not read a response.  This is useful for commands like
181    /// `MONITOR` which yield multiple items.  This needs to be used with
182    /// care because it changes the state of the connection.
183    fn send_packed_command(&mut self, cmd: &[u8]) -> RedisResult<()>;
184
185    /// Sets the write timeout for the connection.
186    ///
187    /// If the provided value is `None`, then `send_packed_command` call will
188    /// block indefinitely. It is an error to pass the zero `Duration` to this
189    /// method.
190    fn set_write_timeout(&self, dur: Option<Duration>) -> RedisResult<()>;
191
192    /// Sets the read timeout for the connection.
193    ///
194    /// If the provided value is `None`, then `recv_response` call will
195    /// block indefinitely. It is an error to pass the zero `Duration` to this
196    /// method.
197    fn set_read_timeout(&self, dur: Option<Duration>) -> RedisResult<()>;
198
199    /// Fetches a single response from the connection.  This is useful
200    /// if used in combination with `send_packed_command`.
201    fn recv_response(&mut self) -> RedisResult<Value>;
202}
203
204impl Connect for Connection {
205    fn connect<T>(info: T, timeout: Option<Duration>) -> RedisResult<Self>
206    where
207        T: IntoConnectionInfo,
208    {
209        connect(&info.into_connection_info()?, timeout)
210    }
211
212    fn send_packed_command(&mut self, cmd: &[u8]) -> RedisResult<()> {
213        Self::send_packed_command(self, cmd)
214    }
215
216    fn set_write_timeout(&self, dur: Option<Duration>) -> RedisResult<()> {
217        Self::set_write_timeout(self, dur)
218    }
219
220    fn set_read_timeout(&self, dur: Option<Duration>) -> RedisResult<()> {
221        Self::set_read_timeout(self, dur)
222    }
223
224    fn recv_response(&mut self) -> RedisResult<Value> {
225        Self::recv_response(self)
226    }
227}
228
229/// Options for creation of connection
230#[derive(Clone, Default)]
231pub struct ClusterConfig {
232    pub(crate) connection_timeout: Option<Duration>,
233    pub(crate) response_timeout: Option<Duration>,
234    #[cfg(feature = "cluster-async")]
235    pub(crate) async_push_sender: Option<std::sync::Arc<dyn crate::aio::AsyncPushSender>>,
236    #[cfg(feature = "cluster-async")]
237    pub(crate) async_dns_resolver: Option<std::sync::Arc<dyn crate::io::AsyncDNSResolver>>,
238}
239
240impl ClusterConfig {
241    /// Creates a new instance of the options with nothing set
242    pub fn new() -> Self {
243        Self::default()
244    }
245
246    /// Sets the connection timeout
247    pub fn set_connection_timeout(mut self, connection_timeout: std::time::Duration) -> Self {
248        self.connection_timeout = Some(connection_timeout);
249        self
250    }
251
252    /// Sets the response timeout
253    pub fn set_response_timeout(mut self, response_timeout: std::time::Duration) -> Self {
254        self.response_timeout = Some(response_timeout);
255        self
256    }
257
258    #[cfg(feature = "cluster-async")]
259    /// Sets a sender to receive pushed values.
260    ///
261    /// The sender can be a channel, or an arbitrary function that handles [crate::PushInfo] values.
262    /// This will fail client creation if the connection isn't configured for RESP3 communications via the [crate::RedisConnectionInfo::set_protocol] function.
263    ///
264    /// # Examples
265    ///
266    /// ```rust
267    /// # use redis::cluster::ClusterConfig;
268    /// let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
269    /// let config = ClusterConfig::new().set_push_sender(tx);
270    /// ```
271    ///
272    /// ```rust
273    /// # use std::sync::{Mutex, Arc};
274    /// # use redis::cluster::ClusterConfig;
275    /// let messages = Arc::new(Mutex::new(Vec::new()));
276    /// let config = ClusterConfig::new().set_push_sender(move |msg|{
277    ///     let Ok(mut messages) = messages.lock() else {
278    ///         return Err(redis::aio::SendError);
279    ///     };
280    ///     messages.push(msg);
281    ///     Ok(())
282    /// });
283    pub fn set_push_sender(mut self, sender: impl crate::aio::AsyncPushSender) -> Self {
284        self.async_push_sender = Some(std::sync::Arc::new(sender));
285        self
286    }
287
288    /// Set asynchronous DNS resolver for the underlying TCP connection.
289    ///
290    /// The parameter resolver must implement the [`crate::io::AsyncDNSResolver`] trait.
291    #[cfg(feature = "cluster-async")]
292    pub fn set_dns_resolver(mut self, resolver: impl crate::io::AsyncDNSResolver) -> Self {
293        self.async_dns_resolver = Some(std::sync::Arc::new(resolver));
294        self
295    }
296}
297
298/// This represents a Redis Cluster connection.
299///
300/// It stores the underlying connections maintained for each node in the cluster,
301/// as well as common parameters for connecting to nodes and executing commands.
302pub struct ClusterConnection<C = Connection> {
303    initial_nodes: Vec<ConnectionInfo>,
304    connections: RefCell<HashMap<NodeAddress, C>>,
305    slots: RefCell<SlotMap>,
306    auto_reconnect: RefCell<bool>,
307    read_timeout: RefCell<Option<Duration>>,
308    write_timeout: RefCell<Option<Duration>>,
309    routing_strategy: Option<Box<dyn ReadRoutingStrategy>>,
310    cluster_params: ClusterParams,
311}
312
313impl<C> ClusterConnection<C>
314where
315    C: ConnectionLike + Connect,
316{
317    pub(crate) fn new(
318        cluster_params: ClusterParams,
319        initial_nodes: Vec<ConnectionInfo>,
320    ) -> RedisResult<Self> {
321        let routing_strategy = cluster_params
322            .read_routing_factory
323            .as_ref()
324            .map(|f| f.create_strategy());
325
326        let connection = Self {
327            connections: RefCell::new(HashMap::new()),
328            slots: RefCell::new(SlotMap::new()),
329            auto_reconnect: RefCell::new(true),
330            read_timeout: RefCell::new(cluster_params.response_timeout),
331            write_timeout: RefCell::new(None),
332            routing_strategy,
333            initial_nodes: initial_nodes.to_vec(),
334            cluster_params,
335        };
336        connection.create_initial_connections()?;
337
338        Ok(connection)
339    }
340
341    /// Set an auto reconnect attribute.
342    /// Default value is true;
343    pub fn set_auto_reconnect(&self, value: bool) {
344        let mut auto_reconnect = self.auto_reconnect.borrow_mut();
345        *auto_reconnect = value;
346    }
347
348    /// Sets the write timeout for the connection.
349    ///
350    /// If the provided value is `None`, then `send_packed_command` call will
351    /// block indefinitely. It is an error to pass the zero `Duration` to this
352    /// method.
353    pub fn set_write_timeout(&self, dur: Option<Duration>) -> RedisResult<()> {
354        // Check if duration is valid before updating local value.
355        if dur.is_some() && dur.unwrap().is_zero() {
356            return Err(RedisError::from((
357                ErrorKind::InvalidClientConfig,
358                "Duration should be None or non-zero.",
359            )));
360        }
361
362        let mut t = self.write_timeout.borrow_mut();
363        *t = dur;
364        let connections = self.connections.borrow();
365        for conn in connections.values() {
366            conn.set_write_timeout(dur)?;
367        }
368        Ok(())
369    }
370
371    /// Sets the read timeout for the connection.
372    ///
373    /// If the provided value is `None`, then `recv_response` call will
374    /// block indefinitely. It is an error to pass the zero `Duration` to this
375    /// method.
376    pub fn set_read_timeout(&self, dur: Option<Duration>) -> RedisResult<()> {
377        // Check if duration is valid before updating local value.
378        if dur.is_some() && dur.unwrap().is_zero() {
379            return Err(RedisError::from((
380                ErrorKind::InvalidClientConfig,
381                "Duration should be None or non-zero.",
382            )));
383        }
384
385        let mut t = self.read_timeout.borrow_mut();
386        *t = dur;
387        let connections = self.connections.borrow();
388        for conn in connections.values() {
389            conn.set_read_timeout(dur)?;
390        }
391        Ok(())
392    }
393
394    /// Check that all connections it has are available (`PING` internally).
395    #[doc(hidden)]
396    pub fn check_connection(&mut self) -> bool {
397        <Self as ConnectionLike>::check_connection(self)
398    }
399
400    pub(crate) fn execute_pipeline(&mut self, pipe: &ClusterPipeline) -> RedisResult<Vec<Value>> {
401        self.send_recv_and_retry_cmds(pipe.commands())
402    }
403
404    /// Returns the connection status.
405    ///
406    /// The connection is open until any `read_response` call received an
407    /// invalid response from the server (most likely a closed or dropped
408    /// connection, otherwise a Redis protocol error). When using unix
409    /// sockets the connection is open until writing a command failed with a
410    /// `BrokenPipe` error.
411    fn create_initial_connections(&self) -> RedisResult<()> {
412        let mut connections = HashMap::with_capacity(self.initial_nodes.len());
413        let mut failed_connections = Vec::new();
414
415        for info in self.initial_nodes.iter() {
416            let addr = NodeAddress::try_from(&info.addr)?;
417
418            match self.connect(&addr) {
419                Ok(mut conn) => {
420                    if conn.check_connection() {
421                        connections.insert(addr, conn);
422                        break;
423                    } else {
424                        failed_connections.push((
425                            addr,
426                            RedisError::from((
427                                ErrorKind::Io,
428                                "Node failed to respond to connection check,",
429                            )),
430                        ));
431                    }
432                }
433                Err(conn_err) => {
434                    failed_connections.push((addr, conn_err));
435                }
436            }
437        }
438
439        if connections.is_empty() {
440            // Create a composite description of why connecting to each node failed.
441            let detail = if failed_connections.is_empty() {
442                "List of initial nodes is empty".to_string()
443            } else {
444                let mut formatted_detail = "Failed to connect to each cluster node (".to_string();
445
446                for (index, (addr, conn_err)) in failed_connections.into_iter().enumerate() {
447                    if index != 0 {
448                        formatted_detail += "; ";
449                    }
450                    use std::fmt::Write;
451                    let _ = write!(&mut formatted_detail, "{addr}: {conn_err}");
452                }
453                formatted_detail += ")";
454                formatted_detail
455            };
456
457            return Err(RedisError::from((
458                ErrorKind::Io,
459                "It failed to check startup nodes.",
460                detail,
461            )));
462        }
463
464        *self.connections.borrow_mut() = connections;
465        self.refresh_slots()?;
466        Ok(())
467    }
468
469    // Query a node to discover slot-> master mappings.
470    fn refresh_slots(&self) -> RedisResult<()> {
471        let mut slots = self.slots.borrow_mut();
472        *slots = self.create_new_slots()?;
473
474        if let Some(ref strategy) = self.routing_strategy {
475            strategy.on_topology_changed(slots.topology());
476        }
477
478        let mut nodes = slots.values().flatten().collect::<Vec<_>>();
479        nodes.sort_unstable();
480        nodes.dedup();
481
482        let mut connections = self.connections.borrow_mut();
483        *connections = nodes
484            .into_iter()
485            .filter_map(|addr| {
486                if let Some(mut conn) = connections.remove(addr)
487                    && conn.check_connection()
488                {
489                    return Some((addr.clone(), conn));
490                }
491
492                if let Ok(mut conn) = self.connect(addr)
493                    && conn.check_connection()
494                {
495                    return Some((addr.clone(), conn));
496                }
497
498                None
499            })
500            .collect();
501
502        Ok(())
503    }
504
505    fn create_new_slots(&self) -> RedisResult<SlotMap> {
506        let mut connections = self.connections.borrow_mut();
507        let mut new_slots = None;
508
509        for (addr, conn) in connections.iter_mut() {
510            let value = conn.req_command(&slot_cmd())?;
511            if let Ok(slots_data) = parse_slots(value, addr.host()) {
512                new_slots = Some(SlotMap::from_slots(slots_data));
513                break;
514            }
515        }
516
517        match new_slots {
518            Some(new_slots) => Ok(new_slots),
519            None => Err(RedisError::from((
520                ErrorKind::Client,
521                "Slot refresh error. didn't get any slots from server",
522            ))),
523        }
524    }
525
526    fn connect(&self, node: &NodeAddress) -> RedisResult<C> {
527        let info = get_connection_info(node, &self.cluster_params);
528
529        let mut conn = C::connect(info, Some(self.cluster_params.connection_timeout))?;
530        // If READONLY is sent to primary nodes, it will have no effect.
531        // We set this unconditionally, because we don't know whether we'll be making read calls
532        // to replicas. (We allow overriding routing per-call)
533        cmd("READONLY").exec(&mut conn)?;
534        conn.set_read_timeout(*self.read_timeout.borrow())?;
535        conn.set_write_timeout(*self.write_timeout.borrow())?;
536        Ok(conn)
537    }
538
539    fn get_connection<'a>(
540        &self,
541        connections: &'a mut HashMap<NodeAddress, C>,
542        route: &Route,
543    ) -> (NodeAddress, RedisResult<&'a mut C>) {
544        let slots = self.slots.borrow();
545        if let Some(addr) = slots.slot_addr_for_route(route, self.routing_strategy.as_deref()) {
546            (addr.clone(), self.get_connection_by_addr(connections, addr))
547        } else {
548            // try a random node next.  This is safe if slots are involved
549            // as a wrong node would reject the request.
550            get_random_connection_or_error(connections)
551        }
552    }
553
554    fn get_connection_by_addr<'a>(
555        &self,
556        connections: &'a mut HashMap<NodeAddress, C>,
557        addr: &NodeAddress,
558    ) -> RedisResult<&'a mut C> {
559        match connections.entry(addr.clone()) {
560            std::collections::hash_map::Entry::Occupied(occupied_entry) => {
561                Ok(occupied_entry.into_mut())
562            }
563            std::collections::hash_map::Entry::Vacant(vacant_entry) => {
564                // Create new connection.
565                // TODO: error handling
566                let conn = self.connect(addr)?;
567                Ok(vacant_entry.insert(conn))
568            }
569        }
570    }
571
572    fn get_addr_for_cmd(&self, cmd: &Cmd) -> RedisResult<NodeAddress> {
573        let slots = self.slots.borrow();
574
575        let addr_for_slot = |route: Route| -> RedisResult<NodeAddress> {
576            let slot_addr = slots
577                .slot_addr_for_route(&route, self.routing_strategy.as_deref())
578                .ok_or((ErrorKind::Client, "Missing slot coverage"))?;
579            Ok(slot_addr.clone())
580        };
581
582        match RoutingInfo::for_routable(cmd) {
583            Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::Random)) => Ok(addr_for_slot(
584                Route::with_slot(Slot::new_random(), SlotAddr::Master),
585            )?),
586            Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::SpecificNode(route))) => {
587                Ok(addr_for_slot(route)?)
588            }
589            _ => fail!(UNROUTABLE_ERROR),
590        }
591    }
592
593    fn map_cmds_to_nodes(&self, cmds: &[Cmd]) -> RedisResult<Vec<NodeCmd>> {
594        let mut cmd_map: HashMap<NodeAddress, NodeCmd> = HashMap::new();
595
596        for (idx, cmd) in cmds.iter().enumerate() {
597            let addr = self.get_addr_for_cmd(cmd)?;
598            let nc = cmd_map
599                .entry(addr.clone())
600                .or_insert_with(|| NodeCmd::new(addr));
601            nc.indexes.push(idx);
602            cmd.write_packed_command(&mut nc.pipe);
603        }
604
605        let mut result = Vec::new();
606        for (_, v) in cmd_map.drain() {
607            result.push(v);
608        }
609        Ok(result)
610    }
611
612    fn execute_on_all<'a>(
613        &'a self,
614        input: Input,
615        addresses: HashSet<&'a NodeAddress>,
616    ) -> Vec<RedisResult<(&'a NodeAddress, Value)>> {
617        addresses
618            .into_iter()
619            .map(|addr| {
620                self.request(
621                    input.clone(),
622                    Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::ByAddress {
623                        host: addr.host().to_string(),
624                        port: addr.port(),
625                    })),
626                )
627                .map(|res| match res {
628                    Output::Single(value) => (addr, value),
629                    // technically this shouldn't be possible, but I prefer not to crash here.
630                    Output::Multi(values) => (addr, Value::Array(values)),
631                })
632            })
633            .collect()
634    }
635
636    fn execute_on_all_nodes<'a>(
637        &'a self,
638        input: Input,
639        slots: &'a mut SlotMap,
640    ) -> Vec<RedisResult<(&'a NodeAddress, Value)>> {
641        self.execute_on_all(input, slots.addresses_for_all_nodes())
642    }
643
644    fn execute_on_all_primaries<'a>(
645        &'a self,
646        input: Input,
647        slots: &'a mut SlotMap,
648    ) -> Vec<RedisResult<(&'a NodeAddress, Value)>> {
649        self.execute_on_all(input, slots.addresses_for_all_primaries())
650    }
651
652    fn execute_multi_slot<'a, 'b>(
653        &'a self,
654        input: Input,
655        slots: &'a mut SlotMap,
656        connections: &'a mut HashMap<NodeAddress, C>,
657        routes: &'b [(Route, Vec<usize>)],
658    ) -> Vec<RedisResult<(&'a NodeAddress, Value)>>
659    where
660        'b: 'a,
661    {
662        slots
663            .addresses_for_multi_slot(routes, self.routing_strategy.as_deref())
664            .enumerate()
665            .map(|(index, addr)| {
666                let addr = addr.ok_or(RedisError::from((
667                    ErrorKind::Io,
668                    "Couldn't find connection",
669                )))?;
670                let connection = self.get_connection_by_addr(connections, addr)?;
671                let (_, indices) = routes.get(index).unwrap();
672                let cmd =
673                    crate::cluster_routing::command_for_multi_slot_indices(&input, indices.iter());
674                connection.req_command(&cmd).map(|res| (addr, res))
675            })
676            .collect()
677    }
678
679    fn execute_on_multiple_nodes(
680        &self,
681        input: Input,
682        routing: MultipleNodeRoutingInfo,
683        response_policy: Option<ResponsePolicy>,
684    ) -> RedisResult<Value> {
685        let mut connections = self.connections.borrow_mut();
686        let mut slots = self.slots.borrow_mut();
687
688        let results = match &routing {
689            MultipleNodeRoutingInfo::MultiSlot((routes, _)) => {
690                self.execute_multi_slot(input, &mut slots, &mut connections, routes)
691            }
692            MultipleNodeRoutingInfo::AllMasters => {
693                drop(connections);
694                self.execute_on_all_primaries(input, &mut slots)
695            }
696            MultipleNodeRoutingInfo::AllNodes => {
697                drop(connections);
698                self.execute_on_all_nodes(input, &mut slots)
699            }
700        };
701
702        match response_policy {
703            Some(ResponsePolicy::AllSucceeded) => {
704                let mut last_result = None;
705                for result in results {
706                    last_result = Some(result?);
707                }
708
709                last_result
710                    .ok_or(
711                        (
712                            ErrorKind::ClusterConnectionNotFound,
713                            "No results received for multi-node operation",
714                        )
715                            .into(),
716                    )
717                    .map(|(_, res)| res)
718            }
719            Some(ResponsePolicy::OneSucceeded) => {
720                let mut last_failure = None;
721
722                for result in results {
723                    match result {
724                        Ok((_, val)) => return Ok(val),
725                        Err(err) => last_failure = Some(err),
726                    }
727                }
728
729                Err(last_failure
730                    .unwrap_or_else(|| (ErrorKind::Io, "Couldn't find a connection").into()))
731            }
732            Some(ResponsePolicy::CombineMaps) => crate::cluster_routing::combine_map_results(
733                results
734                    .into_iter()
735                    .map(|result| result.map(|(_, value)| value))
736                    .collect::<RedisResult<Vec<_>>>()?,
737            ),
738            Some(ResponsePolicy::FirstSucceededNonEmptyOrAllEmpty) => {
739                // Attempt to return the first result that isn't `Nil` or an error.
740                // If no such response is found and all servers returned `Nil`, it indicates that all shards are empty, so return `Nil`.
741                // If we received only errors, return the last received error.
742                // If we received a mix of errors and `Nil`s, we can't determine if all shards are empty,
743                // thus we return the last received error instead of `Nil`.
744                let mut last_failure = None;
745                let num_of_results = results.len();
746                let mut nil_counter = 0;
747                for result in results {
748                    match result.map(|(_, res)| res) {
749                        Ok(Value::Nil) => nil_counter += 1,
750                        Ok(val) => return Ok(val),
751                        Err(err) => last_failure = Some(err),
752                    }
753                }
754                if nil_counter == num_of_results {
755                    Ok(Value::Nil)
756                } else {
757                    Err(last_failure
758                        .unwrap_or_else(|| (ErrorKind::Io, "Couldn't find a connection").into()))
759                }
760            }
761            Some(ResponsePolicy::Aggregate(op)) => {
762                let results = results
763                    .into_iter()
764                    .map(|res| res.map(|(_, val)| val))
765                    .collect::<RedisResult<Vec<_>>>()?;
766                crate::cluster_routing::aggregate(results, op)
767            }
768            Some(ResponsePolicy::AggregateLogical(op)) => {
769                let results = results
770                    .into_iter()
771                    .map(|res| res.map(|(_, val)| val))
772                    .collect::<RedisResult<Vec<_>>>()?;
773                crate::cluster_routing::logical_aggregate(results, op)
774            }
775            Some(ResponsePolicy::CombineArrays) => {
776                let results = results
777                    .into_iter()
778                    .map(|res| res.map(|(_, val)| val))
779                    .collect::<RedisResult<Vec<_>>>()?;
780                match routing {
781                    MultipleNodeRoutingInfo::MultiSlot((vec, pattern)) => {
782                        crate::cluster_routing::combine_and_sort_array_results(
783                            results, &vec, &pattern,
784                        )
785                    }
786                    _ => crate::cluster_routing::combine_array_results(results),
787                }
788            }
789            Some(ResponsePolicy::Special) | None => {
790                // This is our assumption - if there's no coherent way to aggregate the responses, we just map each response to the sender, and pass it to the user.
791                // TODO - once Value::Error is merged, we can use join_all and report separate errors and also pass successes.
792                let results = results
793                    .into_iter()
794                    .map(|result| {
795                        result.map(|(addr, val)| {
796                            (Value::BulkString(addr.to_string().into_bytes()), val)
797                        })
798                    })
799                    .collect::<RedisResult<Vec<_>>>()?;
800                Ok(Value::Map(results))
801            }
802        }
803    }
804
805    #[allow(clippy::unnecessary_unwrap)]
806    fn request(&self, input: Input, route_option: Option<RoutingInfo>) -> RedisResult<Output> {
807        let single_node_routing = match route_option {
808            Some(RoutingInfo::SingleNode(single_node_routing)) => single_node_routing,
809            Some(RoutingInfo::MultiNode((multi_node_routing, response_policy))) => {
810                return self
811                    .execute_on_multiple_nodes(input, multi_node_routing, response_policy)
812                    .map(Output::Single);
813            }
814            None => fail!(UNROUTABLE_ERROR),
815        };
816
817        let mut retries = 0;
818        let mut redirected = None::<Redirect>;
819
820        loop {
821            // Get target address and response.
822            let (addr, rv) = {
823                let mut connections = self.connections.borrow_mut();
824                let (addr, conn) = if let Some(redirected) = redirected.take() {
825                    let (addr, is_asking) = match redirected {
826                        Redirect::Moved(addr) => (addr, false),
827                        Redirect::Ask(addr) => (addr, true),
828                    };
829                    let mut conn = self.get_connection_by_addr(&mut connections, &addr);
830                    if is_asking {
831                        // if we are in asking mode we want to feed a single
832                        // ASKING command into the connection before what we
833                        // actually want to execute.
834                        conn = conn.and_then(|conn| {
835                            conn.req_packed_command(&b"*1\r\n$6\r\nASKING\r\n"[..])
836                                .and_then(|value| value.extract_error())?;
837                            Ok(conn)
838                        });
839                    }
840                    (addr, conn)
841                } else {
842                    match &single_node_routing {
843                        SingleNodeRoutingInfo::Random => {
844                            get_random_connection_or_error(&mut connections)
845                        }
846                        SingleNodeRoutingInfo::SpecificNode(route) => {
847                            self.get_connection(&mut connections, route)
848                        }
849                        SingleNodeRoutingInfo::ByAddress { host, port } => {
850                            let address = NodeAddress::new(host.as_str(), *port);
851                            let conn = self.get_connection_by_addr(&mut connections, &address);
852                            (address, conn)
853                        }
854                        SingleNodeRoutingInfo::RandomPrimary => {
855                            self.get_connection(&mut connections, &Route::new_random_primary())
856                        }
857                    }
858                };
859                (addr, conn.and_then(|conn| input.send(conn)))
860            };
861
862            match rv {
863                Ok(rv) => return Ok(rv),
864                Err(err) => {
865                    if err.kind() == ErrorKind::ClusterConnectionNotFound
866                        && *self.auto_reconnect.borrow()
867                    {
868                        for node in &self.initial_nodes {
869                            let addr = NodeAddress::try_from(&node.addr)?;
870                            if let Ok(mut conn) = self.connect(&addr)
871                                && conn.check_connection()
872                            {
873                                self.connections.borrow_mut().insert(addr, conn);
874                            }
875                        }
876                        self.refresh_slots()?;
877                    }
878
879                    if retries == self.cluster_params.retry_params.number_of_retries {
880                        return Err(err);
881                    }
882                    retries += 1;
883
884                    match err.retry_method() {
885                        RetryMethod::AskRedirect => {
886                            redirected = err.redirect_node().and_then(|(node, _slot)| {
887                                NodeAddress::try_from(node).ok().map(Redirect::Ask)
888                            });
889                        }
890                        RetryMethod::MovedRedirect => {
891                            // Refresh slots.
892                            self.refresh_slots()?;
893                            // Request again.
894                            redirected = err.redirect_node().and_then(|(node, _slot)| {
895                                NodeAddress::try_from(node).ok().map(Redirect::Moved)
896                            });
897                        }
898                        RetryMethod::WaitAndRetry => {
899                            // Sleep and retry.
900                            let sleep_time = self
901                                .cluster_params
902                                .retry_params
903                                .wait_time_for_retry(retries);
904                            thread::sleep(sleep_time);
905                        }
906                        RetryMethod::RefreshSlotsAndRetry => {
907                            // Stale slot map (e.g. READONLY after failover). Sleep first to give
908                            // the cluster time to converge, then refresh so the retry (by slot,
909                            // hence `redirected` stays None) routes on the freshest topology.
910                            let sleep_time = self
911                                .cluster_params
912                                .retry_params
913                                .wait_time_for_retry(retries);
914                            thread::sleep(sleep_time);
915                            self.refresh_slots()?;
916                        }
917                        RetryMethod::Reconnect => {
918                            if *self.auto_reconnect.borrow() {
919                                // if the connection is no longer valid, we should remove it.
920                                self.connections.borrow_mut().remove(&addr);
921                                if let Ok(mut conn) = self.connect(&addr)
922                                    && conn.check_connection()
923                                {
924                                    self.connections.borrow_mut().insert(addr, conn);
925                                }
926                            }
927                        }
928                        RetryMethod::NoRetry => {
929                            return Err(err);
930                        }
931                        RetryMethod::RetryImmediately => {}
932                        RetryMethod::ReconnectFromInitialConnections => {
933                            // TODO - implement reconnect from initial connections
934                            if *self.auto_reconnect.borrow()
935                                && let Ok(mut conn) = self.connect(&addr)
936                                && conn.check_connection()
937                            {
938                                self.connections.borrow_mut().insert(addr, conn);
939                            }
940                        }
941                    }
942                }
943            }
944        }
945    }
946
947    fn send_recv_and_retry_cmds(&self, cmds: &[Cmd]) -> RedisResult<Vec<Value>> {
948        // Vector to hold the results, pre-populated with `Nil` values. This allows the original
949        // cmd ordering to be re-established by inserting the response directly into the result
950        // vector (e.g., results[10] = response).
951        let mut results = vec![Value::Nil; cmds.len()];
952
953        let to_retry = self
954            .send_all_commands(cmds)
955            .and_then(|node_cmds| self.recv_all_commands(&mut results, &node_cmds))?;
956
957        if to_retry.is_empty() {
958            return Ok(results);
959        }
960
961        // Refresh the slots to ensure that we have a clean slate for the retry attempts.
962        self.refresh_slots()?;
963
964        // Given that there are commands that need to be retried, it means something in the cluster
965        // topology changed. Execute each command separately to take advantage of the existing
966        // retry logic that handles these cases.
967        for retry_idx in to_retry {
968            let cmd = &cmds[retry_idx];
969            let routing = RoutingInfo::for_routable(cmd);
970            results[retry_idx] = self.request(Input::Cmd(cmd), routing)?.into();
971        }
972        Ok(results)
973    }
974
975    // Build up a pipeline per node, then send it
976    fn send_all_commands(&self, cmds: &[Cmd]) -> RedisResult<Vec<NodeCmd>> {
977        let mut connections = self.connections.borrow_mut();
978
979        let node_cmds = self.map_cmds_to_nodes(cmds)?;
980        for nc in &node_cmds {
981            self.get_connection_by_addr(&mut connections, &nc.addr)?
982                .send_packed_command(&nc.pipe)?;
983        }
984        Ok(node_cmds)
985    }
986
987    // Receive from each node, keeping track of which commands need to be retried.
988    fn recv_all_commands(
989        &self,
990        results: &mut [Value],
991        node_cmds: &[NodeCmd],
992    ) -> RedisResult<Vec<usize>> {
993        let mut to_retry = Vec::new();
994        let mut connections = self.connections.borrow_mut();
995        let mut first_err = None;
996
997        for nc in node_cmds {
998            for cmd_idx in &nc.indexes {
999                match self
1000                    .get_connection_by_addr(&mut connections, &nc.addr)?
1001                    .recv_response()
1002                {
1003                    Ok(item) => results[*cmd_idx] = item,
1004                    Err(err) if err.is_cluster_error() => to_retry.push(*cmd_idx),
1005                    Err(err) => first_err = first_err.or(Some(err)),
1006                }
1007            }
1008        }
1009        match first_err {
1010            Some(err) => Err(err),
1011            None => Ok(to_retry),
1012        }
1013    }
1014
1015    /// Send a command to the given `routing`.
1016    pub fn route_command(&mut self, cmd: &Cmd, routing: RoutingInfo) -> RedisResult<Value> {
1017        self.request(Input::Cmd(cmd), Some(routing))
1018            .map(|res| res.into())
1019    }
1020}
1021
1022const MULTI: &[u8] = "*1\r\n$5\r\nMULTI\r\n".as_bytes();
1023impl<C: Connect + ConnectionLike> ConnectionLike for ClusterConnection<C> {
1024    fn supports_pipelining(&self) -> bool {
1025        false
1026    }
1027
1028    fn req_command(&mut self, cmd: &Cmd) -> RedisResult<Value> {
1029        if cmd.is_empty() {
1030            return Err(RedisError::make_empty_command());
1031        }
1032        let routing = RoutingInfo::for_routable(cmd);
1033        self.request(Input::Cmd(cmd), routing).map(|res| res.into())
1034    }
1035
1036    fn req_packed_command(&mut self, cmd: &[u8]) -> RedisResult<Value> {
1037        if cmd.is_empty() {
1038            return Err(RedisError::make_empty_command());
1039        }
1040        let actual_cmd = if cmd.starts_with(MULTI) {
1041            &cmd[MULTI.len()..]
1042        } else {
1043            cmd
1044        };
1045        let value = parse_redis_value(actual_cmd)?;
1046        let routing = RoutingInfo::for_routable(&value);
1047        self.request(
1048            Input::Slice {
1049                cmd,
1050                routable: value,
1051            },
1052            routing,
1053        )
1054        .map(|res| res.into())
1055    }
1056
1057    fn req_packed_commands(
1058        &mut self,
1059        cmd: &[u8],
1060        offset: usize,
1061        count: usize,
1062    ) -> RedisResult<Vec<Value>> {
1063        if cmd.is_empty() {
1064            return Err(RedisError::make_empty_command());
1065        }
1066        let actual_cmd = if cmd.starts_with(MULTI) {
1067            &cmd[MULTI.len()..]
1068        } else {
1069            cmd
1070        };
1071        let value = parse_redis_value(actual_cmd)?;
1072        let route = match RoutingInfo::for_routable(&value) {
1073            // we don't allow routing multiple commands to multiple nodes.
1074            Some(RoutingInfo::MultiNode(_)) => None,
1075            Some(RoutingInfo::SingleNode(route)) => Some(route),
1076            None => None,
1077        }
1078        .unwrap_or(SingleNodeRoutingInfo::Random);
1079        self.request(
1080            Input::Commands { cmd, offset, count },
1081            Some(RoutingInfo::SingleNode(route)),
1082        )
1083        .map(|res| res.into())
1084    }
1085
1086    fn get_db(&self) -> i64 {
1087        0
1088    }
1089
1090    fn is_open(&self) -> bool {
1091        let connections = self.connections.borrow();
1092        for conn in connections.values() {
1093            if !conn.is_open() {
1094                return false;
1095            }
1096        }
1097        true
1098    }
1099
1100    fn check_connection(&mut self) -> bool {
1101        let mut connections = self.connections.borrow_mut();
1102        for conn in connections.values_mut() {
1103            if !conn.check_connection() {
1104                return false;
1105            }
1106        }
1107        true
1108    }
1109}
1110
1111#[derive(Debug)]
1112struct NodeCmd {
1113    // The original command indexes
1114    indexes: Vec<usize>,
1115    pipe: Vec<u8>,
1116    addr: NodeAddress,
1117}
1118
1119impl NodeCmd {
1120    fn new(a: NodeAddress) -> NodeCmd {
1121        NodeCmd {
1122            indexes: vec![],
1123            pipe: vec![],
1124            addr: a,
1125        }
1126    }
1127}
1128
1129fn get_random_connection<C: ConnectionLike + Connect + Sized>(
1130    connections: &mut HashMap<NodeAddress, C>,
1131) -> Option<(NodeAddress, &mut C)> {
1132    connections
1133        .iter_mut()
1134        .choose(&mut rng())
1135        .map(|(addr, conn)| (addr.clone(), conn))
1136}
1137
1138fn get_random_connection_or_error<C: ConnectionLike + Connect + Sized>(
1139    connections: &mut HashMap<NodeAddress, C>,
1140) -> (NodeAddress, RedisResult<&mut C>) {
1141    match get_random_connection(connections) {
1142        Some((addr, conn)) => (addr, Ok(conn)),
1143        None => (
1144            // we need to add a fake address in order for the error to be handled - the code that uses it assumes there's an address attached.
1145            NodeAddress::default(),
1146            Err(RedisError::from((
1147                ErrorKind::ClusterConnectionNotFound,
1148                "No connections found",
1149            ))),
1150        ),
1151    }
1152}