redis_driver/commands/
sentinel_commands.rs

1use std::collections::HashMap;
2
3use crate::{
4    prepare_command,
5    resp::{
6        cmd, ArgsOrCollection, CommandArg, CommandArgs, FromKeyValueValueArray, FromValue,
7        HashMapExt, IntoArgs, KeyValueArgOrCollection, Value,
8    },
9    PreparedCommand, Result,
10};
11
12/// A group of Redis commands related to Sentinel
13/// # See Also
14/// [Sentinel Commands](https://redis.io/docs/management/sentinel/#sentinel-commands)
15pub trait SentinelCommands {
16    /// Get the current value of a global Sentinel configuration parameter.
17    ///
18    /// The specified name may be a wildcard.
19    /// Similar to the Redis [`config_get`](crate::ServerCommands::config_get) command.
20    #[must_use]
21    fn sentinel_config_get<N, RN, RV, R>(&mut self, name: N) -> PreparedCommand<Self, R>
22    where
23        Self: Sized,
24        N: Into<CommandArg>,
25        RN: FromValue,
26        RV: FromValue,
27        R: FromKeyValueValueArray<RN, RV>,
28    {
29        prepare_command(self, cmd("SENTINEL").arg("CONFIG").arg("GET").arg(name))
30    }
31
32    /// Set the value of a global Sentinel configuration parameter.
33    #[must_use]
34    fn sentinel_config_set<N, V>(&mut self, name: N, value: V) -> PreparedCommand<Self, ()>
35    where
36        Self: Sized,
37        N: Into<CommandArg>,
38        V: Into<CommandArg>,
39    {
40        prepare_command(
41            self,
42            cmd("SENTINEL")
43                .arg("CONFIG")
44                .arg("SET")
45                .arg(name)
46                .arg(value),
47        )
48    }
49
50    /// Check if the current Sentinel configuration is able to reach the quorum needed to failover a master,
51    /// and the majority needed to authorize the failover.
52    ///
53    /// This command should be used in monitoring systems to check if a Sentinel deployment is ok.
54    #[must_use]
55    fn sentinel_ckquorum<N>(&mut self, master_name: N) -> PreparedCommand<Self, ()>
56    where
57        Self: Sized,
58        N: Into<CommandArg>,
59    {
60        prepare_command(self, cmd("SENTINEL").arg("CKQUORUM").arg(master_name))
61    }
62
63    /// Force a failover as if the master was not reachable,
64    /// and without asking for agreement to other Sentinels
65    /// (however a new version of the configuration will be published
66    /// so that the other Sentinels will update their configurations).
67    #[must_use]
68    fn sentinel_failover<N>(&mut self, master_name: N) -> PreparedCommand<Self, ()>
69    where
70        Self: Sized,
71        N: Into<CommandArg>,
72    {
73        prepare_command(self, cmd("SENTINEL").arg("FAILOVER").arg(master_name))
74    }
75
76    /// Force Sentinel to rewrite its configuration on disk, including the current Sentinel state.
77    ///
78    /// Normally Sentinel rewrites the configuration every time something changes in its state
79    /// (in the context of the subset of the state which is persisted on disk across restart).
80    ///  However sometimes it is possible that the configuration file is lost because of operation errors,
81    /// disk failures, package upgrade scripts or configuration managers.
82    /// In those cases a way to force Sentinel to rewrite the configuration file is handy.
83    /// This command works even if the previous configuration file is completely missing.
84    #[must_use]
85    fn sentinel_flushconfig(&mut self) -> PreparedCommand<Self, ()>
86    where
87        Self: Sized,
88    {
89        prepare_command(self, cmd("SENTINEL").arg("FLUSHCONFIG"))
90    }
91
92    /// Return the ip and port number of the master with that name.
93    ///
94    /// If a failover is in progress or terminated successfully for this master,
95    /// it returns the address and port of the promoted replica.
96    ///
97    /// # Return
98    /// * `None` if sentinel does not know this master
99    /// * A tuple made up of
100    ///     * The IP of the master
101    ///     * The port of the master
102    #[must_use]
103    fn sentinel_get_master_addr_by_name<N>(
104        &mut self,
105        master_name: N,
106    ) -> PreparedCommand<Self, Option<(String, u16)>>
107    where
108        Self: Sized,
109        N: Into<CommandArg>,
110    {
111        prepare_command(
112            self,
113            cmd("SENTINEL")
114                .arg("GET-MASTER-ADDR-BY-NAME")
115                .arg(master_name),
116        )
117    }
118
119    /// Return cached [`info`](crate::ServerCommands::info) output from masters and replicas.
120    #[must_use]
121    fn sentinel_info_cache<N, NN, R>(&mut self, master_names: NN) -> PreparedCommand<Self, R>
122    where
123        Self: Sized,
124        N: Into<CommandArg>,
125        NN: ArgsOrCollection<N>,
126        R: FromKeyValueValueArray<String, Vec<(u64, String)>>,
127    {
128        prepare_command(self, cmd("SENTINEL").arg("INFO-CACHE").arg(master_names))
129    }
130
131    /// Show the state and info of the specified master.
132    #[must_use]
133    fn sentinel_master<N>(&mut self, master_name: N) -> PreparedCommand<Self, SentinelMasterInfo>
134    where
135        Self: Sized,
136        N: Into<CommandArg>,
137    {
138        prepare_command(self, cmd("SENTINEL").arg("MASTER").arg(master_name))
139    }
140
141    /// Show a list of monitored masters and their state.
142    #[must_use]
143    fn sentinel_masters(&mut self) -> PreparedCommand<Self, Vec<SentinelMasterInfo>>
144    where
145        Self: Sized,
146    {
147        prepare_command(self, cmd("SENTINEL").arg("MASTERS"))
148    }
149
150    /// This command tells the Sentinel to start monitoring a new master
151    /// with the specified name, ip, port, and quorum.
152    ///
153    /// It is identical to the sentinel monitor configuration directive in `sentinel.conf` configuration file,
154    /// with the difference that you can't use a hostname in as ip,
155    /// but you need to provide an IPv4 or IPv6 address.
156    #[must_use]
157    fn sentinel_monitor<N, I>(
158        &mut self,
159        name: N,
160        ip: I,
161        port: u16,
162        quorum: usize,
163    ) -> PreparedCommand<Self, ()>
164    where
165        Self: Sized,
166        N: Into<CommandArg>,
167        I: Into<CommandArg>,
168    {
169        prepare_command(
170            self,
171            cmd("SENTINEL")
172                .arg("MONITOR")
173                .arg(name)
174                .arg(ip)
175                .arg(port)
176                .arg(quorum),
177        )
178    }
179
180    /// This command is used in order to remove the specified master.
181    ///
182    /// The master will no longer be monitored,
183    /// and will totally be removed from the internal state of the Sentinel,
184    /// so it will no longer listed by [`sentinel_masters`](crate::SentinelCommands::sentinel_masters) and so forth.
185    #[must_use]
186    fn sentinel_remove<N>(&mut self, name: N) -> PreparedCommand<Self, ()>
187    where
188        Self: Sized,
189        N: Into<CommandArg>,
190    {
191        prepare_command(self, cmd("SENTINEL").arg("REMOVE").arg(name))
192    }
193
194    /// The SET command is very similar to the [`config_set`](crate::ServerCommands::config_set) command of Redis,
195    /// and is used in order to change configuration parameters of a specific master.
196    ///
197    /// Multiple option / value pairs can be specified (or none at all).
198    /// All the configuration parameters that can be configured via `sentinel.conf`
199    /// are also configurable using this command.
200    #[must_use]
201    fn sentinel_set<N, O, V, C>(&mut self, name: N, configs: C) -> PreparedCommand<Self, ()>
202    where
203        Self: Sized,
204        N: Into<CommandArg>,
205        O: Into<CommandArg>,
206        V: Into<CommandArg>,
207        C: KeyValueArgOrCollection<O, V>,
208    {
209        prepare_command(self, cmd("SENTINEL").arg("SET").arg(name).arg(configs))
210    }
211
212    /// Return the ID of the Sentinel instance.
213    #[must_use]
214    fn sentinel_myid(&mut self) -> PreparedCommand<Self, String>
215    where
216        Self: Sized,
217    {
218        prepare_command(self, cmd("SENTINEL").arg("MYID"))
219    }
220
221    /// This command returns information about pending scripts.
222    #[must_use]
223    fn sentinel_pending_scripts(&mut self) -> PreparedCommand<Self, Vec<Value>>
224    where
225        Self: Sized,
226    {
227        prepare_command(self, cmd("SENTINEL").arg("PENDING-SCRIPTS"))
228    }
229
230    /// Show a list of replicas for this master, and their state.
231    #[must_use]
232    fn sentinel_replicas<N>(
233        &mut self,
234        master_name: N,
235    ) -> PreparedCommand<Self, Vec<SentinelReplicaInfo>>
236    where
237        Self: Sized,
238        N: Into<CommandArg>,
239    {
240        prepare_command(self, cmd("SENTINEL").arg("REPLICAS").arg(master_name))
241    }
242
243    /// This command will reset all the masters with matching name.
244    ///
245    /// The pattern argument is a glob-style pattern.
246    /// The reset process clears any previous state in a master (including a failover in progress),
247    /// and removes every replica and sentinel already discovered and associated with the master.
248    /// 
249    /// # Return
250    /// The number of reset masters
251    #[must_use]
252    fn sentinel_reset<P>(&mut self, pattern: P) -> PreparedCommand<Self, usize>
253    where
254        Self: Sized,
255        P: Into<CommandArg>,
256    {
257        prepare_command(self, cmd("SENTINEL").arg("RESET").arg(pattern))
258    }
259
260    ///  Show a list of sentinel instances for this master, and their state.
261    #[must_use]
262    fn sentinel_sentinels<N>(&mut self, master_name: N) -> PreparedCommand<Self, Vec<SentinelInfo>>
263    where
264        Self: Sized,
265        N: Into<CommandArg>,
266    {
267        prepare_command(self, cmd("SENTINEL").arg("SENTINELS").arg(master_name))
268    }
269
270    ///  This command simulates different Sentinel crash scenarios.
271    #[must_use]
272    fn sentinel_simulate_failure(
273        &mut self,
274        mode: SentinelSimulateFailureMode,
275    ) -> PreparedCommand<Self, ()>
276    where
277        Self: Sized,
278    {
279        prepare_command(self, cmd("SENTINEL").arg("SIMULATE-FAILURE").arg(mode))
280    }
281}
282
283#[derive(Debug)]
284pub struct SentinelMasterInfo {
285    pub name: String,
286    pub ip: String,
287    pub port: u16,
288    pub runid: String,
289    pub flags: String,
290    pub link_pending_commands: usize,
291    pub link_refcount: usize,
292    pub last_ping_sent: usize,
293    pub last_ok_ping_reply: usize,
294    pub last_ping_reply: usize,
295    pub down_after_milliseconds: u64,
296    pub info_refresh: u64,
297    pub role_reported: String,
298    pub role_reported_time: u64,
299    pub config_epoch: usize,
300    pub num_slaves: usize,
301    pub num_other_sentinels: usize,
302    pub quorum: usize,
303    pub failover_timeout: u64,
304    pub parallel_syncs: usize,
305}
306
307impl FromValue for SentinelMasterInfo {
308    fn from_value(value: Value) -> Result<Self> {
309        let mut values: HashMap<String, Value> = value.into()?;
310
311        Ok(Self {
312            name: values.remove_or_default("name").into()?,
313            ip: values.remove_or_default("ip").into()?,
314            port: values.remove_or_default("port").into()?,
315            runid: values.remove_or_default("runid").into()?,
316            flags: values.remove_or_default("flags").into()?,
317            link_pending_commands: values.remove_or_default("link-pending-commands").into()?,
318            link_refcount: values.remove_or_default("link-refcount").into()?,
319            last_ping_sent: values.remove_or_default("last-ping-sent").into()?,
320            last_ok_ping_reply: values.remove_or_default("last-ok-ping-reply").into()?,
321            last_ping_reply: values.remove_or_default("last-ping-reply").into()?,
322            down_after_milliseconds: values.remove_or_default("down-after-milliseconds").into()?,
323            info_refresh: values.remove_or_default("info-refresh").into()?,
324            role_reported: values.remove_or_default("role-reported").into()?,
325            role_reported_time: values.remove_or_default("role-reported-time").into()?,
326            config_epoch: values.remove_or_default("config-epoch").into()?,
327            num_slaves: values.remove_or_default("num-slaves").into()?,
328            num_other_sentinels: values.remove_or_default("num-other-sentinels").into()?,
329            quorum: values.remove_or_default("quorum").into()?,
330            failover_timeout: values.remove_or_default("failover-timeout").into()?,
331            parallel_syncs: values.remove_or_default("parallel-syncs").into()?,
332        })
333    }
334}
335
336#[derive(Debug)]
337pub struct SentinelReplicaInfo {
338    pub name: String,
339    pub ip: String,
340    pub port: u16,
341    pub runid: String,
342    pub flags: String,
343    pub link_pending_commands: usize,
344    pub link_refcount: usize,
345    pub last_ping_sent: usize,
346    pub last_ok_ping_reply: usize,
347    pub last_ping_reply: usize,
348    pub down_after_milliseconds: u64,
349    pub info_refresh: u64,
350    pub role_reported: String,
351    pub role_reported_time: u64,
352    pub master_link_down_time: u64,
353    pub master_link_status: String,
354    pub master_host: String,
355    pub master_port: u16,
356    pub slave_priority: u64,
357    pub slave_replica_offset: u64,
358    pub replica_announed: usize,
359}
360
361impl FromValue for SentinelReplicaInfo {
362    fn from_value(value: Value) -> Result<Self> {
363        let mut values: HashMap<String, Value> = value.into()?;
364
365        Ok(Self {
366            name: values.remove_or_default("name").into()?,
367            ip: values.remove_or_default("ip").into()?,
368            port: values.remove_or_default("port").into()?,
369            runid: values.remove_or_default("runid").into()?,
370            flags: values.remove_or_default("flags").into()?,
371            link_pending_commands: values.remove_or_default("link-pending-commands").into()?,
372            link_refcount: values.remove_or_default("link-refcount").into()?,
373            last_ping_sent: values.remove_or_default("last-ping-sent").into()?,
374            last_ok_ping_reply: values.remove_or_default("last-ok-ping-reply").into()?,
375            last_ping_reply: values.remove_or_default("last-ping-reply").into()?,
376            down_after_milliseconds: values.remove_or_default("down-after-milliseconds").into()?,
377            info_refresh: values.remove_or_default("info-refresh").into()?,
378            role_reported: values.remove_or_default("role-reported").into()?,
379            role_reported_time: values.remove_or_default("role-reported-time").into()?,
380            master_link_down_time: values.remove_or_default("master-link-down-time").into()?,
381            master_link_status: values.remove_or_default("master-link-status").into()?,
382            master_host: values.remove_or_default("master-host").into()?,
383            master_port: values.remove_or_default("master-port").into()?,
384            slave_priority: values.remove_or_default("slave-priority").into()?,
385            slave_replica_offset: values.remove_or_default("slave-replica-offset").into()?,
386            replica_announed: values.remove_or_default("replica-announed").into()?,
387        })
388    }
389}
390
391pub struct SentinelInfo {
392    pub name: String,
393    pub ip: String,
394    pub port: u16,
395    pub runid: String,
396    pub flags: String,
397    pub link_pending_commands: usize,
398    pub link_refcount: usize,
399    pub last_ping_sent: usize,
400    pub last_ok_ping_reply: usize,
401    pub last_ping_reply: usize,
402    pub down_after_milliseconds: u64,
403    pub last_hello_message: u64,
404    pub voted_leader: String,
405    pub voted_leader_epoch: usize,
406}
407
408impl FromValue for SentinelInfo {
409    fn from_value(value: Value) -> Result<Self> {
410        let mut values: HashMap<String, Value> = value.into()?;
411
412        Ok(Self {
413            name: values.remove_or_default("name").into()?,
414            ip: values.remove_or_default("ip").into()?,
415            port: values.remove_or_default("port").into()?,
416            runid: values.remove_or_default("runid").into()?,
417            flags: values.remove_or_default("flags").into()?,
418            link_pending_commands: values.remove_or_default("link-pending-commands").into()?,
419            link_refcount: values.remove_or_default("link-refcount").into()?,
420            last_ping_sent: values.remove_or_default("last-ping-sent").into()?,
421            last_ok_ping_reply: values.remove_or_default("last-ok-ping-reply").into()?,
422            last_ping_reply: values.remove_or_default("last-ping-reply").into()?,
423            down_after_milliseconds: values.remove_or_default("down-after-milliseconds").into()?,
424            last_hello_message: values.remove_or_default("last-hello-message").into()?,
425            voted_leader: values.remove_or_default("voted-leader").into()?,
426            voted_leader_epoch: values.remove_or_default("voted-leader-epoch").into()?,
427        })
428    }
429}
430
431/// Different crash simulation scenario modes for
432/// the [`sentinel_simulate_failure`](crate::SentinelCommands::sentinel_simulate_failure) command
433pub enum SentinelSimulateFailureMode {
434    CrashAfterElection,
435    CrashAfterPromotion,
436}
437
438impl IntoArgs for SentinelSimulateFailureMode {
439    fn into_args(self, args: CommandArgs) -> CommandArgs {
440        args.arg(match self {
441            SentinelSimulateFailureMode::CrashAfterElection => "CRASH-AFTER-ELECTION",
442            SentinelSimulateFailureMode::CrashAfterPromotion => "CRASH-AFTER-PROMOTION",
443        })
444    }
445}