🌐 Designing Data-Intensive Applications

Martin Kleppmann β€” 2017

Tesis: Buku paling penting soal sistem backend modern. Data is at the center β€” storage, replication, partitioning, transactions, consistency, batch/stream processing. Bukan hype-driven β€” fundamental trade-offs yang tetep relevan kapanpun.

πŸ“Œ Kenapa Penting

  • Jawab kenapa database no-SQL muncul, kenapa CAP theorem gak sederhana
  • Trade-offs > silver bullets β€” Kleppmann nunjukkin konsekuensi tiap pilihan arsitektur
  • Referensi #1 untuk system design interview

Contoh konsekuensi dari pilihan arsitektur adalah ketika memilih antara single-leader dan multi-leader replication. Single-leader replication memiliki kelebihan dalam hal konsistensi data, tetapi memiliki kekurangan dalam hal failover yang tricky. Sementara itu, multi-leader replication memiliki kelebihan dalam hal availability, tetapi memiliki kekurangan dalam hal conflict resolution yang pain.

🎯 Key Takeaways

1. Storage Engines

  • LSM-Trees (LevelDB, Cassandra, RocksDB) vs B-Trees (MySQL, Postgres)
    • LSM: write-optimized, compaction overhead
    • B-Tree: read-optimized, stable performance
  • Row-oriented vs Column-oriented β€” analytics vs OLTP

Contoh implementasi LSM-Tree adalah Cassandra, yang menggunakan LSM-Tree untuk menyimpan data. Sementara itu, contoh implementasi B-Tree adalah MySQL, yang menggunakan B-Tree untuk menyimpan data.

2. Replication

  • Single-leader β€” standard, but failover tricky
  • Multi-leader β€” conflict resolution pain (CRDTs help)
  • Leaderless (Dynamo-style) β€” quorum reads/writes, but stale reads
  • Synchronous vs Asynchronous β€” durability vs latency trade-off

Contoh implementasi single-leader replication adalah MySQL, yang menggunakan single-leader replication untuk menyimpan data. Sementara itu, contoh implementasi multi-leader replication adalah Cassandra, yang menggunakan multi-leader replication untuk menyimpan data.

3. Partitioning (Sharding)

  • By key range β€” hotspot risk
  • By hash of key β€” even distribution, but range queries suffer
  • Rebalancing β€” jangan pake hash mod N (pakai consistent hashing)

Contoh implementasi partitioning adalah Cassandra, yang menggunakan partitioning untuk menyimpan data. Cassandra menggunakan consistent hashing untuk melakukan rebalancing data.

4. Transactions β€” the Truth

  • ACID is a spectrum, not a binary switch
  • Isolation levels:
    • Read Committed (default Postgres/Oracle)
    • Snapshot Isolation / Repeatable Read
    • Serializable β€” hardest, slowest, but correct
  • Race conditions: dirty reads, dirty writes, read skew, lost updates, write skew, phantom reads

Contoh implementasi isolation level adalah Postgres, yang menggunakan Read Committed sebagai default isolation level. Sementara itu, contoh implementasi Serializable adalah MySQL, yang menggunakan Serializable sebagai isolation level untuk melakukan transaksi.

5. Distributed Transactions

  • Two-Phase Commit (2PC) β€” coordinator = single point of failure, blocking
  • Linearizability vs Eventual Consistency β€” trade-off real di distributed systems
  • CAP: Choose 2 of 3 β€” tapi realitanya lebih nuanced (partition tolerance gak optional)

Contoh implementasi distributed transaction adalah Google’s Spanner, yang menggunakan Two-Phase Commit (2PC) untuk melakukan transaksi.

6. Batch Processing (MapReduce)

  • MapReduce + Spark: β€œBring computation to data, not data to computation”
  • Join strategies: sort-merge, broadcast hash, partitioned hash

Contoh implementasi batch processing adalah Hadoop, yang menggunakan MapReduce untuk melakukan batch processing.

7. Stream Processing

  • Kafka, Flink, Samza β€” unbounded data, processing with state
  • Exactly-once semantics β€” achievable but complex
  • Stream-table joins β€” materialized views real-time

Contoh implementasi stream processing adalah Kafka, yang menggunakan stream processing untuk melakukan real-time processing.

8. Consistency Models

  • Strong consistency β†’ correct, slow
  • Eventual consistency β†’ fast, but stale reads possible
  • Causal consistency β†’ sweet spot (what CRDTs give)
  • Linearizability β†’ gold standard, expensive

