The scylla asynchronous driver provides an interface to work with ScyllaDB and Apache Cassandra databases using CQL
Under the hood it links the ScyllaDB cpp-rs-driver, a Rust reimplementation of the DataStax C/C++ driver.
The framework wraps that C API in a thin coroutine-friendly layer. Every CassFuture is bridged into an awaitable task, so CQL calls suspend the current coroutine instead of blocking a task processor thread, while the driver's own I/O threads keep talking to the cluster.
Main features
- Type-safe operations for INSERT, SELECT, UPDATE, DELETE, COUNT;
- Raw CQL execution for hand-written queries, DDL and aggregations
- Full CQL type coverage;
- Lightweight transactions (LWT)
- USING TTL and USING TIMESTAMP on writes;
- Logged batch inserts;
- Deadline propagation .
- Configurable consistency levels, load balancing, speculative execution;
Metrics
Most important ones:
| Metric name | Description |
| scylla.pool.current-size | current number of open connections |
| scylla.pool.max-size | limit on the number of open connections |
| scylla.pool.overloads | counter of requests that could not get a connection |
| scylla.success | counter of successfully executed requests |
| scylla.errors | counter of failed requests |
| scylla.timings | query timings |
See Retrieving Service Statistics and Metrics (Prometheus/Graphite/...) and Default Metrics Description for info on how to get the metrics.
Usage
To use ScyllaDB you have to add the component components::Scylla and configure it according to the documentation.
#include <userver/storages/scylla/component.hpp>
#include <userver/storages/scylla/session.hpp>
#include <userver/storages/scylla/operations.hpp>
auto session = context.FindComponent<components::Scylla>("scylla-db").GetSession();
auto table = session->GetTable("users");
Typed operations
Each CQL verb has a builder. Bind parameters with Bind, pin rows with Where, stage updates with Set, then hand the builder to Table::Execute
storages::scylla::operations::InsertOne insert;
insert.BindString("name", "Alice");
insert.BindInt32("age", 30);
insert.UsingTtl(3600);
table.Execute(insert);
storages::scylla::operations::SelectOne select;
select.AddAllColumns();
select.WhereString("name", "Alice");
auto row = table.Execute(select);
Rows bound with UsingTtl expire after the given seconds; UsingTimestamp sets an explicit write timestamp for conflict resolution.
Typed row deserialization
struct User {
storages::scylla::Uuid id;
std::string name;
};
void DecodeRow(const storages::scylla::Row& row, User& out) {
out.id = row.Get<storages::scylla::Uuid>("id");
out.name = row.Get<std::string>("name");
}
const auto user = row.As<User>();
UUID's
auto random_id = storages::scylla::Uuid::Random();
auto time_id = storages::scylla::Uuid::TimeBased();
auto parsed = storages::scylla::Uuid::FromString("550e8400-e29b-41d4-a716-446655440000");
auto text = random_id.ToString();
Paging
For large result sets, use ExecutePaged with an opaque paging token, or Session::NewCursor for server-side iteration.
storages::scylla::operations::SelectMany op;
op.AddAllColumns();
op.SetPageSize(100);
std::string cursor;
auto result = table.ExecutePaged(op, cursor);
auto server_cursor = session->NewCursor("SELECT * FROM users", {}, 1000);
while (!server_cursor.Done()) {
auto page = server_cursor.NextPage();
}
Lightweight transactions (LWT)
Conditional insert. Succeeds only if the row does not exist
storages::scylla::operations::InsertOne op;
op.BindString("name", "Alice");
op.BindInt32("age", 30);
op.IfNotExists();
auto result = table.ExecuteLwt(op);
Compare-and-set. update only if a column matches the expected value
storages::scylla::operations::UpdateOne op;
op.SetInt32("age", 31);
op.WhereString("name", "Alice");
op.IfInt32("age", 30);
auto result = table.ExecuteLwt(op);
Conditional delete:
storages::scylla::operations::DeleteOne op;
op.WhereString("name", "Alice");
op.IfExists();
auto result = table.ExecuteLwt(op);
Raw CQL execution
For queries that don't fit the operation builders.
auto rows = session->Execute(
"SELECT * FROM users WHERE name = ?",
std::string{"Alice"});
auto rows2 = session->Execute(
"SELECT * FROM users WHERE age > ? AND age < ?",
std::int64_t{20}, std::int64_t{40});
session->ExecuteVoid(
"CREATE TABLE IF NOT EXISTS ks.cache ("
"key text PRIMARY KEY, value text)");
auto paged = session->ExecutePaged(
"SELECT * FROM users", {}, 100);
Rich CQL types
The driver supports the full CQL type system via storages::scylla::Value:
| CQL type | C++ type |
| text / varchar | std::string |
| int | std::int32_t |
| bigint | std::int64_t |
| smallint | std::int16_t |
| tinyint | std::int8_t |
| boolean | bool |
| float | float |
| double | double |
| uuid / timeuuid | storages::scylla::Uuid |
| timestamp | storages::scylla::Timestamp |
| date | storages::scylla::Date |
| time | storages::scylla::Time |
| blob | storages::scylla::Blob |
| inet | storages::scylla::Inet |
| list<T> | storages::scylla::List |
| set<T> | storages::scylla::Set |
| map<K,V> | storages::scylla::Map |