// STRYKE-SCYLLA — ENGINEERING REPORT

Opt-in stryke connector package · cdylib libstryke_scylla · namespace Scylla · JSON-in/JSON-out FFI over dlopen

>_EXECUTIVE SUMMARY

stryke-scylla is an opt-in connector package in the stryke ecosystem — a ScyllaDB / Apache Cassandra client. It exposes CQL query, DDL, and schema introspection as the Scylla package, against any ScyllaDB or Cassandra cluster over the native CQL binary protocol.

The client is the scylla crate, ScyllaDB's official pure-Rust driver. The cdylib owns one embedded tokio runtime and presents a blocking facade, and ships once on demand rather than being linked into stryke core.

Scylla
stryke namespace
opt-in
Tier
cdylib
Load model (dlopen)
CQL
Driver (scylla, rustls)
34
Public Scylla:: functions
34
scylla__* FFI exports
14
In-crate unit tests
MIT
License

Each public Scylla::* wrapper in lib/Scylla.stk maps to one #[no_mangle] extern "C" fn scylla__* export in src/lib.rs, registered in stryke.toml under [ffi].exports — the surface test pins all 34 wrappers, and the export list, the extern functions, and the wrapper set are kept one-to-one. The 14 unit tests cover the pure logic (escaping, identifier quoting, contact-point normalization, batch construction, CqlValue→JSON, column-position sorting) and run with no cluster.


~TRANSPORT

The client speaks the CQL binary protocol via ScyllaDB's official pure-Rust driver (the scylla crate), which also targets Apache Cassandra. The driver manages a per-node connection pool and token-aware routing internally.


~SYNC OVER ASYNC

The driver is async. Rather than expose futures across the FFI boundary, the cdylib owns a single embedded multi-thread tokio runtime (OnceCell<Runtime>, 2 worker threads, enable_all) and block_ons every driver call, so the stryke-facing API stays synchronous like the other connectors.


~SESSION CACHE + CONNECTION RESOLUTION

A Session is cached in a OnceCell<Mutex<HashMap<ConnKey, Arc<Session>>>> keyed by ConnKey { nodes, username, password, keyspace } — calls sharing those four values reuse the same pooled session; differing on any field opens and caches a distinct one. The connection-free conn_from_opts resolves the opts dict to a ConnKey: a nodes array wins; otherwise node/host (combined with port) is used; an empty result defaults to 127.0.0.1:9042. A contact point without a :port gets the default CQL port appended (normalize_node). The Scylla.stk wrapper fills nodes from $SCYLLA_NODES (comma-split) when the caller passes no node. Auth is applied only when a username or password is non-empty; a non-empty keyspace is USEd on the session right after connect.

opts keyResolution
nodesArray of contact points; each normalized to host:port. Overrides node/host.
node / hostSingle contact point; port appended when it carries no :port.
portDefault 9042; combined with node/host.
username / passwordPasswordAuthenticator credentials; applied only when non-empty.
keyspaceuse_keyspace after connect; part of the cache key.

$ROWS TO JSON

SELECT-style results become an array of row objects keyed by column name (taken from the result's column specs); statements with no result set (DDL/DML) return { "ok": true }, which the row-returning wrappers surface as an empty array. Each CqlValue is mapped by cql_to_json to its natural JSON form, with a debug-string fallback for exotic types so the converter never fails.

CqlValueJSON form
Int, BigInt, SmallInt, TinyIntJSON number
Float, DoubleJSON number
Text, AsciiJSON string
BooleanJSON bool
UuidJSON string (the UUID's string form)
List, SetJSON array (recursively converted)
MapJSON array of [key, value] pairs
Empty / NULL columnJSON null
anything elseformat!("{:?}", …) debug string

&FFI CONTRACT + SAFETY

Each #[no_mangle] extern "C" fn scylla__* is a JSON-string-in / JSON-string-out wrapper. ffi_call parses the argument C-string into a serde_json::Value (null on parse failure), runs the handler inside catch_unwind (AssertUnwindSafe), serializes the result, and hands back a heap CString that stryke later frees via stryke_free_cstring. A handler error becomes { "error": … } and a panic becomes { "error": "stryke-scylla handler panicked" }; the Scylla.stk _decode step turns any error field into a stryke die. Queries are unparameterized CQL — interpolate untrusted values through escape (doubles the single quote per the CQL grammar) or the other pure helpers, which are unit-tested in-crate and validate in CI with no cluster.


+BATCH CONSTRUCTION

batch takes a statements array (errors when missing or empty) and an unlogged flag (default false). build_batch_cql wraps them in a textual BEGIN [UNLOGGED] BATCH … APPLY BATCH; block, trimming each statement and giving it exactly one trailing ; (so no ;;), then runs it as one statement and returns { "ok": true, "count": n }. Atomic within a single partition; cross-partition batches are allowed but not atomic.

GroupFunctions
Livenessversion, ping, server_version, cluster_name, peers
Queryquery, query_row, query_value, execute, batch, raw
Introspectionkeyspaces, tables, columns, count, indexes, views, types, partition_keys, clustering_keys
DDLcreate_keyspace, drop_keyspace, create_table, drop_table, create_index, drop_index, truncate
Pure helpersescape, quote_literal, quote_ident, valid_identifier, format_value, format_in_list, contact_points

/DEPENDENCIES

scylla (CQL binary protocol, rustls TLS), tokio (embedded runtime), serde_json for the FFI payloads, plus anyhow (error type), once_cell (runtime + session statics), and parking_lot (the session-cache mutex). Pure-Rust throughout — no native client library, so the cdylib builds and vendors cleanly.


%BUILD + TEST

The Makefile wraps the standard flow: make debug (cargo build), make test (cargo test then s test t/), make install (s pkg install -g .). cargo test runs the 14 in-crate unit tests with no cluster. On the stryke side, t/test_stryke_scylla_surface.stk asserts all 34 public wrappers resolve and exercises the connection-free helpers; t/test_scylla.stk drives a full keyspace → table → insert → count → query → index → drop lifecycle against a live cluster and short-circuits when ping fails, so a cluster-less CI run still passes the connection-free asserts.

Tested without a clusterWhat it pins
escape_string / quote_literal / quote_identifierCQL quote-doubling and identifier quoting
normalize_node / conn_from_opts / contact_pointsDefault-port append, nodes-array normalization, localhost fallback
cql_to_jsonScalar and collection CqlValue→JSON mapping
format_value / format_in_listTyped CQL literals and the (NULL) empty-list sentinel
build_batch_cqlBATCH wrapping, single trailing ;, UNLOGGED variant
valid_identifier / pluck_column / sort_columns_by_positionIdentifier rules, column extraction, key-order sorting

#PROJECT METADATA

ItemValue
LicenseMIT
Repositorygithub.com/MenkeTechnologies/stryke-scylla
Parent languagestrykelang
Meta umbrellaMenkeTechnologiesMeta
Issuesgithub.com/MenkeTechnologies/stryke-scylla/issues