Skip to content

Commit 0e52ac9

Browse files
authored
Improve retry logic and update unmaintained dependencies for Rust lint CI (#2673)
1 parent 0d4b94b commit 0e52ac9

File tree

12 files changed

+104
-77
lines changed

12 files changed

+104
-77
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
* Core: Add support for sending multi-slot JSON.MSET and JSON.MGET commands ([#2587]https://github.com/valkey-io/valkey-glide/pull/2587)
7878
* Node: Add `JSON.DEBUG` command ([#2572](https://github.com/valkey-io/valkey-glide/pull/2572))
7979
* Node: Add `JSON.NUMINCRBY` and `JSON.NUMMULTBY` command ([#2555](https://github.com/valkey-io/valkey-glide/pull/2555))
80+
* Core: Improve retry logic and update unmaintained dependencies for Rust lint CI ([#2673](https://github.com/valkey-io/valkey-glide/pull/2643))
8081

8182
#### Breaking Changes
8283

benchmarks/rust/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ redis = { path = "../../glide-core/redis-rs/redis", features = ["aio"] }
1515
futures = "0.3.28"
1616
rand = "0.8.5"
1717
itoa = "1.0.6"
18-
futures-time = "^3.0.0"
1918
clap = { version = "4.3.8", features = ["derive"] }
2019
chrono = "0.4.26"
2120
serde_json = "1.0.99"

deny.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ yanked = "deny"
2424
ignore = [
2525
# Unmaintained dependency error that needs more attention due to nested dependencies
2626
"RUSTSEC-2024-0370",
27-
"RUSTSEC-2024-0384",
28-
"RUSTSEC-2024-0388",
2927
]
3028
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
3129
# lower than the range specified will be ignored. Note that ignored advisories
@@ -59,7 +57,7 @@ allow = [
5957
"Unicode-DFS-2016",
6058
"ISC",
6159
"OpenSSL",
62-
"MPL-2.0",
60+
"MPL-2.0"
6361
]
6462
# The confidence threshold for detecting a license from license text.
6563
# The higher the value, the more closely the license text must be to the

glide-core/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ logger_core = { path = "../logger_core" }
2424
dispose = "0.5.0"
2525
tokio-util = { version = "^0.7", features = ["rt"], optional = true }
2626
num_cpus = { version = "^1.15", optional = true }
27-
tokio-retry = "0.3.0"
27+
tokio-retry2 = {version = "0.5", features = ["jitter"]}
28+
2829
protobuf = { version = "3", features = [
2930
"bytes",
3031
"with-bytes",

glide-core/redis-rs/redis/Cargo.toml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,21 @@ dispose = { version = "0.5.0", optional = true }
5353
# Only needed for the connection manager
5454
arc-swap = { version = "1.7.1" }
5555
futures = { version = "0.3.3", optional = true }
56-
tokio-retry = { version = "0.3.0", optional = true }
5756

5857
# Only needed for the r2d2 feature
5958
r2d2 = { version = "0.8.8", optional = true }
6059

6160
# Only needed for cluster
6261
crc16 = { version = "0.4", optional = true }
6362
rand = { version = "0.8", optional = true }
64-
derivative = { version = "2.2.0", optional = true }
6563

6664
# Only needed for async cluster
6765
dashmap = { version = "6.0", optional = true }
6866

6967
async-trait = { version = "0.1.24", optional = true }
7068

7169
# Only needed for tokio support
72-
backoff-tokio = { package = "backoff", version = "0.4.0", optional = true, features = [
73-
"tokio",
74-
] }
70+
tokio-retry2 = {version = "0.5", features = ["jitter"], optional = true}
7571

7672
# Only needed for native tls
7773
native-tls = { version = "0.2", optional = true }
@@ -134,7 +130,7 @@ aio = [
134130
]
135131
geospatial = []
136132
json = ["serde", "serde/derive", "serde_json"]
137-
cluster = ["crc16", "rand", "derivative"]
133+
cluster = ["crc16", "rand"]
138134
script = ["sha1_smol"]
139135
tls-native-tls = ["native-tls"]
140136
tls-rustls = [
@@ -145,10 +141,10 @@ tls-rustls = [
145141
]
146142
tls-rustls-insecure = ["tls-rustls"]
147143
tls-rustls-webpki-roots = ["tls-rustls", "webpki-roots"]
148-
tokio-comp = ["aio", "tokio/net", "backoff-tokio"]
144+
tokio-comp = ["aio", "tokio/net", "tokio-retry2"]
149145
tokio-native-tls-comp = ["tokio-comp", "tls-native-tls", "tokio-native-tls"]
150146
tokio-rustls-comp = ["tokio-comp", "tls-rustls", "tokio-rustls"]
151-
connection-manager = ["futures", "aio", "tokio-retry"]
147+
connection-manager = ["futures", "aio", "tokio-retry2"]
152148
streams = []
153149
cluster-async = ["cluster", "futures", "futures-util", "dashmap"]
154150
keep-alive = ["socket2"]

glide-core/redis-rs/redis/src/aio/connection_manager.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use futures::{
1515
};
1616
use futures_util::future::BoxFuture;
1717
use std::sync::Arc;
18-
use tokio_retry::strategy::{jitter, ExponentialBackoff};
19-
use tokio_retry::Retry;
18+
use tokio_retry2::strategy::{jitter, ExponentialBackoff};
19+
use tokio_retry2::{Retry, RetryError};
2020

2121
/// A `ConnectionManager` is a proxy that wraps a [multiplexed
2222
/// connection][multiplexed-connection] and automatically reconnects to the
@@ -191,12 +191,15 @@ impl ConnectionManager {
191191
connection_timeout: std::time::Duration,
192192
) -> RedisResult<MultiplexedConnection> {
193193
let retry_strategy = exponential_backoff.map(jitter).take(number_of_retries);
194-
Retry::spawn(retry_strategy, || {
195-
client.get_multiplexed_async_connection_with_timeouts(
196-
response_timeout,
197-
connection_timeout,
198-
GlideConnectionOptions::default(),
199-
)
194+
Retry::spawn(retry_strategy, || async {
195+
client
196+
.get_multiplexed_async_connection_with_timeouts(
197+
response_timeout,
198+
connection_timeout,
199+
GlideConnectionOptions::default(),
200+
)
201+
.await
202+
.map_err(RetryError::transient)
200203
})
201204
.await
202205
}

glide-core/redis-rs/redis/src/cluster_async/mod.rs

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ use crate::{
3333
client::GlideConnectionOptions,
3434
cluster_routing::{Routable, RoutingInfo},
3535
cluster_slotmap::SlotMap,
36-
cluster_topology::SLOT_SIZE,
36+
cluster_topology::{
37+
calculate_topology, get_slot, SlotRefreshState, DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES,
38+
DEFAULT_REFRESH_SLOTS_RETRY_BASE_DURATION_MILLIS, DEFAULT_REFRESH_SLOTS_RETRY_BASE_FACTOR,
39+
SLOT_SIZE,
40+
},
3741
cmd,
3842
commands::cluster_scan::{cluster_scan, ClusterScanArgs, ObjectType, ScanStateRC},
3943
FromRedisValue, InfoDict, ToRedisArgs,
@@ -69,10 +73,6 @@ use crate::{
6973
self, MultipleNodeRoutingInfo, Redirect, ResponsePolicy, Route, SingleNodeRoutingInfo,
7074
SlotAddr,
7175
},
72-
cluster_topology::{
73-
calculate_topology, get_slot, SlotRefreshState, DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES,
74-
DEFAULT_REFRESH_SLOTS_RETRY_INITIAL_INTERVAL, DEFAULT_REFRESH_SLOTS_RETRY_MAX_INTERVAL,
75-
},
7676
connection::{PubSubSubscriptionInfo, PubSubSubscriptionKind},
7777
push_manager::PushInfo,
7878
Cmd, ConnectionInfo, ErrorKind, IntoConnectionInfo, RedisError, RedisFuture, RedisResult,
@@ -84,9 +84,10 @@ use std::time::Duration;
8484
#[cfg(feature = "tokio-comp")]
8585
use async_trait::async_trait;
8686
#[cfg(feature = "tokio-comp")]
87-
use backoff_tokio::future::retry;
87+
use tokio_retry2::strategy::{jitter_range, ExponentialFactorBackoff};
8888
#[cfg(feature = "tokio-comp")]
89-
use backoff_tokio::{Error as BackoffError, ExponentialBackoff};
89+
use tokio_retry2::{Retry, RetryError};
90+
9091
#[cfg(feature = "tokio-comp")]
9192
use tokio::{sync::Notify, time::timeout};
9293

@@ -1518,16 +1519,24 @@ where
15181519

15191520
let mut res = Ok(());
15201521
if !skip_slots_refresh {
1521-
let retry_strategy = ExponentialBackoff {
1522-
initial_interval: DEFAULT_REFRESH_SLOTS_RETRY_INITIAL_INTERVAL,
1523-
max_interval: DEFAULT_REFRESH_SLOTS_RETRY_MAX_INTERVAL,
1524-
max_elapsed_time: None,
1525-
..Default::default()
1526-
};
1522+
let retry_strategy = ExponentialFactorBackoff::from_millis(
1523+
DEFAULT_REFRESH_SLOTS_RETRY_BASE_DURATION_MILLIS,
1524+
DEFAULT_REFRESH_SLOTS_RETRY_BASE_FACTOR,
1525+
)
1526+
.map(jitter_range(0.8, 1.2))
1527+
.take(DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES);
15271528
let retries_counter = AtomicUsize::new(0);
1528-
res = retry(retry_strategy, || {
1529+
res = Retry::spawn(retry_strategy, || async {
15291530
let curr_retry = retries_counter.fetch_add(1, atomic::Ordering::Relaxed);
15301531
Self::refresh_slots(inner.clone(), curr_retry)
1532+
.await
1533+
.map_err(|err| {
1534+
if err.kind() == ErrorKind::AllConnectionsUnavailable {
1535+
RetryError::permanent(err)
1536+
} else {
1537+
RetryError::transient(err)
1538+
}
1539+
})
15311540
})
15321541
.await;
15331542
}
@@ -1706,26 +1715,13 @@ where
17061715
false
17071716
}
17081717

1709-
async fn refresh_slots(
1710-
inner: Arc<InnerCore<C>>,
1711-
curr_retry: usize,
1712-
) -> Result<(), BackoffError<RedisError>> {
1718+
async fn refresh_slots(inner: Arc<InnerCore<C>>, curr_retry: usize) -> RedisResult<()> {
17131719
// Update the slot refresh last run timestamp
17141720
let now = SystemTime::now();
17151721
let mut last_run_wlock = inner.slot_refresh_state.last_run.write().await;
17161722
*last_run_wlock = Some(now);
17171723
drop(last_run_wlock);
1718-
Self::refresh_slots_inner(inner, curr_retry)
1719-
.await
1720-
.map_err(|err| {
1721-
if curr_retry > DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES
1722-
|| err.kind() == ErrorKind::AllConnectionsUnavailable
1723-
{
1724-
BackoffError::Permanent(err)
1725-
} else {
1726-
BackoffError::from(err)
1727-
}
1728-
})
1724+
Self::refresh_slots_inner(inner, curr_retry).await
17291725
}
17301726

17311727
pub(crate) fn check_if_all_slots_covered(slot_map: &SlotMap) -> bool {

glide-core/redis-rs/redis/src/cluster_topology.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::cluster_slotmap::{ReadFromReplicaStrategy, SlotMap};
88
use crate::{cluster::TlsMode, ErrorKind, RedisError, RedisResult, Value};
99
#[cfg(all(feature = "cluster-async", not(feature = "tokio-comp")))]
1010
use async_std::sync::RwLock;
11-
use derivative::Derivative;
1211
use std::collections::{hash_map::DefaultHasher, HashMap};
1312
use std::hash::{Hash, Hasher};
1413
use std::sync::atomic::AtomicBool;
@@ -21,11 +20,10 @@ use tracing::info;
2120
// Exponential backoff constants for retrying a slot refresh
2221
/// The default number of refresh topology retries in the same call
2322
pub const DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES: usize = 3;
24-
/// The default maximum interval between two retries of the same call for topology refresh
25-
pub const DEFAULT_REFRESH_SLOTS_RETRY_MAX_INTERVAL: Duration = Duration::from_secs(1);
26-
/// The default initial interval for retrying topology refresh
27-
pub const DEFAULT_REFRESH_SLOTS_RETRY_INITIAL_INTERVAL: Duration = Duration::from_millis(500);
28-
23+
/// The default base duration for retrying topology refresh
24+
pub const DEFAULT_REFRESH_SLOTS_RETRY_BASE_DURATION_MILLIS: u64 = 500;
25+
/// The default base factor for retrying topology refresh
26+
pub const DEFAULT_REFRESH_SLOTS_RETRY_BASE_FACTOR: f64 = 1.5;
2927
// Constants for the intervals between two independent consecutive refresh slots calls
3028
/// The default wait duration between two consecutive refresh slots calls
3129
#[cfg(feature = "cluster-async")]
@@ -58,17 +56,21 @@ impl SlotRefreshState {
5856
}
5957
}
6058

61-
#[derive(Derivative)]
62-
#[derivative(PartialEq, Eq)]
6359
#[derive(Debug)]
6460
pub(crate) struct TopologyView {
6561
pub(crate) hash_value: TopologyHash,
66-
#[derivative(PartialEq = "ignore")]
6762
pub(crate) nodes_count: u16,
68-
#[derivative(PartialEq = "ignore")]
6963
slots_and_count: (u16, Vec<Slot>),
7064
}
7165

66+
impl PartialEq for TopologyView {
67+
fn eq(&self, other: &Self) -> bool {
68+
self.hash_value == other.hash_value
69+
}
70+
}
71+
72+
impl Eq for TopologyView {}
73+
7274
pub(crate) fn slot(key: &[u8]) -> u16 {
7375
crc16::State::<crc16::XMODEM>::calculate(key) % SLOT_SIZE
7476
}

glide-core/src/client/reconnecting_connection.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use telemetrylib::Telemetry;
1717
use tokio::sync::{mpsc, Notify};
1818
use tokio::task;
1919
use tokio::time::timeout;
20-
use tokio_retry::Retry;
20+
use tokio_retry2::{Retry, RetryError};
2121

2222
use super::{run_with_timeout, DEFAULT_CONNECTION_ATTEMPT_TIMEOUT};
2323

@@ -121,7 +121,11 @@ async fn create_connection(
121121
TokioDisconnectNotifier::new(),
122122
)),
123123
};
124-
let action = || get_multiplexed_connection(client, &connection_options);
124+
let action = || async {
125+
get_multiplexed_connection(client, &connection_options)
126+
.await
127+
.map_err(RetryError::transient)
128+
};
125129

126130
match Retry::spawn(retry_strategy.get_iterator(), action).await {
127131
Ok(connection) => {

glide-core/src/retry_strategies.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44
use crate::client::ConnectionRetryStrategy;
55
use std::time::Duration;
6-
use tokio_retry::strategy::{jitter, ExponentialBackoff};
6+
use tokio_retry2::strategy::{jitter_range, ExponentialBackoff};
77

88
#[derive(Clone, Debug)]
99
pub(super) struct RetryStrategy {
@@ -27,7 +27,7 @@ impl RetryStrategy {
2727
pub(super) fn get_iterator(&self) -> impl Iterator<Item = Duration> {
2828
ExponentialBackoff::from_millis(self.exponent_base as u64)
2929
.factor(self.factor as u64)
30-
.map(jitter)
30+
.map(jitter_range(0.8, 1.2))
3131
.take(self.number_of_retries as usize)
3232
}
3333
}
@@ -78,23 +78,39 @@ mod tests {
7878
let mut counter = 0;
7979
for duration in intervals {
8080
counter += 1;
81-
assert!(duration.as_millis() <= interval_duration as u128);
81+
let upper_limit = (interval_duration as f32 * 1.2) as u128;
82+
let lower_limit = (interval_duration as f32 * 0.8) as u128;
83+
assert!(
84+
lower_limit <= duration.as_millis() || duration.as_millis() <= upper_limit,
85+
"{:?}ms <= {:?}ms <= {:?}ms",
86+
lower_limit,
87+
duration.as_millis(),
88+
upper_limit
89+
);
8290
}
8391
assert_eq!(counter, retries);
8492
}
8593

8694
#[test]
8795
fn test_exponential_backoff_with_jitter() {
88-
let retries = 3;
89-
let base = 10;
90-
let factor = 5;
96+
let retries = 5;
97+
let base = 2;
98+
let factor = 100;
9199
let intervals = get_exponential_backoff(base, factor, retries).get_iterator();
92100

93101
let mut counter = 0;
94102
for duration in intervals {
95103
counter += 1;
96104
let unjittered_duration = factor * (base.pow(counter));
97-
assert!(duration.as_millis() <= unjittered_duration as u128);
105+
let upper_limit = (unjittered_duration as f32 * 1.2) as u128;
106+
let lower_limit = (unjittered_duration as f32 * 0.8) as u128;
107+
assert!(
108+
lower_limit <= duration.as_millis() || duration.as_millis() <= upper_limit,
109+
"{:?}ms <= {:?}ms <= {:?}ms",
110+
lower_limit,
111+
duration.as_millis(),
112+
upper_limit
113+
);
98114
}
99115

100116
assert_eq!(counter, retries);

0 commit comments

Comments
 (0)