2222package com .datastax .driver .core ;
2323
2424import com .google .common .annotations .Beta ;
25+ import com .google .common .collect .ImmutableList ;
2526import com .google .common .collect .ImmutableMap ;
2627import com .google .common .collect .ImmutableSet ;
2728import com .google .common .collect .Maps ;
3435import java .util .Collections ;
3536import java .util .HashMap ;
3637import java .util .HashSet ;
38+ import java .util .LinkedHashSet ;
3739import java .util .List ;
3840import java .util .Map ;
3941import java .util .Set ;
5153public 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 ;
@@ -553,18 +557,48 @@ public Set<TokenRange> getTokenRanges(String keyspace, Host host) {
553557 * @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
554558 * that the result might be stale or empty if metadata was explicitly disabled with {@link
555559 * QueryOptions#setMetadataEnabled(boolean)}.
560+ * @deprecated use {@link Metadata#getReplicasList(String, String, Token.Factory, ByteBuffer)}
556561 */
562+ @ Deprecated
557563 @ Beta
558564 public Set <Host > getReplicas (
559565 String keyspace , String table , Token .Factory partitioner , ByteBuffer partitionKey ) {
566+ return new LinkedHashSet <>(this .getReplicasList (keyspace , table , partitioner , partitionKey ));
567+ }
568+
569+ /**
570+ * Extension of legacy method {@link Metadata#getReplicas(String, Token.Factory, ByteBuffer)}.
571+ * Tablets model requires knowledge of the table name to determine the replicas. This method will
572+ * first try to lookup replicas through known tablets metadata. It will default to TokenMap lookup
573+ * if either {@code null} was passed as table name or the tablet lookup is unsuccessful for any
574+ * other reason.
575+ *
576+ * <p>Returns the set of hosts that are replica for a given partition key. Partitioner can be
577+ * {@code null} and then a cluster-wide partitioner will be invoked.
578+ *
579+ * <p>Note that this information is refreshed asynchronously by the control connection, when
580+ * schema or ring topology changes. It might occasionally be stale (or even empty).
581+ *
582+ * @param keyspace the name of the keyspace to get replicas for.
583+ * @param table the name of the table to get replicas for. Necessary for distinction for tablets.
584+ * Unnecessary for regular TokenMap
585+ * @param partitioner the partitioner to use or @{code null} for cluster-wide partitioner.
586+ * @param partitionKey the partition key for which to find the set of replica.
587+ * @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
588+ * that the result might be stale or empty if metadata was explicitly disabled with {@link
589+ * QueryOptions#setMetadataEnabled(boolean)}.
590+ */
591+ @ Beta
592+ public List <Host > getReplicasList (
593+ String keyspace , String table , Token .Factory partitioner , ByteBuffer partitionKey ) {
560594 keyspace = handleId (keyspace );
561595 table = handleId (table );
562596 TokenMap current = tokenMap ;
563597 if (partitioner == null && current != null ) {
564598 partitioner = current .factory ;
565599 }
566600 if (partitioner == null ) {
567- return Collections .emptySet ();
601+ return Collections .emptyList ();
568602 }
569603 Token token = partitioner .hash (partitionKey );
570604
@@ -575,14 +609,13 @@ public Set<Host> getReplicas(
575609 assert (token instanceof Token .TokenLong64 );
576610 return tabletMap .getReplicas (keyspace , table , (long ) token .getValue ());
577611 } else {
578- return Collections .emptySet ();
612+ return Collections .emptyList ();
579613 }
580614 }
581615
582616 // 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 ;
617+ if (current == null ) return Collections .<Host >emptyList ();
618+ return current .getReplicas (keyspace , token );
586619 }
587620
588621 /**
@@ -598,12 +631,33 @@ public Set<Host> getReplicas(
598631 * @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
599632 * that the result might be stale or empty if metadata was explicitly disabled with {@link
600633 * QueryOptions#setMetadataEnabled(boolean)}.
634+ * @deprecated use {@link Metadata#getReplicasList(String, Token.Factory, ByteBuffer)}
601635 */
636+ @ Deprecated
602637 public Set <Host > getReplicas (
603638 String keyspace , Token .Factory partitioner , ByteBuffer partitionKey ) {
604639 return getReplicas (keyspace , null , partitioner , partitionKey );
605640 }
606641
642+ /**
643+ * Returns the immutable list of hosts that are replica for a given partition key. Partitioner can
644+ * be {@code null} and then a cluster-wide partitioner will be invoked.
645+ *
646+ * <p>Note that this information is refreshed asynchronously by the control connection, when
647+ * schema or ring topology changes. It might occasionally be stale (or even empty).
648+ *
649+ * @param keyspace the name of the keyspace to get replicas for.
650+ * @param partitioner the partitioner to use or @{code null} for cluster-wide partitioner.
651+ * @param partitionKey the partition key for which to find the set of replica.
652+ * @return the (immutable) list of replicas for {@code partitionKey} as known by the driver. Note
653+ * that the result might be stale or empty if metadata was explicitly disabled with {@link
654+ * QueryOptions#setMetadataEnabled(boolean)}.
655+ */
656+ public List <Host > getReplicasList (
657+ String keyspace , Token .Factory partitioner , ByteBuffer partitionKey ) {
658+ return getReplicasList (keyspace , null , partitioner , partitionKey );
659+ }
660+
607661 /**
608662 * Returns the set of hosts that are replica for a given token range.
609663 *
@@ -620,15 +674,42 @@ public Set<Host> getReplicas(
620674 * @return the (immutable) set of replicas for {@code range} as known by the driver. Note that the
621675 * result might be stale or empty if metadata was explicitly disabled with {@link
622676 * QueryOptions#setMetadataEnabled(boolean)}.
677+ * @deprecated use {@link Metadata#getReplicasList(String, TokenRange)}
623678 */
624679 public Set <Host > getReplicas (String keyspace , TokenRange range ) {
625680 keyspace = handleId (keyspace );
626681 TokenMap current = tokenMap ;
627682 if (current == null ) {
628683 return Collections .emptySet ();
629684 } else {
630- Set <Host > hosts = current .getReplicas (keyspace , range .getEnd ());
631- return hosts == null ? Collections .<Host >emptySet () : hosts ;
685+ return new HashSet <>(current .getReplicas (keyspace , range .getEnd ()));
686+ }
687+ }
688+
689+ /**
690+ * Returns the list of hosts that are replica for a given token range.
691+ *
692+ * <p>Note that it is assumed that the input range does not overlap across multiple host ranges.
693+ * If the range extends over multiple hosts, it only returns the replicas for those hosts that are
694+ * replicas for the last token of the range. This behavior may change in a future release, see <a
695+ * href="https://datastax-oss.atlassian.net/browse/JAVA-1355">JAVA-1355</a>.
696+ *
697+ * <p>Also note that this information is refreshed asynchronously by the control connection, when
698+ * schema or ring topology changes. It might occasionally be stale (or even empty).
699+ *
700+ * @param keyspace the name of the keyspace to get replicas for.
701+ * @param range the token range.
702+ * @return the list of replicas for {@code range} as known by the driver. Note that the result
703+ * might be stale or empty if metadata was explicitly disabled with {@link
704+ * QueryOptions#setMetadataEnabled(boolean)}.
705+ */
706+ public List <Host > getReplicasList (String keyspace , TokenRange range ) {
707+ keyspace = handleId (keyspace );
708+ TokenMap current = tokenMap ;
709+ if (current == null ) {
710+ return EMPTY_LIST ;
711+ } else {
712+ return current .getReplicas (keyspace , range .getEnd ());
632713 }
633714 }
634715
@@ -985,7 +1066,7 @@ private static class TokenMap {
9851066
9861067 private final Token .Factory factory ;
9871068 private final Map <Host , Set <Token >> primaryToTokens ;
988- private final Map <String , Map <Token , Set <Host >>> tokenToHostsByKeyspace ;
1069+ private final Map <String , Map <Token , ImmutableList <Host >>> tokenToHostsByKeyspace ;
9891070 private final Map <String , Map <Host , Set <TokenRange >>> hostsToRangesByKeyspace ;
9901071 private final List <Token > ring ;
9911072 private final Set <TokenRange > tokenRanges ;
@@ -997,7 +1078,7 @@ private TokenMap(
9971078 Set <TokenRange > tokenRanges ,
9981079 Map <Token , Host > tokenToPrimary ,
9991080 Map <Host , Set <Token >> primaryToTokens ,
1000- Map <String , Map <Token , Set <Host >>> tokenToHostsByKeyspace ,
1081+ Map <String , Map <Token , ImmutableList <Host >>> tokenToHostsByKeyspace ,
10011082 Map <String , Map <Host , Set <TokenRange >>> hostsToRangesByKeyspace ) {
10021083 this .factory = factory ;
10031084 this .ring = ring ;
@@ -1042,15 +1123,13 @@ private static TokenMap build(
10421123 Set <TokenRange > tokenRanges ,
10431124 Map <Token , Host > tokenToPrimary ) {
10441125 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 >>>();
1126+ Map <String , Map <Token , ImmutableList <Host >>> tokenToHosts = new HashMap <>();
1127+ Map <ReplicationStrategy , Map <Token , ImmutableList <Host >>> replStrategyToHosts =
1128+ new HashMap <>();
1129+ Map <String , Map <Host , Set <TokenRange >>> hostsToRanges = new HashMap <>();
10511130 for (KeyspaceMetadata keyspace : keyspaces ) {
10521131 ReplicationStrategy strategy = keyspace .replicationStrategy ();
1053- Map <Token , Set <Host >> ksTokens = replStrategyToHosts .get (strategy );
1132+ Map <Token , ImmutableList <Host >> ksTokens = replStrategyToHosts .get (strategy );
10541133 if (ksTokens == null ) {
10551134 ksTokens =
10561135 (strategy == null )
@@ -1077,14 +1156,14 @@ private static TokenMap build(
10771156 factory , ring , tokenRanges , tokenToPrimary , allTokens , tokenToHosts , hostsToRanges );
10781157 }
10791158
1080- private Set <Host > getReplicas (String keyspace , Token token ) {
1159+ private ImmutableList <Host > getReplicas (String keyspace , Token token ) {
10811160
1082- Map <Token , Set <Host >> tokenToHosts = tokenToHostsByKeyspace .get (keyspace );
1083- if (tokenToHosts == null ) return Collections . emptySet () ;
1161+ Map <Token , ImmutableList <Host >> tokenToHosts = tokenToHostsByKeyspace .get (keyspace );
1162+ if (tokenToHosts == null ) return EMPTY_LIST ;
10841163
10851164 // 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 ;
1165+ ImmutableList <Host > hosts = tokenToHosts .get (token );
1166+ if (hosts != null ) return ImmutableList . copyOf ( hosts ) ;
10881167
10891168 // Otherwise, find closest "primary" token on the ring
10901169 int i = Collections .binarySearch (ring , token );
@@ -1096,10 +1175,10 @@ private Set<Host> getReplicas(String keyspace, Token token) {
10961175 return tokenToHosts .get (ring .get (i ));
10971176 }
10981177
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 ());
1178+ private static Map <Token , ImmutableList <Host >> makeNonReplicatedMap (Map <Token , Host > input ) {
1179+ Map <Token , ImmutableList <Host >> output = new HashMap <>(input .size ());
11011180 for (Map .Entry <Token , Host > entry : input .entrySet ())
1102- output .put (entry .getKey (), ImmutableSet .of (entry .getValue ()));
1181+ output .put (entry .getKey (), ImmutableList .of (entry .getValue ()));
11031182 return output ;
11041183 }
11051184
@@ -1119,11 +1198,11 @@ private static Set<TokenRange> makeTokenRanges(List<Token> ring, Token.Factory f
11191198 }
11201199
11211200 private static Map <Host , Set <TokenRange >> computeHostsToRangesMap (
1122- Set <TokenRange > tokenRanges , Map <Token , Set <Host >> ksTokens , int hostCount ) {
1201+ Set <TokenRange > tokenRanges , Map <Token , ImmutableList <Host >> ksTokens , int hostCount ) {
11231202 Map <Host , ImmutableSet .Builder <TokenRange >> builders =
11241203 Maps .newHashMapWithExpectedSize (hostCount );
11251204 for (TokenRange range : tokenRanges ) {
1126- Set <Host > replicas = ksTokens .get (range .getEnd ());
1205+ List <Host > replicas = ksTokens .get (range .getEnd ());
11271206 for (Host host : replicas ) {
11281207 ImmutableSet .Builder <TokenRange > hostRanges = builders .get (host );
11291208 if (hostRanges == null ) {
0 commit comments