Skip to content

Commit

Permalink
Upgrade Java driver for Cassandra to latest version
Browse files Browse the repository at this point in the history
This allows a full support of Cassandra 5 without errors/warnings
  • Loading branch information
maximevw committed May 14, 2024
1 parent 18ea976 commit 5479ced
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 67 deletions.
2 changes: 1 addition & 1 deletion modules/cassandra/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ configurations.all {

dependencies {
api project(":database-commons")
api "com.datastax.cassandra:cassandra-driver-core:3.10.0"
api "com.datastax.oss:java-driver-core:4.17.0"

testImplementation 'com.datastax.oss:java-driver-core:4.17.0'
testImplementation 'org.assertj:assertj-core:3.25.1'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package org.testcontainers.containers;

import com.datastax.driver.core.Cluster;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
import com.datastax.oss.driver.internal.core.loadbalancing.DcInferringLoadBalancingPolicy;
import com.github.dockerjava.api.command.InspectContainerResponse;
import org.apache.commons.io.IOUtils;
import org.testcontainers.containers.delegate.CassandraDatabaseDelegate;
Expand All @@ -14,6 +19,7 @@
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Optional;

import javax.script.ScriptException;
Expand Down Expand Up @@ -48,8 +54,6 @@ public class CassandraContainer<SELF extends CassandraContainer<SELF>> extends G

private String initScriptPath;

private boolean enableJmxReporting;

/**
* @deprecated use {@link #CassandraContainer(DockerImageName)} instead
*/
Expand All @@ -67,7 +71,6 @@ public CassandraContainer(DockerImageName dockerImageName) {
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);

addExposedPort(CQL_PORT);
this.enableJmxReporting = false;

withEnv("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch");
withEnv("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0");
Expand Down Expand Up @@ -156,14 +159,6 @@ public SELF withInitScript(String initScriptPath) {
return self();
}

/**
* Initialize Cassandra client with JMX reporting enabled or disabled
*/
public SELF withJmxReporting(boolean enableJmxReporting) {
this.enableJmxReporting = enableJmxReporting;
return self();
}

/**
* Get username
*
Expand All @@ -189,37 +184,50 @@ public String getPassword() {
}

/**
* Get configured Cluster
*
* Can be used to obtain connections to Cassandra in the container
* Get a session on the configured cluster.
*
* @deprecated For Cassandra driver 3.x, use {@link #getHost()} and {@link #getMappedPort(int)} with
* the driver's {@link Cluster#builder() Cluster.Builder} {@code addContactPoint(String)} and
* {@code withPort(int)} methods to create a Cluster object. For Cassandra driver 4.x, use
* {@link #getContactPoint()} and {@link #getLocalDatacenter()} with the driver's {@code CqlSession.builder()}
* {@code addContactPoint(InetSocketAddress)} and {@code withLocalDatacenter(String)} methods to create
* a Session Object. See https://docs.datastax.com/en/developer/java-driver/ for more on the driver.
* Can be used to obtain connections to Cassandra in the container.
*/
@Deprecated
public Cluster getCluster() {
return getCluster(this, enableJmxReporting);
}
public CqlSession getCqlSession() {
return getCqlSession(this);
}

public static CqlSession getCqlSession(ContainerState containerState) {
final ProgrammaticDriverConfigLoaderBuilder driverConfigLoaderBuilder = DriverConfigLoader.programmaticBuilder();
boolean dcAvailable = containerState.getClass().isAssignableFrom(CassandraContainer.class);

// If the ContainerState is not a CassandraContainer instance, use DcInferringLoadBalancingPolicy to not have
// to specify the local datacenter to establish the connection, otherwise we can keep the default load balancing
// policy requiring to explicitly specify the local data center.
// See https://docs.datastax.com/en/developer/java-driver/latest/manual/core/configuration/reference/ for
// further information.
if (!dcAvailable) {
driverConfigLoaderBuilder.withClass(
DefaultDriverOption.LOAD_BALANCING_POLICY_CLASS,
DcInferringLoadBalancingPolicy.class
);
}

@Deprecated
public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) {
final Cluster.Builder builder = Cluster
// Ignore warnings when a CQL script modifies the current keyspace. Typically, this generates unnecessary logs
// when executing an init script using multiple keyspaces.
driverConfigLoaderBuilder.withBoolean(DefaultDriverOption.REQUEST_WARN_IF_SET_KEYSPACE, false);

// Using Java driver 4.x, a feature called debouncing has been introduced: schema and topology changes received
// from the server could be accumulated before being processed by the driver. For more information, see:
// https://docs.datastax.com/en/developer/java-driver/latest/manual/core/performance/index.html#debouncing
// and https://stackoverflow.com/a/74152732/13292108
// To maintain good performance, reduce the default values for the schema debouncing properties.
driverConfigLoaderBuilder.withInt(DefaultDriverOption.METADATA_SCHEMA_MAX_EVENTS, 1);
driverConfigLoaderBuilder.withDuration(DefaultDriverOption.METADATA_SCHEMA_WINDOW, Duration.ofMillis(1));

final CqlSessionBuilder cqlSessionBuilder = CqlSession
.builder()
.addContactPoint(containerState.getHost())
.withPort(containerState.getMappedPort(CQL_PORT));
if (!enableJmxReporting) {
builder.withoutJMXReporting();
.withConfigLoader(driverConfigLoaderBuilder.build())
.addContactPoint(new InetSocketAddress(containerState.getHost(), containerState.getMappedPort(CQL_PORT)));
if (dcAvailable) {
cqlSessionBuilder.withLocalDatacenter(((CassandraContainer<?>) containerState).getLocalDatacenter());
}
return builder.build();
}

@Deprecated
public static Cluster getCluster(ContainerState containerState) {
return getCluster(containerState, false);
return cqlSessionBuilder.build();
}

/**
Expand All @@ -240,7 +248,6 @@ public String getLocalDatacenter() {
return getEnvMap().getOrDefault("CASSANDRA_DC", DEFAULT_LOCAL_DATACENTER);
}

@Deprecated
private DatabaseDelegate getDatabaseDelegate() {
return new CassandraDatabaseDelegate(this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.testcontainers.containers.delegate;

import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.CassandraContainer;
Expand All @@ -16,14 +16,14 @@
*/
@Slf4j
@RequiredArgsConstructor
public class CassandraDatabaseDelegate extends AbstractDatabaseDelegate<Session> {
public class CassandraDatabaseDelegate extends AbstractDatabaseDelegate<CqlSession> {

private final ContainerState container;

@Override
protected Session createNewConnection() {
protected CqlSession createNewConnection() {
try {
return CassandraContainer.getCluster(container).newSession();
return CassandraContainer.getCqlSession(container);
} catch (DriverException e) {
log.error("Could not obtain cassandra connection");
throw new ConnectionCreationException("Could not obtain cassandra connection", e);
Expand Down Expand Up @@ -51,9 +51,9 @@ public void execute(
}

@Override
protected void closeConnectionQuietly(Session session) {
protected void closeConnectionQuietly(CqlSession session) {
try {
session.getCluster().close();
session.close();
} catch (Exception e) {
log.error("Could not close cassandra connection", e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package org.testcontainers.containers;

import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.testcontainers.containers.wait.CassandraQueryWaitStrategy;
import org.testcontainers.utility.DockerImageName;

import java.net.InetSocketAddress;

import static org.assertj.core.api.Assertions.assertThat;

@Slf4j
Expand Down Expand Up @@ -112,7 +113,7 @@ public void testCassandraQueryWaitStrategy() {
public void testCassandraGetCluster() {
try (CassandraContainer<?> cassandraContainer = new CassandraContainer<>()) {
cassandraContainer.start();
ResultSet resultSet = performQuery(cassandraContainer.getCluster(), BASIC_QUERY);
ResultSet resultSet = performQuery(cassandraContainer.getCqlSession(), BASIC_QUERY);
assertThat(resultSet.wasApplied()).as("Query was applied").isTrue();
assertThat(resultSet.one().getString(0)).as("Result set has release_version").isNotNull();
}
Expand All @@ -127,18 +128,22 @@ private void testInitScript(CassandraContainer<?> cassandraContainer) {
}

private ResultSet performQuery(CassandraContainer<?> cassandraContainer, String cql) {
Cluster explicitCluster = Cluster
final CqlSession cqlSession = CqlSession
.builder()
.addContactPoint(cassandraContainer.getHost())
.withPort(cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT))
.addContactPoint(
new InetSocketAddress(
cassandraContainer.getHost(),
cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT)
)
)
.withLocalDatacenter(cassandraContainer.getLocalDatacenter())
.build();
return performQuery(explicitCluster, cql);
return performQuery(cqlSession, cql);
}

private ResultSet performQuery(Cluster cluster, String cql) {
try (Cluster closeableCluster = cluster) {
Session session = closeableCluster.newSession();
return session.execute(cql);
}
private ResultSet performQuery(CqlSession session, String cql) {
final ResultSet rs = session.execute(cql);
session.close();
return rs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,19 @@

import static org.assertj.core.api.Assertions.assertThat;

public class CassandraDriver3Test {
public class CassandraServer5Test {

@Rule
public CassandraContainer<?> cassandra = new CassandraContainer<>("cassandra:3.11.2");
public CassandraContainer<?> cassandra = new CassandraContainer<>("cassandra:5");

@Test
public void testCassandraGetContactPoint() {
try (
// cassandra {
CqlSession session = CqlSession
.builder()
.addContactPoint(this.cassandra.getContactPoint())
.withLocalDatacenter(this.cassandra.getLocalDatacenter())
.build()
// }
) {
session.execute(
"CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" +
Expand All @@ -31,7 +29,7 @@ public void testCassandraGetContactPoint() {

KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test"));

assertThat(keyspace).as("keyspace created").isNotNull();
assertThat(keyspace).as("test keyspace created").isNotNull();
}
}
}

0 comments on commit 5479ced

Please sign in to comment.