Skip to content

Commit 3adff13

Browse files
authored
Move metadata internal API to use List instead of Set as replicas store (#446)
In almost all use cases replicas are getting converted into a List. To make performance better make these API provide List instead.
1 parent 3635e7f commit 3adff13

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)