21
21
*/
22
22
package com .datastax .driver .core ;
23
23
24
+ import com .google .common .annotations .Beta ;
24
25
import com .google .common .collect .ImmutableMap ;
25
26
import com .google .common .collect .ImmutableSet ;
26
27
import com .google .common .collect .Maps ;
35
36
import java .util .HashSet ;
36
37
import java .util .List ;
37
38
import java .util .Map ;
39
+ import java .util .NavigableSet ;
38
40
import java .util .Set ;
39
41
import java .util .TreeSet ;
40
42
import java .util .UUID ;
41
43
import java .util .concurrent .ConcurrentHashMap ;
42
44
import java .util .concurrent .ConcurrentMap ;
43
45
import java .util .concurrent .CopyOnWriteArrayList ;
44
46
import java .util .concurrent .locks .ReentrantLock ;
47
+ import java .util .stream .Collectors ;
45
48
import org .slf4j .Logger ;
46
49
import org .slf4j .LoggerFactory ;
47
50
@@ -60,8 +63,8 @@ public class Metadata {
60
63
final ConcurrentMap <String , KeyspaceMetadata > keyspaces =
61
64
new ConcurrentHashMap <String , KeyspaceMetadata >();
62
65
private volatile TokenMap tokenMap ;
63
-
64
66
final ReentrantLock lock = new ReentrantLock ();
67
+ private final TabletMap tabletMap ;
65
68
66
69
// See https://github.com/apache/cassandra/blob/trunk/doc/cql3/CQL.textile#appendixA
67
70
private static final IntObjectHashMap <List <char []>> RESERVED_KEYWORDS =
@@ -146,6 +149,7 @@ public class Metadata {
146
149
147
150
Metadata (Cluster .Manager cluster ) {
148
151
this .cluster = cluster ;
152
+ this .tabletMap = TabletMap .emptyMap (cluster );
149
153
}
150
154
151
155
// 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) {
514
518
}
515
519
516
520
/**
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.
519
529
*
520
530
* <p>Note that this information is refreshed asynchronously by the control connection, when
521
531
* schema or ring topology changes. It might occasionally be stale (or even empty).
522
532
*
523
533
* @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
524
536
* @param partitioner the partitioner to use or @{code null} for cluster-wide partitioner.
525
537
* @param partitionKey the partition key for which to find the set of replica.
526
538
* @return the (immutable) set of replicas for {@code partitionKey} as known by the driver. Note
527
539
* that the result might be stale or empty if metadata was explicitly disabled with {@link
528
540
* QueryOptions#setMetadataEnabled(boolean)}.
529
541
*/
542
+ @ Beta
530
543
public Set <Host > getReplicas (
531
- String keyspace , Token .Factory partitioner , ByteBuffer partitionKey ) {
544
+ String keyspace , String table , Token .Factory partitioner , ByteBuffer partitionKey ) {
532
545
keyspace = handleId (keyspace );
533
546
TokenMap current = tokenMap ;
534
547
if (current == null ) {
@@ -537,11 +550,40 @@ public Set<Host> getReplicas(
537
550
if (partitioner == null ) {
538
551
partitioner = current .factory ;
539
552
}
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
540
563
Set <Host > hosts = current .getReplicas (keyspace , partitioner .hash (partitionKey ));
541
564
return hosts == null ? Collections .<Host >emptySet () : hosts ;
542
565
}
543
566
}
544
567
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
+
545
587
/**
546
588
* Returns the set of hosts that are replica for a given token range.
547
589
*
@@ -860,6 +902,58 @@ void triggerOnMaterializedViewRemoved(MaterializedViewMetadata view) {
860
902
}
861
903
}
862
904
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
+
863
957
private static class TokenMap {
864
958
865
959
private final Token .Factory factory ;
0 commit comments