Skip to content

scylladb/cpp-rs-driver

Repository files navigation

ScyllaDB CPP RS Driver


Wrapper around ScyllaDB's Rust Driver, which is API-compatible with both ScyllaDB and Datastax C/C++ Driver and may be considered a drop-in replacement (with some minor limitations, see Limitations).

Note: It is work in progress, bug reports and pull requests are welcome!

CMake options

In this section we will go over most important CMake options and what each of them controls.

  • CASS_BUILD_SHARED - Build shared driver library (.so). ON by default.
  • CASS_BUILD_STATIC - Build static driver library (.a). ON by default.
  • CASS_BUILD_INTEGRATION_TESTS - Build integration tests (see Testing section below) OFF by default.
  • CASS_BUILD_EXAMPLES - Build examples (see Examples section below). OFF by default.
  • CASS_USE_STATIC_LIBS - Link against static libraries when building tests/examples. OFF by default.
  • CMAKE_BUILD_TYPE - Controls the cargo profile library is built with. Possible values are: Debug, RelWithDebInfo and Release. Default value is Debug. For more information, see the profiles definitions in scylla-rust-wrapper/Cargo.toml.

For example, to build a shared driver library (no static library) in release mode and integration tests you can do:

mkdir build
cd build
cmake -DCASS_BUILD_STATIC=OFF -DCASS_BUILD_INTEGRATION_TESTS=ON -DCMAKE_BUILD_TYPE=Release ..
make

Examples


There are some examples in the examples directory. To run a single example:

cd examples/cloud
gcc cloud.c PATH_TO_CPP_RUST/scylla-rust-wrapper/target/debug/libscylla_cpp_driver.so -Wl,-rpath,PATH_TO_CPP_RUST/build  -I PATH_TO_CPP_RUST/include -o cloud
./cloud path_to_connection_bundle username password
#include <cassandra.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  /* Setup and connect to cluster */
  CassFuture* connect_future = NULL;
  CassCluster* cluster = cass_cluster_new();
  CassSession* session = cass_session_new();
  char* hosts = "127.0.0.1";
  if (argc > 1) {
    hosts = argv[1];
  }

  /* Add contact points */
  cass_cluster_set_contact_points(cluster, hosts);

  /* Provide the cluster object as configuration to connect the session */
  connect_future = cass_session_connect(session, cluster);

  if (cass_future_error_code(connect_future) == CASS_OK) {
    CassFuture* close_future = NULL;

    /* Build statement and execute query */
    const char* query = "SELECT release_version FROM system.local WHERE key='local'";
    CassStatement* statement = cass_statement_new(query, 0);

    CassFuture* result_future = cass_session_execute(session, statement);

    if (cass_future_error_code(result_future) == CASS_OK) {
      /* Retrieve result set and get the first row */
      const CassResult* result = cass_future_get_result(result_future);
      const CassRow* row = cass_result_first_row(result);

      if (row) {
        const CassValue* value = cass_row_get_column_by_name(row, "release_version");

        const char* release_version;
        size_t release_version_length;
        cass_value_get_string(value, &release_version, &release_version_length);
        printf("release_version: '%.*s'\n", (int)release_version_length, release_version);
      }

      cass_result_free(result);
    } else {
      /* Handle error */
      const char* message;
      size_t message_length;
      cass_future_error_message(result_future, &message, &message_length);
      fprintf(stderr, "Unable to run query: '%.*s'\n", (int)message_length, message);
    }

    cass_statement_free(statement);
    cass_future_free(result_future);

    /* Close the session */
    close_future = cass_session_close(session);
    cass_future_wait(close_future);
    cass_future_free(close_future);
  } else {
    /* Handle error */
    const char* message;
    size_t message_length;
    cass_future_error_message(connect_future, &message, &message_length);
    fprintf(stderr, "Unable to connect: '%.*s'\n", (int)message_length, message);
  }

  cass_future_free(connect_future);
  cass_cluster_free(cluster);
  cass_session_free(session);

  return 0;
}

Logging


The logging API and implementation are compatible with the C++ driver, for more details please refer to the logging documentation. As the tracing framework is used under the hood to instrument the collection of logs from the Rust driver and the scylla-rust-wrapper, the logging level and callback are passed through a custom event subscriber which is globally set as default when cass_log_set_level is called. So, cass_log_set_level must be called only once as subsequent attempts trying to modify the globally set event subscriber will be ignored. Also, Rust programs using CPP RS Driver under the hood must avoid calling tracing::subscriber::set_global_default as this will cause conflicts.

