Skip to content

Commit e6673a9

Browse files
committed
Move metadata internal API to use List instead of Set as replicas store
In almost all use cases replicas are getting converted into a List. To make performance better make these API provide List instead.
1 parent 7b6f2d4 commit e6673a9

File tree

12 files changed

+136
-85
lines changed

12 files changed

+136
-85
lines changed

driver-core/src/main/java/com/datastax/driver/core/Metadata.java

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
package com.datastax.driver.core;
2323

2424
import com.google.common.annotations.Beta;
25+
import com.google.common.collect.ImmutableList;
2526
import com.google.common.collect.ImmutableMap;
2627
import com.google.common.collect.ImmutableSet;
2728
import com.google.common.collect.Maps;
@@ -34,6 +35,7 @@
3435
import java.util.Collections;
3536
import java.util.HashMap;
3637
import java.util.HashSet;
38+
import java.util.LinkedHashSet;
3739
import java.util.List;
3840
import java.util.Map;
3941
import java.util.Set;
@@ -51,6 +53,8 @@
5153
public class Metadata {
5254

5355
private static final Logger logger = LoggerFactory.getLogger(Metadata.class);
56+
private static final ImmutableList<Host> EMPTY_LIST =
57+
ImmutableList.copyOf(Collections.emptyList());
5458

5559
final Cluster.Manager cluster;
5660
volatile String clusterName;
@@ -557,14 +561,24 @@ public Set<TokenRange> getTokenRanges(String keyspace, Host host) {
557561
@Beta
558562
public Set<Host> getReplicas(
559563
String keyspace, String table, Token.Factory partitioner, ByteBuffer partitionKey) {
564+
return new LinkedHashSet<>(this.getReplicasList(keyspace, table, partitioner, partitionKey));
565+
}
566+
567+
/**
568+
* Same as {@link Metadata#getReplicas(String, String, Token.Factory, ByteBuffer)}. But returns
569+
* List, use it for better performance.
570+
*/
571+
@Beta
572+
public List<Host> getReplicasList(
573+
String keyspace, String table, Token.Factory partitioner, ByteBuffer partitionKey) {
560574
keyspace = handleId(keyspace);
561575
table = handleId(table);
562576
TokenMap current = tokenMap;
563577
if (partitioner == null && current != null) {
564578
partitioner = current.factory;
565579
}
566580
if (partitioner == null) {
567-
return Collections.emptySet();
581+
return Collections.emptyList();
568582
}
569583
Token token = partitioner.hash(partitionKey);
570584

@@ -575,14 +589,13 @@ public Set<Host> getReplicas(
575589
assert (token instanceof Token.TokenLong64);
576590
return tabletMap.getReplicas(keyspace, table, (long) token.getValue());
577591
} else {
578-
return Collections.emptySet();
592+
return Collections.emptyList();
579593
}
580594
}
581595

582596
// TokenMap:
583-
if (current == null) return Collections.<Host>emptySet();
584-
Set<Host> hosts = current.getReplicas(keyspace, token);
585-
return hosts == null ? Collections.<Host>emptySet() : hosts;
597+
if (current == null) return Collections.<Host>emptyList();
598+
return current.getReplicas(keyspace, token);
586599
}
587600

588601
/**
@@ -627,8 +640,34 @@ public Set<Host> getReplicas(String keyspace, TokenRange range) {
627640
if (current == null) {
628641
return Collections.emptySet();
629642
} else {
630-
Set<Host> hosts = current.getReplicas(keyspace, range.getEnd());
631-
return hosts == null ? Collections.<Host>emptySet() : hosts;
643+
return new HashSet<>(current.getReplicas(keyspace, range.getEnd()));
644+
}
645+
}
646+
647+
/**
648+
* Returns the list of hosts that are replica for a given token range.
649+
*
650+
* <p>Note that it is assumed that the input range does not overlap across multiple host ranges.
651+
* If the range extends over multiple hosts, it only returns the replicas for those hosts that are
652+
* replicas for the last token of the range. This behavior may change in a future release, see <a
653+
* href="https://datastax-oss.atlassian.net/browse/JAVA-1355">JAVA-1355</a>.
654+
*
655+
* <p>Also note that this information is refreshed asynchronously by the control connection, when
656+
* schema or ring topology changes. It might occasionally be stale (or even empty).
657+
*
658+
* @param keyspace the name of the keyspace to get replicas for.
659+
* @param range the token range.
660+
* @return the list of replicas for {@code range} as known by the driver. Note that the result
661+
* might be stale or empty if metadata was explicitly disabled with {@link
662+
* QueryOptions#setMetadataEnabled(boolean)}.
663+
*/
664+
public List<Host> getReplicasList(String keyspace, TokenRange range) {
665+
keyspace = handleId(keyspace);
666+
TokenMap current = tokenMap;
667+
if (current == null) {
668+
return Collections.emptyList();
669+
} else {
670+
return current.getReplicas(keyspace, range.getEnd());
632671
}
633672
}
634673

@@ -985,7 +1024,7 @@ private static class TokenMap {
9851024

9861025
private final Token.Factory factory;
9871026
private final Map<Host, Set<Token>> primaryToTokens;
988-
private final Map<String, Map<Token, Set<Host>>> tokenToHostsByKeyspace;
1027+
private final Map<String, Map<Token, ImmutableList<Host>>> tokenToHostsByKeyspace;
9891028
private final Map<String, Map<Host, Set<TokenRange>>> hostsToRangesByKeyspace;
9901029
private final List<Token> ring;
9911030
private final Set<TokenRange> tokenRanges;
@@ -997,7 +1036,7 @@ private TokenMap(
9971036
Set<TokenRange> tokenRanges,
9981037
Map<Token, Host> tokenToPrimary,
9991038
Map<Host, Set<Token>> primaryToTokens,
1000-
Map<String, Map<Token, Set<Host>>> tokenToHostsByKeyspace,
1039+
Map<String, Map<Token, ImmutableList<Host>>> tokenToHostsByKeyspace,
10011040
Map<String, Map<Host, Set<TokenRange>>> hostsToRangesByKeyspace) {
10021041
this.factory = factory;
10031042
this.ring = ring;
@@ -1042,15 +1081,13 @@ private static TokenMap build(
10421081
Set<TokenRange> tokenRanges,
10431082
Map<Token, Host> tokenToPrimary) {
10441083
Set<Host> hosts = allTokens.keySet();
1045-
Map<String, Map<Token, Set<Host>>> tokenToHosts =
1046-
new HashMap<String, Map<Token, Set<Host>>>();
1047-
Map<ReplicationStrategy, Map<Token, Set<Host>>> replStrategyToHosts =
1048-
new HashMap<ReplicationStrategy, Map<Token, Set<Host>>>();
1049-
Map<String, Map<Host, Set<TokenRange>>> hostsToRanges =
1050-
new HashMap<String, Map<Host, Set<TokenRange>>>();
1084+
Map<String, Map<Token, ImmutableList<Host>>> tokenToHosts = new HashMap<>();
1085+
Map<ReplicationStrategy, Map<Token, ImmutableList<Host>>> replStrategyToHosts =
1086+
new HashMap<>();
1087+
Map<String, Map<Host, Set<TokenRange>>> hostsToRanges = new HashMap<>();
10511088
for (KeyspaceMetadata keyspace : keyspaces) {
10521089
ReplicationStrategy strategy = keyspace.replicationStrategy();
1053-
Map<Token, Set<Host>> ksTokens = replStrategyToHosts.get(strategy);
1090+
Map<Token, ImmutableList<Host>> ksTokens = replStrategyToHosts.get(strategy);
10541091
if (ksTokens == null) {
10551092
ksTokens =
10561093
(strategy == null)
@@ -1077,14 +1114,14 @@ private static TokenMap build(
10771114
factory, ring, tokenRanges, tokenToPrimary, allTokens, tokenToHosts, hostsToRanges);
10781115
}
10791116

1080-
private Set<Host> getReplicas(String keyspace, Token token) {
1117+
private ImmutableList<Host> getReplicas(String keyspace, Token token) {
10811118

1082-
Map<Token, Set<Host>> tokenToHosts = tokenToHostsByKeyspace.get(keyspace);
1083-
if (tokenToHosts == null) return Collections.emptySet();
1119+
Map<Token, ImmutableList<Host>> tokenToHosts = tokenToHostsByKeyspace.get(keyspace);
1120+
if (tokenToHosts == null) return EMPTY_LIST;
10841121

10851122
// If the token happens to be one of the "primary" tokens, get result directly
1086-
Set<Host> hosts = tokenToHosts.get(token);
1087-
if (hosts != null) return hosts;
1123+
ImmutableList<Host> hosts = tokenToHosts.get(token);
1124+
if (hosts != null) return ImmutableList.copyOf(hosts);
10881125

10891126
// Otherwise, find closest "primary" token on the ring
10901127
int i = Collections.binarySearch(ring, token);
@@ -1096,10 +1133,10 @@ private Set<Host> getReplicas(String keyspace, Token token) {
10961133
return tokenToHosts.get(ring.get(i));
10971134
}
10981135

1099-
private static Map<Token, Set<Host>> makeNonReplicatedMap(Map<Token, Host> input) {
1100-
Map<Token, Set<Host>> output = new HashMap<Token, Set<Host>>(input.size());
1136+
private static Map<Token, ImmutableList<Host>> makeNonReplicatedMap(Map<Token, Host> input) {
1137+
Map<Token, ImmutableList<Host>> output = new HashMap<>(input.size());
11011138
for (Map.Entry<Token, Host> entry : input.entrySet())
1102-
output.put(entry.getKey(), ImmutableSet.of(entry.getValue()));
1139+
output.put(entry.getKey(), ImmutableList.of(entry.getValue()));
11031140
return output;
11041141
}
11051142

@@ -1119,11 +1156,11 @@ private static Set<TokenRange> makeTokenRanges(List<Token> ring, Token.Factory f
11191156
}
11201157

11211158
private static Map<Host, Set<TokenRange>> computeHostsToRangesMap(
1122-
Set<TokenRange> tokenRanges, Map<Token, Set<Host>> ksTokens, int hostCount) {
1159+
Set<TokenRange> tokenRanges, Map<Token, ImmutableList<Host>> ksTokens, int hostCount) {
11231160
Map<Host, ImmutableSet.Builder<TokenRange>> builders =
11241161
Maps.newHashMapWithExpectedSize(hostCount);
11251162
for (TokenRange range : tokenRanges) {
1126-
Set<Host> replicas = ksTokens.get(range.getEnd());
1163+
List<Host> replicas = ksTokens.get(range.getEnd());
11271164
for (Host host : replicas) {
11281165
ImmutableSet.Builder<TokenRange> hostRanges = builders.get(host);
11291166
if (hostRanges == null) {

driver-core/src/main/java/com/datastax/driver/core/ReplicationStategy.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package com.datastax.driver.core;
1717

18-
import com.google.common.collect.ImmutableSet;
18+
import com.google.common.collect.ImmutableList;
1919
import com.google.common.collect.Maps;
2020
import com.google.common.collect.Sets;
2121
import java.util.HashMap;
@@ -67,7 +67,7 @@ static ReplicationStrategy create(Map<String, String> replicationOptions) {
6767
}
6868
}
6969

70-
abstract Map<Token, Set<Host>> computeTokenToReplicaMap(
70+
abstract Map<Token, ImmutableList<Host>> computeTokenToReplicaMap(
7171
String keyspaceName, Map<Token, Host> tokenToPrimary, List<Token> ring);
7272

7373
private static Token getTokenWrapping(int i, List<Token> ring) {
@@ -83,18 +83,18 @@ private SimpleStrategy(ReplicationFactor replicationFactor) {
8383
}
8484

8585
@Override
86-
Map<Token, Set<Host>> computeTokenToReplicaMap(
86+
Map<Token, ImmutableList<Host>> computeTokenToReplicaMap(
8787
String keyspaceName, Map<Token, Host> tokenToPrimary, List<Token> ring) {
8888

8989
int rf = Math.min(replicationFactor.fullReplicas(), ring.size());
9090

91-
Map<Token, Set<Host>> replicaMap = new HashMap<Token, Set<Host>>(tokenToPrimary.size());
91+
Map<Token, ImmutableList<Host>> replicaMap = new HashMap<>(tokenToPrimary.size());
9292
for (int i = 0; i < ring.size(); i++) {
9393
// Consecutive sections of the ring can assigned to the same host
94-
Set<Host> replicas = new LinkedHashSet<Host>();
94+
Set<Host> replicas = new LinkedHashSet<>();
9595
for (int j = 0; j < ring.size() && replicas.size() < rf; j++)
9696
replicas.add(tokenToPrimary.get(getTokenWrapping(i + j, ring)));
97-
replicaMap.put(ring.get(i), ImmutableSet.copyOf(replicas));
97+
replicaMap.put(ring.get(i), ImmutableList.copyOf(replicas));
9898
}
9999
return replicaMap;
100100
}
@@ -125,7 +125,7 @@ private NetworkTopologyStrategy(Map<String, ReplicationFactor> replicationFactor
125125
}
126126

127127
@Override
128-
Map<Token, Set<Host>> computeTokenToReplicaMap(
128+
Map<Token, ImmutableList<Host>> computeTokenToReplicaMap(
129129
String keyspaceName, Map<Token, Host> tokenToPrimary, List<Token> ring) {
130130

131131
logger.debug("Computing token to replica map for keyspace: {}.", keyspaceName);
@@ -135,7 +135,7 @@ Map<Token, Set<Host>> computeTokenToReplicaMap(
135135

136136
// This is essentially a copy of org.apache.cassandra.locator.NetworkTopologyStrategy
137137
Map<String, Set<String>> racks = getRacksInDcs(tokenToPrimary.values());
138-
Map<Token, Set<Host>> replicaMap = new HashMap<Token, Set<Host>>(tokenToPrimary.size());
138+
Map<Token, ImmutableList<Host>> replicaMap = new HashMap<>(tokenToPrimary.size());
139139
Map<String, Integer> dcHostCount = Maps.newHashMapWithExpectedSize(replicationFactors.size());
140140
Set<String> warnedDcs = Sets.newHashSetWithExpectedSize(replicationFactors.size());
141141
// find maximum number of nodes in each DC
@@ -157,7 +157,7 @@ Map<Token, Set<Host>> computeTokenToReplicaMap(
157157
}
158158

159159
// Preserve order - primary replica will be first
160-
Set<Host> replicas = new LinkedHashSet<Host>();
160+
Set<Host> replicas = new LinkedHashSet<>();
161161
for (int j = 0; j < ring.size() && !allDone(allDcReplicas, dcHostCount); j++) {
162162
Host h = tokenToPrimary.get(getTokenWrapping(i + j, ring));
163163
String dc = h.getDatacenter();
@@ -214,7 +214,7 @@ Map<Token, Set<Host>> computeTokenToReplicaMap(
214214
}
215215
}
216216

217-
replicaMap.put(ring.get(i), ImmutableSet.copyOf(replicas));
217+
replicaMap.put(ring.get(i), ImmutableList.copyOf(replicas));
218218
}
219219

220220
long duration = System.currentTimeMillis() - startTime;

driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,11 @@ private Iterator<Host> getReplicas(
121121
tableName = defs.getTable(0);
122122
}
123123

124-
final Set<Host> replicas =
124+
final List<Host> replicas =
125125
manager
126126
.cluster
127127
.getMetadata()
128-
.getReplicas(Metadata.quote(keyspace), tableName, partitioner, partitionKey);
128+
.getReplicasList(Metadata.quote(keyspace), tableName, partitioner, partitionKey);
129129

130130
// replicas are stored in the right order starting with the primary replica
131131
return replicas.iterator();

driver-core/src/main/java/com/datastax/driver/core/SessionManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ else if (fetchSize != Integer.MAX_VALUE)
659659
boolean skipMetadata =
660660
protocolVersion != ProtocolVersion.V1
661661
&& bs.statement.getPreparedId().resultSetMetadata.variables != null;
662+
662663
Requests.QueryProtocolOptions options =
663664
new Requests.QueryProtocolOptions(
664665
Message.Request.Type.EXECUTE,

0 commit comments

Comments
 (0)