By the end of this lesson
  • Deconstruct Cassandra's masterless Dynamo-style peer-to-peer ring topology and Murmur3 token allocation
  • Formulate tunable quorum consistency proofs and evaluate Phi Accrual failure detection algorithms
  • Trace low-level storage engine execution across CommitLog, MemTable skiplists, Bloom filters, and SSTables
  • Implement an algorithmic TypeScript Cassandra ring and LSM storage engine with quorum coordination and tombstone resolution

1. Context & Architecture Reality

Project Identity, Version & License Baseline

Project ComponentCanonical RepositoryPinned ReleaseGovernance / LicenseEdition Boundary
Apache Cassandraapache/cassandrav5.0 (LTS)Apache Software Foundation (Apache 2.0)Open-source core
ScyllaDBscylladb/scylladbv5.4ScyllaDB Inc (AGPL v3.0)Open-source core vs Enterprise
SYSTEM ARCHITECTURE BLUEPRINTDISTRIBUTED SYSTEMS
Apache Cassandra Architecture: Masterless Token Ring and Storage Engine
ARCHITECTURAL FLOW · STEP 1 OF 3

1. Write Path (Append Only) & CommitLog (Disk)

Establishes the ingress and structural boundary across Write Path (Append Only) & CommitLog (Disk): Sequential append; crash recovery; ConcurrentSkipListMap in memory; Immutable Sorted String Tables Zero in-place updates! Blazing fast.

Architectural Invariant: 6. Merge rows, resolve Tombstones Read Repair Trigger: If replica hashes differ, background read repair syncs stale nodes.
ARCHITECTURAL FLOW · STEP 2 OF 3

2. Size-Tiered Compaction (STCS) & Leveled Compaction (LCS)

Coordinates core data-plane execution and state transitions through Size-Tiered Compaction (STCS) & Leveled Compaction (LCS): Groups SSTables of similar size. Great for write-heavy append workloads; Exponential size tiers L0, L1, L2... Guarantees 90% reads hit <= 1 SSTable; Deletes write a Tombstone marker. Reading across 100,000 tombstones triggers TombstoneOverwhelmingException and crashes JVM with severe Out-Of-Memory.

Architectural Invariant: Routes to N natural endpoints Network Topology Strategy: Rack-aware placement prevents single switch failure data loss. Zero Single Point of Failure.
ARCHITECTURAL FLOW · STEP 3 OF 3

3. Node Storage Engine: Write and Read Paths & Tunable Consistency and Quorum Mathematics

Enforces durability, state convergence, and output verification across Node Storage Engine: Write and Read Paths & Tunable Consistency and Quorum Mathematics: Guarantees read and write quorums overlap on at least 1 current replica node. Write ONE / Read ALL: W=1, R=N (R+W > N) → Blazing writes, slow/brittle reads Write QUORUM / Read QUORUM: W=&#8970;N/2&#8971;+1, R=&#8970;N/2&#8971;+1 → Balanced strong consistency LOCAL_QUORUM: Quorum strictly inside coordinator's datacenter (Zero cross-DC WAN wait!); Hinted Handoff: Temporarily buffers writes for dead node for up to 3 hours.

Architectural Invariant: Guarantees read and write quorums overlap on at least 1 current replica node. Write ONE / Read ALL: W=1, R=N (R+W > N) → Blazing writes, slow/brittle reads Write QUORUM / Read QUORUM: W=&#8970;N/2&#8971;+1, R=&#8970;N/2&#8971;+1 → Balanced strong consistency LOCAL_QUORUM: Quorum strictly inside coordinator's datacenter (Zero cross-DC WAN wait!).
NOTE

Curriculum Bridge (INF-I04-001 CORE-M32-001): Whereas Step 47 (INF-I04-001) established the general Dynamo-style key-value model (tunable quorums , sloppy quorums, and Merkle tree anti-entropy), this open-source architecture study inspects Apache Cassandra v5.0 and ScyllaDB v5.4 codebase internals: Murmur3Partitioner vnode token allocation, CQL wide-row clustering keys, the -Accrual failure detector (FailureDetector.java), StorageProxy mutation stages, CommitLog / MemTable / SSTableReader read-path promotion, tombstone gc_grace_seconds zombie resurrection hazards, and Lightweight Transactions (LWT) / Accord consensus.