Note: The logging configuration must be done before any other driver function is called, otherwise, the default logging callback will be used, and logs will appear on stderr.
void on_log(const CassLogMessage* message, void* data) {
  /* Handle logging */
}

int main() {
  void* log_data = NULL /* Custom log resource */;
  cass_log_set_callback(on_log, log_data);
  cass_log_set_level(CASS_LOG_INFO);

  /* Create cluster and connect session */
}

Features


The driver inherits almost all the features of C/C++ and Rust drivers, such as:

  • Asynchronous API
  • Shard-aware routing
  • Simple, Prepared and Batch statements
  • Query paging
  • CQL binary protocol version 4
  • Load balancing policies
  • Retry policies
  • SSL
  • Authentication
  • Tuples and UDTs
  • Nested collections
  • Data types
  • Schema metadata (keyspace metadata, materialized views, etc.)

Limitations

This section covers all the functions that were present in old cpp-driver but are not present in this driver, and current status / reasons.

Not done (yet)

Functions here are simply not (yet) implemented. We may implement them at some point. External contributions for those are welcome.

Index metadata

Issue: #272 Implementation will need support in Rust Driver first. Issue: scylladb/scylla-rust-driver#1034

  • cass_index_meta_name
  • cass_index_meta_options
  • cass_index_meta_target
  • cass_index_meta_type
  • cass_table_meta_index
  • cass_table_meta_index_by_name
  • cass_table_meta_index_by_name_n
  • cass_table_meta_index_count
  • cass_iterator_get_index_meta
  • cass_iterator_indexes_from_table_meta
Custom allocator

Function: cass_alloc_set_functions Issue: #277

cass_cluster_set_prepare_on_all_hosts

Issue: #278 Will need Rust Driver support first: scylladb/scylla-rust-driver#1284

cass_session_get_speculative_execution_metrics

Issue: #266 Will need Rust Driver support first: scylladb/scylla-rust-driver#1041

Clustering key order

Issue: #271

  • cass_materialized_view_meta_clustering_key_order
  • cass_table_meta_clustering_key_order
Schema version

Issue: #270 Rust Driver issue: scylladb/scylla-rust-driver#1307

  • cass_schema_meta_snapshot_version
  • cass_schema_meta_version
Custom authenticator

Issue: #265

  • cass_authenticator_address
  • cass_authenticator_class_name
  • cass_authenticator_exchange_data
  • cass_authenticator_hostname
  • cass_authenticator_response
  • cass_authenticator_set_error
  • cass_authenticator_set_error_n
  • cass_authenticator_set_exchange_data
  • cass_authenticator_set_response
  • cass_cluster_set_authenticator_callbacks
cass_cluster_set_use_hostname_resolution

We need to understand use cases for this, and decide if we want this or not. Issue: #426

Features not supported by ScyllaDB

Those functions need features that are not supported by ScyllaDB, or are only experimental. We will most likely not work on them (until ScyllaDB supports those features), but we may consider external contributions.

Custom payload

ScyllaDB has basically no support for custom payload. They are only used for Tablets support in drivers: https://github.com/scylladb/scylladb/blob/master/docs/dev/protocol-extensions.md#negotiate-sending-tablets-info-to-the-drivers Issue: #261 Implementation will need support in Rust Driver first. No issue for that yet.

  • cass_custom_payload_free
  • cass_custom_payload_new
  • cass_custom_payload_remove
  • cass_custom_payload_remove_n
  • cass_custom_payload_set
  • cass_custom_payload_set_n
  • cass_future_custom_payload_item
  • cass_future_custom_payload_item_count
  • cass_batch_set_custom_payload
  • cass_statement_set_custom_payload
Set keyspace on statement

This requires CQLv5 protocol, not yet supported by ScyllaDB or Rust Driver. Issue: #279 Implementation will need support in Rust Driver first. No issue for that yet.

  • cass_batch_set_keyspace
  • cass_batch_set_keyspace_n
  • cass_statement_set_keyspace
  • cass_statement_set_keyspace_n
UDF metadata

