Skip to content

Commit e561a2d

Browse files
Bouncheckavelanarius
authored andcommitted
Expose ShardingInfo
Adds ShardingInfo field to DefaultNode and getShardingInfo() to Node interface. Adds NodeShardingInfo interface as public API. With this change, having a token and having determined the Node it belongs to, users can further use NodeShardingInfo to determine the shard it belongs to. Fixes #232.
1 parent 31a379b commit e561a2d

File tree

6 files changed

+140
-3
lines changed

6 files changed

+140
-3
lines changed

Diff for: core/src/main/java/com/datastax/oss/driver/api/core/metadata/Node.java

+9
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,13 @@ public interface Node {
214214
*/
215215
@Nullable
216216
UUID getSchemaVersion();
217+
218+
/**
219+
* Node's sharding information.
220+
*
221+
* <p>May be null if the node is not a Scylla node or the connection pool to the node was never
222+
* created.
223+
*/
224+
@Nullable
225+
NodeShardingInfo getShardingInfo();
217226
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.datastax.oss.driver.api.core.metadata;
2+
3+
import com.datastax.oss.driver.api.core.metadata.token.Token;
4+
5+
/** Holds sharding information for a particular Node. */
6+
public interface NodeShardingInfo {
7+
8+
public int getShardsCount();
9+
10+
/**
11+
* Returns a shardId for a given Token.
12+
*
13+
* <p>Accepts all types of Tokens but if the Token is not an instance of {@link
14+
* com.datastax.oss.driver.internal.core.metadata.token.TokenLong64} then the return value could
15+
* be not meaningful (e.g. random shard). This method does not verify if the given Token belongs
16+
* to the Node.
17+
*/
18+
public int shardId(Token t);
19+
}

Diff for: core/src/main/java/com/datastax/oss/driver/internal/core/metadata/DefaultNode.java

+18-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
2424
import com.datastax.oss.driver.internal.core.metrics.NodeMetricUpdater;
2525
import com.datastax.oss.driver.internal.core.metrics.NoopNodeMetricUpdater;
26+
import com.datastax.oss.driver.internal.core.protocol.ShardingInfo;
2627
import edu.umd.cs.findbugs.annotations.NonNull;
2728
import edu.umd.cs.findbugs.annotations.Nullable;
2829
import java.io.Serializable;
@@ -35,8 +36,8 @@
3536
import net.jcip.annotations.ThreadSafe;
3637

3738
/**
38-
* Implementation note: all the mutable state in this class is read concurrently, but only mutated
39-
* from {@link MetadataManager}'s admin thread.
39+
* Implementation note: (almost) all the mutable state in this class is read concurrently, but only
40+
* mutated from {@link MetadataManager}'s admin thread. Node's ShardingInfo is an exception.
4041
*/
4142
@ThreadSafe
4243
public class DefaultNode implements Node, Serializable {
@@ -67,6 +68,10 @@ public class DefaultNode implements Node, Serializable {
6768

6869
volatile NodeDistance distance;
6970

71+
// Initially null. A copy of ShardingInfo. Updated with values by DriverChannel during pool
72+
// initialization.
73+
private volatile ShardingInfo shardingInfo;
74+
7075
public DefaultNode(EndPoint endPoint, InternalDriverContext context) {
7176
this.endPoint = endPoint;
7277
this.state = NodeState.UNKNOWN;
@@ -77,6 +82,7 @@ public DefaultNode(EndPoint endPoint, InternalDriverContext context) {
7782
// problem because the node updater only needs the connect address to initialize.
7883
this.metricUpdater = context.getMetricsFactory().newNodeUpdater(this);
7984
this.upSinceMillis = -1;
85+
this.shardingInfo = null;
8086
}
8187

8288
@NonNull
@@ -193,4 +199,14 @@ public String toString() {
193199
public Set<String> getRawTokens() {
194200
return rawTokens;
195201
}
202+
203+
@Nullable
204+
@Override
205+
public ShardingInfo getShardingInfo() {
206+
return shardingInfo;
207+
}
208+
209+
public void setShardingInfo(ShardingInfo shardingInfo) {
210+
this.shardingInfo = shardingInfo;
211+
}
196212
}

Diff for: core/src/main/java/com/datastax/oss/driver/internal/core/pool/ChannelPool.java

+1
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ private void addChannel(DriverChannel c) {
340340

341341
private void initialize(DriverChannel c) {
342342
shardingInfo = c.getShardingInfo();
343+
((DefaultNode) node).setShardingInfo(shardingInfo);
343344
int wanted = getConfiguredSize(distance);
344345
int shardsCount = shardingInfo == null ? 1 : shardingInfo.getShardsCount();
345346
wantedCount = wanted / shardsCount + (wanted % shardsCount > 0 ? 1 : 0);

Diff for: core/src/main/java/com/datastax/oss/driver/internal/core/protocol/ShardingInfo.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
2121
*/
2222
package com.datastax.oss.driver.internal.core.protocol;
2323

24+
import com.datastax.oss.driver.api.core.metadata.NodeShardingInfo;
2425
import com.datastax.oss.driver.api.core.metadata.token.Token;
2526
import com.datastax.oss.driver.internal.core.metadata.token.TokenLong64;
2627
import java.util.List;
2728
import java.util.Map;
2829
import java.util.concurrent.ThreadLocalRandom;
2930

3031
/** Keeps the information the driver maintains on data layout of a given node. */
31-
public class ShardingInfo {
32+
public class ShardingInfo implements NodeShardingInfo {
3233
private static final String SCYLLA_SHARD_PARAM_KEY = "SCYLLA_SHARD";
3334
private static final String SCYLLA_NR_SHARDS_PARAM_KEY = "SCYLLA_NR_SHARDS";
3435
private static final String SCYLLA_PARTITIONER = "SCYLLA_PARTITIONER";
@@ -48,6 +49,7 @@ private ShardingInfo(
4849
this.shardingIgnoreMSB = shardingIgnoreMSB;
4950
}
5051

52+
@Override
5153
public int getShardsCount() {
5254
return shardsCount;
5355
}
@@ -60,6 +62,7 @@ public String getShardingAlgorithm() {
6062
return shardingAlgorithm;
6163
}
6264

65+
@Override
6366
public int shardId(Token t) {
6467
if (!(t instanceof TokenLong64)) {
6568
return ThreadLocalRandom.current().nextInt(shardsCount);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.datastax.oss.driver.examples.basic;
2+
3+
import com.datastax.oss.driver.api.core.CqlIdentifier;
4+
import com.datastax.oss.driver.api.core.CqlSession;
5+
import com.datastax.oss.driver.api.core.DefaultProtocolVersion;
6+
import com.datastax.oss.driver.api.core.cql.ResultSet;
7+
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
8+
import com.datastax.oss.driver.api.core.cql.TraceEvent;
9+
import com.datastax.oss.driver.api.core.metadata.Metadata;
10+
import com.datastax.oss.driver.api.core.metadata.Node;
11+
import com.datastax.oss.driver.api.core.metadata.TokenMap;
12+
import com.datastax.oss.driver.api.core.metadata.token.Token;
13+
import com.datastax.oss.driver.api.core.type.codec.TypeCodecs;
14+
import java.nio.ByteBuffer;
15+
import java.util.Set;
16+
17+
/**
18+
* Demonstrates usage of TokenMap and NodeShardingInfo Needs a Scylla cluster to be running locally
19+
* or adjustment of session builder.
20+
*/
21+
public class TokenMapAndShardIdLookup {
22+
23+
private static String CREATE_KEYSPACE =
24+
"CREATE KEYSPACE IF NOT EXISTS tokenmap_example_ks "
25+
+ "WITH replication = {"
26+
+ "'class': 'SimpleStrategy', "
27+
+ "'replication_factor': 1"
28+
+ "}";
29+
30+
private static String CREATE_TABLE =
31+
""
32+
+ "CREATE TABLE IF NOT EXISTS tokenmap_example_ks.example_tab ("
33+
+ "my_column bigint,"
34+
+ "PRIMARY KEY (my_column)"
35+
+ ")";
36+
37+
private static String INSERT_COLUMN =
38+
"INSERT INTO tokenmap_example_ks.example_tab (my_column) VALUES (2)";
39+
40+
private static String SELECT_COLUMN =
41+
"SELECT * FROM tokenmap_example_ks.example_tab WHERE my_column = 2";
42+
43+
private static ByteBuffer PARTITION_KEY = TypeCodecs.BIGINT.encode(2L, DefaultProtocolVersion.V3);
44+
45+
public static void main(String[] args) {
46+
47+
try (CqlSession session = CqlSession.builder().build()) {
48+
49+
System.out.printf("Connected session: %s%n", session.getName());
50+
51+
session.execute(CREATE_KEYSPACE);
52+
session.execute(CREATE_TABLE);
53+
session.execute(INSERT_COLUMN);
54+
55+
Metadata metadata = session.refreshSchema();
56+
57+
System.out.println("Prepared example data");
58+
59+
TokenMap tokenMap = metadata.getTokenMap().get();
60+
61+
Set<Node> nodes =
62+
tokenMap.getReplicas(CqlIdentifier.fromCql("tokenmap_example_ks"), PARTITION_KEY);
63+
System.out.println("Replica set size: " + nodes.size());
64+
65+
Token token = tokenMap.newToken(PARTITION_KEY);
66+
assert nodes.size() > 0;
67+
Node node = nodes.iterator().next();
68+
69+
assert node.getShardingInfo() != null;
70+
int shardId = node.getShardingInfo().shardId(token);
71+
72+
System.out.println(
73+
"Hardcoded partition key should belong to shard number "
74+
+ shardId
75+
+ " (on Node: "
76+
+ node
77+
+ ")");
78+
79+
System.out.println("You can compare it with SELECT query trace:");
80+
// If there is only 1 node, then the SELECT has to hit the one we did shardId calculation for.
81+
SimpleStatement statement = SimpleStatement.builder(SELECT_COLUMN).setTracing(true).build();
82+
ResultSet rs = session.execute(statement);
83+
84+
for (TraceEvent event : rs.getExecutionInfo().getQueryTrace().getEvents()) {
85+
System.out.println(event);
86+
}
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)