In the mid-2000s, traditional relational databases dominated software architectures. When web applications expanded from thousands to millions of concurrent users, database scalability hit a rigid barrier. Relational engines rely on centralized ACID locks, B-Tree in-place page overwrites, and single-primary replication. Scaling writes required buying larger multi-socket SMP servers, creating enormous cost and operational vulnerability.

In 2007, Amazon published the seminal Dynamo paper, outlining a masterless, highly available distributed key-value store. Shortly thereafter, Avinash Lakshman (co-author of Dynamo) and Prashant Malik created Apache Cassandra at Facebook to power inbox search.

Cassandra married Dynamo’s completely decentralized peer-to-peer ring topology with Google Bigtable’s LSM-Tree columnar data model. In Cassandra:

  • There is no primary or secondary node. Every node is architecturally identical.
  • Any node can accept a read or write request for any data item, acting as the Coordinator.
  • Partitions scale horizontally across a token consistent hashing ring.
  • Writes append sequentially to an immutable commit log and in-memory skiplist, enabling sustained write throughput of hundreds of thousands of operations per second per node.
+--------------------------------------------------------------------------------------------------+
|                            THE MASTERLESS TOKEN RING TOPOLOGY                                    |
|                                                                                                  |
|                               Token Space: -2^63 to +2^63-1                                      |
|                                                                                                  |
|                                  [Node 1: Token -4.6e18]                                         |
|                                      /             \                                             |
|                                     /               \                                            |
|                                    /                 \                                           |
|              [Node 4: Token 4.6e18]                   [Node 2: Token -1.5e18]                    |
|                                    \                 /                                           |
|                                     \               /                                            |
|                                      \             /                                             |
|                                  [Node 3: Token 1.5e18]                                          |
|                                                                                                  |
|   Client Query: SELECT * FROM users WHERE user_id = 'usr_84920'                                  |
|   1. Murmur3Partitioner('usr_84920') -> Token: -2,410,582,910,481                                |
|   2. Natural Endpoints: Routed to clockwise owners: Node 2, Node 3, Node 4                       |
|   3. Coordinator (Node 1) queries replicas using requested Consistency Level (QUORUM)            |
+--------------------------------------------------------------------------------------------------+

2. Core Problem & Failure Modes

Designing and operating a masterless, peer-to-peer database introduces fundamental engineering challenges:

  1. The Consistency-Availability Dilemma (CAP Theorem): Because Cassandra does not use consensus algorithms (like Paxos or Raft) for normal writes, writes proceed without cluster-wide synchronization. Replicas accept writes with timestamps. If network partitions occur, replicas on different sides of the network accept conflicting writes. Cassandra relies on Last-Write-Wins (LWW) based on client-provided microsecond wall-clock timestamps, making it vulnerable to system clock drift.

  2. The Tombstone Catastrophe: Because SSTables are immutable on disk, a DELETE operation cannot delete data in place. Instead, it writes an entry called a Tombstone containing a deletion timestamp. If an application performs millions of deletions (e.g., using Cassandra as a message queue), read queries scanning across partitions must read tens of thousands of tombstones into memory to determine which rows are alive, triggering JVM garbage collection pauses and catastrophic TombstoneOverwhelmingException crashes.

  3. Zombie Record Resurrection: To prevent tombstones from consuming disk space forever, Cassandra purges them after gc_grace_seconds (default: 10 days). If a node goes offline for 12 days and then rejoins the cluster after tombstones have been compacted away on all healthy nodes, the resurrected node still holds its old pre-deletion data. During subsequent read repairs, the resurrected node shares its obsolete records, resurrecting deleted data sitewide as “zombies.”

  4. Hinted Handoff Throttling & Thunder: When a replica is unreachable, the Coordinator writes a “Hint” to local disk to replay once the dead node recovers. If 5 nodes in a 50-node cluster go down for 2 hours, the remaining 45 nodes accumulate gigabytes of hints. When the 5 nodes reboot, all 45 nodes simultaneously replay their hints, overwhelming the recovering nodes with a massive thunderous write storm that instantly crashes them again.


3. Technical Deep Dive

Consistent Hashing & Murmur3 Partitioner

Cassandra maps data partitions to physical nodes using consistent hashing over the signed 64-bit integer token domain (reserving , Long.MIN_VALUE, as an internal minimum sentinel):

