2121 */
2222package com .datastax .driver .core ;
2323
24+ import com .google .common .annotations .Beta ;
2425import com .google .common .collect .ImmutableMap ;
2526import com .google .common .collect .ImmutableSet ;
2627import com .google .common .collect .Maps ;
3536import java .util .HashSet ;
3637import java .util .List ;
3738import java .util .Map ;
39+ import java .util .NavigableSet ;
3840import java .util .Set ;
3941import java .util .TreeSet ;
4042import java .util .UUID ;
4143import java .util .concurrent .ConcurrentHashMap ;
4244import java .util .concurrent .ConcurrentMap ;
4345import java .util .concurrent .CopyOnWriteArrayList ;
4446import java .util .concurrent .locks .ReentrantLock ;
47+ import java .util .stream .Collectors ;
4548import org .slf4j .Logger ;
4649import org .slf4j .LoggerFactory ;
4750
@@ -60,8 +63,8 @@ public class Metadata {
6063 final ConcurrentMap <String , KeyspaceMetadata > keyspaces =
6164 new ConcurrentHashMap <String , KeyspaceMetadata >();
6265 private volatile TokenMap tokenMap ;
63-
6466 final ReentrantLock lock = new ReentrantLock ();
67+ private final TabletMap tabletMap ;
6568
6669 // See https://github.com/apache/cassandra/blob/trunk/doc/cql3/CQL.textile#appendixA
6770 private static final IntObjectHashMap <List <char []>> RESERVED_KEYWORDS =
@@ -146,6 +149,7 @@ public class Metadata {
146149
147150 Metadata (Cluster .Manager cluster ) {
148151 this .cluster = cluster ;
152+ this .tabletMap = TabletMap .emptyMap (cluster );
149153 }
150154
151155 // rebuilds the token map with the current hosts, typically when refreshing schema metadata
@@ -514,21 +518,30 @@ public Set<TokenRange> getTokenRanges(String keyspace, Host host) {
514518 }
515519
516520 /**
517- * Returns the set of hosts that are replica for a given partition key. Partitioner can be {@code
518- * null} and then a cluster-wide partitioner will be invoked.
521+ * Extension of legacy method {@link Metadata#getReplicas(String, Token.Factory, ByteBuffer)}.
522+ * Tablets model requires knowledge of the table name to determine the replicas. This method will
523+ * first try to lookup replicas through known tablets metadata. It will default to TokenMap lookup
524+ * if either {@code null} was passed as table name or the tablet lookup is unsuccessful for any
525+ * other reason.
526+ *
527+ * <p>Returns the set of hosts that are replica for a given partition key. Partitioner can be
528+ * {@code null} and then a cluster-wide partitioner will be invoked.
519529 *
520530 * <p>Note that this information is refreshed asynchronously by the control connection, when
521531 * schema or ring topology changes. It might occasionally be stale (or even empty).
522532 *
523533 * @param keyspace the name of the keyspace to get replicas for.
534+ * @param table the name of the table to get replicas for. Necessary for distinction for tablets.
535+ * Unnecessary for regular TokenMap
524536 * @param partitioner the partitioner to use or @{code null} for cluster-wide partitioner.
525537 * @param partitionKey the partition key for which to find the set of replica.
526538 * @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
527539 * that the result might be stale or empty if metadata was explicitly disabled with {@link
528540 * QueryOptions#setMetadataEnabled(boolean)}.
529541 */
542+ @ Beta
530543 public Set <Host > getReplicas (
531- String keyspace , Token .Factory partitioner , ByteBuffer partitionKey ) {
544+ String keyspace , String table , Token .Factory partitioner , ByteBuffer partitionKey ) {
532545 keyspace = handleId (keyspace );
533546 TokenMap current = tokenMap ;
534547 if (current == null ) {
@@ -537,11 +550,40 @@ public Set<Host> getReplicas(
537550 if (partitioner == null ) {
538551 partitioner = current .factory ;
539552 }
553+ // If possible, try tablet lookup first
554+ if (keyspace != null && table != null ) {
555+ Token token = partitioner .hash (partitionKey );
556+ assert (token instanceof Token .TokenLong64 );
557+ Set <UUID > hostUuids = tabletMap .getReplicas (keyspace , table , (long ) token .getValue ());
558+ if (!hostUuids .isEmpty ()) {
559+ return hostUuids .stream ().map (this ::getHost ).collect (Collectors .toSet ());
560+ }
561+ }
562+ // Fall back to tokenMap
540563 Set <Host > hosts = current .getReplicas (keyspace , partitioner .hash (partitionKey ));
541564 return hosts == null ? Collections .<Host >emptySet () : hosts ;
542565 }
543566 }
544567
568+ /**
569+ * Returns the set of hosts that are replica for a given partition key. Partitioner can be {@code
570+ * null} and then a cluster-wide partitioner will be invoked.
571+ *
572+ * <p>Note that this information is refreshed asynchronously by the control connection, when
573+ * schema or ring topology changes. It might occasionally be stale (or even empty).
574+ *
575+ * @param keyspace the name of the keyspace to get replicas for.
576+ * @param partitioner the partitioner to use or @{code null} for cluster-wide partitioner.
577+ * @param partitionKey the partition key for which to find the set of replica.
578+ * @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
579+ * that the result might be stale or empty if metadata was explicitly disabled with {@link
580+ * QueryOptions#setMetadataEnabled(boolean)}.
581+ */
582+ public Set <Host > getReplicas (
583+ String keyspace , Token .Factory partitioner , ByteBuffer partitionKey ) {
584+ return getReplicas (keyspace , null , partitioner , partitionKey );
585+ }
586+
545587 /**
546588 * Returns the set of hosts that are replica for a given token range.
547589 *
@@ -860,6 +902,58 @@ void triggerOnMaterializedViewRemoved(MaterializedViewMetadata view) {
860902 }
861903 }
862904
905+ @ Beta
906+ public int getShardForTabletToken (
907+ String keyspace , String table , Token .TokenLong64 token , Host host ) {
908+ if (tabletMap == null ) {
909+ logger .trace (
910+ "Could not determine shard for token {} on host {} because tablets metadata is currently null. "
911+ + "Returning -1." ,
912+ token ,
913+ host );
914+ return -1 ;
915+ }
916+ UUID targetHostUuid = host .getHostId ();
917+ long tokenValue = (long ) token .getValue ();
918+ TabletMap .KeyspaceTableNamePair key = new TabletMap .KeyspaceTableNamePair (keyspace , table );
919+ NavigableSet <TabletMap .Tablet > targetTablets = tabletMap .getMapping ().get (key );
920+ if (targetTablets == null ) {
921+ logger .trace (
922+ "Could not determine shard for token {} on host {} because table {}.{} is not present in tablets "
923+ + "metadata. Returning -1." ,
924+ token ,
925+ host ,
926+ keyspace ,
927+ table );
928+ return -1 ;
929+ }
930+ TabletMap .Tablet row = targetTablets .ceiling (TabletMap .Tablet .malformedTablet (tokenValue ));
931+ if (row != null && row .getFirstToken () < tokenValue ) {
932+ for (TabletMap .HostShardPair hostShardPair : row .getReplicas ()) {
933+ if (hostShardPair .getHost ().equals (targetHostUuid )) {
934+ return hostShardPair .getShard ();
935+ }
936+ }
937+ }
938+ logger .trace (
939+ "Could not find tablet corresponding to token {} on host {} for table {} in keyspace {}. Returning -1." ,
940+ token ,
941+ host ,
942+ table ,
943+ keyspace );
944+ return -1 ;
945+ }
946+
947+ /**
948+ * Getter for current {@link TabletMap}.
949+ *
950+ * @return current {@link TabletMap}
951+ */
952+ @ Beta
953+ public TabletMap getTabletMap () {
954+ return tabletMap ;
955+ }
956+
863957 private static class TokenMap {
864958
865959 private final Token .Factory factory ;
0 commit comments