Skip to content

Commit 374c7bb

Browse files
authored
Refactored the connections map from HashMap to DashMap (#180)
* Refactored the connections map from HashMap to DashMap to minimize write lock contention on the connection container * Replaced the connection map key from ArcStr to String
1 parent e6635ac commit 374c7bb

File tree

4 files changed

+101
-78
lines changed

4 files changed

+101
-78
lines changed

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

redis/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ crc16 = { version = "0.4", optional = true }
6363
rand = { version = "0.8", optional = true }
6464
derivative = { version = "2.2.0", optional = true }
6565

66+
# Only needed for async cluster
67+
dashmap = { version = "6.0", optional = true }
68+
6669
# Only needed for async_std support
6770
async-std = { version = "1.8.0", optional = true }
6871
async-trait = { version = "0.1.24", optional = true }
@@ -124,7 +127,7 @@ tokio-native-tls-comp = ["tokio-comp", "tls-native-tls", "tokio-native-tls"]
124127
tokio-rustls-comp = ["tokio-comp", "tls-rustls", "tokio-rustls"]
125128
connection-manager = ["futures", "aio", "tokio-retry"]
126129
streams = []
127-
cluster-async = ["cluster", "futures", "futures-util"]
130+
cluster-async = ["cluster", "futures", "futures-util", "dashmap"]
128131
keep-alive = ["socket2"]
129132
sentinel = ["rand"]
130133
tcp_nodelay = []

redis/src/cluster_async/connections_container.rs

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
use crate::cluster_async::ConnectionFuture;
2-
use arcstr::ArcStr;
3-
use futures::FutureExt;
4-
use rand::seq::IteratorRandom;
5-
use std::collections::HashMap;
6-
use std::net::IpAddr;
7-
82
use crate::cluster_routing::{Route, SlotAddr};
93
use crate::cluster_slotmap::{ReadFromReplicaStrategy, SlotMap, SlotMapValue};
104
use crate::cluster_topology::TopologyHash;
5+
use dashmap::DashMap;
6+
use futures::FutureExt;
7+
use rand::seq::IteratorRandom;
8+
use std::net::IpAddr;
119

1210
/// A struct that encapsulates a network connection along with its associated IP address.
1311
#[derive(Clone, Eq, PartialEq, Debug)]
@@ -86,11 +84,12 @@ pub(crate) enum ConnectionType {
8684
PreferManagement,
8785
}
8886

89-
pub(crate) struct ConnectionsMap<Connection>(pub(crate) HashMap<ArcStr, ClusterNode<Connection>>);
87+
pub(crate) struct ConnectionsMap<Connection>(pub(crate) DashMap<String, ClusterNode<Connection>>);
9088

9189
impl<Connection> std::fmt::Display for ConnectionsMap<Connection> {
9290
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93-
for (address, node) in self.0.iter() {
91+
for item in self.0.iter() {
92+
let (address, node) = (item.key(), item.value());
9493
match node.user_connection.ip {
9594
Some(ip) => writeln!(f, "{address} - {ip}")?,
9695
None => writeln!(f, "{address}")?,
@@ -101,7 +100,7 @@ impl<Connection> std::fmt::Display for ConnectionsMap<Connection> {
101100
}
102101

103102
pub(crate) struct ConnectionsContainer<Connection> {
104-
connection_map: HashMap<ArcStr, ClusterNode<Connection>>,
103+
connection_map: DashMap<String, ClusterNode<Connection>>,
105104
pub(crate) slot_map: SlotMap,
106105
read_from_replica_strategy: ReadFromReplicaStrategy,
107106
topology_hash: TopologyHash,
@@ -118,7 +117,7 @@ impl<Connection> Default for ConnectionsContainer<Connection> {
118117
}
119118
}
120119

121-
pub(crate) type ConnectionAndAddress<Connection> = (ArcStr, Connection);
120+
pub(crate) type ConnectionAndAddress<Connection> = (String, Connection);
122121

123122
impl<Connection> ConnectionsContainer<Connection>
124123
where
@@ -139,7 +138,7 @@ where
139138
}
140139

141140
/// Returns true if the address represents a known primary node.
142-
pub(crate) fn is_primary(&self, address: &ArcStr) -> bool {
141+
pub(crate) fn is_primary(&self, address: &String) -> bool {
143142
self.connection_for_address(address).is_some()
144143
&& self
145144
.slot_map
@@ -213,9 +212,10 @@ where
213212
pub(crate) fn all_node_connections(
214213
&self,
215214
) -> impl Iterator<Item = ConnectionAndAddress<Connection>> + '_ {
216-
self.connection_map
217-
.iter()
218-
.map(move |(address, node)| (address.clone(), node.user_connection.conn.clone()))
215+
self.connection_map.iter().map(move |item| {
216+
let (node, address) = (item.key(), item.value());
217+
(node.clone(), address.user_connection.conn.clone())
218+
})
219219
}
220220

221221
pub(crate) fn all_primary_connections(
@@ -228,16 +228,19 @@ where
228228
}
229229

230230
pub(crate) fn node_for_address(&self, address: &str) -> Option<ClusterNode<Connection>> {
231-
self.connection_map.get(address).cloned()
231+
self.connection_map
232+
.get(address)
233+
.map(|item| item.value().clone())
232234
}
233235

234236
pub(crate) fn connection_for_address(
235237
&self,
236238
address: &str,
237239
) -> Option<ConnectionAndAddress<Connection>> {
238-
self.connection_map
239-
.get_key_value(address)
240-
.map(|(address, conn)| (address.clone(), conn.user_connection.conn.clone()))
240+
self.connection_map.get(address).map(|item| {
241+
let (address, conn) = (item.key(), item.value());
242+
(address.clone(), conn.user_connection.conn.clone())
243+
})
241244
}
242245

243246
pub(crate) fn random_connections(
@@ -249,24 +252,27 @@ where
249252
.iter()
250253
.choose_multiple(&mut rand::thread_rng(), amount)
251254
.into_iter()
252-
.map(move |(address, node)| {
255+
.map(move |item| {
256+
let (address, node) = (item.key(), item.value());
253257
let conn = node.get_connection(&conn_type);
254258
(address.clone(), conn)
255259
})
256260
}
257261

258262
pub(crate) fn replace_or_add_connection_for_address(
259-
&mut self,
260-
address: impl Into<ArcStr>,
263+
&self,
264+
address: impl Into<String>,
261265
node: ClusterNode<Connection>,
262-
) -> ArcStr {
266+
) -> String {
263267
let address = address.into();
264268
self.connection_map.insert(address.clone(), node);
265269
address
266270
}
267271

268-
pub(crate) fn remove_node(&mut self, address: &ArcStr) -> Option<ClusterNode<Connection>> {
269-
self.connection_map.remove(address)
272+
pub(crate) fn remove_node(&self, address: &String) -> Option<ClusterNode<Connection>> {
273+
self.connection_map
274+
.remove(address)
275+
.map(|(_key, value)| value)
270276
}
271277

272278
pub(crate) fn len(&self) -> usize {
@@ -302,13 +308,13 @@ mod tests {
302308
}
303309
}
304310
}
305-
fn remove_nodes(container: &mut ConnectionsContainer<usize>, addresss: &[&str]) {
306-
for address in addresss {
311+
fn remove_nodes(container: &ConnectionsContainer<usize>, addresses: &[&str]) {
312+
for address in addresses {
307313
container.remove_node(&(*address).into());
308314
}
309315
}
310316

311-
fn remove_all_connections(container: &mut ConnectionsContainer<usize>) {
317+
fn remove_all_connections(container: &ConnectionsContainer<usize>) {
312318
remove_nodes(
313319
container,
314320
&[
@@ -366,7 +372,7 @@ mod tests {
366372
],
367373
ReadFromReplicaStrategy::AlwaysFromPrimary, // this argument shouldn't matter, since we overload the RFR strategy.
368374
);
369-
let mut connection_map = HashMap::new();
375+
let connection_map = DashMap::new();
370376
connection_map.insert(
371377
"primary1".into(),
372378
create_cluster_node(1, use_management_connections),
@@ -514,7 +520,7 @@ mod tests {
514520

515521
#[test]
516522
fn get_replica_connection_for_replica_route_if_some_but_not_all_replicas_were_removed() {
517-
let mut container = create_container();
523+
let container = create_container();
518524
container.remove_node(&"replica3-2".into());
519525

520526
assert_eq!(
@@ -540,8 +546,8 @@ mod tests {
540546

541547
#[test]
542548
fn get_primary_connection_for_replica_route_if_all_replicas_were_removed() {
543-
let mut container = create_container();
544-
remove_nodes(&mut container, &["replica2-1", "replica3-1", "replica3-2"]);
549+
let container = create_container();
550+
remove_nodes(&container, &["replica2-1", "replica3-1", "replica3-2"]);
545551

546552
assert_eq!(
547553
2,
@@ -593,15 +599,15 @@ mod tests {
593599

594600
#[test]
595601
fn get_connection_by_address_returns_none_if_connection_was_removed() {
596-
let mut container = create_container();
602+
let container = create_container();
597603
container.remove_node(&"primary1".into());
598604

599605
assert!(container.connection_for_address("primary1").is_none());
600606
}
601607

602608
#[test]
603609
fn get_connection_by_address_returns_added_connection() {
604-
let mut container = create_container();
610+
let container = create_container();
605611
let address = container.replace_or_add_connection_for_address(
606612
"foobar",
607613
ClusterNode::new_only_with_user_conn(4),
@@ -630,8 +636,8 @@ mod tests {
630636

631637
#[test]
632638
fn get_random_connections_returns_none_if_all_connections_were_removed() {
633-
let mut container = create_container();
634-
remove_all_connections(&mut container);
639+
let container = create_container();
640+
remove_all_connections(&container);
635641

636642
assert_eq!(
637643
0,
@@ -643,8 +649,8 @@ mod tests {
643649

644650
#[test]
645651
fn get_random_connections_returns_added_connection() {
646-
let mut container = create_container();
647-
remove_all_connections(&mut container);
652+
let container = create_container();
653+
remove_all_connections(&container);
648654
let address = container.replace_or_add_connection_for_address(
649655
"foobar",
650656
ClusterNode::new_only_with_user_conn(4),
@@ -694,7 +700,7 @@ mod tests {
694700

695701
#[test]
696702
fn get_all_user_connections_returns_added_connection() {
697-
let mut container = create_container();
703+
let container = create_container();
698704
container.replace_or_add_connection_for_address(
699705
"foobar",
700706
ClusterNode::new_only_with_user_conn(4),
@@ -711,7 +717,7 @@ mod tests {
711717

712718
#[test]
713719
fn get_all_user_connections_does_not_return_removed_connection() {
714-
let mut container = create_container();
720+
let container = create_container();
715721
container.remove_node(&"primary1".into());
716722

717723
let mut connections: Vec<_> = container
@@ -738,7 +744,7 @@ mod tests {
738744

739745
#[test]
740746
fn get_all_primaries_does_not_return_removed_connection() {
741-
let mut container = create_container();
747+
let container = create_container();
742748
container.remove_node(&"primary1".into());
743749

744750
let mut connections: Vec<_> = container
@@ -752,7 +758,7 @@ mod tests {
752758

753759
#[test]
754760
fn len_is_adjusted_on_removals_and_additions() {
755-
let mut container = create_container();
761+
let container = create_container();
756762

757763
assert_eq!(container.len(), 6);
758764

@@ -769,7 +775,7 @@ mod tests {
769775
#[test]
770776
fn len_is_not_adjusted_on_removals_of_nonexisting_connections_or_additions_of_existing_connections(
771777
) {
772-
let mut container = create_container();
778+
let container = create_container();
773779

774780
assert_eq!(container.len(), 6);
775781

@@ -785,7 +791,7 @@ mod tests {
785791

786792
#[test]
787793
fn remove_node_returns_connection_if_it_exists() {
788-
let mut container = create_container();
794+
let container = create_container();
789795

790796
let connection = container.remove_node(&"primary1".into());
791797
assert_eq!(connection, Some(ClusterNode::new_only_with_user_conn(1)));
@@ -796,7 +802,7 @@ mod tests {
796802

797803
#[test]
798804
fn test_is_empty() {
799-
let mut container = create_container();
805+
let container = create_container();
800806

801807
assert!(!container.is_empty());
802808
container.remove_node(&"primary1".into());
@@ -829,7 +835,7 @@ mod tests {
829835

830836
#[test]
831837
fn is_primary_returns_false_for_removed_node() {
832-
let mut container = create_container();
838+
let container = create_container();
833839
let address = "primary1".into();
834840
container.remove_node(&address);
835841

0 commit comments

Comments
 (0)