Every row has a Partition Key. The default partitioner (org.apache.cassandra.dht.Murmur3Partitioner) computes the 128-bit MurmurHash3 (Murmur3_x64_128) of the serialized partition key bytes and extracts the first signed 64-bit integer word (normalizing to ):

Nodes are assigned positions on the ring. To ensure uniform data distribution and parallel streaming during bootstrap, Cassandra uses Virtual Nodes (vnodes): instead of assigning one monolithic range per server, each physical node owns virtual tokens across the ring. While Cassandra 3.x defaulted to num_tokens: 256, Cassandra 4.0 and 5.0 reduced the default to num_tokens: 16 paired with deterministic replication-aware allocation (allocate_tokens_for_local_replication_factor: 3). Lowering from to slashes anti-entropy repair sub-range fan-out and reduces the probability that two simultaneous node failures share overlapping token ranges, while still keeping per-node storage variance under .

When a partition key with token arrives, Cassandra locates the first node whose token position is . That node, along with the next physically distinct nodes clockwise on the ring (respecting rack and datacenter failure boundaries via NetworkTopologyStrategy), form the Natural Endpoints for that row.

Tunable Quorum Intersection Mathematics

Let be the Replication Factor. A client specifies a Write Consistency Level () and a Read Consistency Level ().

The Quorum Overlap Theorem

Under the pigeonhole principle, if the sum of nodes in the read quorum and the write quorum exceeds the total replication factor :

Then the set of nodes written to and the set of nodes read from must overlap on at least one common node:

Because Cassandra tags every write with a microsecond-precision timestamp, the overlapping node returns the newest timestamp, providing strong read-your-writes overlap without distributed locks.

NOTE

Quorum Overlap vs. True Linearizability (Paxos v2 & Accord): As formally demonstrated in CORE-M20-001, satisfying guarantees that a completed write will be observed by a subsequent read, but it does not guarantee strict Linearizability during concurrent in-flight writes. If a write is partially replicated, two concurrent readers may contact different overlapping subsets and observe a newer value followed by an older value. For single-partition compare-and-set linearizability (IF NOT EXISTS), Cassandra provides Lightweight Transactions (LWT) (SERIAL consistency): classic Cassandra 2.0–4.0 required 4 Paxos round-trips, Cassandra 4.1 introduced Paxos v2 (CEP-14) cutting uncontended LWT commits to 2 round-trips, and the Accord protocol (CEP-15) extends Cassandra 5.x toward single-round-trip, leaderless, strictly serializable multi-partition transactions.

Total Replication Factor N = 3

   Write Quorum (W = 2):  [Node 1]  [Node 2]
   Read Quorum  (R = 2):            [Node 2]  [Node 3]
                                       ^
                                       |
                   Overlapping Node holding latest timestamp!

Cassandra 5.0 Storage Engine Advances: Trie Memtables, UCS & SAI

In Cassandra 5.0, three major storage engine enhancements modernize the classic Bigtable-inspired LSM pipeline:

  1. Trie-Indexed Memtables & BTI SSTables (CEP-19 / CEP-25): Byte-ordered trie structures reduce JVM heap object overhead and accelerate point/range partition lookups compared to legacy ConcurrentSkipListMap MemTables.
  2. Storage-Attached Indexing (SAI, CEP-7): Replaces brittle legacy secondary indexes (2i / SASI) with column indexes attached directly to the SSTable flush and compaction lifecycle—using balanced kd-trees (BKD trees) for numeric/range predicates and byte-ordered tries for exact/string predicates without separate index compaction storms.
  3. Unified Compaction Strategy (UCS, CEP-26): Generalizes Size-Tiered (STCS), Leveled (LCS), and Time-Window (TWCS) compaction under tunable sharded density levels, allowing operators to transition between write-optimized and read-optimized fan-out online.

The (Phi) Accrual Failure Detector

Rather than using a crude binary heartbeat timeout (which misinterprets transient network spikes or GC pauses as node crashes), Cassandra uses the Accrual Failure Detector (Hayashibara et al., 2004).

Nodes exchange state every via Gossip. In Hayashibara’s general formulation, given elapsed silence since the last received heartbeat and historical inter-arrival distribution , the suspicion level is defined on a base-10 logarithmic scale:

