Apache Cassandra

7 interview angles 6 min read source

Apache Cassandra

Wide-column NoSQL DB, distributed by default. Designed at Facebook (2008), open-sourced, then donated to Apache. Used at Netflix, Apple, Spotify, Uber for massive-scale always-on workloads.

For most Python backend roles, you’re unlikely to choose Cassandra for new projects (it’s specialized). You might encounter it at very-large-scale shops or in inherited systems. Knowing the model matters for the interview.

Core model

Cassandra is partitioned, replicated, eventually consistent, AP-leaning (per CAP).

ring of nodes

hash(partition_key) → maps to one node (the coordinator for that partition)
                    → replicated to N other nodes (replication factor)

No master / leader for writes. Any node can accept a write or read; the coordinator routes to replicas.

Data model

Wide-column store. Tables look like relational tables but with key differences:

CREATE TABLE user_events (
    user_id UUID,
    event_time TIMESTAMP,
    event_type TEXT,
    metadata MAP<TEXT, TEXT>,
    PRIMARY KEY ((user_id), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
  • Partition key: (user_id) — determines which node holds the data. Single value or composite.
  • Clustering columns: event_time — order rows within a partition. Multiple clustering cols allowed.
  • Primary key: partition key + clustering columns together.

All rows with the same partition key live on the same set of nodes (replicas). Reads scoped to a partition are efficient; cross-partition queries are expensive.

Reading and writing — CQL

-- Write
INSERT INTO user_events (user_id, event_time, event_type)
VALUES (uuid(), now(), 'login');

-- Read by partition
SELECT * FROM user_events WHERE user_id = ? LIMIT 100;

-- Read with clustering filter
SELECT * FROM user_events
WHERE user_id = ? AND event_time > '2026-01-01';

Critical: queries must include the full partition key. Otherwise Cassandra has to scan all nodes — slow, often disallowed.

-- BAD: no partition key
SELECT * FROM user_events WHERE event_type = 'login';
-- Returns error or requires ALLOW FILTERING (a smell)

Data modeling — query-first

Cassandra inverts normalized data modeling. You denormalize per query pattern.

-- Query 1: get all events for a user
CREATE TABLE events_by_user (
    user_id UUID,
    event_time TIMESTAMP,
    ...
    PRIMARY KEY ((user_id), event_time)
);

-- Query 2: get all users who triggered an event type today
CREATE TABLE users_by_event_type_day (
    event_type TEXT,
    day DATE,
    user_id UUID,
    PRIMARY KEY ((event_type, day), user_id)
);

Two separate tables, same data. The write path inserts to both; the read path queries the right table per question.

No JOINs. Joins are application-side, or you denormalize.

Consistency levels

Per-operation, not per-table:

session.execute(query, consistency_level=ConsistencyLevel.QUORUM)
Level Meaning
ONE one replica acks (fast, low durability)
QUORUM majority of replicas ack
LOCAL_QUORUM majority within the local DC (cross-DC efficient)
ALL all replicas ack (rare)
ANY even hinted-handoff target acks (write-only; weakest)

Tunable consistency: if you write with QUORUM and read with QUORUM, with replication factor 3, you get strong consistency (W + R > N).

Trade-off: stronger consistency = higher latency + more nodes involved per operation.

Replication

CREATE KEYSPACE my_keyspace
WITH REPLICATION = {'class': 'NetworkTopologyStrategy', 'us-east': 3, 'us-west': 3};

Per-DC replication factor. Cross-DC writes happen asynchronously.

When Cassandra wins

  • Massive scale. Linear horizontal scaling. Petabytes spread across hundreds of nodes.
  • Always-on. No single point of failure; survives multi-node + multi-DC outages.
  • Time-series workloads. Event logs, sensor data, audit trails.
  • Geo-distributed writes. Per-DC writes accepted locally; replicated asynchronously.

When Cassandra doesn’t

  • Complex queries (JOINs, ad-hoc filtering, aggregations). No.
  • Strong consistency requirements by default. Possible with QUORUM/ALL but adds latency.
  • Small data. Operational complexity is huge; Postgres is much simpler at the < 100 GB scale.
  • Frequent updates / deletes on the same row. Cassandra’s tombstone-based deletion causes problems with many deletes (read amplification).

Python client — DataStax driver

from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider

auth_provider = PlainTextAuthProvider(username='user', password='pass')
cluster = Cluster(['node1.example.com', 'node2.example.com'], auth_provider=auth_provider)
session = cluster.connect('my_keyspace')

# Prepared statement (faster, safer)
stmt = session.prepare("SELECT * FROM user_events WHERE user_id = ?")
rows = session.execute(stmt, [user_id])

for row in rows:
    print(row.event_type, row.event_time)

cluster.shutdown()

Prepared statements are heavily preferred — better performance, less risk of CQL injection.

Async via execute_async:

future = session.execute_async(stmt, [user_id])
result = future.result()    # blocks

For asyncio integration: there’s cassandra-driver with asyncio reactor option, or use aiocassandra (wrapper).

Tombstones — the famous gotcha

When you delete a row, Cassandra marks it with a tombstone (not removed immediately). On read, all tombstones in the relevant partition are scanned and applied.

If a partition accumulates many tombstones (many deletes), reads slow down dramatically. The tombstone threshold (default: 100k per partition) triggers warnings; beyond it, queries can fail.

Causes:

  • Soft-delete-heavy workloads.
  • Range deletes.
  • TTL’d rows that aren’t compacted promptly.

Fix:

  • Avoid frequent deletes; design for append-only.
  • Run nodetool compact to compact away tombstones.
  • Lower gc_grace_seconds (default 10 days) to compact tombstones sooner — but at the risk of “deleted data” reappearing if a node is down too long.

Operational complexity

Cassandra is operationally heavier than Postgres:

  • Repair schedules (nodetool repair) to maintain consistency between replicas.
  • Compaction tuning (size-tiered vs leveled).
  • Snapshot management for backups.
  • Multi-DC topology configuration.

Managed Cassandra: AWS Keyspaces, Azure Cosmos DB Cassandra API, DataStax Astra. These offload most ops.

When you’d choose Cassandra in 2026

Honestly: rarely for greenfield. Modern alternatives:

  • DynamoDB for AWS-native; managed; similar wide-column model.
  • ScyllaDB for raw Cassandra-compatible performance.
  • Postgres for almost anything where Cassandra would be overkill.
  • TimescaleDB for time-series (Postgres extension).
  • YugabyteDB / CockroachDB for distributed SQL with relational features.

You’d choose Cassandra in 2026 if:

  1. You already run it at scale and the operational model is sunk cost.
  2. You explicitly need multi-DC active-active writes and the tunable consistency model.
  3. You have a workload Cassandra is genuinely best at (very-write-heavy, time-series, etc.) and you have the ops expertise.

Interview angle

  • “What’s the Cassandra data model?” — wide-column store. Tables have a partition key + clustering columns. Partition key determines node placement; clustering columns order rows within a partition. Query-first design: denormalize per access pattern.
  • “Why must queries include the partition key?” — Cassandra needs the partition key to route to the right node(s). Without it, the query scatters across all nodes — slow and discouraged (ALLOW FILTERING is a code smell).
  • “What’s tunable consistency?” — per-operation ConsistencyLevel. ONE (fastest, weakest), QUORUM (majority), LOCAL_QUORUM (local DC majority), ALL (all replicas). W+R>N gives effective strong consistency.
  • “Cassandra vs DynamoDB?” — similar partition-key model. DynamoDB: managed (AWS); pay-per-request; no JOINs. Cassandra: self-hosted (or managed via Astra / Keyspaces); operational complexity; tunable consistency. DynamoDB is easier on AWS; Cassandra wins multi-cloud and at extreme write-heavy scale.
  • “What are tombstones and why do they matter?” — Cassandra marks deletes with tombstones (immutable storage). On read, tombstones in the partition are scanned. Many tombstones → slow reads, eventual failures. Avoid delete-heavy workloads; tune gc_grace_seconds and compaction.
  • “When would you NOT use Cassandra?” — JOINs, ad-hoc queries, strong consistency by default, small scale, frequent updates/deletes. Postgres is simpler for < 100 GB workloads; DynamoDB is easier for AWS-only NoSQL; ScyllaDB for Cassandra-compatible at higher perf.
  • “Production gotchas?” — multi-row reads across partitions are slow; queries without the partition key scatter; tombstone accumulation kills reads; repair schedule must run regularly; compaction tuning matters for write-heavy workloads.