Cassandra with Kafka

Apache Kafka is a distributed event-streaming platform. Cassandra is a distributed database built for high-speed writes. These two systems complement each other naturally: Kafka captures and buffers real-time event streams, and Cassandra stores and serves that data at scale. Together they power architectures for IoT pipelines, clickstream analytics, financial transaction processing, and real-time dashboards.

Why Combine Kafka and Cassandra

Kafka Strengths               Cassandra Strengths
──────────────────────────────────────────────────────────────
Durable event log             Fast, scalable writes
High-throughput ingestion      Flexible data modeling
Decoupled producers/consumers  Geo-distributed storage
Replay events from any offset  Always-on availability
Buffering for backpressure     Sub-millisecond read latency

Common Integration Patterns

Pattern                           Flow
──────────────────────────────────────────────────────────────────────
Kafka → Cassandra                 Kafka consumer reads events and
  (stream ingestion)              writes to Cassandra tables

Cassandra → Kafka                 Change Data Capture (CDC) sends
  (change data capture)           Cassandra mutations to Kafka topics

Kafka ↔ Cassandra (bidirectional) Hybrid: read from Cassandra for
                                  enrichment, write results to Kafka

Pattern 1: Kafka Consumer Writing to Cassandra

A Kafka consumer reads messages from a topic and inserts them into Cassandra. This is the most common pattern for building real-time data stores.

IoT Device → Kafka Topic "sensor-readings" → Consumer App → Cassandra

Java Consumer + Cassandra Writer

import org.apache.kafka.clients.consumer.*;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import java.time.Duration;
import java.util.*;

public class SensorConsumer {

  public static void main(String[] args) {

    // --- Kafka setup ---
    Properties kafkaProps = new Properties();
    kafkaProps.put("bootstrap.servers", "kafka-broker:9092");
    kafkaProps.put("group.id",          "sensor-cassandra-writer");
    kafkaProps.put("key.deserializer",
      "org.apache.kafka.common.serialization.StringDeserializer");
    kafkaProps.put("value.deserializer",
      "org.apache.kafka.common.serialization.StringDeserializer");
    kafkaProps.put("auto.offset.reset",  "earliest");
    kafkaProps.put("enable.auto.commit", "false");   // manual commits

    KafkaConsumer<String, String> consumer = new KafkaConsumer<>(kafkaProps);
    consumer.subscribe(List.of("sensor-readings"));

    // --- Cassandra setup ---
    CqlSession session = CqlSession.builder()
      .withLocalDatacenter("us-east")
      .withKeyspace("iot")
      .build();

    PreparedStatement insertReading = session.prepare(
      "INSERT INTO sensor_readings (sensor_id, day, read_time, value) " +
      "VALUES (?, ?, toTimestamp(now()), ?)"
    );

    // --- Consume and write loop ---
    try {
      while (true) {
        ConsumerRecords<String, String> records =
          consumer.poll(Duration.ofMillis(500));

        for (ConsumerRecord<String, String> record : records) {
          // Parse JSON payload: {"sensor_id":"S1","day":"2024-06-15","value":22.5}
          // (Simplified: assume values are pre-parsed)
          String sensorId = record.key();
          String day      = "2024-06-15";
          double value    = Double.parseDouble(record.value());

          session.execute(insertReading.bind(sensorId, day, value));
        }

        consumer.commitSync();   // commit after successful Cassandra writes
      }
    } finally {
      consumer.close();
      session.close();
    }
  }
}

Pattern 2: Kafka Connect — Cassandra Sink Connector

Kafka Connect is a framework for streaming data between Kafka and external systems without writing custom consumer code. The DataStax Kafka Connector (or community alternatives) acts as a Cassandra Sink — it reads from Kafka topics and writes to Cassandra tables automatically.

Kafka Topic → Kafka Connect → DataStax Kafka Connector → Cassandra Table

Connector Configuration (JSON)