While Hayashibara’s original paper modeled inter-arrival times with a Gaussian integral, Apache Cassandra’s actual ArrivalWindow implementation (CASSANDRA-2597, org.apache.cassandra.gms.ArrivalWindow) models heartbeat arrivals using an exponential distribution with sample mean over the last intervals (), avoiding floating-point tail underflow under bursty JVM pauses:

  • Numerical Verification: With Cassandra’s gossip cadence () and default phi_convict_threshold = 8 (corresponding to tail probability under the exponential arrival model), a silent peer is convicted at:

If cross-DC congestion stretches the observed mean from to , automatically expands to without operator tuning.


4. System Design Patterns & Trade-offs

+----------------------------------------------------------------------------------------------------+
| TRADEOFF MATRIX: CASSANDRA COMPACTION STRATEGIES                                                   |
+--------------------------+------------------------------------+------------------------------------+
| Dimension                | Size-Tiered Compaction (STCS)      | Leveled Compaction (LCS)           |
+--------------------------+------------------------------------+------------------------------------+
| Primary Workload         | High write throughput (appends)    | Read-heavy, frequent updates/deletes|
| Disk Space Overhead      | High (Requires up to 50% free disk)| Low (Requires only ~10% free disk) |
| Read Performance         | Poor: Must check many SSTables     | Excellent: 90% reads hit 1 SSTable |
| Write Amplification      | Moderate                           | High (Rewrites data into levels)   |
+--------------------------+------------------------------------+------------------------------------+
| Dimension                | LOCAL_QUORUM                       | QUORUM (Global)                    |
+--------------------------+------------------------------------+------------------------------------+
| WAN Latency Penalty      | Zero (Acknowledged within local DC)| Adds 50-100ms cross-country RTT    |
| Multi-Region Disaster Tol| Survives entire remote DC loss     | Blocks if remote DC is partitioned |
+--------------------------+------------------------------------+------------------------------------+

5. Practical Implementation

Below is a complete, production-grade TypeScript simulation demonstrating Cassandra’s core storage and coordinator mechanics:

  1. MemTable skiplist with on-disk SSTable representation.
  2. Murmur3 consistent hashing ring routing.
  3. Quorum write coordinator with timestamp conflict resolution.
  4. Tombstone deletion and reconciliation.
import { createHash } from 'crypto';

export interface RowData {
  partitionKey: string;
  data: Record<string, string | number>;
  timestampMicros: number;
  isTombstone: boolean;
}

/**
 * In-Memory MemTable representation
 */
export class MemTable {
  private rows: Map<string, RowData> = new Map();

  public put(row: RowData): void {
    const existing = this.rows.get(row.partitionKey);
    if (!existing || row.timestampMicros >= existing.timestampMicros) {
      this.rows.set(row.partitionKey, row);
    }
  }

  public get(partitionKey: string): RowData | null {
    return this.rows.get(partitionKey) || null;
  }

  public flush(): SSTable {
    const sortedEntries = Array.from(this.rows.values()).sort((a, b) =>
      a.partitionKey.localeCompare(b.partitionKey)
    );
    this.rows.clear();
    return new SSTable(sortedEntries);
  }
}

/**
 * Immutable On-Disk Sorted String Table (SSTable)
 */
export class SSTable {
  private bloomFilter: Set<string> = new Set();

  constructor(private readonly entries: RowData[]) {
    // Populate simple Bloom Filter simulation
    for (const entry of entries) {
      this.bloomFilter.add(entry.partitionKey);
    }
  }

  public mayContain(partitionKey: string): boolean {
    return this.bloomFilter.has(partitionKey);
  }

  public search(partitionKey: string): RowData | null {
    if (!this.mayContain(partitionKey)) return null;

    // Binary search across sorted immutable array
    let low = 0;
    let high = this.entries.length - 1;
    while (low <= high) {
      const mid = Math.floor((low + high) / 2);
      const cmp = this.entries[mid].partitionKey.localeCompare(partitionKey);
      if (cmp === 0) return this.entries[mid];
      if (cmp < 0) low = mid + 1;
      else high = mid - 1;
    }
    return null;
  }
}

/**
 * Cassandra Physical Node Replica
 */
export class CassandraNode {
  public memTable: MemTable = new MemTable();
  public ssTables: SSTable[] = [];