UDFs are experimental in ScyllaDB Issue: #275 Implementation will need support in Rust Driver first. Issue: scylladb/scylla-rust-driver#1034

  • cass_function_meta_argument
  • cass_function_meta_argument_count
  • cass_function_meta_argument_type_by_name
  • cass_function_meta_argument_type_by_name_n
  • cass_function_meta_body
  • cass_function_meta_called_on_null_input
  • cass_function_meta_full_name
  • cass_function_meta_language
  • cass_function_meta_name
  • cass_function_meta_return_type
  • cass_iterator_functions_from_keyspace_meta
  • cass_iterator_get_function_meta
  • cass_keyspace_meta_function_by_name
  • cass_keyspace_meta_function_by_name_n
UDA metadata

UDAs are experimental in ScyllaDB Issue: #273 Implementation will need support in Rust Driver first. Issue: scylladb/scylla-rust-driver#1034

  • cass_aggregate_meta_argument_count
  • cass_aggregate_meta_argument_type
  • cass_aggregate_meta_final_func
  • cass_aggregate_meta_full_name
  • cass_aggregate_meta_init_cond
  • cass_aggregate_meta_name
  • cass_aggregate_meta_return_type
  • cass_aggregate_meta_state_func
  • cass_aggregate_meta_state_type
  • cass_iterator_aggregates_from_keyspace_meta
  • cass_iterator_get_aggregate_meta
  • cass_keyspace_meta_aggregate_by_name
  • cass_keyspace_meta_aggregate_by_name_n
Table / Keyspace metadata: is_virtual

Scylla does not have the same system tables that Cassandra exposes this information in. Issue: #268

  • cass_keyspace_meta_is_virtual
  • cass_table_meta_is_virtual

Not planned

Functions in this section were intentionally not implemented. They may be incompatible with driver architecture, deprecated / no-op in the old driver, or footguns that make no sense. We do not plan to implement those, unless there is a very good reason to. If you require some of those functionalities, please open an issue, and we'll discuss how we can solve this.

Raw metadata accessors

Those APIs are not really compatible with the driver architecture, implementing it would be difficult. If you need some of it, please open an issue, so that we can learn about your use case and figure out how we can address it.

  • cass_keyspace_meta_field_by_name
  • cass_keyspace_meta_field_by_name_n
  • cass_table_meta_field_by_name
  • cass_table_meta_field_by_name_n
  • cass_materialized_view_meta_field_by_name
  • cass_materialized_view_meta_field_by_name_n
  • cass_column_meta_field_by_name
  • cass_column_meta_field_by_name_n
  • cass_iterator_fields_from_column_meta
  • cass_iterator_fields_from_keyspace_meta
  • cass_iterator_fields_from_materialized_view_meta
  • cass_iterator_fields_from_table_meta
  • cass_iterator_get_meta_field_name
  • cass_iterator_get_meta_field_value
  • cass_function_meta_field_by_name
  • cass_function_meta_field_by_name_n
  • cass_iterator_fields_from_function_meta
  • cass_aggregate_meta_field_by_name
  • cass_aggregate_meta_field_by_name_n
  • cass_iterator_fields_from_aggregate_meta
  • cass_index_meta_field_by_name
  • cass_index_meta_field_by_name_n
  • cass_iterator_fields_from_index_meta
Meaningless for our driver

Because of different architectures of new and old driver, those configurations can't be directly translated.

  • cass_cluster_set_max_reusable_write_objects
  • cass_cluster_set_new_request_ratio
  • cass_cluster_set_queue_size_io
Footguns, ancient APIs

Those API are simply bad in our opinion, and have good replacements.

  • cass_statement_add_key_index: Prepared statements should be used instead
  • cass_cluster_set_prepare_on_up_or_add_host: Driver automatically reprepares statements, and events are unreliable.
  • cass_cluster_set_no_compact: Ancient feature, we see no point in supporting it.
Tracing

Semantics of those functions are really weird. Old driver fetches tracing data in order to wait until it becomes available, but has no API to expose it to the user, who must fetch the data again. If some users need tracing APIs, we'll prefer to implement new ones that actually expose tracing info.

  • cass_cluster_set_tracing_consistency
  • cass_cluster_set_tracing_max_wait_time
  • cass_cluster_set_tracing_retry_wait_time
"Custom" type

This is deprecated basically everywhere and has no good use, especially with Scylla.

  • cass_collection_append_custom
  • cass_collection_append_custom_n
  • cass_statement_bind_custom
  • cass_statement_bind_custom_by_name
  • cass_statement_bind_custom_by_name_n
  • cass_statement_bind_custom_n
  • cass_tuple_set_custom
  • cass_tuple_set_custom_n
  • cass_user_type_set_custom
  • cass_user_type_set_custom_by_name
  • cass_user_type_set_custom_by_name_n
  • cass_user_type_set_custom_n
