ScyllaDB V Developer Deep
Dive Series: Rust-Based Drivers
and UDFs with WebAssembly
Piotr Sarna, Principal Software Engineer
Performance
Enhancements
+ AWS I4i
Benchmarking
ON-DEMAND WEBINAR
Resiliency and
Strong Consistency
via Raft
ON-DEMAND WEBINAR
Brought to you by
P99 CONF | VIRTUAL EVENT | OCTOBER 19 + 20
All Things Performance
The event for developers who care about P99
percentiles and high-performance, low-latency
applications.
Register at p99conf.io
Poll
Where are you in your NoSQL adoption?
ScyllaDB V Developer Deep
Dive Series: Rust-Based Drivers
and UDFs with WebAssembly
Piotr Sarna, Principal Software Engineer
Today’s Presenter
Piotr Sarna Principal Software Engineer
+ Software engineer keen on open-source
projects, C++ and Rust
+ Used to develop a distributed file system
+ Wrote a few patches for the Linux kernel
+ Graduated from University of Warsaw with
MSc in Computer Science
+ Maintainer of the ScyllaDB Rust Driver project
Speaker Photo
+ Infoworld 2020 Technology of the Year!
+ Founded by designers of KVM Hypervisor
The Database Built for Gamechangers
7
“ScyllaDB stands apart...It’s the rare product
that exceeds my expectations.”
– Martin Heller, InfoWorld contributing editor and reviewer
“For 99.9% of applications, ScyllaDB delivers all the
power a customer will ever need, on workloads that other
databases can’t touch – and at a fraction of the cost of
an in-memory solution.”
– Adrian Bridgewater, Forbes senior contributor
+ Resolves challenges of legacy NoSQL databases
+ >5x higher throughput
+ >20x lower latency
+ >75% TCO savings
+ DBaaS/Cloud, Enterprise and Open Source solutions
+ Proven globally at scale
8
+400 Gamechangers Leverage ScyllaDB
Seamless experiences
across content + devices
Fast computation of flight
pricing
Corporate fleet
management
Real-time analytics
2,000,000 SKU -commerce
management
Real-time location tracking
for friends/family
Video recommendation
management
IoT for industrial
machines
Synchronize browser
properties for millions
Threat intelligence service
using JanusGraph
Real time fraud detection
across 6M transactions/day
Uber scale, mission critical
chat & messaging app
Network security threat
detection
Power ~50M X1 DVRs with
billions of reqs/day
Precision healthcare via
Edison AI
Inventory hub for retail
operations
Property listings and
updates
Unified ML feature store
across the business
Cryptocurrency exchange
app
Geography-based
recommendations
Distributed storage for
distributed ledger tech
Global operations- Avon,
Body Shop + more
Predictable performance for
on sale surges
GPS-based exercise
tracking
Agenda ■ ScyllaDB Rust driver
• origin
• runtime
• shard-awareness
• observability
• porting other drivers to Rust
■ UDF/UDA on Wasm
• WebAssembly
• user-defined functions
• user-defined aggregates
• coding UDF/UDA with WebAssembly
ScyllaDB Rust Driver
ScyllaDB Rust Driver: Origin
Our design goals:
+ fully asynchronous
+ token-aware, shard-aware
+ ease of use
+ maximizing performance
+ minimizing overheads
+ extensibility
+ an extremely cool logo
Tokio
Most popular async runtime, which should also mean best adoption
It would be tempting to write the driver in a runtime-agnostic way, but it's hard:
+ not all API's are well defined yet
+ Tokio offers quite complete support for:
+ TCP communication
+ time management - timeout, intervals
+ synchronization routines - mutexes, semaphores, channels, and other useful abstractions
+ Very actively developed
Concise API
A central component of our driver is a session, established once and then used to
communicate with Scylla. It has many customizable parameters, but most of them have
sensible defaults.
let uri = "127.0.0.1:9042";
let session: Session = SessionBuilder::new().known_node(uri).build().await?;
if let Some(rows) = session.query("SELECT a, b, c FROM ks.t", &[]).await?.rows {
for row in rows.into_typed::<(i32, i32, String)>() {
let (a, b, c) = row?;
println!("a, b, c: {}, {}, {}", a, b, c);
}
}
Connection Management
Ability to customize the number of connections is critical for performance. Our driver uses a
default of 1 connection per shard, but can be customized to instead establish a fixed number
of connections, be it per node or per shard.
In ScyllaDB, drivers can try to connect not only to a node that holds given data, but also
directly to a core which owns a particular partition, which implies better latency.
Both token- and shard-awareness is built in into ScyllaDB Rust Driver from the start.
Token-aware & Shard-aware Routing
Automatic Prepared Statements
ScyllaDB relies on prepared statements in order to make queries more efficient.
ScyllaDB Rust Driver provides a CachingSession abstraction, which automatically
ensures that all statements are properly prepared.
ref: https://www.scylladb.com/2017/12/13/prepared-statements-scylla/
Scylla Rust Driver comes with the tracing crate support, which allows seamless integration
with existing logging environments and easy debugging.
Observability
[sarna@localhost scylla-rust-driver]$ RUST_LOG=debug cargo run --example logging
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
Running `target/debug/examples/logging`
Dec 17 13:52:49.580 INFO logging: Connecting to 127.0.0.1:9042
Dec 17 13:52:49.580 DEBUG scylla::transport::connection_pool: [127.0.0.1] Started asynchronous pool worker
Dec 17 13:52:49.581 DEBUG scylla::transport::cluster: Requesting topology refresh
Dec 17 13:52:49.583 DEBUG scylla::transport::connection_pool: [127.0.0.1] New sharder: Some(Sharder { nr_shards: 4, msb_ignore: 12 }), clearing all
connections
Dec 17 13:52:49.583 DEBUG scylla::transport::connection_pool: [127.0.0.1] Updating shard aware port: Some(19042)
Dec 17 13:52:49.584 DEBUG scylla::transport::connection_pool: [127.0.0.1] Pool is full, clearing 0 excess connections
Dec 17 13:52:49.598 DEBUG scylla::transport::connection_pool: [127.0.0.1] Started asynchronous pool worker
Dec 17 13:52:49.600 DEBUG scylla::transport::connection_pool: [127.0.0.1] New sharder: Some(Sharder { nr_shards: 4, msb_ignore: 12 }), clearing all
connections
Dec 17 13:52:49.600 DEBUG scylla::transport::connection_pool: [127.0.0.1] Updating shard aware port: Some(19042)
Dec 17 13:52:49.601 DEBUG scylla::transport::connection_pool: [127.0.0.1] Scheduling next refill in 50 ms
Dec 17 13:52:49.617 DEBUG scylla::transport::session: Detected USE KEYSPACE query, setting session's keyspace to ks
Dec 17 13:52:49.617 DEBUG scylla::transport::connection_pool: [127.0.0.1] Requested keyspace change: ks
Dec 17 13:52:49.617 DEBUG scylla::transport::connection_pool: [127.0.0.1] Successfully changed current keyspace
Individual queries can also be inspected for profiling purposes.
Observability
[sarna@localhost scylla-rust-driver]$ cargo run --example tracing
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
Running `target/debug/examples/tracing`Connecting to 127.0.0.1:9042 …
Tracing command QUERY performed by Some(127.0.0.1), which took 244µs total
│ UUID │ Elapsed │ Command
├─────────────────────────────────────
│dc83e5e9 │ 0µs │ Parsing a statement
│dc83ea28 │ 109µs │ Processing a statement
│dc83eb78 │ 143µs │ read_data: querying locally
│dc83eb9e │ 146µs │ Start querying token range (-inf, {minimum token, end}]
│dc83ebef │ 154µs │ Creating shard reader on shard: 0
│dc83ec31 │ 161µs │ Scanning cache for range (-inf, {minimum token, end}] and slice {(-inf, +inf)}
│dc83ecbf │ 175µs │ Page stats: 0 partition(s), 0 static row(s) (0 live, 0 dead), 0 clustering row(s) (0 live, 0 dead) and 0 range tombstone(s)
│dc83ed30 │ 187µs │ Querying is done
│dc83ed77 │ 194µs │ read_data: querying locally
│dc83ed89 │ 195µs │ Start querying token range ({minimum token, end}, +inf)
│dc83edaa │ 199µs │ Creating shard reader on shard: 0
│dc83ee50 │ 215µs │ Scanning cache for range ({minimum token, end}, +inf) and slice {(-inf, +inf)}
│dc83ee8f │ 222µs │ Page stats: 0 partition(s), 0 static row(s) (0 live, 0 dead), 0 clustering row(s) (0 live, 0 dead) and 0 range tombstone(s)
│dc83eebd │ 226µs │ Querying is done
│dc83ef32 │ 238µs │ Done processing - preparing a result
└─────────────────────────────────────
Since ScyllaDB Rust Driver generally proves to be faster (and often more reliable) than CQL
drivers implemented in other languages, our plan is to replace the existing drivers by bindings
to our Rust project:
+ C/C++ work-in-progress: https://github.com/hackathon-rust-cpp/cpp-rust-driver
+ a fair part of the API already works Python and more coming soon!
+ PoC for the new Python driver: https://github.com/psarna/scylla-python-driver
+ based on battle-tested PyO3 crate for binding Rust and Python, with async and Tokio support
+ more coming!
Rewriting Other Drivers on Top of Rust
User-defined Functions
Powered by WebAssembly
Binary format for expressing executable code, executed on a stack-based virtual machine.
Designed to be:
+ portable
+ easily embeddable
+ efficient
WebAssembly is binary, but it also specifies a standard human-readable format: WAT
(WebAssembly Text Format).
WebAssembly
User-defined functions are a CQL feature that allows applying a custom function to the query
result rows.
User-defined Functions
cassandra@cqlsh:ks> SELECT id, inv(id), mult(id, inv(id)) FROM t;
id | ks.inv(id) | ks.mult(id, ks.inv(id))
----+------------+-------------------------
7 | 0.142857 | 1
1 | 1 | 1
0 | Infinity | NaN
4 | 0.25 | 1
(4 rows)
A powerful tool for combining functions into accumulators, which aggregate results from
single rows.
User-defined Aggregates
cassandra@cqlsh:ks> SELECT * FROM words;
word
------------
monkey
rhinoceros
dog
(3 rows)
cassandra@cqlsh:ks> SELECT avg_length(word) FROM words;
ks.avg_length(word)
-----------------------------------------------
The average string length is 6.3333333333333!
(1 rows)
CREATE FUNCTION accumulate_len(acc tuple<bigint,bigint>, a text)
RETURNS NULL ON NULL INPUT
RETURNS tuple<bigint,bigint>
LANGUAGE lua as 'return {acc[1] + 1, acc[2] + #a}';
CREATE OR REPLACE FUNCTION present(res tuple<bigint,bigint>)
RETURNS NULL ON NULL INPUT
RETURNS text
LANGUAGE lua as
'return "The average string length is " .. res[2]/res[1] .. "!"';
CREATE OR REPLACE AGGREGATE avg_length(text)
SFUNC accumulate_len
STYPE tuple<bigint,bigint>
FINALFUNC present INITCOND (0,0);
WebAssembly programs can be written by hand (not very convenient unless you're a Lisp enthusiast),
but multiple languages compile to WebAssembly:
+ Rust
+ C++
+ assemblyscript
+ (and more)
ScyllaDB provides helper libraries for popular languages (currently: Rust) which facilitate
creating user-defined functions.
Coding UDF with WebAssembly
A regular Rust function which accumulates a list of strings into a single string,
separated by commas:
Coding UDF with WebAssembly
fn commas(strings: Option<Vec<String>>) -> Option<String> {
match strings {
None => None,
Some(actual) => Some(actual.join(", ")),
}
}
A user-defined function, runnable from Scylla, which accumulates a list of strings into a single
string, separated by commas:
Coding UDF with WebAssembly
#[scylla_bindgen::scylla_bindgen]
fn commas(strings: Option<Vec<String>>) -> Option<String> {
match strings {
None => None,
Some(actual) => Some(actual.join(", ")),
}
}
Support for Wasm-based user-defined functions and user-defined aggregates is already
available in experimental mode.
Enable it for testing today by adding these entries to your scylla.yaml configuration file:
Experimental Status
enable_user_defined_functions: true
experimental_features:
- udf
Poll
How much data do you under management of your
transactional database?
Q&A
Performance
Enhancements +
AWS I4i
Benchmarking
Resiliency
and Strong
Consistency
via Raft
ON-DEMAND DEEP DIVES
Thank you
for joining us today.
@scylladb scylladb/
slack.scylladb.com
@scylladb
company/scylladb/
scylladb/
sarna@scylladb.com

ScyllaDB V Developer Deep Dive Series: Rust-Based Drivers and UDFs with WebAssembly

  • 1.
    ScyllaDB V DeveloperDeep Dive Series: Rust-Based Drivers and UDFs with WebAssembly Piotr Sarna, Principal Software Engineer
  • 2.
    Performance Enhancements + AWS I4i Benchmarking ON-DEMANDWEBINAR Resiliency and Strong Consistency via Raft ON-DEMAND WEBINAR
  • 3.
    Brought to youby P99 CONF | VIRTUAL EVENT | OCTOBER 19 + 20 All Things Performance The event for developers who care about P99 percentiles and high-performance, low-latency applications. Register at p99conf.io
  • 4.
    Poll Where are youin your NoSQL adoption?
  • 5.
    ScyllaDB V DeveloperDeep Dive Series: Rust-Based Drivers and UDFs with WebAssembly Piotr Sarna, Principal Software Engineer
  • 6.
    Today’s Presenter Piotr SarnaPrincipal Software Engineer + Software engineer keen on open-source projects, C++ and Rust + Used to develop a distributed file system + Wrote a few patches for the Linux kernel + Graduated from University of Warsaw with MSc in Computer Science + Maintainer of the ScyllaDB Rust Driver project Speaker Photo
  • 7.
    + Infoworld 2020Technology of the Year! + Founded by designers of KVM Hypervisor The Database Built for Gamechangers 7 “ScyllaDB stands apart...It’s the rare product that exceeds my expectations.” – Martin Heller, InfoWorld contributing editor and reviewer “For 99.9% of applications, ScyllaDB delivers all the power a customer will ever need, on workloads that other databases can’t touch – and at a fraction of the cost of an in-memory solution.” – Adrian Bridgewater, Forbes senior contributor + Resolves challenges of legacy NoSQL databases + >5x higher throughput + >20x lower latency + >75% TCO savings + DBaaS/Cloud, Enterprise and Open Source solutions + Proven globally at scale
  • 8.
    8 +400 Gamechangers LeverageScyllaDB Seamless experiences across content + devices Fast computation of flight pricing Corporate fleet management Real-time analytics 2,000,000 SKU -commerce management Real-time location tracking for friends/family Video recommendation management IoT for industrial machines Synchronize browser properties for millions Threat intelligence service using JanusGraph Real time fraud detection across 6M transactions/day Uber scale, mission critical chat & messaging app Network security threat detection Power ~50M X1 DVRs with billions of reqs/day Precision healthcare via Edison AI Inventory hub for retail operations Property listings and updates Unified ML feature store across the business Cryptocurrency exchange app Geography-based recommendations Distributed storage for distributed ledger tech Global operations- Avon, Body Shop + more Predictable performance for on sale surges GPS-based exercise tracking
  • 9.
    Agenda ■ ScyllaDBRust driver • origin • runtime • shard-awareness • observability • porting other drivers to Rust ■ UDF/UDA on Wasm • WebAssembly • user-defined functions • user-defined aggregates • coding UDF/UDA with WebAssembly
  • 10.
  • 11.
    ScyllaDB Rust Driver:Origin Our design goals: + fully asynchronous + token-aware, shard-aware + ease of use + maximizing performance + minimizing overheads + extensibility + an extremely cool logo
  • 12.
    Tokio Most popular asyncruntime, which should also mean best adoption It would be tempting to write the driver in a runtime-agnostic way, but it's hard: + not all API's are well defined yet + Tokio offers quite complete support for: + TCP communication + time management - timeout, intervals + synchronization routines - mutexes, semaphores, channels, and other useful abstractions + Very actively developed
  • 13.
    Concise API A centralcomponent of our driver is a session, established once and then used to communicate with Scylla. It has many customizable parameters, but most of them have sensible defaults. let uri = "127.0.0.1:9042"; let session: Session = SessionBuilder::new().known_node(uri).build().await?; if let Some(rows) = session.query("SELECT a, b, c FROM ks.t", &[]).await?.rows { for row in rows.into_typed::<(i32, i32, String)>() { let (a, b, c) = row?; println!("a, b, c: {}, {}, {}", a, b, c); } }
  • 14.
    Connection Management Ability tocustomize the number of connections is critical for performance. Our driver uses a default of 1 connection per shard, but can be customized to instead establish a fixed number of connections, be it per node or per shard.
  • 15.
    In ScyllaDB, driverscan try to connect not only to a node that holds given data, but also directly to a core which owns a particular partition, which implies better latency. Both token- and shard-awareness is built in into ScyllaDB Rust Driver from the start. Token-aware & Shard-aware Routing
  • 16.
    Automatic Prepared Statements ScyllaDBrelies on prepared statements in order to make queries more efficient. ScyllaDB Rust Driver provides a CachingSession abstraction, which automatically ensures that all statements are properly prepared. ref: https://www.scylladb.com/2017/12/13/prepared-statements-scylla/
  • 17.
    Scylla Rust Drivercomes with the tracing crate support, which allows seamless integration with existing logging environments and easy debugging. Observability [sarna@localhost scylla-rust-driver]$ RUST_LOG=debug cargo run --example logging Finished dev [unoptimized + debuginfo] target(s) in 0.06s Running `target/debug/examples/logging` Dec 17 13:52:49.580 INFO logging: Connecting to 127.0.0.1:9042 Dec 17 13:52:49.580 DEBUG scylla::transport::connection_pool: [127.0.0.1] Started asynchronous pool worker Dec 17 13:52:49.581 DEBUG scylla::transport::cluster: Requesting topology refresh Dec 17 13:52:49.583 DEBUG scylla::transport::connection_pool: [127.0.0.1] New sharder: Some(Sharder { nr_shards: 4, msb_ignore: 12 }), clearing all connections Dec 17 13:52:49.583 DEBUG scylla::transport::connection_pool: [127.0.0.1] Updating shard aware port: Some(19042) Dec 17 13:52:49.584 DEBUG scylla::transport::connection_pool: [127.0.0.1] Pool is full, clearing 0 excess connections Dec 17 13:52:49.598 DEBUG scylla::transport::connection_pool: [127.0.0.1] Started asynchronous pool worker Dec 17 13:52:49.600 DEBUG scylla::transport::connection_pool: [127.0.0.1] New sharder: Some(Sharder { nr_shards: 4, msb_ignore: 12 }), clearing all connections Dec 17 13:52:49.600 DEBUG scylla::transport::connection_pool: [127.0.0.1] Updating shard aware port: Some(19042) Dec 17 13:52:49.601 DEBUG scylla::transport::connection_pool: [127.0.0.1] Scheduling next refill in 50 ms Dec 17 13:52:49.617 DEBUG scylla::transport::session: Detected USE KEYSPACE query, setting session's keyspace to ks Dec 17 13:52:49.617 DEBUG scylla::transport::connection_pool: [127.0.0.1] Requested keyspace change: ks Dec 17 13:52:49.617 DEBUG scylla::transport::connection_pool: [127.0.0.1] Successfully changed current keyspace
  • 18.
    Individual queries canalso be inspected for profiling purposes. Observability [sarna@localhost scylla-rust-driver]$ cargo run --example tracing Finished dev [unoptimized + debuginfo] target(s) in 0.06s Running `target/debug/examples/tracing`Connecting to 127.0.0.1:9042 … Tracing command QUERY performed by Some(127.0.0.1), which took 244µs total │ UUID │ Elapsed │ Command ├───────────────────────────────────── │dc83e5e9 │ 0µs │ Parsing a statement │dc83ea28 │ 109µs │ Processing a statement │dc83eb78 │ 143µs │ read_data: querying locally │dc83eb9e │ 146µs │ Start querying token range (-inf, {minimum token, end}] │dc83ebef │ 154µs │ Creating shard reader on shard: 0 │dc83ec31 │ 161µs │ Scanning cache for range (-inf, {minimum token, end}] and slice {(-inf, +inf)} │dc83ecbf │ 175µs │ Page stats: 0 partition(s), 0 static row(s) (0 live, 0 dead), 0 clustering row(s) (0 live, 0 dead) and 0 range tombstone(s) │dc83ed30 │ 187µs │ Querying is done │dc83ed77 │ 194µs │ read_data: querying locally │dc83ed89 │ 195µs │ Start querying token range ({minimum token, end}, +inf) │dc83edaa │ 199µs │ Creating shard reader on shard: 0 │dc83ee50 │ 215µs │ Scanning cache for range ({minimum token, end}, +inf) and slice {(-inf, +inf)} │dc83ee8f │ 222µs │ Page stats: 0 partition(s), 0 static row(s) (0 live, 0 dead), 0 clustering row(s) (0 live, 0 dead) and 0 range tombstone(s) │dc83eebd │ 226µs │ Querying is done │dc83ef32 │ 238µs │ Done processing - preparing a result └─────────────────────────────────────
  • 19.
    Since ScyllaDB RustDriver generally proves to be faster (and often more reliable) than CQL drivers implemented in other languages, our plan is to replace the existing drivers by bindings to our Rust project: + C/C++ work-in-progress: https://github.com/hackathon-rust-cpp/cpp-rust-driver + a fair part of the API already works Python and more coming soon! + PoC for the new Python driver: https://github.com/psarna/scylla-python-driver + based on battle-tested PyO3 crate for binding Rust and Python, with async and Tokio support + more coming! Rewriting Other Drivers on Top of Rust
  • 20.
  • 21.
    Binary format forexpressing executable code, executed on a stack-based virtual machine. Designed to be: + portable + easily embeddable + efficient WebAssembly is binary, but it also specifies a standard human-readable format: WAT (WebAssembly Text Format). WebAssembly
  • 22.
    User-defined functions area CQL feature that allows applying a custom function to the query result rows. User-defined Functions cassandra@cqlsh:ks> SELECT id, inv(id), mult(id, inv(id)) FROM t; id | ks.inv(id) | ks.mult(id, ks.inv(id)) ----+------------+------------------------- 7 | 0.142857 | 1 1 | 1 | 1 0 | Infinity | NaN 4 | 0.25 | 1 (4 rows)
  • 23.
    A powerful toolfor combining functions into accumulators, which aggregate results from single rows. User-defined Aggregates cassandra@cqlsh:ks> SELECT * FROM words; word ------------ monkey rhinoceros dog (3 rows) cassandra@cqlsh:ks> SELECT avg_length(word) FROM words; ks.avg_length(word) ----------------------------------------------- The average string length is 6.3333333333333! (1 rows) CREATE FUNCTION accumulate_len(acc tuple<bigint,bigint>, a text) RETURNS NULL ON NULL INPUT RETURNS tuple<bigint,bigint> LANGUAGE lua as 'return {acc[1] + 1, acc[2] + #a}'; CREATE OR REPLACE FUNCTION present(res tuple<bigint,bigint>) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE lua as 'return "The average string length is " .. res[2]/res[1] .. "!"'; CREATE OR REPLACE AGGREGATE avg_length(text) SFUNC accumulate_len STYPE tuple<bigint,bigint> FINALFUNC present INITCOND (0,0);
  • 24.
    WebAssembly programs canbe written by hand (not very convenient unless you're a Lisp enthusiast), but multiple languages compile to WebAssembly: + Rust + C++ + assemblyscript + (and more) ScyllaDB provides helper libraries for popular languages (currently: Rust) which facilitate creating user-defined functions. Coding UDF with WebAssembly
  • 25.
    A regular Rustfunction which accumulates a list of strings into a single string, separated by commas: Coding UDF with WebAssembly fn commas(strings: Option<Vec<String>>) -> Option<String> { match strings { None => None, Some(actual) => Some(actual.join(", ")), } }
  • 26.
    A user-defined function,runnable from Scylla, which accumulates a list of strings into a single string, separated by commas: Coding UDF with WebAssembly #[scylla_bindgen::scylla_bindgen] fn commas(strings: Option<Vec<String>>) -> Option<String> { match strings { None => None, Some(actual) => Some(actual.join(", ")), } }
  • 27.
    Support for Wasm-baseduser-defined functions and user-defined aggregates is already available in experimental mode. Enable it for testing today by adding these entries to your scylla.yaml configuration file: Experimental Status enable_user_defined_functions: true experimental_features: - udf
  • 28.
    Poll How much datado you under management of your transactional database?
  • 29.
    Q&A Performance Enhancements + AWS I4i Benchmarking Resiliency andStrong Consistency via Raft ON-DEMAND DEEP DIVES
  • 30.
    Thank you for joiningus today. @scylladb scylladb/ slack.scylladb.com @scylladb company/scylladb/ scylladb/ sarna@scylladb.com