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