deadpool_redis/sentinel/
config.rs

1pub use crate::config::ConfigError;
2use crate::{ConnectionAddr, ConnectionInfo, RedisConnectionInfo};
3
4use super::{CreatePoolError, Pool, PoolBuilder, PoolConfig, Runtime};
5
6/// Configuration object.
7///
8/// # Example (from environment)
9///
10/// By enabling the `serde` feature you can read the configuration using the
11/// [`config`](https://crates.io/crates/config) crate as following:
12/// ```env
13/// REDIS_SENTINEL__URLS=redis://127.0.0.1:26379,redis://127.0.0.1:26380
14/// REDIS_SENTINEL__MASTER_NAME=mymaster
15/// REDIS_SENTINEL__SERVER_TYPE=master
16/// REDIS_SENTINEL__POOL__MAX_SIZE=16
17/// REDIS_SENTINEL__POOL__TIMEOUTS__WAIT__SECS=2
18/// REDIS_SENTINEL__POOL__TIMEOUTS__WAIT__NANOS=0
19/// ```
20/// ```rust
21/// #[derive(serde::Deserialize)]
22/// struct Config {
23///     redis_sentinel: deadpool_redis::sentinel::Config,
24/// }
25///
26/// impl Config {
27///     pub fn from_env() -> Result<Self, config::ConfigError> {
28///         let mut cfg = config::Config::builder()
29///            .add_source(
30///                config::Environment::default()
31///                .separator("__")
32///                .try_parsing(true)
33///                .list_separator(","),
34///            )
35///            .build()?;
36///            cfg.try_deserialize()
37///     }
38/// }
39/// ```
40#[derive(Clone, Debug)]
41#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
42pub struct Config {
43    /// Redis URLs.
44    ///
45    /// See [Connection Parameters](redis#connection-parameters).
46    pub urls: Option<Vec<String>>,
47    /// ServerType
48    ///
49    /// [`SentinelServerType`]
50    #[cfg_attr(feature = "serde", serde(default))]
51    pub server_type: SentinelServerType,
52    /// Sentinel setup master name. default value is `mymaster`
53    #[cfg_attr(feature = "serde", serde(default = "default_master_name"))]
54    pub master_name: String,
55    /// [`redis::ConnectionInfo`] structures.
56    pub connections: Option<Vec<ConnectionInfo>>,
57    /// [`redis::sentinel::SentinelNodeConnectionInfo`] structures.
58    pub node_connection_info: Option<SentinelNodeConnectionInfo>,
59    /// Pool configuration.
60    pub pool: Option<PoolConfig>,
61}
62
63impl Config {
64    /// Creates a new [`Pool`] using this [`Config`].
65    ///
66    /// # Errors
67    ///
68    /// See [`CreatePoolError`] for details.
69    pub fn create_pool(&self, runtime: Option<Runtime>) -> Result<Pool, CreatePoolError> {
70        let mut builder = self.builder().map_err(CreatePoolError::Config)?;
71        if let Some(runtime) = runtime {
72            builder = builder.runtime(runtime);
73        }
74        builder.build().map_err(CreatePoolError::Build)
75    }
76
77    /// Creates a new [`PoolBuilder`] using this [`Config`].
78    ///
79    /// # Errors
80    ///
81    /// See [`ConfigError`] for details.
82    pub fn builder(&self) -> Result<PoolBuilder, ConfigError> {
83        let manager = match (&self.urls, &self.connections) {
84            (Some(urls), None) => super::Manager::new(
85                urls.iter().map(|url| url.as_str()).collect(),
86                self.master_name.clone(),
87                self.node_connection_info.clone(),
88                self.server_type,
89            )?,
90            (None, Some(connections)) => super::Manager::new(
91                connections.clone(),
92                self.master_name.clone(),
93                self.node_connection_info.clone(),
94                self.server_type,
95            )?,
96            (None, None) => super::Manager::new(
97                vec![ConnectionInfo::default()],
98                self.master_name.clone(),
99                self.node_connection_info.clone(),
100                self.server_type,
101            )?,
102            (Some(_), Some(_)) => return Err(ConfigError::UrlAndConnectionSpecified),
103        };
104        let pool_config = self.get_pool_config();
105        Ok(Pool::builder(manager).config(pool_config))
106    }
107
108    /// Returns [`deadpool::managed::PoolConfig`] which can be used to construct
109    /// a [`deadpool::managed::Pool`] instance.
110    #[must_use]
111    pub fn get_pool_config(&self) -> PoolConfig {
112        self.pool.unwrap_or_default()
113    }
114
115    /// Creates a new [`Config`] from the given Redis URL (like
116    /// `redis://127.0.0.1`).
117    #[must_use]
118    pub fn from_urls<T: Into<Vec<String>>>(
119        urls: T,
120        master_name: String,
121        server_type: SentinelServerType,
122    ) -> Config {
123        Config {
124            urls: Some(urls.into()),
125            connections: None,
126            master_name,
127            server_type,
128            pool: None,
129            node_connection_info: None,
130        }
131    }
132
133    /// Sets the connection info used to connect to the underlying redis servers (eg: tls mode/db/username/..)
134    pub fn with_node_connection_info(
135        mut self,
136        node_connection_info: Option<SentinelNodeConnectionInfo>,
137    ) -> Self {
138        self.node_connection_info = node_connection_info;
139        self
140    }
141}
142
143impl Default for Config {
144    fn default() -> Self {
145        let default_connection_info = ConnectionInfo {
146            addr: ConnectionAddr::Tcp("127.0.0.1".to_string(), 26379),
147            ..ConnectionInfo::default()
148        };
149
150        Self {
151            urls: None,
152            connections: Some(vec![default_connection_info.clone()]),
153            server_type: SentinelServerType::Master,
154            master_name: default_master_name(),
155            pool: None,
156            node_connection_info: None,
157        }
158    }
159}
160
161fn default_master_name() -> String {
162    "mymaster".to_string()
163}
164
165/// This type is a wrapper for [`redis::sentinel::SentinelServerType`] for serialize/deserialize.
166#[derive(Debug, Clone, Copy, Default)]
167#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
168pub enum SentinelServerType {
169    #[default]
170    /// Master connections only
171    Master,
172    /// Replica connections only
173    Replica,
174}
175
176impl From<redis::sentinel::SentinelServerType> for SentinelServerType {
177    fn from(value: redis::sentinel::SentinelServerType) -> Self {
178        match value {
179            redis::sentinel::SentinelServerType::Master => SentinelServerType::Master,
180            redis::sentinel::SentinelServerType::Replica => SentinelServerType::Replica,
181        }
182    }
183}
184
185impl From<SentinelServerType> for redis::sentinel::SentinelServerType {
186    fn from(value: SentinelServerType) -> Self {
187        match value {
188            SentinelServerType::Master => redis::sentinel::SentinelServerType::Master,
189            SentinelServerType::Replica => redis::sentinel::SentinelServerType::Replica,
190        }
191    }
192}
193
194/// This type is a wrapper for [`redis::TlsMode`] for serialize/deserialize.
195#[derive(Debug, Clone, Copy, Default)]
196#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
197pub enum TlsMode {
198    #[default]
199    /// Secure verify certification.
200    Secure,
201    /// Insecure do not verify certification.
202    Insecure,
203}
204
205impl From<redis::TlsMode> for TlsMode {
206    fn from(value: redis::TlsMode) -> Self {
207        match value {
208            redis::TlsMode::Insecure => TlsMode::Insecure,
209            redis::TlsMode::Secure => TlsMode::Secure,
210        }
211    }
212}
213
214impl From<TlsMode> for redis::TlsMode {
215    fn from(value: TlsMode) -> Self {
216        match value {
217            TlsMode::Insecure => redis::TlsMode::Insecure,
218            TlsMode::Secure => redis::TlsMode::Secure,
219        }
220    }
221}
222
223/// This type is a wrapper for [`redis::sentinel::SentinelNodeConnectionInfo`] for serialize/deserialize/debug.
224#[derive(Clone, Debug, Default)]
225#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
226#[cfg_attr(feature = "serde", serde(crate = "serde"))]
227pub struct SentinelNodeConnectionInfo {
228    /// The TLS mode of the connection, or None if we do not want to connect using TLS
229    /// (just a plain TCP connection).
230    pub tls_mode: Option<TlsMode>,
231
232    /// The Redis specific/connection independent information to be used.
233    pub redis_connection_info: Option<RedisConnectionInfo>,
234}
235
236impl From<SentinelNodeConnectionInfo> for redis::sentinel::SentinelNodeConnectionInfo {
237    fn from(info: SentinelNodeConnectionInfo) -> Self {
238        Self {
239            tls_mode: info.tls_mode.map(|m| m.into()),
240            redis_connection_info: info.redis_connection_info.map(|i| i.into()),
241        }
242    }
243}
244
245impl From<redis::sentinel::SentinelNodeConnectionInfo> for SentinelNodeConnectionInfo {
246    fn from(info: redis::sentinel::SentinelNodeConnectionInfo) -> Self {
247        Self {
248            tls_mode: info.tls_mode.map(|m| m.into()),
249            redis_connection_info: info.redis_connection_info.map(|m| m.into()),
250        }
251    }
252}