Deprecated and no-op in old driver

Those APIs were already deprecated, and not doing anything. We see no reason to keep them.

  • cass_cluster_set_max_concurrent_creation
  • cass_cluster_set_max_concurrent_requests_threshold
  • cass_cluster_set_max_connections_per_host
  • cass_cluster_set_max_requests_per_flush
  • cass_cluster_set_pending_requests_high_water_mark
  • cass_cluster_set_pending_requests_low_water_mark
  • cass_cluster_set_queue_size_event
  • cass_cluster_set_write_bytes_high_water_mark
  • cass_cluster_set_write_bytes_low_water_mark
  • cass_log_cleanup
  • cass_log_set_queue_size
DSE functions

Our driver doesn't support DSE-exclusive features.

  • cass_cluster_set_cloud_secure_connection_bundle
  • cass_cluster_set_cloud_secure_connection_bundle_n
  • cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init
  • cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init_n
  • cass_cluster_set_monitor_reporting_interval

Testing


Integration tests from the original cpp-driver are compilable but not all tests pass yet. Some tests are added to GitHub Actions workflows and are used to test every pull request on both ScyllaDB and Cassandra clusters.

To build and run the integration tests several requirements need to be met:

  • Install libuv and openssl on your system:
# On Ubuntu
sudo apt-get install libuv1-dev
sudo apt-get install libssl1.0.0

# On Fedora
sudo dnf install libuv-devel
sudo dnf install openssl-devel

Finally, to build the integration tests:

mkdir build && cd build
cmake -DCASS_BUILD_INTEGRATION_TESTS=ON  .. && make

Now, use --gtest_filter to run certain integration tests:

./cassandra-integration-tests --scylla --version=<SCYLLA_VERSION> --category=CASSANDRA --verbose=ccm --gtest_filter="ClusterTests.*"
Note: Tests that pass with ScyllaDB and Cassandra clusters can be found in Makefile: SCYLLA_TEST_FILTER and CASSANDRA_TEST_FILTER env variables.

Creating Installable Packages


If you want to distribute the driver or install it system-wide, you can create installable packages rather than copying files manually. Packages bundle the driver's libraries, headers, and metadata so your operating system's package manager (like apt, dnf, or the macOS/Windows installer) can install, update, and remove the driver cleanly.

This project uses CPack, a packaging tool included with CMake, to generate packages for all major platforms.

Step 1: Build the Driver

First, compile the driver in release mode:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release

Step 2: Create the Package

From the build directory, run cpack with a "generator" flag (-G) that matches your target system:

Linux

Linux has two main package formats:

  • DEB (.deb) — used by Debian, Ubuntu, and derivatives
  • RPM (.rpm) — used by Fedora, RHEL, Rocky Linux, CentOS, and derivatives
cd build
cpack -G DEB -C Release   # Creates a .deb file
cpack -G RPM -C Release   # Creates a .rpm file

You can then install the resulting package with your package manager:

sudo apt install ./scylla-cpp-driver*.deb    # Debian/Ubuntu
sudo dnf install ./scylla-cpp-driver*.rpm    # Fedora/RHEL

macOS

macOS supports two distribution formats:

  • productbuild (.pkg) — standard macOS installer package
  • DragNDrop (.dmg) — disk image that users drag to Applications
cd build
cpack -G productbuild -C Release   # Creates a .pkg installer
cpack -G DragNDrop -C Release      # Creates a .dmg disk image

Windows

Windows uses MSI installers, generated via the WiX Toolset (version 3.11 or later).

Installing WiX Toolset:

  • Via Chocolatey (recommended):

    choco install wixtoolset
  • Manual download: Get the installer from WiX Toolset releases and run the .exe installer. Make sure to add WiX to your PATH during installation.

Creating the package:

cd build
cpack -G WIX -C Release

The installer places files in Program Files\ScyllaDB\Scylla CPP Driver.

Automated Packaging in CI

GitHub Actions automatically builds packages on every release. See:

  • .github/workflows/build-cpack-packages.yml — builds packages for all platforms
  • .github/workflows/release-upload-packages.yml — attaches packages to GitHub releases

Getting Help


Reference Documentation


Other Drivers


License


This project is licensed under the GNU Lesser General Public License v2.1

About

API-compatible rewrite of https://github.com/scylladb/cpp-driver as a wrapper for Rust driver.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 12