  constructor(public readonly nodeId: string, public readonly token: bigint) {}

  public write(row: RowData): void {
    this.memTable.put(row);
  }

  public read(partitionKey: string): RowData | null {
    // 1. Check MemTable
    const memResult = this.memTable.get(partitionKey);
    let bestResult: RowData | null = memResult;

    // 2. Search SSTables newest to oldest
    for (let i = this.ssTables.length - 1; i >= 0; i--) {
      const sstResult = this.ssTables[i].search(partitionKey);
      if (sstResult) {
        if (!bestResult || sstResult.timestampMicros > bestResult.timestampMicros) {
          bestResult = sstResult;
        }
      }
    }

    return bestResult;
  }
}

/**
 * Coordinator Node and Ring Router
 */
export class CassandraCoordinator {
  private nodes: CassandraNode[] = [];

  constructor(nodes: CassandraNode[]) {
    this.nodes = [...nodes].sort((a, b) => (a.token < b.token ? -1 : 1));
  }

  // Simulates 64-bit token hashing across the ring (production Cassandra uses Murmur3_x64_128)
  public hashToToken(key: string): bigint {
    const hash = createHash('md5').update(key).digest('hex').substring(0, 16);
    return BigInt('0x' + hash);
  }

  public getNaturalEndpoints(key: string, replicationFactor: number): CassandraNode[] {
    const token = this.hashToToken(key);
    let startIdx = this.nodes.findIndex((n) => n.token >= token);
    if (startIdx === -1) startIdx = 0;

    const endpoints: CassandraNode[] = [];
    for (let i = 0; i < replicationFactor; i++) {
      endpoints.push(this.nodes[(startIdx + i) % this.nodes.length]);
    }
    return endpoints;
  }

  /**
   * Executes Quorum Write
   */
  public writeQuorum(
    partitionKey: string,
    data: Record<string, string | number>,
    isTombstone: boolean = false,
    timestampMicros: number = Date.now() * 1000
  ): boolean {
    const replicas = this.getNaturalEndpoints(partitionKey, 3);
    const quorumCount = Math.floor(replicas.length / 2) + 1; // 2 out of 3
    const row: RowData = {
      partitionKey,
      data,
      timestampMicros,
      isTombstone
    };

    let acks = 0;
    for (const replica of replicas) {
      replica.write(row);
      acks++;
    }

    return acks >= quorumCount;
  }

  /**
   * Executes Quorum Read with Last-Write-Wins and Tombstone resolution
   */
  public readQuorum(partitionKey: string): Record<string, string | number> | null {
    const replicas = this.getNaturalEndpoints(partitionKey, 3);
    const quorumCount = Math.floor(replicas.length / 2) + 1;

    const responses: RowData[] = [];
    for (const replica of replicas) {
      const res = replica.read(partitionKey);
      if (res) responses.push(res);
    }

    if (responses.length < quorumCount) {
      return null;
    }

    // Resolve Last-Write-Wins
    responses.sort((a, b) => b.timestampMicros - a.timestampMicros);
    const newest = responses[0];

    // If newest record is a Tombstone, the row is deleted!
    if (!newest || newest.isTombstone) {
      return null;
    }

    return newest.data;
  }
}

function runDemo(): void {
  console.log('=== Cassandra Peer-to-Peer Token Ring & Quorum Simulation ===');
  const nodes = [
    new CassandraNode('node-1', 0x0000000000000000n),
    new CassandraNode('node-2', 0x4000000000000000n),
    new CassandraNode('node-3', 0x8000000000000000n),
    new CassandraNode('node-4', 0xc000000000000000n),
  ];
  const coordinator = new CassandraCoordinator(nodes);

  const key = 'usr_84920';
  const endpoints = coordinator.getNaturalEndpoints(key, 3);
  console.log(`Natural endpoints (RF=3) for '${key}': [${endpoints.map((n) => n.nodeId).join(', ')}]`);

  const committed = coordinator.writeQuorum(key, { user_id: key, status: 'active' }, false, 1714580000000);
  console.log(`Quorum write (W=2) committed: ${committed}`);

  // Flush MemTables to immutable SSTables on replicas
  for (const replica of endpoints) {
    replica.ssTables.push(replica.memTable.flush());
  }
  console.log('Flushed replica MemTables to immutable SSTables with Bloom filters');

  const activeRow = coordinator.readQuorum(key);
  console.log('Quorum read (R=2) from SSTables: ' + JSON.stringify(activeRow));

  // Issue Tombstone delete with strictly newer microsecond timestamp
  coordinator.writeQuorum(key, {}, true, 1714580005000);
  const afterDelete = coordinator.readQuorum(key);
  console.log('Quorum read after Tombstone deletion: ' + JSON.stringify(afterDelete));
}
runDemo();

