Cassandra partition key routing to wide-column storage

Apache Cassandra for system design interviews

Wide-column data model, partition keys, consistent hashing, replication, QUORUM, LSM writes, gossip — plus query-driven modeling with Discord and Ticketmaster examples.

Why Cassandra

Cassandra combines ideas from Dynamo and Bigtable: massive footprints, high query volume, flexible sparse columns. It's open-source, runs in a cluster, and scales out horizontally. This deep dive covers what makes it attractive, how it works internally, and when to reach for it in an interview.

01Model

Keyspace · table · wide rows.

02Scale

Consistent hash · vnodes.

03Writes

LSM · append · compaction.

04Design

Access patterns first — no JOINs.

Data model

Keyspace — top-level unit (like a database in Postgres). Defines replication strategy.
Table — rows within a keyspace, with a schema and primary key.
Row — one record, keyed by primary key.
Column — name, type, value; sparse rows OK — not every row needs every column. Each column carries timestamp metadata; conflicts resolve via last-write-wins.

Nested keyspace table row column structure with sparse rows.
Think JSON nesting — flexible flat and nested data.
{
  "keyspace1": {
    "table1": {
      "row1": { "col1": 1, "col2": "2" },
      "row2": { "col1": 10, "col3": 3.0 },
      "row3": { "col4": { "company": "Lettuce", "city": "Seattle" } }
    }
  }
}

Primary key

Every row is unique via a primary key — partition key(s) plus optional clustering key(s). Same concept as DynamoDB's primary key, shared 1:1.

  • Partition key — determines which partition (and node) holds the row.
  • Clustering key — sort order of rows within a partition.
Partition key channel_id bucket and clustering key message_id DESC.
Partition key picks the node; clustering key picks the order.
-- Partition only
CREATE TABLE t (a text, b text, c text, PRIMARY KEY (a));

-- Partition + clustering
CREATE TABLE t (a text, b text, c text, PRIMARY KEY ((a), b))
  WITH CLUSTERING ORDER BY (b ASC);

-- Composite partition key
CREATE TABLE t (a text, b text, c text, d text, PRIMARY KEY ((a, b), c));

Partitioning

Cassandra scales by partitioning data across cluster nodes using consistent hashing. Unlike hash(value) % num_nodes — which remaps most keys when nodes change and can skew load — consistent hashing maps values to a ring and walks clockwise to the first token/node. Adding or removing a node only remaps adjacent ranges.

Virtual nodes (vnodes) map multiple ring positions to each physical node for even load and flexible capacity (bigger machines can own more vnodes). See Consistent hashing for the full ring story.

Consistent hash ring with eight vnode positions t1 through t8 and color-coded physical nodes.
Virtual nodes (vnodes) — multiple ring positions per physical machine for even load.
Hash ring with token values 0 through 87 assigned to nodes n1 through n8.
Partition key hashed to a token — walk clockwise to find the owning node.

Replication

Partitions replicate to multiple nodes for availability. With replication factor 3, Cassandra hashes the key, then scans clockwise for two additional vnodes — skipping vnodes on the same physical node so one host failure doesn't take multiple replicas.

Data hashed to node n2 on the ring, then replicated clockwise to nodes n3 and n4.
RF=3 — primary on n2, replicas on the next nodes clockwise.
  • NetworkTopologyStrategy — production default; rack- and datacenter-aware.
  • SimpleStrategy — clockwise scan only; fine for dev/test.
ALTER KEYSPACE lettuce WITH REPLICATION = {
  'class': 'NetworkTopologyStrategy',
  'dc1': 3, 'dc2': 2
};

Consistency

Cassandra is subject to the CAP theorem. No transactions or full ACID — only atomic, isolated row-level writes within a partition. You tune read/write consistency levels: how many replicas must respond (ONE through ALL).

QUORUM — majority (n/2 + 1) of replicas. QUORUM on both reads and writes guarantees overlap: with RF=3, 2 nodes participate in each operation, so at least one node seen on read also saw the write.

Write to nodes n1 and n3, read from n2 and n3 — n3 overlap guarantees the read sees the write.
QUORUM — W + R > N; overlapping replica n3 bounds staleness.

Query routing

Any node can act as coordinator. Nodes learn cluster topology via gossip, compute partition location from the partition key, and fan out to replicas per consistency level.

Query arrives at node n2 which becomes coordinator and routes to replica nodes on the ring.
Any node can coordinate — no dedicated master for reads or writes.

Storage model (LSM)

Cassandra favors write speed over read speed. Creates, updates, and deletes are append-style entries (deletes write tombstones). An LSM tree backs this — same family as DynamoDB-style engines. See Database indexing for LSM vs B-tree trade-offs. Metrics-oriented LSM + compression + time partitions: Time-series databases.

  1. Write hits commit log (WAL) for durability.
  2. Write goes to memtable (in-memory, sorted by primary key).
  3. Memtable flushes to immutable SSTable on disk.
  4. Reads: memtable first, then bloom filter + SSTables newest-first.
  5. Compaction merges SSTables and purges tombstones.
