Skip to content

Commit 41de670

Browse files
authored
Added new routing option: SingleNodeRoutingInfo::RandomPrimary (#194)
* Added new routing option: `SingleNodeRoutingInfo::RandomPrimary` Signed-off-by: Eran Ifrah <eifrah@amazon.com>
1 parent 8e89ad8 commit 41de670

File tree

3 files changed

+56
-11
lines changed

3 files changed

+56
-11
lines changed

redis/src/cluster.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ use std::str::FromStr;
4141
use std::thread;
4242
use std::time::Duration;
4343

44-
use rand::{seq::IteratorRandom, thread_rng, Rng};
44+
use rand::{seq::IteratorRandom, thread_rng};
4545

4646
use crate::cluster_pipeline::UNROUTABLE_ERROR;
4747
use crate::cluster_routing::{
48-
MultipleNodeRoutingInfo, ResponsePolicy, Routable, SingleNodeRoutingInfo, SlotAddr,
48+
MultipleNodeRoutingInfo, ResponsePolicy, Routable, SingleNodeRoutingInfo,
4949
};
5050
use crate::cluster_slotmap::SlotMap;
51-
use crate::cluster_topology::{parse_and_count_slots, SLOT_SIZE};
51+
use crate::cluster_topology::parse_and_count_slots;
5252
use crate::cmd::{cmd, Cmd};
5353
use crate::connection::{
5454
connect, Connection, ConnectionAddr, ConnectionInfo, ConnectionLike, RedisConnectionInfo,
@@ -459,12 +459,9 @@ where
459459
};
460460

461461
match RoutingInfo::for_routable(cmd) {
462-
Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::Random)) => {
463-
let mut rng = thread_rng();
464-
Ok(addr_for_slot(Route::new(
465-
rng.gen_range(0..SLOT_SIZE),
466-
SlotAddr::Master,
467-
))?)
462+
Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::Random))
463+
| Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::RandomPrimary)) => {
464+
Ok(addr_for_slot(Route::new_random_primary())?)
468465
}
469466
Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::SpecificNode(route))) => {
470467
Ok(addr_for_slot(route)?)
@@ -730,6 +727,9 @@ where
730727
SingleNodeRoutingInfo::SpecificNode(route) => {
731728
self.get_connection(&mut connections, route)?
732729
}
730+
SingleNodeRoutingInfo::RandomPrimary => {
731+
self.get_connection(&mut connections, &Route::new_random_primary())?
732+
}
733733
SingleNodeRoutingInfo::ByAddress { host, port } => {
734734
let address = format!("{host}:{port}");
735735
let conn = self.get_connection_by_addr(&mut connections, &address)?;

redis/src/cluster_async/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,9 @@ impl<C> From<SingleNodeRoutingInfo> for InternalSingleNodeRouting<C> {
588588
SingleNodeRoutingInfo::SpecificNode(route) => {
589589
InternalSingleNodeRouting::SpecificNode(route)
590590
}
591+
SingleNodeRoutingInfo::RandomPrimary => {
592+
InternalSingleNodeRouting::SpecificNode(Route::new_random_primary())
593+
}
591594
SingleNodeRoutingInfo::ByAddress { host, port } => {
592595
InternalSingleNodeRouting::ByAddress(format!("{host}:{port}"))
593596
}
@@ -620,6 +623,9 @@ fn route_for_pipeline(pipeline: &crate::Pipeline) -> RedisResult<Option<Route>>
620623
Some(cluster_routing::RoutingInfo::SingleNode(
621624
SingleNodeRoutingInfo::SpecificNode(route),
622625
)) => Some(route),
626+
Some(cluster_routing::RoutingInfo::SingleNode(
627+
SingleNodeRoutingInfo::RandomPrimary,
628+
)) => Some(Route::new_random_primary()),
623629
Some(cluster_routing::RoutingInfo::MultiNode(_)) => None,
624630
Some(cluster_routing::RoutingInfo::SingleNode(SingleNodeRoutingInfo::ByAddress {
625631
..

redis/src/cluster_routing.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rand::Rng;
12
use std::cmp::min;
23
use std::collections::HashMap;
34

@@ -66,6 +67,8 @@ pub enum RoutingInfo {
6667
pub enum SingleNodeRoutingInfo {
6768
/// Route to any node at random
6869
Random,
70+
/// Route to any *primary* node
71+
RandomPrimary,
6972
/// Route to the node that matches the [Route]
7073
SpecificNode(Route),
7174
/// Route to the node with the given address.
@@ -610,7 +613,13 @@ impl RoutingInfo {
610613
.and_then(|x| std::str::from_utf8(x).ok())
611614
.and_then(|x| x.parse::<u64>().ok())?;
612615
if key_count == 0 {
613-
Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::Random))
616+
if is_readonly_cmd(cmd) {
617+
Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::Random))
618+
} else {
619+
Some(RoutingInfo::SingleNode(
620+
SingleNodeRoutingInfo::RandomPrimary,
621+
))
622+
}
614623
} else {
615624
r.arg_idx(3).map(|key| RoutingInfo::for_key(cmd, key))
616625
}
@@ -949,6 +958,17 @@ impl Route {
949958
pub fn slot_addr(&self) -> SlotAddr {
950959
self.1
951960
}
961+
962+
/// Returns a new Route for a random primary node
963+
pub fn new_random_primary() -> Self {
964+
Self::new(random_slot(), SlotAddr::Master)
965+
}
966+
}
967+
968+
/// Choose a random slot from `0..SLOT_SIZE` (excluding)
969+
fn random_slot() -> u16 {
970+
let mut rng = rand::thread_rng();
971+
rng.gen_range(0..crate::cluster_topology::SLOT_SIZE)
952972
}
953973

954974
#[cfg(test)]
@@ -1096,12 +1116,31 @@ mod tests {
10961116
cmd("EVAL").arg(r#"redis.call("PING");"#).arg(0),
10971117
cmd("EVALSHA").arg(r#"redis.call("PING");"#).arg(0),
10981118
] {
1119+
// EVAL / EVALSHA are expected to be routed to a RandomPrimary
10991120
assert_eq!(
11001121
RoutingInfo::for_routable(cmd),
1101-
Some(RoutingInfo::SingleNode(SingleNodeRoutingInfo::Random))
1122+
Some(RoutingInfo::SingleNode(
1123+
SingleNodeRoutingInfo::RandomPrimary
1124+
))
11021125
);
11031126
}
11041127

1128+
// FCALL (with 0 keys) is expected to be routed to a random primary node
1129+
assert_eq!(
1130+
RoutingInfo::for_routable(cmd("FCALL").arg("foo").arg(0)),
1131+
Some(RoutingInfo::SingleNode(
1132+
SingleNodeRoutingInfo::RandomPrimary
1133+
))
1134+
);
1135+
1136+
// While FCALL with N keys is expected to be routed to a specific node
1137+
assert_eq!(
1138+
RoutingInfo::for_routable(cmd("FCALL").arg("foo").arg(1).arg("mykey")),
1139+
Some(RoutingInfo::SingleNode(
1140+
SingleNodeRoutingInfo::SpecificNode(Route::new(slot(b"mykey"), SlotAddr::Master))
1141+
))
1142+
);
1143+
11051144
for (cmd, expected) in [
11061145
(
11071146
cmd("EVAL")

0 commit comments

Comments
 (0)