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 = 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 ) {
0 commit comments