Write path: commit log on disk, memtable in memory, flush to SSTables, then trim commit log.
Append-first writes — memtable flush creates immutable SSTables; compaction merges them later.

Gossip and fault tolerance

Nodes exchange cluster state via gossip — peer-to-peer, with seed nodes as guaranteed gossip hubs so sub-clusters don't form. Vector clocks (generation + version) let nodes ignore stale gossip.

Phi accrual failure detection convicts unresponsive nodes during gossip. Nodes aren't permanently "down" unless an admin decommissions them — intermittent failures shouldn't trigger full rebalancing.

Hinted handoff — when a coordinator can't reach a replica, it stores a short-lived hint so the write succeeds; delivers when the node returns. Long outages need repair/rebuild, not hints alone.

Coordinator n2 stores a hint when replica n8 is offline, then delivers on hinted handoff when n8 returns.
Fault tolerance — hints cover brief replica outages without failing the write.

Query-driven data modeling

Relational modeling is entity-driven with normalization and JOINs. Cassandra is query-driven: design access patterns first, denormalize aggressively, one table per query shape. No JOINs, no foreign keys, single-table queries only.

  • Partition key — keeps related reads on one partition (avoid scatter-gather).
  • Partition size — hot or unbounded partitions hurt (Discord's lesson).
  • Clustering key — sort order for timeline queries.
  • Denormalization — duplicate data across tables for different access patterns.

Example: Discord messages

Channels are busy; users read recent messages in reverse chronological order. Original schema:

CREATE TABLE messages (
  channel_id bigint,
  message_id bigint,
  author_id bigint,
  content text,
  PRIMARY KEY (channel_id, message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

message_id as Snowflake ID (time-sortable UUID) avoids primary-key collisions vs millisecond timestamps. Problem: mega-channels created partitions too large and growing forever. Fix — add a bucket (10-day window from Discord epoch) to the partition key:

CREATE TABLE messages (
  channel_id bigint,
  bucket int,
  message_id bigint,
  author_id bigint,
  content text,
  PRIMARY KEY ((channel_id, bucket), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

Example: Ticketmaster seat browsing

Event seat map UX: users browse sections first, then individual seats. Event-wide partition with 10k+ seats forces heavy aggregation. Add section_id to partition key for seat-level queries; separate event_sections table denormalizes section totals and price floors for the venue map — eventual consistency is fine (UI shows "100+" not exact counts).

Stadium section map with Section 124 highlighted showing price floor and ticket count for venue-level browse.
Section-level partition — venue map reads event_sections; seat drill-down reads tickets.
CREATE TABLE tickets (
  event_id bigint,
  section_id bigint,
  seat_id bigint,
  price bigint,
  PRIMARY KEY ((event_id, section_id), seat_id)
);

CREATE TABLE event_sections (
  event_id bigint,
  section_id bigint,
  num_tickets bigint,
  price_floor bigint,
  PRIMARY KEY (event_id, section_id)
);

Advanced features

  • Storage Attached Indexes (SAI) — secondary indexes with flexible queries; slower than partition-key lookups.
  • Materialized views — Cassandra-maintained denormalized tables from a source table.
  • Search plugins — ElasticSearch/Solr integration for full-text (see Elasticsearch).

Cassandra in an interview

When to use it

  • Availability over strict consistency; high horizontal scale.
  • High write throughput — metrics, logs, chat, IoT.
  • Sparse/flexible schemas with clear, upfront access patterns.

When not to

  • Strong consistency requirements, complex JOINs, ad-hoc analytics.
  • You haven't defined access patterns — you'll paint yourself into a corner.

Cost and performance levers

Cassandra query-first design

Hot partition warning

Partition key = country when 80% traffic is one country → hotspot. Add bucket suffix or choose higher-cardinality key.

Failure modes to mention

Call out at least one dependency failure (DB down, cache stampede, queue lag, region outage) and your mitigation (timeouts, retries with jitter, degraded mode, circuit breaker).

Interview Q&A by level

Practice saying these out loud for Cassandra. Interviewers grade clarity and judgment more than buzzwords.

Interview takeaway

Match depth to the bar: define → trade off → operate. Don't dump principal answers in an entry-level screen.

Wrapping up

Cassandra is versatile but not universal. Its value shows up when query-driven schemas, partition keys, and LSM write paths match your access patterns. Internals — consistent hashing, replication, QUORUM, compaction — are what separate a name-drop from a defensible design.

Compare with PostgreSQL (interview default), Amazon DynamoDB (managed AWS alternative), write scaling context in Scaling writes, modeling trade-offs in Data modeling, and LSM depth in Database indexing.

← Lattice