import com.evolutiongaming.scassandra._
val config = CassandraConfig.Default
val session = for {
cluster <- CassandraCluster.of[IO](config, clusterId = 0)
session <- cluster.connect
} yield session
val name = for {
resultSet <- session.use { session => session.execute("SELECT name FROM users") }
} yield {
val row = resultSet.one()
row.decode[String]("name")
}
name.unsafeRunSync()addSbtPlugin("com.evolution" % "sbt-artifactory-plugin" % "0.0.2")
libraryDependencies += "com.evolutiongaming" %% "scassandra" % "3.2.1"scassandra-v4 is a thin wrapper over the Java driver 4.
It is published separately, under a new organization and package, so both versions could be used in the same
application during the migration.
libraryDependencies += "com.evolution" %% "scassandra-v4" % "<version>"
// translation of the configuration of scassandra for the Java driver 3, depends on both versions
libraryDependencies += "com.evolution" %% "scassandra-v4-compat" % "<version>"import cats.effect.IO
import com.evolution.scassandra._
import com.evolution.scassandra.syntax._
import com.typesafe.config.ConfigFactory
val config = CassandraConfig.fromConfig(ConfigFactory.load(), "my-service.cassandra").fold(
failures => throw new IllegalArgumentException(failures.prettyPrint()),
identity,
)
val names = CassandraSession.make[IO](config).use { session =>
session.executeStream("SELECT name FROM users").map(_.decode[String]("name")).toList
}The configuration is the configuration of the Java driver 4 (reference), which could be read from any path, so an application could have several sessions with different configurations:
my-service.cassandra {
basic.contact-points = ["host1:9042", "host2:9042"]
basic.load-balancing-policy.local-datacenter = "dc1"
}CassandraConfig.fromConfig(config)reads the default path of the driver,datastax-java-driver;CassandraConfig.fromConfig(config, "my-service.cassandra")reads the given path;ConfigReader[CassandraConfig]reads it with pureconfig.
The missing options get the defaults of the Java driver 4, which differ from the defaults of scassandra for the Java driver 3, e.g. a 2s request timeout instead of 12s.
The configuration of scassandra for the Java driver 3 could be kept as is, and moved to the configuration of the
driver 4 gradually, under the driver key:
my-service.cassandra {
contact-points = ["host1", "host2"]
query.consistency = "LOCAL_QUORUM"
driver {
basic.request.timeout = 3 seconds
}
}scassandra-v4-compat reads and translates it, using the reader of scassandra for the Java driver 3, so the
values are interpreted exactly as before, and the missing keys get the defaults of scassandra for the Java
driver 3, not of the Java driver 4:
CassandraConfigCompat.fromLegacyConfig(config, "my-service.cassandra")reads it;CassandraConfigCompat.legacyConfigReaderreads it with pureconfig;CassandraConfigCompat.fromV3(config, driverOverrides)translates the case class of scassandra for the Java driver 3.
They return a TranslatedConfig: the config to create a session with, and the warnings about the keys that
have no equivalent in the Java driver 4 and are ignored. The warnings are not logged, log them yourself.
Once all the keys are moved to driver, read it with CassandraConfig.fromConfig(config, "my-service.cassandra.driver")
and drop scassandra-v4-compat. Mind that the options missing under driver then get the defaults of the Java
driver 4 instead of the defaults of scassandra for the Java driver 3, so set every option which should keep its
previous value, e.g. basic.request.timeout = 12 seconds.
See the translation of the legacy configuration for the details on how each key is translated, and which keys have no equivalent.
Most of the API is kept: CassandraSession, DecodeByName, DecodeByIdx, DecodeRow, EncodeByName,
EncodeByIdx, EncodeRow, UpdateByName, UpdateByIdx, UpdateRow, ToCql, TableName,
ReplicationStrategyConfig, CreateKeyspaceIfNotExists, CassandraHealthCheck, NextHostRetryPolicy and the
syntax object. Replace com.evolutiongaming.scassandra with com.evolution.scassandra in the imports, and
mind the following differences:
- Statements are immutable.
bind().encode("id", id)returns a new statement, which should be executed, while the original one stays unchanged. Code ignoring the returned statement compiles, but loses the values. - There is no cluster. A session of the driver 4 holds the connections and the metadata of the whole
cluster, so
CassandraClusterandCassandraClusterOfare replaced withCassandraSession.make. UseCassandraSession.make(builder)to customize the session, e.g. to register listeners, codecs or a custom configuration loader, instead ofCassandraClusterOf.addClusterJObserveHook. - The keyspace of a session is set when it is created, with
basic.session-keyspaceinstead ofconnect(keyspace). AUSEquery still changes it, which the driver 4 discourages, as it applies to all the requests in flight. - Results are paged asynchronously.
executereturns the driverAsyncResultSet, usestreamorexecuteStreamto read all the pages. Do not reuse anAsyncResultSet: the rows of its current page can be read only once, so a second stream of it silently misses them, whileexecuteStreamexecutes the statement again on each consumption. - The driver types are used directly, e.g.
ConsistencyLevel,Metadata,CqlDuration,java.time.LocalDate,CqlIdentifier. The codecs of the driver could be used withDecodeByName.fromCodec,fromClassandfromGenericType, and similar methods of the other type classes. - The request timeout applies to the whole request, including retries and speculative executions.
- Policies are set in the configuration. The driver 4 creates them itself, so
NextHostRetryPolicycannot be set on a statement. Set it in the configuration, with the requiredmax-retries:To apply it to some statements only, set it in an execution profile, e.g.advanced.retry-policy { class = com.evolution.scassandra.NextHostRetryPolicy max-retries = 3 }
profiles.journal.advanced.retry-policy, and select the profile withstatement.setExecutionProfileName("journal"). The driver 4 has noLoggingRetryPolicy. ReplicationStrategyConfigfails on an unknown strategy or an invalid value, where v3 silently usedSimpleStrategywith a replication factor of 1.ReplicationStrategyConfigreads thesimpleblock withoutreplication-strategy, where v3 ignored it and used a replication factor of 1. E.g.simple.replication-factor = 3alone creates a keyspace with 3 replicas.- Resources are created with
makeinstead ofof, i.e.CassandraSession.make,CassandraHealthCheck.make. CassandraHealthCheck.maketakes the session of the application, not aResourceof a session, as a session of the driver 4 is heavyweight, and a separate one would not check the session in use.- Removed:
init,newSessionandstate/stateSnapshotof the session, usesession.unwrapfor the driver features not covered by the wrapper, e.g.getMetadata.getNodesandNode.getOpenConnections;- the metadata wrappers, use the driver
Metadatareturned bysession.metadata; execute(query, values*), useSimpleStatement.newInstance(query, values*);trace, usesetTracingof the statement;FromGFuture, the driver 4 usesCompletionStage;Masked,CassandraConfig.toStringmasks the values of the keys likepassword,secret,tokenandcredential;- the configuration case classes, e.g.
PoolingConfigandQueryConfig, use the configuration of the driver.