Simulation Execution Output & State Transitions

=== Cassandra Peer-to-Peer Token Ring & Quorum Simulation ===
Natural endpoints (RF=3) for 'usr_84920': [node-1, node-2, node-3]
Quorum write (W=2) committed: true
Flushed replica MemTables to immutable SSTables with Bloom filters
Quorum read (R=2) from SSTables: {"user_id":"usr_84920","status":"active"}
Quorum read after Tombstone deletion: null

6. Failure Scenarios & Edge Cases

1. Clock Skew and Last-Write-Wins Silent Overwrites

Cassandra resolves conflicting updates using client-provided timestamps. If Server A’s clock is 200 ms fast due to NTP drift, every write issued through Server A receives a future timestamp. Subsequent writes issued through Server B (with an accurate clock) will be silently ignored because their timestamps appear older than Server A’s write!

  • Mitigation: Enforce microsecond clock synchronization via PTP (Precision Time Protocol) or AWS Time Sync. Never generate timestamps in application code; use Cassandra’s server-side coordinator timestamps.

2. High Tombstone Scans Crashing JVM via Out-Of-Memory

A queue pattern is implemented where worker threads write items and delete them upon completion. Over a week, a single partition accumulates 300,000 tombstones. When a worker attempts SELECT * FROM queue WHERE partition_id = 1 LIMIT 1;, Cassandra must read all 300,000 tombstones into JVM off-heap/heap buffers to confirm whether an active record exists.

  • Mitigation: Never use Cassandra as a queue. Configure tombstone_failure_threshold (default: 100,000) to fail fast, and tune gc_grace_seconds to 0 or use TWCS (Time Window Compaction Strategy) for time-series data.

7. Production Operations & Observability

Essential Cassandra Operator Metrics

+----------------------------------------------------------------------------------------------------+
| KEY PRODUCTION CASSANDRA METRICS                                                                   |
+--------------------------+-----------------------+-------------------------------------------------+
| Metric Name              | Source Component      | Critical Threshold                              |
+--------------------------+-----------------------+-------------------------------------------------+
| DroppedMessages: WRITE   | Stage Manager / Net   | > 0 / min indicates disk saturation / overload  |
+--------------------------+-----------------------+-------------------------------------------------+
| PendingTasks: Compaction | CompactionManager     | > 50 indicates compaction backlog & read lag    |
+--------------------------+-----------------------+-------------------------------------------------+
| StorageProxy: ReadRepair | Coordinator Proxy     | Spike indicates replica desynchronization       |
+--------------------------+-----------------------+-------------------------------------------------+
| ClientRequest: p99 Lat   | Driver Transport      | > 50ms indicates JVM GC pause or disk fsync wait|
+--------------------------+-----------------------+-------------------------------------------------+

Disk Space Rules for Compaction

During Size-Tiered Compaction, Cassandra merges four 50 GB SSTables into one 200 GB SSTable. During the merge, both the old and new files reside on disk simultaneously. Cassandra nodes running STCS must never exceed 50% disk utilization, or compaction will fail with disk-full panics!


8. Security & Compliance

  1. Internode Encryption: Replicas stream raw unencrypted partition data during repair and gossip. Configure server_encryption_options with mutual TLS (mTLS) to encrypt all internode traffic on port 7001.
  2. Client-to-Node Encryption: Enforce TLS 1.3 on native transport port 9042 with client certificate validation.

9. Real-World Case Studies

Netflix Global Cassandra Fleet

Netflix operates tens of thousands of Cassandra nodes storing user viewing history, ratings, and device metadata.

  • Multi-Region Active-Active: Netflix deploys Cassandra across 3 AWS regions (us-east-1, us-west-2, eu-west-1). All application writes use LOCAL_QUORUM.
  • Async Cross-DC Replication: Cassandra’s background storage proxy streams mutations asynchronously across regions over AWS Direct Connect links, keeping cross-region latency off the customer streaming critical path.

