Rust and Scylla

16 min to complete

Scylla and Rust

In this lesson, we’ll build a simple Rust application that will connect to a Scylla cluster and perform basic queries. For establishing communication between our application and the Scylla server, we will use CDRS, which is an open-source Scylla driver for Rust.

Starting Scylla DB in Docker

If you haven’t done so yet, download the example from git:

git clone https://github.com/scylladb/scylla-code-samples.git
cd scylla-code-samples/rust/ps-logger/

To quickly get Scylla up and running, we will use the official Docker image:

docker run \
  -p 9042:9042/tcp \
  --name some-scylla \
  --hostname some-scylla \
  -d scylladb/scylla:4.1.0 \
   --smp 1 --memory=750M --overprovisioned 1

Note that in this lesson, we assume that the Scylla instance is run on a local machine. 

Wait a few seconds until the node is up. We will use cqlsh to create a keyspace and table on our Scylla server:

docker exec -it some-scylla cqlsh

Data schema

Our application will be able to store and query temperature time-series data. Each measurement will contain the following information:

  • The sensor ID for the sensor that measured the temperature
  • The time the temperature was measured
  • The temperature value 

First, let’s create a keyspace called tutorial:

CREATE KEYSPACE IF NOT EXISTS tutorial
  WITH REPLICATION = {
    'class': 'SimpleStrategy',
    'replication_factor': 1
};

As this is just an example, we’ll use SimpleStrategy with a single datacenter. Our cluster has a single node, so we’ll set the Replication Factor to one.

Keep in mind that SimpleStrategy should not be used in production.

Based on the desired query being the temperature reported by a specific device for a given time interval, we’ll create the following table:

CREATE TABLE IF NOT EXISTS tutorial.temperature (
  device UUID,
  time timestamp,
  temperature smallint,
  PRIMARY KEY(device, time)
);

You can learn more about Basic Data Modeling here

The application we’re building will be able to query all temperatures measured by a given device within a selected time frame. That’s why we will use the following SELECT query:

SELECT * FROM tutorial.temperature
  WHERE device = ?
  AND time > ?
  AND time < ?; 

where ? will be replaced with actual values – device ID, time-from, and time-to, respectively.

Rust and connection to the DB

If you don’t already have Rust and Cargo installed, go ahead and install it using the rustup.rs toolchain:

exit
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 

This will help you to install Rust compiler with other helpful tools.

Our application name is temperature, and the required dependencies are defined in the Cargo.toml file:

Where:

  • CDRS – Rust Scylla/Casandra driver
  • cdrs_helpers_derive helper utilities, which will help us to implement marshaling and unmarshalling for our data types.
  • uuid – package that provides UUID.
  • time – package for working with time primitives.

Next, we’ll declare these external dependencies inside the /src/main.rs file:

The file /src/db.rs will hold the logic for working with the Scylla instance. To create an authorized connection with the database server, CDRS needs the following information:

  • authenticator – any structure that implements an Authenticator for a given authentication strategy defined by the Scylla Server. For this example, we won’t use authentication so NoneAuthenticator should be used:
  • cluster configuration – a list of connection configurations provided for each node in a cluster.

Having that provided, we can create a new CDRS session (still in the /src/db.rs file):

In this lesson, the SingleNode load balancing strategy is used. CDRS also provides Random, RoundRobin, and RoundRobinSync strategies. If none of these satisfy your requirements, you can create your own strategy by implementing LoadBalancingStrategy for your structure, see an example here

Next, still in the file /src/db.rs, we define functions for creating the keyspace and table where we will store temperature measurements. We’ll use queries for creating the keyspace and a table:

Next, we’ll create a structure that will represent a single temperature measurement that will be stored in the tutorial.temperature table this is defined in the file /src/temperature_measurement.rs:

As part of standard Debug, we’ve derived one extra trait – TryFromRow, provided by CDRS. It will help us to convert a Scylla row into a TemperatureMeasurement Rust structure. Derive during the compilation time auto-generates code according to associated procedural macros. Procedural macros and derive is an advanced topic that is out of the scope of this lesson.

Still, in the file /src/temperature_measurement.rs, the  into_query_value method does the opposite; it converts our structure into values which can be read by the Scylla server:

In the file /src/db.rs, we define the insert query. Scylla will use each value as a replacement for ?:

query_with_values allows us to use query string templates with ? as a placeholder for dynamic values. The values themselves are provided as a second argument.

Reading Measurements

Now we can create the select-query logic in the /src/db.rs module:

The important steps are:

  • Make a select query with the specified parameters (device ID, start and end date);
  • Read the response and convert it into rows
  • Once the rows are obtained, convert each row via the previously generated method TemperatureMeasurement::try_from_row(row).

To recap, so far, we created two important modules:

  • The db module (./src/db.rs) which is responsible for providing DB-related helpers;
  • The temperature_measurement module (./src/temperature_measurement.rs) which contains a model of temperature measurement.

Now, it’s time to create the main function which will connect to Scylla, add a couple of measurements to it, and read them back (defined in /src/main.rs):

Finally, to run the example:

cargo run

Conclusion

In this lesson, we created a simple Rust application that allows us to connect to a one node Scylla cluster, store, and select temperature measurements from sensors. 

Future topics that were not discussed in this lesson include query preparation, query batching, and execution of prepared queries. Another topic which we left aside is multi-node clusters and connection pools. Stay tuned for more lessons and also check out the CDRS examples.

*This lesson was written with the help of Alex (Oleksandr) Pikalov. He’s a Rust enthusiast and author of CDRS, an open-source Rust Scylla and Cassandra driver. Alex is a principal Javascript engineer, and he holds an M.SC. Degree in Physics.

 

fa-angle-up