Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{RedisPool, RedisStore, into_error};
|
||||
use crate::{Deserialize, write::now};
|
||||
use redis::AsyncCommands;
|
||||
|
||||
impl RedisStore {
|
||||
pub async fn key_set(&self, key: &[u8], value: &[u8], expires: Option<u64>) -> trc::Result<()> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.key_set_(
|
||||
pool.get().await.map_err(into_error)?.as_mut(),
|
||||
key,
|
||||
value,
|
||||
expires,
|
||||
)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.key_set_(
|
||||
pool.get().await.map_err(into_error)?.as_mut(),
|
||||
key,
|
||||
value,
|
||||
expires,
|
||||
)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.key_set_(
|
||||
pool.get().await.map_err(into_error)?.as_mut(),
|
||||
key,
|
||||
value,
|
||||
expires,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn key_incr(&self, key: &[u8], value: i64, expires: Option<u64>) -> trc::Result<i64> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.key_incr_(
|
||||
pool.get().await.map_err(into_error)?.as_mut(),
|
||||
key,
|
||||
value,
|
||||
expires,
|
||||
)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.key_incr_(
|
||||
pool.get().await.map_err(into_error)?.as_mut(),
|
||||
key,
|
||||
value,
|
||||
expires,
|
||||
)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.key_incr_(
|
||||
pool.get().await.map_err(into_error)?.as_mut(),
|
||||
key,
|
||||
value,
|
||||
expires,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn try_lock(&self, key: &[u8], expires: u64) -> trc::Result<bool> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.try_lock_(pool.get().await.map_err(into_error)?.as_mut(), key, expires)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.try_lock_(pool.get().await.map_err(into_error)?.as_mut(), key, expires)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.try_lock_(pool.get().await.map_err(into_error)?.as_mut(), key, expires)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn key_delete(&self, key: &[u8]) -> trc::Result<()> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.key_delete_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.key_delete_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.key_delete_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn key_delete_prefix(&self, prefix: &[u8]) -> trc::Result<()> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.key_delete_prefix_(pool.get().await.map_err(into_error)?.as_mut(), prefix)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.key_delete_prefix_(pool.get().await.map_err(into_error)?.as_mut(), prefix)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.key_delete_prefix_(pool.get().await.map_err(into_error)?.as_mut(), prefix)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn key_get<T: Deserialize + std::fmt::Debug + 'static>(
|
||||
&self,
|
||||
key: &[u8],
|
||||
) -> trc::Result<Option<T>> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.key_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.key_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.key_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn counter_get(&self, key: &[u8]) -> trc::Result<i64> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.counter_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.counter_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.counter_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn key_exists(&self, key: &[u8]) -> trc::Result<bool> {
|
||||
match &self.pool {
|
||||
RedisPool::Single(pool) => {
|
||||
self.key_exists_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Cluster(pool) => {
|
||||
self.key_exists_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
RedisPool::Sentinel(pool) => {
|
||||
self.key_exists_(pool.get().await.map_err(into_error)?.as_mut(), key)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn key_get_<T: Deserialize + std::fmt::Debug + 'static>(
|
||||
&self,
|
||||
conn: &mut impl AsyncCommands,
|
||||
key: &[u8],
|
||||
) -> trc::Result<Option<T>> {
|
||||
if let Some(value) = redis::cmd("GET")
|
||||
.arg(key)
|
||||
.query_async::<Option<Vec<u8>>>(conn)
|
||||
.await
|
||||
.map_err(into_error)?
|
||||
{
|
||||
T::deserialize_owned(value).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn counter_get_(&self, conn: &mut impl AsyncCommands, key: &[u8]) -> trc::Result<i64> {
|
||||
redis::cmd("GET")
|
||||
.arg(key)
|
||||
.query_async::<Option<i64>>(conn)
|
||||
.await
|
||||
.map(|x| x.unwrap_or(0))
|
||||
.map_err(into_error)
|
||||
}
|
||||
|
||||
async fn key_exists_(&self, conn: &mut impl AsyncCommands, key: &[u8]) -> trc::Result<bool> {
|
||||
conn.exists(key).await.map_err(into_error)
|
||||
}
|
||||
|
||||
async fn key_set_(
|
||||
&self,
|
||||
conn: &mut impl AsyncCommands,
|
||||
key: &[u8],
|
||||
value: &[u8],
|
||||
expires: Option<u64>,
|
||||
) -> trc::Result<()> {
|
||||
if let Some(expires) = expires {
|
||||
conn.set_ex(key, value, expires).await.map_err(into_error)
|
||||
} else {
|
||||
conn.set(key, value).await.map_err(into_error)
|
||||
}
|
||||
}
|
||||
|
||||
async fn key_incr_(
|
||||
&self,
|
||||
conn: &mut impl AsyncCommands,
|
||||
key: &[u8],
|
||||
value: i64,
|
||||
expires: Option<u64>,
|
||||
) -> trc::Result<i64> {
|
||||
if let Some(expires) = expires {
|
||||
redis::pipe()
|
||||
.atomic()
|
||||
.incr(key, value)
|
||||
.expire(key, expires as i64)
|
||||
.ignore()
|
||||
.query_async::<Vec<i64>>(conn)
|
||||
.await
|
||||
.map_err(into_error)
|
||||
.map(|v| v.first().copied().unwrap_or(0))
|
||||
} else {
|
||||
conn.incr(key, value).await.map_err(into_error)
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_lock_(
|
||||
&self,
|
||||
conn: &mut impl AsyncCommands,
|
||||
key: &[u8],
|
||||
expires: u64,
|
||||
) -> trc::Result<bool> {
|
||||
redis::cmd("SET")
|
||||
.arg(key)
|
||||
.arg(now() + expires)
|
||||
.arg("NX")
|
||||
.arg("EX")
|
||||
.arg(expires as i64)
|
||||
.query_async::<Option<String>>(conn)
|
||||
.await
|
||||
.map(|reply| reply.is_some())
|
||||
.map_err(into_error)
|
||||
}
|
||||
|
||||
async fn key_delete_(&self, conn: &mut impl AsyncCommands, key: &[u8]) -> trc::Result<()> {
|
||||
conn.del(key).await.map_err(into_error)
|
||||
}
|
||||
|
||||
async fn key_delete_prefix_(
|
||||
&self,
|
||||
conn: &mut impl AsyncCommands,
|
||||
prefix: &[u8],
|
||||
) -> trc::Result<()> {
|
||||
let mut pattern = Vec::with_capacity(prefix.len() + 1);
|
||||
pattern.extend_from_slice(prefix);
|
||||
pattern.push(b'*');
|
||||
|
||||
let mut cursor = 0;
|
||||
loop {
|
||||
let (new_cursor, keys): (u64, Vec<Vec<u8>>) = redis::cmd("SCAN")
|
||||
.cursor_arg(cursor)
|
||||
.arg("MATCH")
|
||||
.arg(&pattern)
|
||||
.arg("COUNT")
|
||||
.arg(100)
|
||||
.query_async(conn)
|
||||
.await
|
||||
.map_err(into_error)?;
|
||||
|
||||
if !keys.is_empty() {
|
||||
conn.del::<_, ()>(&keys).await.map_err(into_error)?;
|
||||
}
|
||||
|
||||
if new_cursor != 0 {
|
||||
cursor = new_cursor;
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::InMemoryStore;
|
||||
use deadpool::{
|
||||
Runtime,
|
||||
managed::{Manager, Pool},
|
||||
};
|
||||
use redis::{
|
||||
Client, ConnectionAddr, IntoConnectionInfo, ProtocolVersion, TlsMode,
|
||||
cluster::{ClusterClient, ClusterClientBuilder},
|
||||
cluster_read_routing::RandomReplicaStrategy,
|
||||
sentinel::{SentinelClient, SentinelClientBuilder, SentinelServerType},
|
||||
};
|
||||
use registry::{
|
||||
schema::{enums::RedisProtocol, structs},
|
||||
types::duration::Duration,
|
||||
};
|
||||
use std::{fmt::Display, sync::Arc};
|
||||
|
||||
pub mod lookup;
|
||||
pub mod pool;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RedisStore {
|
||||
pub pool: RedisPool,
|
||||
}
|
||||
|
||||
pub struct RedisConnectionManager {
|
||||
pub client: Client,
|
||||
timeout: std::time::Duration,
|
||||
}
|
||||
|
||||
pub struct RedisClusterConnectionManager {
|
||||
pub client: ClusterClient,
|
||||
timeout: std::time::Duration,
|
||||
}
|
||||
|
||||
pub struct RedisSentinelConnectionManager {
|
||||
pub client: tokio::sync::Mutex<SentinelClient>,
|
||||
timeout: std::time::Duration,
|
||||
}
|
||||
|
||||
pub enum RedisPool {
|
||||
Single(Pool<RedisConnectionManager>),
|
||||
Cluster(Pool<RedisClusterConnectionManager>),
|
||||
Sentinel(Pool<RedisSentinelConnectionManager>),
|
||||
}
|
||||
|
||||
impl RedisStore {
|
||||
pub async fn open_single(config: structs::RedisStore) -> Result<InMemoryStore, String> {
|
||||
Ok(InMemoryStore::Redis(Arc::new(RedisStore {
|
||||
pool: RedisPool::Single(build_pool(
|
||||
RedisConnectionManager {
|
||||
client: Client::open(config.url)
|
||||
.map_err(|err| format!("Failed to open Redis client: {err:?}"))?,
|
||||
timeout: config.timeout.into_inner(),
|
||||
},
|
||||
config.pool_max_connections,
|
||||
config.pool_timeout_create,
|
||||
config.pool_timeout_wait,
|
||||
config.pool_timeout_recycle,
|
||||
)?),
|
||||
})))
|
||||
}
|
||||
|
||||
pub async fn open_cluster(config: structs::RedisClusterStore) -> Result<InMemoryStore, String> {
|
||||
let mut builder = ClusterClientBuilder::new(config.urls);
|
||||
if let Some(value) = config.auth_username {
|
||||
builder = builder.username(value);
|
||||
}
|
||||
if let Some(value) = config.auth_secret.secret().await?.map(|v| v.into_owned()) {
|
||||
builder = builder.password(value);
|
||||
}
|
||||
if let Some(value) = config.max_retries {
|
||||
builder = builder.retries(value as u32);
|
||||
}
|
||||
if let Some(value) = config.max_retry_wait {
|
||||
builder = builder.max_retry_wait(value.as_millis());
|
||||
}
|
||||
if let Some(value) = config.min_retry_wait {
|
||||
builder = builder.min_retry_wait(value.as_millis());
|
||||
}
|
||||
if config.read_from_replicas {
|
||||
builder = builder.read_routing_strategy(RandomReplicaStrategy);
|
||||
}
|
||||
if matches!(config.protocol_version, RedisProtocol::Resp3) {
|
||||
builder = builder.use_protocol(ProtocolVersion::RESP3);
|
||||
}
|
||||
|
||||
let client = builder
|
||||
.build()
|
||||
.map_err(|err| format!("Failed to open Redis client: {err:?}"))?;
|
||||
|
||||
Ok(InMemoryStore::Redis(Arc::new(RedisStore {
|
||||
pool: RedisPool::Cluster(build_pool(
|
||||
RedisClusterConnectionManager {
|
||||
client,
|
||||
timeout: config.timeout.into_inner(),
|
||||
},
|
||||
config.pool_max_connections,
|
||||
config.pool_timeout_create,
|
||||
config.pool_timeout_wait,
|
||||
config.pool_timeout_recycle,
|
||||
)?),
|
||||
})))
|
||||
}
|
||||
|
||||
pub async fn open_sentinel(
|
||||
config: structs::RedisSentinelStore,
|
||||
) -> Result<InMemoryStore, String> {
|
||||
let mut sentinels = Vec::with_capacity(config.urls.len());
|
||||
let mut tls_mode = None;
|
||||
for url in config.urls {
|
||||
let info = url
|
||||
.into_connection_info()
|
||||
.map_err(|err| format!("Invalid Redis Sentinel URL: {err}"))?;
|
||||
let url_tls_mode = match info.addr() {
|
||||
ConnectionAddr::TcpTls { insecure: true, .. } => Some(TlsMode::Insecure),
|
||||
ConnectionAddr::TcpTls {
|
||||
insecure: false, ..
|
||||
} => Some(TlsMode::Secure),
|
||||
_ => None,
|
||||
};
|
||||
if sentinels.is_empty() {
|
||||
tls_mode = url_tls_mode;
|
||||
} else if tls_mode != url_tls_mode {
|
||||
return Err(
|
||||
"All Redis Sentinel URLs must use the same scheme and TLS settings".to_string(),
|
||||
);
|
||||
}
|
||||
sentinels.push(info.addr().clone());
|
||||
}
|
||||
|
||||
let mut builder =
|
||||
SentinelClientBuilder::new(sentinels, config.service_name, SentinelServerType::Master)
|
||||
.map_err(|err| format!("Failed to create Redis Sentinel client: {err:?}"))?;
|
||||
|
||||
if let Some(value) = config.auth_username {
|
||||
builder = builder.set_client_to_redis_username(value);
|
||||
}
|
||||
if let Some(value) = config.auth_secret.secret().await?.map(|v| v.into_owned()) {
|
||||
builder = builder.set_client_to_redis_password(value);
|
||||
}
|
||||
if let Some(value) = config.sentinel_username {
|
||||
builder = builder.set_client_to_sentinel_username(value);
|
||||
}
|
||||
if let Some(value) = config
|
||||
.sentinel_secret
|
||||
.secret()
|
||||
.await?
|
||||
.map(|v| v.into_owned())
|
||||
{
|
||||
builder = builder.set_client_to_sentinel_password(value);
|
||||
}
|
||||
if matches!(config.protocol_version, RedisProtocol::Resp3) {
|
||||
builder = builder.set_client_to_redis_protocol(ProtocolVersion::RESP3);
|
||||
}
|
||||
if let Some(tls_mode) = tls_mode {
|
||||
builder = builder.set_client_to_redis_tls_mode(tls_mode);
|
||||
}
|
||||
|
||||
let client = builder
|
||||
.build()
|
||||
.map_err(|err| format!("Failed to open Redis Sentinel client: {err:?}"))?;
|
||||
|
||||
Ok(InMemoryStore::Redis(Arc::new(RedisStore {
|
||||
pool: RedisPool::Sentinel(build_pool(
|
||||
RedisSentinelConnectionManager {
|
||||
client: tokio::sync::Mutex::new(client),
|
||||
timeout: config.timeout.into_inner(),
|
||||
},
|
||||
config.pool_max_connections,
|
||||
config.pool_timeout_create,
|
||||
config.pool_timeout_wait,
|
||||
config.pool_timeout_recycle,
|
||||
)?),
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
fn build_pool<M: Manager>(
|
||||
manager: M,
|
||||
max_size: u64,
|
||||
create_timeout: Option<Duration>,
|
||||
wait_timeout: Option<Duration>,
|
||||
recycle_timeout: Option<Duration>,
|
||||
) -> Result<Pool<M>, String> {
|
||||
Pool::builder(manager)
|
||||
.runtime(Runtime::Tokio1)
|
||||
.max_size(max_size as usize)
|
||||
.create_timeout(create_timeout.map(|v| v.into_inner()))
|
||||
.wait_timeout(wait_timeout.map(|v| v.into_inner()))
|
||||
.recycle_timeout(recycle_timeout.map(|v| v.into_inner()))
|
||||
.build()
|
||||
.map_err(|err| format!("Failed to build pool: {err}"))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn into_error(err: impl Display) -> trc::Error {
|
||||
trc::StoreEvent::RedisError.reason(err)
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RedisPool {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Single(_) => f.debug_tuple("Single").finish(),
|
||||
Self::Cluster(_) => f.debug_tuple("Cluster").finish(),
|
||||
Self::Sentinel(_) => f.debug_tuple("Sentinel").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{
|
||||
RedisClusterConnectionManager, RedisConnectionManager, RedisSentinelConnectionManager,
|
||||
into_error,
|
||||
};
|
||||
use deadpool::managed;
|
||||
use redis::{
|
||||
aio::{ConnectionLike, MultiplexedConnection},
|
||||
cluster_async::ClusterConnection,
|
||||
};
|
||||
|
||||
impl managed::Manager for RedisConnectionManager {
|
||||
type Type = MultiplexedConnection;
|
||||
type Error = trc::Error;
|
||||
|
||||
async fn create(&self) -> Result<MultiplexedConnection, trc::Error> {
|
||||
match tokio::time::timeout(self.timeout, self.client.get_multiplexed_async_connection())
|
||||
.await
|
||||
{
|
||||
Ok(conn) => conn.map_err(into_error),
|
||||
Err(_) => Err(trc::StoreEvent::RedisError.ctx(trc::Key::Details, "Connection Timeout")),
|
||||
}
|
||||
}
|
||||
|
||||
async fn recycle(
|
||||
&self,
|
||||
conn: &mut MultiplexedConnection,
|
||||
_: &managed::Metrics,
|
||||
) -> managed::RecycleResult<trc::Error> {
|
||||
conn.req_packed_command(&redis::cmd("PING"))
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|err| managed::RecycleError::Backend(into_error(err)))
|
||||
}
|
||||
}
|
||||
|
||||
impl managed::Manager for RedisClusterConnectionManager {
|
||||
type Type = ClusterConnection;
|
||||
type Error = trc::Error;
|
||||
|
||||
async fn create(&self) -> Result<ClusterConnection, trc::Error> {
|
||||
match tokio::time::timeout(self.timeout, self.client.get_async_connection()).await {
|
||||
Ok(conn) => conn.map_err(into_error),
|
||||
Err(_) => Err(trc::StoreEvent::RedisError.ctx(trc::Key::Details, "Connection Timeout")),
|
||||
}
|
||||
}
|
||||
|
||||
async fn recycle(
|
||||
&self,
|
||||
conn: &mut ClusterConnection,
|
||||
_: &managed::Metrics,
|
||||
) -> managed::RecycleResult<trc::Error> {
|
||||
conn.req_packed_command(&redis::cmd("PING"))
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|err| managed::RecycleError::Backend(into_error(err)))
|
||||
}
|
||||
}
|
||||
|
||||
impl managed::Manager for RedisSentinelConnectionManager {
|
||||
type Type = MultiplexedConnection;
|
||||
type Error = trc::Error;
|
||||
|
||||
async fn create(&self) -> Result<MultiplexedConnection, trc::Error> {
|
||||
let mut client = self.client.lock().await;
|
||||
match tokio::time::timeout(self.timeout, client.get_async_connection()).await {
|
||||
Ok(conn) => conn.map_err(into_error),
|
||||
Err(_) => Err(trc::StoreEvent::RedisError.ctx(trc::Key::Details, "Connection Timeout")),
|
||||
}
|
||||
}
|
||||
|
||||
async fn recycle(
|
||||
&self,
|
||||
conn: &mut MultiplexedConnection,
|
||||
_: &managed::Metrics,
|
||||
) -> managed::RecycleResult<trc::Error> {
|
||||
conn.req_packed_command(&redis::cmd("PING"))
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|err| managed::RecycleError::Backend(into_error(err)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user