intermediate~2h

Cassandra — Why Writes Are Fast (and What Compaction Costs You)

Cassandra is famous for extremely fast writes, for a specific, learnable reason. What matters for a developer isn't the file format underneath — it's that the speed is borrowed from reads and paid back by a background process you have to choose a strategy for.

Learning objectives

  • Explain, at the level a developer needs, why Cassandra writes don't pay a read-before-write cost.
  • Explain why an update or delete doesn't modify data in place, and what that means for read latency.
  • Choose an appropriate compaction strategy for a write-heavy, read-heavy, or time-series workload.

◆ The problem

A relational UPDATE typically has to find the existing row first — an index lookup — then lock it, then modify it in place. Under high write concurrency, that lookup-and-lock sequence is real, measurable contention, and it's part of why write throughput on a single relational node has a ceiling.

Cassandra's write path skips essentially all of that. A write is appended to a commit log on disk (a write-ahead log, for crash recovery — the same durability idea as Transaction Mastery, Chapter 05, just Cassandra-specific and append-only) and recorded in an in-memory structure — full stop. No lookup of the existing value, no read-before-write, no lock contention with other writers touching the same partition. A write's cost barely depends on how much data already exists for that key, which is the entire reason Cassandra can sustain very high, very predictable write throughput.

One thing worth knowing without needing to manage it: that in-memory structure is periodically flushed to disk as an immutable file. You don't trigger or tune this directly, but its immutability is exactly what the rest of this chapter is about.

Because on-disk files are immutable, an UPDATE to an existing row doesn't rewrite that row's old data in place — it writes a new version to a new file. A DELETE doesn't erase anything either — it writes a small marker (a "tombstone") recording that the row or column should now be treated as deleted. Nothing is ever modified in place; every change is a new, separate write.

The consequence for a developer: a single partition's true, current data can be scattered across several on-disk files at once, and a read has to check all of them and reconcile the results — newest version wins, tombstones suppress the values they mark as deleted — before it can return an answer. The more separate write events have hit a partition since it was last consolidated, the more files a read might have to touch. This is the direct, visible cost of Cassandra's fast-write design, and it's precisely the problem the next section's background process exists to bound.

▲ Common mistake

Using a Cassandra row or partition as something that gets updated or deleted-from very frequently — a mutable counter, or a queue pattern with delete-immediately-after-read — generates a disproportionate number of tombstones and file fragments relative to the actual live data, and read latency on that partition visibly degrades. This is a well-documented, real production failure mode, not a theoretical one: Cassandra can even be configured to warn, or outright fail, a read that has to scan past too many tombstones to find its answer, precisely because of this pattern.

Compaction is a background process that merges multiple on-disk files for the same table into fewer, larger ones — combining the scattered versions of a partition's rows back into one place, and permanently discarding data that's been superseded by a newer version or fully tombstoned past its grace period. It runs continuously and automatically; you never trigger it per write. What you do choose is the strategy it uses, because that choice changes the trade-off between write throughput, read latency, and background CPU/disk overhead — and picking the wrong one for a workload is a real, recurring production tuning question, not an academic one.

StrategyHow it groups filesBest forTrade-off
SizeTieredCompactionStrategy (STCS)Merges similarly-sized files together as they accumulateWrite-heavy workloads; the general defaultA partition's data can stay spread across several files longer, and large merges can temporarily need extra disk space
LeveledCompactionStrategy (LCS)Organizes files into fixed-size levels, bounding how many files a single-partition read ever has to checkRead-heavy workloads needing predictable, low read latencyDoes more total background compaction I/O ("write amplification") to maintain that bound
TimeWindowCompactionStrategy (TWCS)Groups files by time window (e.g. one window per day); never merges across windowsTime-series data with TTL-based expiry — sensor readings, logs, eventsAn entire expired window can be dropped wholesale once every row in it has expired — cheap, but a poor fit for data that gets updated long after it was first written
-- set at table creation, for a time-series table with a 30-day TTL CREATE TABLE sensor_readings ( sensor_id uuid, reading_time timestamp, value double, PRIMARY KEY ((sensor_id), reading_time) ) WITH CLUSTERING ORDER BY (reading_time DESC) AND default_time_to_live = 2592000 AND compaction = {'class': 'TimeWindowCompactionStrategy', 'compaction_window_unit': 'DAYS', 'compaction_window_size': 1}; -- or changed later on an existing table ALTER TABLE sensor_readings WITH compaction = {'class': 'LeveledCompactionStrategy'};

As a rule of thumb for an interview or a design review: default to STCS unless you have a specific reason not to; move to LCS when read latency predictability matters more than compaction overhead (a user-facing lookup path); move to TWCS when the table is genuinely time-ordered and rows expire via TTL rather than being updated.

💻 Code example

CREATE TABLE sensor_readings ( sensor_id uuid, reading_time timestamp, value double, PRIMARY KEY ((sensor_id), reading_time) ) WITH CLUSTERING ORDER BY (reading_time DESC) AND default_time_to_live = 2592000 AND compaction = {'class': 'TimeWindowCompactionStrategy', 'compaction_window_unit': 'DAYS', 'compaction_window_size': 1}; ALTER TABLE sensor_readings WITH compaction = {'class': 'LeveledCompactionStrategy'};

Want a visual for this concept?

Generate a diagram tailored to “Cassandra — Why Writes Are Fast (and What Compaction Costs You)” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Cassandra — CQL, Secondary Indexes & Materialized Views← Back to all Cassandra chapters