10. Technology Comparison Matrix

+----------------------------------------------------------------------------------------------------+
| DISTRIBUTED NOSQL AND STORAGE ENGINE COMPARISON                                                    |
+----------------------+--------------------+--------------------+-----------------------------------+
| System               | Topology           | Consensus / Coor.  | Storage Engine                    |
+----------------------+--------------------+--------------------+-----------------------------------+
| Apache Cassandra     | Masterless Ring    | Tunable Quorum     | LSM-Tree (CommitLog + SSTables)   |
+----------------------+--------------------+--------------------+-----------------------------------+
| ScyllaDB             | Masterless Ring    | Tunable Quorum     | C++ Seastar thread-per-core LSM   |
+----------------------+--------------------+--------------------+-----------------------------------+
| Amazon DynamoDB      | Managed Multi-AZ   | Multi-Paxos        | Internal SSD engine (RocksDB-like)|
+----------------------+--------------------+--------------------+-----------------------------------+
| Apache HBase         | Master / RegionSrv | ZooKeeper + HDFS   | LSM-Tree on distributed HDFS WAL  |
+----------------------+--------------------+--------------------+-----------------------------------+
| CockroachDB          | Multi-Raft Ranges  | Raft Consensus     | Pebble (Go LSM-Tree)              |
+----------------------+--------------------+--------------------+-----------------------------------+

11. Design Challenge & Exercise

Scenario: High-Volume Sensor Ingestion Pipeline

You are designing the telemetry database for an IoT fleet of 5,000,000 smart meters, each emitting a metric event every 10 seconds (500,000 writes/second total).

Requirements:

  1. Zero data loss during write ingestion ().
  2. Queries request the last 24 hours of data for a specific meter (meter_id).
  3. Historical data older than 90 days must be permanently deleted.

Your Task:

  • Design the CQL table schema (Partition Key, Clustering Key, Compaction Strategy).
  • Explain why Time Window Compaction Strategy (TWCS) is superior to Size-Tiered Compaction for this workload.
  • Calculate the storage footprint and disk space headroom needed for 90 days of retention.
View Reference Architecture & Worked Solutions (CORE-M32-001)

1. CQL Table Schema Design (Partition Key, Clustering Key & Compaction Strategy)

  • Partition Sizing Check: Each smart meter emits . At , a single meter generates or over . Bucketing the partition key by (meter_id, day_bucket) (or (meter_id, month_bucket)) bounds partition size strictly below Cassandra’s safety ceiling while ensuring that a 24-hour range query reads at most partitions in reverse chronological order:
CREATE KEYSPACE iot_telemetry WITH replication = {
  'class': 'NetworkTopologyStrategy',
  'us_east_1': 3
};

CREATE TABLE iot_telemetry.meter_readings (
  meter_id       text,
  day_bucket     date,          -- e.g., '2026-11-09' bounds partition to 8,640 rows (~864 KB)
  event_ts       timestamp,
  kilowatt_hrs   double,
  voltage        float,
  status_flags   smallint,
  PRIMARY KEY ((meter_id, day_bucket), event_ts)
) WITH CLUSTERING ORDER BY (event_ts DESC)
  AND default_time_to_live = 7776000  -- 90 days in seconds (90 * 86,400)
  AND gc_grace_seconds = 86400        -- 1 day (safe for immutable TTL-only time-series)
  AND compaction = {
    'class': 'TimeWindowCompactionStrategy',
    'compaction_window_unit': 'DAYS',
    'compaction_window_size': 1
  }
  AND compression = {
    'class': 'LZ4Compressor',
    'chunk_length_in_kb': 64
  };

