Best practices for Cassandra drivers

If your applications use Cassandra drivers, use these guidelines to help improve performance and minimize resource utilization.

Session and cluster handling

All drivers use root objects to connect to Hyper-Converged Database (HCD) clusters. These root objects are expensive to create because they initialize and maintain connection pools to every node in a cluster. However, a single root object can handle thousands of queries concurrently, so you can use the same driver session instance to execute all the queries for an application.

Follow these best practices to ensure that you instantiate and reuse these objects effectively:

  • Create only one long-lived root object for each physical cluster.

  • Create one session instance for each application, and then reuse that session for the entire lifetime of the application. For example, don’t open and close the session object for each individual request or batch of requests.

Most Cassandra drivers provide separate cluster and session objects. The exceptions are the Java and Node.js drivers that combine the cluster and session concepts into one object. For more information about session and cluster configuration, see your driver’s documentation:

C/C++ driver

Create one CassCluster for each physical cluster.

Create and reuse one CassSession for each application.

For an example, see Get started with the C++ driver.

C# driver

Create one Cassandra.Cluster for each physical cluster.

Create and reuse one Cassandra.Session for each application.

For an example, see Get started with the C# driver.

Go driver

Create one NewCluster for each physical cluster.

Create and reuse one Session for each application.

For an example, see Get started with the Go driver.

Java driver

The Java driver defines clusters and sessions together in CqlSession. Create one CqlSession instance for each target physical cluster, and then reuse those instances throughout your application. For more information and examples, see the documentation for your version of the Java driver:

If you want the driver to retry the connection if it fails to establish a CQL session, set advanced.reconnect-on-init to true. For more information, see Cassandra driver reconnection policies.

Node.js driver

The Node.js driver combines the concepts of session and cluster into a single Client interface. Create one Client instance for a given cluster, and then use that instance across your application. For more information and examples, see the documentation for your version of the Node.js driver:

Python driver

Create one Cluster for each physical cluster.

Create and reuse one Session for each application.

For more information and examples, see the documentation for your version of the Python driver:

For more information and best practices to help optimize driver connections, see Performance tuning for Cassandra drivers.

Use asynchronous queries for bulk data access

DataStax recommends executing queries asynchronously when processing large amounts of data, including large numbers of queries or long-running queries. For more information and recommended usage patterns, see Asynchronous query execution with Cassandra drivers.

In large partitions, fetch rows in batches

When dealing with large partitions, don’t attempt to read the complete partition at once. Doing so can exceed memory limits granted to your operating system process.

Example: Iteration with the Java driver
// Avoid patterns that read all rows into memory at once:
List<Row> rows =
   session.execute("SELECT * FROM employees WHERE company = 'DS'").all();

// Prefer patterns that fetch rows in batches:
Iterator<Row> iterator =
   session.execute("SELECT * FROM employees WHERE company = 'DS'").iterator();
while (iterator.hasNext()) {
   Row row = iterator.next();
   // process...
}
Example: Iteration with the Python driver
from cassandra.query import SimpleStatement
query = "SELECT * FROM users"  # Assume users contains 100 rows
statement = SimpleStatement(query, fetch_size=10) # Fetch rows in batches
for user_row in session.execute(statement):
    process_user(user_row)

By default, drivers pre-fetch 5000 rows, and this setting is configurable. For more information about paging and iteration, see Result paging with Cassandra drivers, Use object mappers in Cassandra drivers, and your driver’s documentation.

Use prepared statements for frequently run queries

Prepared statements are queries that you can run multiple times with different parameters. They can make your applications more efficient by reducing resource requirements and eliminating redundancies in code. For more information, see Prepared statements with Cassandra drivers.

Use lightweight transactions (LWTs) judiciously

Lightweight transaction (LWT) statements aren’t intended as general purpose optimistic locking mechanisms.

LWTs require three round trips between nodes to propose, agree, and publish the new row state, and the row must be read from disk.

Compared to traditional CQL statements, LWTs usually have higher resource requirements, higher response latency, and lower throughput.