Contoh implementasi consistency model adalah Cassandra, yang menggunakan eventual consistency sebagai default consistency model.

πŸ“– Bab Penting

BabJudulMengapa
2Data Models and Query LanguagesRelational vs Document vs Graph β€” kapan pilih apa
5ReplicationWajib β€” paling sering ditanya interview
6PartitioningKapan, gimana, rebalancing
7TransactionsIsolation levels + race conditions
8The Trouble with Distributed SystemsClock, crashes, network β€” things always go wrong
10Batch ProcessingMapReduce + Spark fundamentals
11Stream ProcessingKafka, Flink, exactly-once

⚠️ Tantangan

  • Tebal 600+ hal β€” dense, but clearer dari textbook akademik
  • Butuh dasar distributed systems (kalo fresh, baca OSTEP Part OS dulu)
  • Contoh-contoh konkret (Cassandra, MongoDB, Kafka, Postgres, etc.) β€” tapi gak perlu hafal implementasi detail

πŸ”— Koneksi

βœ… Checklist

  • Paham B-Tree vs LSM-Tree bedanya apa
  • Bisa jelasin kapan pilih single-leader vs leaderless replication
  • Isolation levels: definisiin + kasus race condition tiap level
  • Konsistensi model: eventual β†’ causal β†’ linearizable
  • Gampang2nya system design interview pakai konsep dari buku ini

Implementasi Contoh

Berikut adalah contoh implementasi dari beberapa konsep yang dibahas di atas:

Contoh Implementasi LSM-Tree

import os
import struct
 
class LSMTree:
    def __init__(self, db_path):
        self.db_path = db_path
        self.log_file = os.path.join(db_path, 'log.dat')
        self.tree_file = os.path.join(db_path, 'tree.dat')
 
    def write(self, key, value):
        with open(self.log_file, 'ab') as f:
            f.write(struct.pack('!I', len(key)) + key + struct.pack('!I', len(value)) + value)
 
    def read(self, key):
        with open(self.log_file, 'rb') as f:
            while True:
                key_len = struct.unpack('!I', f.read(4))[0]
                k = f.read(key_len)
                value_len = struct.unpack('!I', f.read(4))[0]
                value = f.read(value_len)
                if k == key:
                    return value
                if not f.read():
                    break
        return None
 
# Example usage:
db = LSMTree('/tmp/db')
db.write(b'key1', b'value1')
print(db.read(b'key1'))  # prints b'value1'

Contoh Implementasi B-Tree

import os
import struct
 
class BTree:
    def __init__(self, db_path):
        self.db_path = db_path
        self.tree_file = os.path.join(db_path, 'tree.dat')
 
    def insert(self, key, value):
        with open(self.tree_file, 'ab') as f:
            f.write(struct.pack('!I', len(key)) + key + struct.pack('!I', len(value)) + value)
 
    def search(self, key):
        with open(self.tree_file, 'rb') as f:
            while True:
                key_len = struct.unpack('!I', f.read(4))[0]
                k = f.read(key_len)
                value_len = struct.unpack('!I', f.read(4))[0]
                value = f.read(value_len)
                if k == key:
                    return value
                if not f.read():
                    break
        return None
 
# Example usage:
db = BTree('/tmp/db')
db.insert(b'key1', b'value1')
print(db.search(b'key1'))  # prints b'value1'

Troubleshooting

Berikut adalah beberapa tips untuk melakukan troubleshooting pada sistem yang menggunakan konsep-konsep di atas:

  • Pastikan bahwa sistem memiliki cukup ruang penyimpanan dan sumber daya untuk menangani beban kerja.
  • Periksa log sistem untuk mengetahui apakah ada error atau peringatan yang dapat membantu dalam melakukan troubleshooting.
  • Gunakan tool-tool seperti top atau htop untuk memantau penggunaan sumber daya sistem dan mendeteksi jika ada proses yang menggunakan sumber daya secara berlebihan.
  • Periksa konfigurasi sistem untuk memastikan bahwa semua parameter telah dikonfigurasi dengan benar.
  • Lakukan testing pada sistem untuk memastikan bahwa sistem berfungsi dengan benar dan dapat menangani beban kerja yang diharapkan.

Dengan mengikuti tips di atas, Anda dapat melakukan troubleshooting pada sistem yang menggunakan konsep-konsep di atas dan memastikan bahwa sistem berfungsi dengan baik dan efisien.