2. Why Time Window Compaction Strategy (TWCS) Outperforms Size-Tiered Compaction (STCS)

  • STCS Failure Mode on TTL Workloads: Size-Tiered Compaction merges SSTables of similar byte size regardless of the timestamp of the rows inside them. Over 90 days, older SSTables merge into massive multi-hundred-gigabyte files mixing Day 1 data with Day 60 data. When Day 1 rows pass their 90-day TTL, Cassandra cannot drop the SSTable because it still contains live Day 60 rows; moreover, STCS requires free disk headroom to stage merges of its largest SSTables.
  • TWCS Advantage: TimeWindowCompactionStrategy groups flushed MemTables into discrete 1-day time buckets (compaction_window_unit = 'DAYS', size = 1). SSTables within the current 24-hour window are compacted via STCS, and once the day ends, that day’s window is sealed into a single immutable SSTable and never compacted again.
    1. Single-SSTable 24-Hour Reads: A query for the last 24 hours of meter_id checks the active MemTable and at most daily TWCS SSTables (via min/max timestamp metadata), avoiding multi-file read amplification.
    2. Zero-I/O Expiration: Once all rows in a sealed 1-day SSTable exceed default_time_to_live + gc_grace_seconds (), Cassandra drops the entire SSTable file from the filesystem via a single unlink() syscall—eliminating tombstone compaction scans and rewrite amplification completely.

3. 90-Day Storage Footprint & Disk Headroom Calculation

  • Step 1 — Total Row Volume Over 90 Days:
  • Step 2 — Raw vs. Compressed Unique Data Size:
    • Assuming an average serialized cell/row footprint (including CQL clustering prefix and timestamps) of raw ( cluster-wide ingest):
    • With LZ4Compressor ( chunks) achieving a conservative compression ratio on sorted numeric/timestamp columns ( on disk):
  • Step 3 — Replication Factor ( for LOCAL_QUORUM) & Headroom:
    • Across replicas (plus of gc_grace_seconds retention, i.e., ):
    • Unlike STCS (which requires free space), TWCS only compacts SSTables inside the current 1-day window ( cluster-wide). Allocating a operational headroom margin (keeping disk utilization to accommodate CommitLog segments, active-day compactions, and OS page cache):

12. Summary & Architectural Decision Guide

When to Choose Apache Cassandra vs. Alternatives

  • Choose Apache Cassandra (or ScyllaDB) when your workload demands high-throughput, multi-datacenter active-active writes (LOCAL_QUORUM), predictable partition-key access patterns, and graceful degradation during regional network partitions without leader election stalls.
  • Avoid Cassandra for ad-hoc multi-table JOIN queries, queue/delete-churn workloads (tombstone accumulation), or strict multi-row ACID financial ledgers unless leveraging Cassandra 5.x Accord (CEP-15) or a dedicated Distributed SQL engine (CORE-M33-001).

Production Checklist

  1. Token Allocation: Use Cassandra 4.x/5.0 defaults (num_tokens: 16 with allocate_tokens_for_local_replication_factor: 3) rather than legacy 256-vnode configurations to keep repair sub-ranges small and reduce correlated replica failure risk.
  2. Consistency & Repair: Pair LOCAL_QUORUM reads and writes () with scheduled incremental/sub-range anti-entropy repairs (cassandra-reaper) running well inside gc_grace_seconds (e.g., every 7 days for a 10-day grace window).
  3. Compaction & Indexing: Match compaction to workload physics—TWCS or UCS for immutable time-series with TTLs, LCS or UCS for read-heavy updates—and prefer Storage-Attached Indexes (SAI) in Cassandra 5.0 over legacy secondary indexes.

13. Authoritative References & Version Context

MASTERY GATE · STEP 65 OF 94

Knowledge check & lesson completion

Score 4 / 4 (100%) on this check to mark this lesson Completed ✓, advance your position on the Master Spine, and add these concepts to your Spaced-Repetition Review Deck.

1. Why does Apache Cassandra achieve near-linear horizontal write scalability compared to single-primary B+Tree relational databases?
2. In Cassandra's storage engine, how does a CQL compound PRIMARY KEY ((tenant_id, bucket_day), event_ts, event_id) physically organize data across the cluster and within each SSTable?
3. What is a Tombstone in Cassandra, and what failure occurs if an offline replica rejoins the cluster after gc_grace_seconds has elapsed and healthy replicas have already compacted away tombstones?
4. How does Cassandra's (Phi) Accrual Failure Detector improve upon a static heartbeat timeout (such as declaring a peer dead after exactly 5 seconds of silence)?
PERSONAL NOTEBOOK

Chapter Notes

Personal engineering insights and observations saved to this browser.

0 notes
Tip: You can also highlight text in the article above to quote directly.

Loading personal notes...