Was this page helpful?
ScyllaDB Java Driver is available under the Apache v2 License. ScyllaDB Java Driver is a fork of DataStax Java Driver. See Copyright here.
OptionalLocalDcHelper no longer logs “you specified X as the local DC, but some contact points are
from a different DC”. It compared the configured DC against contact-point placeholder nodes, whose
datacenter is never populated, so it fired on every session that configured a local DC, wherever the
contact points actually were. The warning for a configured DC that matches no node in the cluster is
unchanged. checkLocalDatacenterCompatibility is removed, so drop any override of it.
advanced.control-connection.reconnection.fallback-to-original-contact-points now defaults to
true. Once a control-connection reconnection round has exhausted the live nodes, the original
contact points are tried again. A contact point given as a hostname is kept unresolved and looked up
again on each connect, so a cluster that moved to new addresses is found again once the JVM’s DNS
cache (networkaddress.cache.ttl) has expired; this is what
#215 asked for. A resolved contact point
(advanced.resolve-contact-points = true, or a programmatic InetSocketAddress that was already
resolved) is appended as it is and is not re-resolved.
The cost is one extra connection attempt per contact point per exhausted round: at plan time the
contact points are hostnames and the live nodes resolved addresses, so the two cannot be
deduplicated. Set the option to false if your contact points are IP literals or their records
never change, or to keep reconnection rounds short; the control connection then re-resolves
nothing.
When the control connection reaches a contact point kept as a hostname (the default,
advanced.resolve-contact-points = false), at startup or through the reconnection fallback above,
the name is now resolved to all of its addresses: the one the resolver returns first is tried first,
as before, and up to advanced.connection.max-candidate-addresses - 1 of the others (5 addresses in
all by default) follow in random order before the contact point is given up on. A dead first record
no longer fails CqlSession.build(). What changes:
AllNodesFailedException carries one entry per address tried, under a temporary node named
cluster.example.com/10.0.0.1:9042, instead of one entry for the hostname.
The node the control connection reaches through a contact point is registered under that labelled
address. Its getEndPoint().resolve() is now resolved (getAddress() is no longer null), its
toString() and the node metric tag read cluster.example.com/10.0.0.1:9042 rather than
cluster.example.com:9042, and connections opened to it later go to that address instead of
resolving the name again. Its host string, Dropwizard metric prefix
(nodes.cluster_example_com:9042.*), TLS hostname and the endpoint handed to AuthProvider are
unchanged. A node already known under its own address keeps that endpoint.
Startup takes up to max-candidate-addresses × (connect-timeout + the init handshake) when every
record is dead. Set the option to 1 for the previous one-attempt behaviour.
A NodeStateListener sees onDown for each address that failed before the session was
initialized, as it did for the contact point itself.
Nothing else expands: IP-literal contact points, already-resolved contact points
(resolve-contact-points = true, a programmatic resolved InetSocketAddress), custom EndPoints
and every node discovered from the cluster are tried as they are.
Two CQL STARTUP options are new. The server stores them in its client-connection system table
(system.clients on ScyllaDB, system_views.clients on Cassandra 4.1+), so that operators can group
a client’s connections and inspect its driver settings while investigating an incident.
SESSION_ID — a driver-generated identifier, shared by all of a session’s connections. It is sent
on every connection, unconditionally: it is an innate behavior with no configuration option to
turn it off. It is not derived from CLIENT_ID, which remains user-settable and unchanged.
DRIVER_CONFIG — a compact JSON description of the effective configuration of the session’s
default execution profile (connection/socket settings, timeouts,
retry/reconnection/speculative-execution/load-balancing policies, connection pooling, query
defaults, and TLS). Only the control connection sends it, since it describes the whole session.
It reports settings only — never credentials, statements or data — and identifies non-built-in
policies by class name: the simple name, or the fully-qualified name when the policy is an
anonymous class (which has no simple name). Reporting it is best-effort: if the report cannot be
built, or would exceed 32 KiB, it is skipped (with a warning) rather than allowed to interfere
with connecting. It is serialized with Jackson, which the driver
allows you to exclude; on such a classpath the report is
skipped and an informational message is logged at startup. SESSION_ID is unaffected.
Reporting the configuration is enabled by default. To turn it off:
datastax-java-driver.advanced.driver-config-reporting.enabled = false
Note that this option does not affect SESSION_ID.
BasicLoadBalancingPolicy accessors widened from protected to public¶BasicLoadBalancingPolicy.getLocalDatacenter() and getLocalRack() are now public, so that the
configuration report above can describe the datacenter and rack the policy actually resolved rather
than whatever the profile currently says.
Binary compatibility is unaffected — an already-compiled subclass keeps working. But if you
extend BasicLoadBalancingPolicy and
override either method, you have to widen your override to public in order to recompile: Java does
not allow an override to reduce visibility.
The driver now supports automatic address translation for cloud private-endpoint deployments
(e.g. AWS PrivateLink, Azure Private Link, GCP Private Service Connect)
through the new client routes feature. When enabled, the driver reads endpoint mappings from the
system.client_routes system table and translates peer addresses transparently at connection time,
with automatic refresh on CLIENT_ROUTES_CHANGE events.
Configure it programmatically on the session builder:
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.config.ClientRoutesConfig;
import com.datastax.oss.driver.api.core.config.ClientRouteProxy;
import java.net.InetSocketAddress;
ClientRoutesConfig config = ClientRoutesConfig.builder()
.addEndpoint(new ClientRouteProxy(
"<connection-id>",
"my-cluster.region.provider.scylladb.com"))
.build();
CqlSession session = CqlSession.builder()
.addContactPoint(InetSocketAddress.createUnresolved("my-cluster.region.provider.scylladb.com", 9042))
.withClientRoutesConfig(config)
.withLocalDatacenter("datacenter1")
.build();
Or via HOCON configuration file:
datastax-java-driver {
basic.contact-points = [ "my-cluster.region.provider.scylladb.com:9042" ]
basic.load-balancing-policy.local-datacenter = "datacenter1"
advanced.client-routes {
endpoints = [
{ connection-id = "<connection-id>",
connection-addr = "my-cluster.region.provider.scylladb.com" }
]
}
}
Key points:
Mutually exclusive with a custom AddressTranslator and with cloud secure connect bundles —
providing both throws IllegalStateException at session build time.
Requires ScyllaDB Enterprise ≥ 2026.1 (scylladb/scylladb#27323). The feature is not available on ScyllaDB OSS or Apache Cassandra.
See Client routes for full details.
DefaultSslEngineFactory now includes an optional keystore reloading interval, for detecting changes in the local
client keystore file. This is relevant in environments with mTLS enabled and short-lived client certificates, especially
when an application restart might not always happen between a new keystore becoming available and the previous
keystore certificate expiring.
This feature is disabled by default for compatibility. To enable, see keystore-reload-interval in reference.conf.
With the completion of JAVA-3042 the driver now passes our automated test matrix for Java Driver releases. If you discover an issue with the Java Driver running on Java 17, please let us know. We will triage and address Java 17 issues.
The 4.16.0 release introduced support for the CQL vector datatype. This release modifies the CqlVector
value type used to represent a CQL vector to make it easier to use. CqlVector now implements the Iterable interface
as well as several methods modelled on the JDK’s List interface. For more, see
JAVA-3060.
The builder interface was replaced with factory methods that resemble similar methods on CqlDuration.
For example, the following code will create a keyspace and table, populate that table with some data, and then execute
a query that will return a vector type. This data is retrieved directly via Row.getVector() and the resulting
CqlVector value object can be interrogated directly.
try (CqlSession session = new CqlSessionBuilder().withLocalDatacenter("datacenter1").build()) {
session.execute("DROP KEYSPACE IF EXISTS test");
session.execute("CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}");
session.execute("CREATE TABLE test.foo(i int primary key, j vector<float, 3>)");
session.execute("CREATE CUSTOM INDEX ann_index ON test.foo(j) USING 'StorageAttachedIndex'");
session.execute("INSERT INTO test.foo (i, j) VALUES (1, [8, 2.3, 58])");
session.execute("INSERT INTO test.foo (i, j) VALUES (2, [1.2, 3.4, 5.6])");
session.execute("INSERT INTO test.foo (i, j) VALUES (5, [23, 18, 3.9])");
ResultSet rs=session.execute("SELECT j FROM test.foo WHERE j ann of [3.4, 7.8, 9.1] limit 1");
for (Row row : rs){
CqlVector<Float> v = row.getVector(0, Float.class);
System.out.println(v);
if (Iterables.size(v) != 3) {
throw new RuntimeException("Expected vector with three dimensions");
}
}
}
You can also use the CqlVector type with prepared statements:
PreparedStatement preparedInsert = session.prepare("INSERT INTO test.foo (i, j) VALUES (?,?)");
CqlVector<Float> vector = CqlVector.newInstance(1.4f, 2.5f, 3.6f);
session.execute(preparedInsert.bind(3, vector));
In some cases, it makes sense to access the vector directly as an array of some numerical type. This version supports such use cases by providing a codec which translates a CQL vector to and from a primitive array. Only float arrays are supported. You can find more information about this codec in the manual documentation on custom codecs
Before JAVA-2995, CodecNotFoundException
was extending RuntimeException. This is a discrepancy as all other exceptions extend
DriverException, which in turn extends RuntimeException.
This was causing integrators to do workarounds in order to react on all exceptions correctly.
The change introduced by JAVA-2995 shouldn’t be a problem for most users. But if your code was using a logic such as below, it won’t compile anymore:
try {
doSomethingWithDriver();
} catch(DriverException e) {
} catch(CodecNotFoundException e) {
}
You need to either reverse the catch order and catch CodecNotFoundException first:
try {
doSomethingWithDriver();
} catch(CodecNotFoundException e) {
} catch(DriverException e) {
}
Or catch only DriverException:
try {
doSomethingWithDriver();
} catch(DriverException e) {
}
JAVA-2959 changed the behavior for when a
request cannot be executed because all nodes tried were busy. Previously you would get back a
NoNodeAvailableException but you will now get back an AllNodesFailedException where the
getAllErrors map contains a NodeUnavailableException for that node.
Previous versions of the Java Driver defined a mandatory dependency on the Esri geometry library. This library offered support for primitive geometric types supported by DSE. As of driver 4.14.0 this dependency is now optional.
If you do not use DSE (or if you do but do not use the support for geometric types within DSE) you should experience no disruption. If you are using geometric types with DSE you’ll now need to explicitly declare a dependency on the Esri library:
<dependency>
<groupId>com.esri.geometry</groupId>
<artifactId>esri-geometry-api</artifactId>
<version>${esri.version}</version>
</dependency>
See the integration section in the manual for more details.
JAVA-2940 introduced an enhanced support for building GraalVM native images.
If you were building a native image for your application, please verify your native image builder configuration. Most of the extra configuration required until now is likely to not be necessary anymore.
Refer to this manual page for details.
JAVA-2951 introduced the ability to register more than one instance of the following interfaces:
Multiple components can now be registered both programmatically and through the configuration. If both approaches are used, components will add up and will all be registered (whereas previously, the programmatic approach would take precedence over the configuration one).
When using the programmatic approach to register multiple components, you should use the new
SessionBuilder methods addRequestTracker, addNodeStateListener and addSchemaChangeListener:
CqlSessionBuilder builder = CqlSession.builder();
builder
.addRequestTracker(tracker1)
.addRequestTracker(tracker2);
builder
.addNodeStateListener(nodeStateListener1)
.addNodeStateListener(nodeStateListener2);
builder
.addSchemaChangeListener(schemaChangeListener1)
.addSchemaChangeListener(schemaChangeListener2);
To support registration of multiple components through the configuration, the following configuration options were deprecated because they only allow one component to be declared:
advanced.request-tracker.class
advanced.node-state-listener.class
advanced.schema-change-listener.class
They are still honored, but the driver will log a warning if they are used. They should now be replaced with the following ones, that accept a list of classes to instantiate, instead of just one:
advanced.request-tracker.classes
advanced.node-state-listener.classes
advanced.schema-change-listener.classes
Example:
datastax-java-driver {
advanced {
# RequestLogger is a driver built-in tracker
request-tracker.classes = [RequestLogger,com.example.app.MyRequestTracker]
node-state-listener.classes = [com.example.app.MyNodeStateListener1,com.example.app.MyNodeStateListener2]
schema-change-listener.classes = [com.example.app.MySchemaChangeListener]
}
}
When more than one component of the same type is registered, the driver will distribute received signals to all components in sequence, by order of their registration, starting with the programmatically-provided ones. If a component throws an error, the error is intercepted and logged.
The MicroProfile Metrics library has been upgraded from version 2.4 to 3.0. Since this upgrade
involves backwards-incompatible binary changes, users of this library and of the
java-driver-metrics-microprofile module are required to take the appropriate action:
If your application is still using MicroProfile Metrics < 3.0, you can still upgrade the core
driver to 4.12, but you now must keep java-driver-metrics-microprofile in version 4.11 or lower,
as newer versions will not work.
If your application is using MicroProfile Metrics >= 3.0, then you must upgrade to driver 4.12 or
higher, as previous versions of java-driver-metrics-microprofile will not work.
@GetEntity and @SetEntity methods can now be lenient¶Thanks to JAVA-2935, @GetEntity and
@SetEntity methods now have a new lenient attribute.
If the attribute is false (the default value), then the source row or the target statement must
contain a matching column for every property in the entity definition. If such a column is not
found, an error will be thrown. This corresponds to the mapper’s current behavior prior to the
introduction of the new attribute.
If the new attribute is explicitly set to true however, the mapper will operate on a best-effort
basis and attempt to read or write all entity properties that have a matching column in the source
row or in the target statement, leaving unmatched properties untouched.
This new, lenient behavior allows to achieve the equivalent of driver 3.x lenient mapping.
Read the manual pages on @GetEntity methods and @SetEntity methods for more details and examples of lenient mode.
Thanks to JAVA-2704, 4.11.0 is the first version in the driver 4.x series to fully support Cassandra’s native protocol version 5, which has been promoted from beta to production-ready in the upcoming Cassandra 4.0 release.
Users should not experience any disruption. When connecting to Cassandra 4.0, V5 will be transparently selected as the protocol version to use.
JAVA-2872 introduced the ability to configure
how metric identifiers are generated. Metric names can now be configured, but most importantly,
metric tags are now supported. See the metrics section of the online
manual, or the advanced.metrics.id-generator section in the
reference.conf file for details.
Users should not experience any disruption. However, those using metrics libraries that support tags
are encouraged to try out the new TaggingMetricIdGenerator, as it generates metric names and tags
that will look more familiar to users of libraries such as Micrometer or MicroProfile Metrics (and
look nicer when exported to Prometheus or Graphite).
NodeDistanceEvaluator API¶All driver built-in load-balancing policies now accept a new optional component called NodeDistanceEvaluator. This component gets invoked each time a node is added to the cluster or comes back up. If the evaluator returns a non-null distance for the node, that distance will be used, otherwise the driver will use its built-in logic to assign a default distance to it.
This component replaces the old “node filter” component. As a consequence, all withNodeFilter
methods in SessionBuilder are now deprecated and should be replaced by the equivalent
withNodeDistanceEvaluator methods.
If you have an existing node filter implementation, it can be converted to a NodeDistanceEvaluator
very easily:
Predicate<Node> nodeFilter = ...
NodeDistanceEvaluator nodeEvaluator =
(node, dc) -> nodeFilter.test(node) ? null : NodeDistance.IGNORED;
The above can also be achieved by an adapter class as shown below:
public class NodeFilterToDistanceEvaluatorAdapter implements NodeDistanceEvaluator {
private final Predicate<Node> nodeFilter;
public NodeFilterToDistanceEvaluatorAdapter(@NonNull Predicate<Node> nodeFilter) {
this.nodeFilter = nodeFilter;
}
@Nullable @Override
public NodeDistance evaluateDistance(@NonNull Node node, @Nullable String localDc) {
return nodeFilter.test(node) ? null : NodeDistance.IGNORED;
}
}
Finally, the datastax-java-driver.basic.load-balancing-policy.filter.class configuration option
has been deprecated; it should be replaced with a node distance evaluator class defined by the
datastax-java-driver.basic.load-balancing-policy.evaluator.class option instead.
JAVA-2899 re-introduced the ability to perform cross-datacenter failover using the driver’s built-in load balancing policies. See Load balancing in the manual for details.
Cross-datacenter failover is disabled by default, therefore existing applications should not experience any disruption.
RetryVerdict API¶JAVA-2900 introduced RetryVerdict, a new
interface that allows custom retry policies to customize the request before it is retried.
For this reason, the following methods in the RetryPolicy interface were added; they all return
a RetryVerdict instance:
The following methods were deprecated and will be removed in the next major version:
Driver 4.10.0 also re-introduced a retry policy whose behavior is equivalent to the
DowngradingConsistencyRetryPolicy from driver 3.x. See this
FAQ entry
for more information.
Uuids utility class¶JAVA-2449 modified the implementation of
Uuids.random(): this/index.md method does not delegate anymore to the JDK’s java.util.UUID.randomUUID()
implementation, but instead re-implements random UUID generation using the non-cryptographic
random number generator java.util.Random.
For most users, non-cryptographic strength is enough and this change should translate into better
performance when generating UUIDs for database insertion. However, in the unlikely case where your
application requires cryptographic strength for UUID generation, you should update your code to
use java.util.UUID.randomUUID() instead of com.datastax.oss.driver.api.core.uuid.Uuids.random()
from now on.
This release also introduces two new methods for random UUID generation:
Uuids.random(Random): similar to Uuids.random() but allows to pass a custom instance of
java.util.Random and/or re-use the same instance across calls.
Uuids.random(SplittableRandom): similar to Uuids.random() but uses a
java.util.SplittableRandom instead.
JAVA-2871 now allows for a more fine-grained control over which keyspaces should qualify for metadata and token map computation, including the ability to exclude keyspaces based on their names.
From now on, the following keyspaces are automatically excluded:
The system keyspace;
All keyspaces starting with system_;
DSE-specific keyspaces:
All keyspaces starting with dse_;
The solr_admin keyspace;
The OpsCenter keyspace.
This means that they won’t show up anymore in Metadata.getKeyspaces(), and TokenMap will return
empty replicas and token ranges for them. If you need the driver to keep computing metadata and
token map for these keyspaces, you now must modify the following configuration option:
datastax-java-driver.advanced.metadata.schema.refreshed-keyspaces.
Until driver 4.9.0, the driver declared a mandatory dependency to Apache TinkerPop, a library required only when connecting to DSE Graph. The vast majority of Apache Cassandra users did not need that library, but were paying the price of having that heavy-weight library in their application’s classpath.
Starting with driver 4.10.0, TinkerPop is now considered an optional dependency.
Regular users of Apache Cassandra that do not use DSE Graph will not notice any disruption.
DSE Graph users, however, will now have to explicitly declare a dependency to Apache TinkerPop. This
can be achieved with Maven by adding the following dependencies to the <dependencies> section of
your POM file:
<dependency>
<groupId>org.apache.tinkerpop</groupId>
<artifactId>gremlin-core</artifactId>
<version>${tinkerpop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tinkerpop</groupId>
<artifactId>tinkergraph-gremlin</artifactId>
<version>${tinkerpop.version}</version>
</dependency>
See the integration section in the manual for more details as well as a driver vs. TinkerPop version compatibility matrix.
These versions are subject to JAVA-2676, a bug that causes performance degradations in certain scenarios. We strongly recommend upgrading to at least 4.6.1.
DataStax Enterprise support is now available directly in the main driver. There is no longer a separate DSE driver.
The great news is that reactive execution is now available for everyone.
See the CqlSession.executeReactive methods.
Apart from that, the only visible change is that DSE-specific features are now exposed in the API:
new execution methods: CqlSession.executeGraph, CqlSession.executeContinuously*. They all
have default implementations so this doesn’t break binary compatibility. You can just ignore them.
new driver dependencies: TinkerPop, ESRI, Reactive Streams. If you want to keep your classpath lean, you can exclude some dependencies when you don’t use the corresponding DSE features; see the Integration>Driver dependencies section.
Adjust your Maven coordinates to use the unified artifact:
<!-- Replace: -->
<dependency>
<groupId>com.datastax.dse</groupId>
<artifactId>dse-java-driver-core</artifactId>
<version>2.3.0</version>
</dependency>
<!-- By: -->
<dependency>
<groupId>com.scylladb</groupId>
<artifactId>java-driver-core</artifactId>
<version>4.4.0</version>
</dependency>
<!-- Do the same for the other modules: query builder, mapper... -->
The new driver is a drop-in replacement for the DSE driver. Note however that we’ve deprecated a few DSE-specific types in favor of their OSS equivalents. They still work, so you don’t need to make the changes right away; but you will get deprecation warnings:
DseSession: use CqlSession instead, it can now do everything that a DSE session does. This
also applies to the builder:
// Replace:
DseSession session = DseSession.builder().build()
// By:
CqlSession session = CqlSession.builder().build()
DseDriverConfigLoader: the driver no longer needs DSE-specific config loaders. All the factory
methods in this class now redirect to DriverConfigLoader. On that note, dse-reference.conf
does not exist anymore, all the driver defaults are now in
reference.conf.
plain-text authentication: there is now a single implementation that works with both Cassandra and
DSE. If you used DseProgrammaticPlainTextAuthProvider, replace it by
PlainTextProgrammaticAuthProvider. Similarly, if you wrote a custom implementation by
subclassing DsePlainTextAuthProviderBase, extend PlainTextAuthProviderBase instead.
DseLoadBalancingPolicy: DSE-specific features (the slow replica avoidance mechanism) have been
merged into DefaultLoadBalancingPolicy. DseLoadBalancingPolicy still exists for backward
compatibility, but it is now identical to the default policy.
The default class loader used by the driver when instantiating classes by reflection changed. Unless specified by the user, the driver will now use the same class loader that was used to load the driver classes themselves, in order to ensure that implemented interfaces and implementing classes are fully compatible.
This should ensure a more streamlined experience for OSGi users, who do not need anymore to define a specific class loader to use.
However if you are developing a web application and your setup corresponds to the following
scenario, then you will now be required to explicitly define another class loader to use: if in your
application the driver jar is loaded by the web server’s system class loader (for example,
because the driver jar was placed in the “/lib” folder of the web server), then the default class
loader will be the server’s system class loader. Then if the application tries to load, say, a
custom load balancing policy declared in the web app’s “WEB-INF/lib” folder, then the default class
loader will not be able to locate that class. Instead, you must use the web app’s class loader, that
you can obtain in most web environments by calling Thread.getContextClassLoader():
CqlSession.builder()
.addContactEndPoint(...)
.withClassLoader(Thread.currentThread().getContextClassLoader())
.build();
See the javadocs of SessionBuilder.withClassLoader for more information.
4.1.0 marks the introduction of the new object mapper in the 4.x series.
Like driver 3, it relies on annotations to configure mapped entities and queries. However, there are a few notable differences:
it uses compile-time annotation processing instead of runtime reflection;
the “mapper” and “accessor” concepts have been unified into a single “DAO” component, that handles both pre-defined CRUD patterns, and user-provided queries.
Refer to the mapper manual for all the details.
NettyOptions#afterBootstrapInitialized is now responsible for setting socket options on driver
connections (see advanced.socket in the configuration). If you had written a custom NettyOptions
for 4.0, you’ll have to copy over – and possibly adapt – the contents of
DefaultNettyOptions#afterBootstrapInitialized (if you didn’t override NettyOptions, you don’t
have to change anything).
Version 4 is a major redesign of the internal architecture, and is not binary compatible with previous versions. That upgrade has its own page: see Migrating from Java Driver 3.x.
Was this page helpful?
ScyllaDB Java Driver is available under the Apache v2 License. ScyllaDB Java Driver is a fork of DataStax Java Driver. See Copyright here.