Skip to content

Commit 54f9276

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 54f9276

File tree

12 files changed

+171
-85
lines changed

12 files changed

+171
-85
lines changed

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

Lines changed: 104 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,7 @@
5153
public class Metadata {
5254

5355
private static final Logger logger = LoggerFactory.getLogger(Metadata.class);
56+
private static final ImmutableList<Host> EMPTY_LIST = ImmutableList.of();
5457

5558
final Cluster.Manager cluster;
5659
volatile String clusterName;
@@ -553,18 +556,48 @@ public Set<TokenRange> getTokenRanges(String keyspace, Host host) {
553556
* @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
554557
* that the result might be stale or empty if metadata was explicitly disabled with {@link
555558
* QueryOptions#setMetadataEnabled(boolean)}.
559+
* @deprecated use {@link Metadata#getReplicasList(String, String, Token.Factory, ByteBuffer)}
556560
*/
561+
@Deprecated
557562
@Beta
558563
public Set<Host> getReplicas(
559564
String keyspace, String table, Token.Factory partitioner, ByteBuffer partitionKey) {
565+
return new LinkedHashSet<>(this.getReplicasList(keyspace, table, partitioner, partitionKey));
566+
}
567+
568+
/**
569+
* Extension of legacy method {@link Metadata#getReplicas(String, Token.Factory, ByteBuffer)}.
570+
* Tablets model requires knowledge of the table name to determine the replicas. This method will
571+
* first try to lookup replicas through known tablets metadata. It will default to TokenMap lookup
572+
* if either {@code null} was passed as table name or the tablet lookup is unsuccessful for any
573+
* other reason.
574+
*
575+
* <p>Returns the list of hosts that are replica for a given partition key. Partitioner can be
576+
* {@code null} and then a cluster-wide partitioner will be invoked.
577+
*
578+
* <p>Note that this information is refreshed asynchronously by the control connection, when
579+
* schema or ring topology changes. It might occasionally be stale (or even empty).
580+
*
581+
* @param keyspace the name of the keyspace to get replicas for.
582+
* @param table the name of the table to get replicas for. Necessary for distinction for tablets.
583+
* Unnecessary for regular TokenMap
584+
* @param partitioner the partitioner to use or @{code null} for cluster-wide partitioner.
585+
* @param partitionKey the partition key for which to find the list of replica.
586+
* @return the (immutable) list of replicas for {@code partitionKey} as known by the driver. Note
587+
* that the result might be stale or empty if metadata was explicitly disabled with {@link
588+
* QueryOptions#setMetadataEnabled(boolean)}.
589+
*/
590+
@Beta
591+
public List<Host> getReplicasList(
592+
String keyspace, String table, Token.Factory partitioner, ByteBuffer partitionKey) {
560593
keyspace = handleId(keyspace);
561594
table = handleId(table);
562595
TokenMap current = tokenMap;
563596
if (partitioner == null && current != null) {
564597
partitioner = current.factory;
565598
}
566599
if (partitioner == null) {
567-
return Collections.emptySet();
600+
return EMPTY_LIST;
568601
}
569602
Token token = partitioner.hash(partitionKey);
570603

@@ -575,14 +608,13 @@ public Set<Host> getReplicas(
575608
assert (token instanceof Token.TokenLong64);
576609
return tabletMap.getReplicas(keyspace, table, (long) token.getValue());
577610
} else {
578-
return Collections.emptySet();
611+
return EMPTY_LIST;
579612
}
580613
}
581614

582615
// 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;
616+
if (current == null) return EMPTY_LIST;
617+
return current.getReplicas(keyspace, token);
586618
}
587619

588620
/**
@@ -598,12 +630,33 @@ public Set<Host> getReplicas(
598630
* @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
599631
* that the result might be stale or empty if metadata was explicitly disabled with {@link
600632
* QueryOptions#setMetadataEnabled(boolean)}.
633+
* @deprecated use {@link Metadata#getReplicasList(String, Token.Factory, ByteBuffer)}
601634
*/
635+
@Deprecated
602636
public Set<Host> getReplicas(
603637
String keyspace, Token.Factory partitioner, ByteBuffer partitionKey) {
604638
return getReplicas(keyspace, null, partitioner, partitionKey);
605639
}
606640

641+
/**
642+
* Returns the immutable list of hosts that are replica for a given partition key. Partitioner can
643+
* be {@code null} and then a cluster-wide partitioner will be invoked.
644+
*
645+
* <p>Note that this information is refreshed asynchronously by the control connection, when
646+
* schema or ring topology changes. It might occasionally be stale (or even empty).
647+
*
648+
* @param keyspace the name of the keyspace to get replicas for.
649+
* @param partitioner the partitioner to use or @{code null} for cluster-wide partitioner.
650+
* @param partitionKey the partition key for which to find the set of replica.
651+
* @return the (immutable) list of replicas for {@code partitionKey} as known by the driver. Note
652+
* that the result might be stale or empty if metadata was explicitly disabled with {@link
653+
* QueryOptions#setMetadataEnabled(boolean)}.
654+
*/
655+
public List<Host> getReplicasList(
656+
String keyspace, Token.Factory partitioner, ByteBuffer partitionKey) {
657+
return getReplicasList(keyspace, null, partitioner, partitionKey);
658+
}
659+
607660
/**
608661
* Returns the set of hosts that are replica for a given token range.
609662
*
@@ -620,15 +673,42 @@ public Set<Host> getReplicas(
620673
* @return the (immutable) set of replicas for {@code range} as known by the driver. Note that the
621674
* result might be stale or empty if metadata was explicitly disabled with {@link
622675
* QueryOptions#setMetadataEnabled(boolean)}.
676+
* @deprecated use {@link Metadata#getReplicasList(String, TokenRange)}
623677
*/
624678
public Set<Host> getReplicas(String keyspace, TokenRange range) {
625679
keyspace = handleId(keyspace);
626680
TokenMap current = tokenMap;
627681
if (current == null) {
628682
return Collections.emptySet();
629683
} else {
630-
Set<Host> hosts = current.getReplicas(keyspace, range.getEnd());
631-
return hosts == null ? Collections.<Host>emptySet() : hosts;
684+
return new HashSet<>(current.getReplicas(keyspace, range.getEnd()));
685+
}
686+
}
687+
688+
/**
689+
* Returns the list of hosts that are replica for a given token range.
690+
*
691+
* <p>Note that it is assumed that the input range does not overlap across multiple host ranges.
692+
* If the range extends over multiple hosts, it only returns the replicas for those hosts that are
693+
* replicas for the last token of the range. This behavior may change in a future release, see <a
694+
* href="https://datastax-oss.atlassian.net/browse/JAVA-1355">JAVA-1355</a>.
695+
*
696+
* <p>Also note that this information is refreshed asynchronously by the control connection, when
697+
* schema or ring topology changes. It might occasionally be stale (or even empty).
698+
*
699+
* @param keyspace the name of the keyspace to get replicas for.
700+
* @param range the token range.
701+
* @return the list of replicas for {@code range} as known by the driver. Note that the result
702+
* might be stale or empty if metadata was explicitly disabled with {@link
703+
* QueryOptions#setMetadataEnabled(boolean)}.
704+
*/
705+
public List<Host> getReplicasList(String keyspace, TokenRange range) {
706+
keyspace = handleId(keyspace);
707+
TokenMap current = tokenMap;
708+
if (current == null) {
709+
return EMPTY_LIST;
710+
} else {
711+
return current.getReplicas(keyspace, range.getEnd());
632712
}
633713
}
634714

@@ -985,7 +1065,7 @@ private static class TokenMap {
9851065

9861066
private final Token.Factory factory;
9871067
private final Map<Host, Set<Token>> primaryToTokens;
988-
private final Map<String, Map<Token, Set<Host>>> tokenToHostsByKeyspace;
1068+
private final Map<String, Map<Token, ImmutableList<Host>>> tokenToHostsByKeyspace;
9891069
private final Map<String, Map<Host, Set<TokenRange>>> hostsToRangesByKeyspace;
9901070
private final List<Token> ring;
9911071
private final Set<TokenRange> tokenRanges;
@@ -997,7 +1077,7 @@ private TokenMap(
9971077
Set<TokenRange> tokenRanges,
9981078
Map<Token, Host> tokenToPrimary,
9991079
Map<Host, Set<Token>> primaryToTokens,
1000-
Map<String, Map<Token, Set<Host>>> tokenToHostsByKeyspace,
1080+
Map<String, Map<Token, ImmutableList<Host>>> tokenToHostsByKeyspace,
10011081
Map<String, Map<Host, Set<TokenRange>>> hostsToRangesByKeyspace) {
10021082
this.factory = factory;
10031083
this.ring = ring;
@@ -1042,15 +1122,13 @@ private static TokenMap build(
10421122
Set<TokenRange> tokenRanges,
10431123
Map<Token, Host> tokenToPrimary) {
10441124
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>>>();
1125+
Map<String, Map<Token, ImmutableList<Host>>> tokenToHosts = new HashMap<>();
1126+
Map<ReplicationStrategy, Map<Token, ImmutableList<Host>>> replStrategyToHosts =
1127+
new HashMap<>();
1128+
Map<String, Map<Host, Set<TokenRange>>> hostsToRanges = new HashMap<>();
10511129
for (KeyspaceMetadata keyspace : keyspaces) {
10521130
ReplicationStrategy strategy = keyspace.replicationStrategy();
1053-
Map<Token, Set<Host>> ksTokens = replStrategyToHosts.get(strategy);
1131+
Map<Token, ImmutableList<Host>> ksTokens = replStrategyToHosts.get(strategy);
10541132
if (ksTokens == null) {
10551133
ksTokens =
10561134
(strategy == null)
@@ -1077,14 +1155,14 @@ private static TokenMap build(
10771155
factory, ring, tokenRanges, tokenToPrimary, allTokens, tokenToHosts, hostsToRanges);
10781156
}
10791157

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

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

10851163
// 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;
1164+
ImmutableList<Host> hosts = tokenToHosts.get(token);
1165+
if (hosts != null) return ImmutableList.copyOf(hosts);
10881166

10891167
// Otherwise, find closest "primary" token on the ring
10901168
int i = Collections.binarySearch(ring, token);
@@ -1096,10 +1174,10 @@ private Set<Host> getReplicas(String keyspace, Token token) {
10961174
return tokenToHosts.get(ring.get(i));
10971175
}
10981176

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());
1177+
private static Map<Token, ImmutableList<Host>> makeNonReplicatedMap(Map<Token, Host> input) {
1178+
Map<Token, ImmutableList<Host>> output = new HashMap<>(input.size());
11011179
for (Map.Entry<Token, Host> entry : input.entrySet())
1102-
output.put(entry.getKey(), ImmutableSet.of(entry.getValue()));
1180+
output.put(entry.getKey(), ImmutableList.of(entry.getValue()));
11031181
return output;
11041182
}
11051183

@@ -1119,11 +1197,11 @@ private static Set<TokenRange> makeTokenRanges(List<Token> ring, Token.Factory f
11191197
}
11201198

11211199
private static Map<Host, Set<TokenRange>> computeHostsToRangesMap(
1122-
Set<TokenRange> tokenRanges, Map<Token, Set<Host>> ksTokens, int hostCount) {
1200+
Set<TokenRange> tokenRanges, Map<Token, ImmutableList<Host>> ksTokens, int hostCount) {
11231201
Map<Host, ImmutableSet.Builder<TokenRange>> builders =
11241202
Maps.newHashMapWithExpectedSize(hostCount);
11251203
for (TokenRange range : tokenRanges) {
1126-
Set<Host> replicas = ksTokens.get(range.getEnd());
1204+
List<Host> replicas = ksTokens.get(range.getEnd());
11271205
for (Host host : replicas) {
11281206
ImmutableSet.Builder<TokenRange> hostRanges = builders.get(host);
11291207
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)