Scylla University: Coding with Scala, Part 1
Tags: Scylladb, Scala Programming, Scala, Database, NoSQL

by Guy Shtub

This post is based on the Scylla Specific Drivers, Overview, Paging and Shard Awareness lesson in Scylla University, ScyllaDB’s free resource to learn NoSQL database development and Scylla administration. You can find the lesson here in Scylla University.

In a previous lesson, we explained how a Scylla Administrator restores and backs-up a cluster. As the number of mutants is on the rise, Division 3 decided that we must use more applications to connect to the mutant catalog and decided to hire Java developers to create powerful applications that can monitor the mutants. This lesson will explore how to connect to a Scylla cluster using the Phantom library for Scala: a Scala-idiomatic wrapper over the standard Java driver.

When creating applications that communicate with a database such as Scylla, it is crucial that the programming language being used includes support for database connectivity. Since Scylla is compatible with Cassandra, we can use any of the available Cassandra libraries. For example, in Go, there is GoCQL and GoCQLX. In Node.js, there is the cassandra-driver. For the JVM, we have the standard Java driver available. Scala applications on JVM can use it, but a library tailored for Scala’s features offers a more enjoyable and type-safe development experience. Since Division 3 wants to start investing in Scala, let’s begin by writing a sample Scala application.

Creating a Sample Scala Application

The sample application that we will create connects to a Scylla cluster, displays the contents of the Mutant Catalog table, inserts and deletes data, and shows the table’s contents after each action. First, we will go through each section of the code used and then explain how to run the code in a Docker container that accesses the Scylla Mutant Monitoring cluster. You can see the file here: scylla-code-samples/mms/scala/scala-app/src/main/scala/com/scylla/mms/App.scala.

As mentioned, we’re using the Phantom library for working with Scylla. The library’s recommended usage pattern involves modeling the database, table, and data service as classes. We’ll delve into those in a future lesson.

First, let’s focus on how we set up the connection itself. We start by importing the library:

import com.outworkers.Phantom.dsl._

This import brings all the necessary types and implicits into scope. The main entry point for our application is the App object’s main method. In it, we set up the connection to the cluster:

Next, in a list, we specify the DNS names through which the application contacts Scylla and the keyspace to use.

Finally, we instantiate the MutantsDatabase and the MutantsService classes. These classes represent, respectively, the collection of tables in use by our application and a domain-specific interface to interact with those tables.

We’re not operating on low-level types like ResultSet or Row objects with the Phantom library, but rather on strongly-typed domain objects. Specifically, for this lesson, we will be working with a Mutant data type defined as follows:

You can find the file here: scylla-code-samples/mms/scala/scala-app/src/main/scala/com/scylla/mms/model/Mutant.scala

The MutantsService class contains implementations of the functionality required for this lesson. You can find it here:

scylla-code-samples/mms/scala/scala-app/src/main/scala/com/scylla/mms/service/MutantService.scala.

Let’s consider how we would fetch all the mutant definitions present in the table:

Phantom provides a domain-specific language for interacting with Scylla. Instead of threading CQL strings through our code, we’re working with a strongly-typed interface that verifies, at compile-time, that the queries we are generating are correct (according to the data type definitions).

In this case, we are using the select method to fetch all Mutant records from Scylla. Note that by default, Phantom uses Scala’s Future data type for representing all the Scylla operations. Behind the scenes, Phantom uses a non-blocking network I/O to efficiently perform all of the queries. If you’d like to know more about Future, this is a good tutorial.

Moving forward, the following method allows us to store an instance of the Mutant data type into Scylla:

Everything is strongly typed, and as such, we’re using actual instances of our data types to interact with Scylla. This gives us increased maintainability of our codebase. If we refactor the Mutant data type, this code will no longer compile, and we’ll be forced to fix it.

And lastly, here’s a method that deletes a mutant with a specific name:

We can see that Phantom’s DSL can represent predicates in a type-safe fashion as well. We’re forced to use a matching type for comparing the firstName and lastName.

We can sequence the calls to these methods in our main entry point using a for comprehension on the Future values returned. The code below is from the following file:

scylla-code-samples/mms/scala/scala-app/src/main/scala/com/scylla/mms/App.scala

In this example, we execute all the futures sequentially (with some additional callbacks registered on them for printing out the information).

With the coding part done, let’s set up a Scylla Cluster and then run the sample application in Docker.

Set-up a Scylla Cluster

The example requires a single DC cluster. Follow this procedure to remove previous clusters and set up a new cluster.

Once the cluster is up, we’ll create the catalog keyspace and populate it with data.

The first task is to create the keyspace named catalog for the mutants’ catalog.

docker exec -it mms_scylla-node1_1 cqlsh CREATE KEYSPACE catalog WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy','DC1' : 3};

Now that the keyspace is created, it is time to create a table to hold the mutant data.

use catalog; CREATE TABLE mutant_data (

first_name text,

last_name text,

address text,

picture_location text,

PRIMARY KEY((first_name, last_name)));

Now let’s add a few mutants to the catalog with the following statements:

insert into mutant_data ("first_name","last_name","address","picture_location") VALUES ('Bob','Loblaw','1313 Mockingbird Lane', 'http://www.facebook.com/bobloblaw');

insert into mutant_data ("first_name","last_name","address","picture_location") VALUES ('Bob','Zemuda','1202 Coffman Lane', 'http://www.facebook.com/bzemuda');

insert into mutant_data ("first_name","last_name","address","picture_location") VALUES ('Jim','Jeffries','1211 Hollywood Lane', 'http://www.facebook.com/jeffries');

Building the Scala Example

If you previously built the Scala Docker, you can skip directly to the section Running the Scala Example below.

Otherwise, to build the application in Docker, change into the scala subdirectory in scylla-code-samples:

cd scylla-code-samples/mms/scala

Now we can build and run the container:

docker build -t scala-app . docker run -d --net=mms_web --name some-scala-app scala-app

To connect to the shell of the container, run the following command:

docker exec -it some-scala-app sh

Running the Scala Example

Finally, the sample Scala application can be run:

java -jar scala-app/target/scala-2.13/mms-scala-app-assembly-0.1.0-SNAPSHOT.jar

The output of the application will be:

Conclusion

In this lesson, we explained how to create a sample Scala application that executes a few basic CQL operations on a Scylla cluster using the Phantom library. These were only the basics, and there are more exciting topics that Division 3 wants developers to explore. In the next lesson, we will review the structure of an application using the Phantom library.

In the meantime, please be safe out there and continue to monitor the mutants!

*This lesson was written with the help of Itamar Ravid. Itamar is a Distributed systems engineer with a decade of experience in a wide range of technologies. He focuses on JVM-based microservices in Scala using functional programming. Thank you, Itamar!

TAKE THIS LESSON IN SCYLLA UNIVERSITY