Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Cassandra Sink Connector

Stream Kafka events to Apache Cassandra tables using the DataStax Kafka Connector.


The Cassandra Sink Connector writes Kafka records to Cassandra tables, supporting:

  • Automatic table mapping from topic structure
  • Multiple consistency levels
  • TTL configuration
  • Batch writes for performance
  • Exactly-once semantics (idempotent upserts)

Terminal window
# Download connector
curl -O https://downloads.datastax.com/kafka/kafka-connect-cassandra-sink.tar.gz
tar -xzf kafka-connect-cassandra-sink.tar.gz
# Copy to Connect plugin path
cp -r kafka-connect-cassandra-sink /usr/share/kafka/plugins/
services:
kafka-connect:
image: confluentinc/cp-kafka-connect:7.5.0
environment:
CONNECT_PLUGIN_PATH: /usr/share/kafka/plugins
volumes:
- ./kafka-connect-cassandra-sink:/usr/share/kafka/plugins/cassandra-sink

{
"name": "cassandra-sink",
"config": {
"connector.class": "com.datastax.oss.kafka.sink.CassandraSinkConnector",
"tasks.max": "3",
"topics": "events",
"contactPoints": "cassandra1,cassandra2,cassandra3",
"loadBalancing.localDc": "datacenter1",
"port": "9042",
"topic.events.keyspace.table.mapping": "analytics.events_by_time",
"topic.events.keyspace.table.consistencyLevel": "LOCAL_QUORUM"
}
}

PropertyDescriptionDefault
contactPointsCassandra contact pointsRequired
portCQL native port9042
loadBalancing.localDcLocal datacenter for routingRequired
maxConcurrentRequestsMax concurrent requests500
maxNumberOfRecordsInBatchBatch size32
{
"auth.provider": "PLAIN",
"auth.username": "cassandra_user",
"auth.password": "cassandra_password"
}
{
"ssl.provider": "JDK",
"ssl.hostnameValidation": true,
"ssl.keystore.path": "/path/to/keystore.jks",
"ssl.keystore.password": "keystore_password",
"ssl.truststore.path": "/path/to/truststore.jks",
"ssl.truststore.password": "truststore_password"
}

Map topic fields directly to table columns.

Kafka Message:

{
"event_id": "abc123",
"event_type": "click",
"timestamp": 1705312800000,
"user_id": "user456"
}

Cassandra Table:

CREATE TABLE analytics.events_by_time (
event_date date,
event_time timestamp,
event_id text,
event_type text,
user_id text,
PRIMARY KEY ((event_date), event_time, event_id)
) WITH CLUSTERING ORDER BY (event_time DESC);

Configuration:

{
"topic.events.keyspace.table.mapping": "analytics.events_by_time",
"topic.events.analytics.events_by_time.mapping": "event_id=value.event_id, event_type=value.event_type, event_time=value.timestamp, user_id=value.user_id, event_date=now()"
}
FunctionDescriptionExample
value.fieldExtract from valuevalue.user_id
key.fieldExtract from keykey.partition_key
header.fieldExtract from headerheader.trace_id
now()Current timestampevent_time=now()
{
"topic.events.analytics.events_by_time.mapping": "event_date=value.timestamp:toDate, event_time=value.timestamp, event_id=value.event_id"
}

LevelDescriptionUse Case
LOCAL_ONEOne replica in local DCHigh throughput, lower durability
LOCAL_QUORUMMajority in local DCBalanced durability/performance
QUORUMMajority across all DCsStrong consistency
ALLAll replicasMaximum durability
{
"topic.events.keyspace.table.consistencyLevel": "LOCAL_QUORUM"
}

Automatically expire records after a specified time.

{
"topic.events.keyspace.table.ttl": "86400",
"topic.events.keyspace.table.ttlTimeUnit": "SECONDS"
}
Time UnitDescription
SECONDSTTL in seconds
MINUTESTTL in minutes
HOURSTTL in hours
DAYSTTL in days

{
"errors.tolerance": "all",
"errors.deadletterqueue.topic.name": "dlq-cassandra-sink",
"errors.deadletterqueue.topic.replication.factor": 3,
"errors.deadletterqueue.context.headers.enable": true
}
{
"maxNumberOfRecordsInBatch": 32,
"queryExecutionTimeout": 30,
"connectionPoolLocalSize": 4
}

{
"maxNumberOfRecordsInBatch": 100,
"maxConcurrentRequests": 1000
}
{
"tasks.max": "6"
}

Recommendation: Set tasks.max equal to or greater than the number of topic partitions for maximum parallelism.

{
"connectionPoolLocalSize": 4,
"connectionPoolRemoteSize": 2
}

{
"name": "events-to-cassandra",
"config": {
"connector.class": "com.datastax.oss.kafka.sink.CassandraSinkConnector",
"tasks.max": "6",
"topics": "events",
"contactPoints": "cass1.example.com,cass2.example.com,cass3.example.com",
"loadBalancing.localDc": "datacenter1",
"port": "9042",
"auth.provider": "PLAIN",
"auth.username": "${secrets:cassandra/username}",
"auth.password": "${secrets:cassandra/password}",
"ssl.provider": "JDK",
"ssl.truststore.path": "/etc/kafka-connect/ssl/truststore.jks",
"ssl.truststore.password": "${secrets:ssl/truststore-password}",
"topic.events.keyspace.table.mapping": "analytics.events_by_time",
"topic.events.analytics.events_by_time.mapping": "event_date=value.timestamp:toDate, event_time=value.timestamp, event_id=value.event_id, event_type=value.event_type, user_id=value.user_id, payload=value.payload",
"topic.events.keyspace.table.consistencyLevel": "LOCAL_QUORUM",
"topic.events.keyspace.table.ttl": "365",
"topic.events.keyspace.table.ttlTimeUnit": "DAYS",
"maxNumberOfRecordsInBatch": 100,
"maxConcurrentRequests": 1000,
"errors.tolerance": "all",
"errors.deadletterqueue.topic.name": "dlq-cassandra-sink",
"errors.deadletterqueue.topic.replication.factor": 3
}
}

MetricDescription
connector-metrics/record-send-rateRecords sent per second
connector-metrics/record-error-rateError rate
connector-metrics/batch-size-avgAverage batch size
Terminal window
# Check connector status
curl http://connect:8083/connectors/cassandra-sink/status
# Check task status
curl http://connect:8083/connectors/cassandra-sink/tasks/0/status

IssueCauseSolution
Connection timeoutNetwork/firewallVerify connectivity to contact points
Authentication failedInvalid credentialsCheck username/password
WriteTimeoutExceptionSlow clusterIncrease queryExecutionTimeout
High error rateSchema mismatchVerify table mapping