For additional considerations for LWTs, see the following:

Avoid allow filtering

Don’t use the ALLOW FILTERING CQL clause unless you can guarantee that the given table is small and performing a full scan will have acceptable response latency.

Even if the clause is limited to a single partition, ALLOW FILTERING cannot guarantee the same performance as searching on clustering columns.

When searching on clustering columns, Cassandra-based databases can perform a binary search. When searching on non-clustering columns, they must read and compare all rows.

Use idempotent statements

A statement is idempotent if executing it multiple times leaves the database in the same state as executing it only once. This also means that the request is safe to retry, and your driver can automatically retry the request in the event of a failure. For more information, see Query idempotence in Cassandra drivers and Retry policies for Cassandra drivers.

Manage tombstones

A tombstone is a marker that indicates deleted table data.

HCD databases store updates to tables in immutable SSTable files to maintain throughput and avoid reading stale data. Deleted data, time-to-live (TTL) data, and null values create tombstones that allow the database to reconcile the logically deleted data with new queries across the cluster.

While tombstones are a necessary byproduct of a distributed database, they can cause warnings and log errors. Additionally, heavy deletes and nulls use extra disk space and decrease performance on reads.

Example: Tombstone creation from writing nulls

The following example demonstrates how tombstones are created from writing nulls.

Assume a table has the following schema:

CREATE TABLE test_ks.my_table_compound_key (
        primary_key text,
        clustering_key text,
        regular_col text,
        PRIMARY KEY (primary_key, clustering_key)
        )

The following query doesn’t create any tombstones:

INSERT INTO my_table_compound_key (primary_key, clustering_key)
        VALUES ('pk1', 'ck1');

However, this query does create a tombstone because it explicitly writes null to regular_col:

INSERT INTO my_table_compound_key (primary_key, clustering_key, regular_col)
        VALUES ('pk1', 'ck1', null);

Cull and prevent tombstones

Culling tombstones and avoiding tombstone creation increases database and application performance:

  • Design your data model to avoid unnecessary deletes.

    For example, minimize explicit data removal and leverage TTL. With TTL, make sure garbage collection occurs promptly, and be aware that each map key has a separate TTL value.

  • When writing, construct queries properly to avoid writing nulls.

  • When reading, use efficient filters in your queries to avoid fetching unnecessary rows, which can contain tombstones.

    For example, when reading all rows from a partition, tombstones are scanned. You can use ordering on clustering columns to avoid reading lots of tombstones.

  • Optimize garbage collection and compaction strategies to remove tombstones efficiently.

  • Use tracing to monitor how tombstones impact query performance.

For more information, see Tombstones.

Performance tuning for Cassandra drivers

For information about performance tuning, see metrics, connection pools, load balancing, and your driver’s documentation.

C/C++ driver

See C/C++ driver performance tips and C/C++ driver tuning.

C# driver

See C# driver tuning policies.

Go driver

See GoCQL driver performance.

Java driver

For all performance tuning options and general information about performance tuning for the Java driver, see the documentation for your version of the Java driver:

For applications that send or store large payloads with the Java driver, DataStax recommends the following configurations in addition to other performance tuning practices:

Node.js driver

See the documentation for your version of the Node.js driver:

Python driver

See the documentation for your version of the Python driver:

Was this helpful?

Give Feedback

How can we improve the documentation?

© Copyright IBM Corporation 2026 | Privacy policy | Terms of use Manage Privacy Choices

Apache, Apache Cassandra, Cassandra, Apache Tomcat, Tomcat, Apache Lucene, Apache Solr, Apache Hadoop, Hadoop, Apache Pulsar, Pulsar, Apache Spark, Spark, Apache TinkerPop, TinkerPop, Apache Kafka and Kafka are either registered trademarks or trademarks of the Apache Software Foundation or its subsidiaries in Canada, the United States and/or other countries. Kubernetes is the registered trademark of the Linux Foundation.

General Inquiries: Contact IBM