{
  "name": "cassandra-sink-sensor",
  "config": {
    "connector.class":
      "com.datastax.oss.kafka.sink.CassandraSinkConnector",
    "tasks.max": "4",
    "topics": "sensor-readings",

    "contactPoints": "10.0.0.1,10.0.0.2",
    "loadBalancing.localDc": "us-east",
    "auth.username": "alice",
    "auth.password": "Al1ce$ecure!",

    "topic.sensor-readings.iot.sensor_readings.mapping":
      "sensor_id=value.sensor_id, day=value.day, value=value.reading",
    "topic.sensor-readings.iot.sensor_readings.consistencyLevel":
      "LOCAL_QUORUM",
    "topic.sensor-readings.iot.sensor_readings.ttl": "2592000"
  }
}

Deploy the Connector

curl -X POST http://kafka-connect:8083/connectors \
  -H "Content-Type: application/json" \
  -d @cassandra-sink-config.json

Pattern 3: Cassandra Change Data Capture (CDC) → Kafka

Cassandra CDC captures every mutation (INSERT, UPDATE, DELETE) and writes it to a CDC log directory. A CDC reader (such as the Debezium Cassandra connector) reads from this log and publishes changes to Kafka topics, enabling downstream consumers to react to data changes.

Cassandra mutation
    │ CDC log (/var/lib/cassandra/cdc_raw/)
    ▼
Debezium Cassandra Source Connector
    │ Kafka topic "cassandra.ecommerce.orders"
    ▼
Downstream consumers (analytics, search index, notifications)

Enable CDC on a Table

-- Enable CDC in cassandra.yaml:
-- cdc_enabled: true
-- cdc_raw_directory: /var/lib/cassandra/cdc_raw

-- Enable CDC on the table:
ALTER TABLE ecommerce.orders WITH cdc = true;

Exactly-Once Semantics

Kafka guarantees at-least-once delivery by default. When writing to Cassandra, duplicate Kafka messages cause duplicate inserts. Because Cassandra INSERT is an upsert, duplicate inserts with the same primary key are idempotent — the second write simply overwrites the first with the same values. This makes Kafka-to-Cassandra pipelines naturally resilient to duplicates when the primary key is deterministic (derived from the event, not randomly generated).

Event: { order_id: "ORD-001", total: 99.99 }
  → Kafka delivers twice (at-least-once)

Write 1: INSERT INTO orders (order_id, total) VALUES ('ORD-001', 99.99)
Write 2: INSERT INTO orders (order_id, total) VALUES ('ORD-001', 99.99)
  → Second write is a no-op: same PK, same values

Result: exactly one row with correct data ✓

Throughput Tuning

Tuning Parameter               Recommendation
──────────────────────────────────────────────────────────────
Kafka consumer tasks.max       Set to number of Cassandra nodes
                               or topic partition count
Cassandra write consistency    LOCAL_ONE for max throughput
Batch Kafka records            Write multiple records per
                               Cassandra UNLOGGED BATCH per partition
Cassandra async writes         Use execute_async; wait for futures
                               in groups of 100–500
Consumer poll interval         100–500 ms depending on latency needs

Typical End-to-End Architecture

Sources                 Kafka                    Cassandra
──────────────────────────────────────────────────────────────
Mobile Apps      ──▶   Topic: user-events  ──▶  Table: user_activity
IoT Devices      ──▶   Topic: sensor-data  ──▶  Table: sensor_readings
Payment Service  ──▶   Topic: payments     ──▶  Table: payment_ledger
Web Servers      ──▶   Topic: page-views   ──▶  Counter: page_views

Summary

Kafka and Cassandra work together by combining Kafka's durable, high-throughput event streaming with Cassandra's fast, distributed storage. The three integration patterns are: writing a custom Kafka consumer that inserts into Cassandra, using Kafka Connect with the DataStax Sink Connector for a no-code pipeline, and using Cassandra CDC with Debezium to publish Cassandra mutations to Kafka. Cassandra's upsert behavior makes the pipeline naturally tolerant of Kafka's at-least-once delivery. Tune throughput by using LOCAL_ONE consistency, async writes, and multiple consumer tasks aligned with partition count.

Leave a Comment

Your email address will not be published. Required fields are marked *