CockroachDB #
Dalam lanskap manajemen database modern, tim pengembang sering kali dihadapkan pada pertukaran (trade-off) yang menyulitkan antara konsistensi data dengan skalabilitas. Selama bertahun-tahun, database relasional tradisional (RDBMS) seperti PostgreSQL atau MySQL menjadi standar emas untuk transaksi keuangan karena jaminan ACID (Atomicity, Consistency, Isolation, Durability) yang ketat. Namun, RDBMS tradisional sangat sulit diskalakan secara horizontal lintas wilayah geografis (multi-region). Sebaliknya, database NoSQL (seperti Cassandra atau MongoDB) menawarkan skalabilitas horizontal yang masif, namun mengorbankan konsistensi data (eventual consistency) dan tidak mendukung transaksi relasional yang kompleks secara native.
CockroachDB hadir sebagai pionir kategori database NewSQL untuk mengeliminasi pertukaran tersebut. Dirancang dari nol sebagai distributed SQL database yang kompatibel dengan wire protocol PostgreSQL, CockroachDB menggabungkan skalabilitas horizontal elastis khas NoSQL dengan jaminan konsistensi ACID tingkat tertinggi (serializable isolation) khas RDBMS tradisional. Filosofi utama di balik nama "Cockroach" (Kecoak) adalah ketahanan hidup yang luar biasa: database ini dirancang untuk tetap aktif beroperasi tanpa kehilangan data sedikit pun bahkan jika terjadi kegagalan hardware di tingkat node virtual, zona ketersediaan (availability zone), hingga kegagalan total satu region pusat data cloud.
Arsitektur NewSQL: Menjembatani SQL Tradisional dan NoSQL #
Untuk memahami posisi unik CockroachDB, kita harus membandingkan karakteristik NewSQL dengan arsitektur database tradisional dan NoSQL:
- RDBMS Tradisional (SQL): Memiliki konsistensi data yang sangat kuat, namun mengandalkan penskalaan vertikal (scale-up dengan memperbesar VM). Penskalaan horizontal membutuhkan teknik sharding manual di tingkat aplikasi yang sangat rumit dan merusak konsistensi relasional.
- NoSQL: Mendukung penskalaan horizontal secara native dengan memecah data ke berbagai node. Namun, sebagian besar NoSQL mengabaikan integritas transaksi ACID global untuk mencapai performa tinggi, sehingga rawan memicu inkonsistensi data pada aplikasi keuangan.
- NewSQL (CockroachDB): Menggunakan sistem penyimpanan pasangan kunci-nilai (Key-Value) terdistribusi di latar belakang, namun mengekspos antarmuka SQL relasional yang lengkap ke aplikasi. CockroachDB secara otomatis menangani sharding data, replikasi konsensus, koordinasi transaksi ACID terdistribusi, serta perutean kueri secara transparan tanpa mengubah kode aplikasi kita.
Arsitektur Internal dan Struktur Berlapis #
Arsitektur internal CockroachDB dibagi menjadi beberapa lapisan logis (layered architecture) yang masing-masing memiliki tanggung jawab spesifik dalam memproses kueri SQL pengguna menjadi penyimpanan fisik disk.
flowchart TD
Client["SQL Client / App"] -->|"PostgreSQL Wire Protocol"| SQL["SQL Layer (Parser, Planner, Optimizer)"]
SQL -->|"KV Requests"| KV["KV Layer (Range Router, Leaseholders)"]
KV -->|"Raft Log Replication"| Consensus["Consensus Layer (Raft Protocol)"]
Consensus -->|"Write to Storage Engine (Pebble DB)"| Disk["Storage Layer (Local SSDs/Cloud Storage)"]
subgraph Raft Consensus Group
direction LR
LH["Replica 1 (Leaseholder & Leader)"]
R2["Replica 2 (Follower)"]
R3["Replica 3 (Follower)"]
LH -.-> R2
LH -.-> R3
end
Consensus --> RaftConsensusGroup
style SQL stroke:#0288d1,stroke-width:2px
style KV stroke:#0288d1,stroke-width:2px
style Consensus stroke:#0288d1,stroke-width:2px
1. SQL Layer (Parser, Planner, dan Optimizer) #
Ketika aplikasi mengirimkan kueri SQL via PostgreSQL wire protocol, kueri tersebut akan diterima oleh node mana pun di kluster CockroachDB (setiap node bertindak sebagai pintu masuk yang setara). SQL Layer melakukan parsing teks kueri, memverifikasi hak akses pengguna, dan menggunakan Cost-Based Optimizer (CBO) untuk menyusun rencana eksekusi kueri terdistribusi yang paling efisien berdasarkan statistik data yang aktif. Rencana eksekusi kemudian diterjemahkan menjadi serangkaian operasi baca-tulis tingkat rendah bertipe Key-Value (KV).
2. KV Layer (Penyimpanan Key-Value Terdistribusi) #
Di bawah lapisan SQL, CockroachDB merepresentasikan seluruh data tabel, indeks, dan metadata sistem sebagai satu ruang alamat Key-Value raksasa yang terurut secara monolitik. Kunci (key) berisi informasi seperti ID tabel, ID indeks, dan nilai primary key, sedangkan nilai (value) berisi data kolom biner. KV Layer bertanggung jawab memetakan kunci tersebut ke lokasi node fisik yang tepat di dalam kluster.
3. Consensus Layer (Protokol Raft) #
Consensus Layer menjamin konsistensi data saat proses replikasi terjadi. CockroachDB membagi ruang alamat KV menjadi potongan-potongan kecil berukuran sekitar 64 Megabyte yang disebut Ranges. Setiap range direplikasi ke beberapa node fisik (default 3 replika) menggunakan protokol konsensus Raft. Di dalam setiap kelompok Raft range, satu replika bertindak sebagai Leaseholder (pemimpin transaksi) yang melayani request baca-tulis aktif, sementara replika lainnya bertindak sebagai follower yang mereplikasi log transaksi secara asinkron guna menjamin toleransi bencana.
4. Storage Layer (Engine Penyimpanan Pebble) #
Di tingkat terbawah, setiap node CockroachDB menuliskan log konsensus Raft dan halaman data aktif ke dalam media penyimpanan lokal (SSD) menggunakan engine penyimpanan internal bernama Pebble. Pebble adalah engine Log-Structured Merge-tree (LSM-tree) berkinerja sangat tinggi yang ditulis dalam bahasa Go, menggantikan engine RocksDB bawaan versi lama untuk optimasi memori dan kecepatan kueri baca-tulis sekuensial.
Mekanisme Range-Based Sharding dan Konsensus Raft #
Penskalaan horizontal otomatis tanpa downtime pada CockroachDB dimungkinkan oleh dua mekanisme cerdas: Range-Based Sharding dan Raft Consensus.
1. Range Splitting dan Merging Otomatis #
Ketika sebuah tabel terus diisi dengan data transaksi baru, ukuran suatu range akan membengkak melampaui batas 64MB. Untuk mencegah kemacetan pemrosesan pada satu node, CockroachDB secara otomatis akan membagi range tersebut menjadi dua range baru (Range Split) secara instan. Sebaliknya, jika data dihapus secara massal sehingga ukuran range menyusut di bawah batas minimal, sistem akan menggabungkannya kembali (Range Merge) untuk menghemat memori internal kluster.
2. Rebalancing dan Penyeimbangan Beban Otomatis #
Kluster CockroachDB memantau kapasitas penyimpanan disk dan beban CPU pada setiap node secara real-time. Jika kita menambahkan node server baru ke dalam kluster, sistem secara otomatis akan memindahkan (rebalance) sebagian range dari node yang padat ke node baru di latar belakang tanpa mengganggu jalannya transaksi aktif aplikasi.
CockroachDB Serverless dan Model Biaya Request Units (RU) #
Untuk mengakomodasi kebutuhan startup dan pengembang aplikasi mikro, Cockroach Labs menyediakan opsi CockroachDB Serverless yang berjalan di atas infrastruktur multi-tenant terkelola penuh.
Konsep Serverless CockroachDB #
Layanan serverless ini memisahkan lapisan compute dan storage secara elastis. Kita tidak perlu menentukan ukuran RAM, jumlah vCPU, atau kapasitas disk kluster saat inisiasi. CockroachDB Serverless akan mengalokasikan resource secara dinamis sesuai beban traffic masuk, mendukung fitur mati total saat idle (scale-to-zero), serta menjamin ketersediaan tinggi di tingkat multi-zone secara default.
Model Biaya Berbasis Request Units (RU) #
Sistem billing CockroachDB Serverless dihitung berdasarkan akumulasi aktivitas kueri kita yang diformulasikan ke dalam satuan Request Units (RU). Satu RU merepresentasikan kombinasi penggunaan komputasi CPU dan I/O data:
- Kueri Read Ringan: Membaca satu baris data berdasarkan indeks primer biasanya hanya menghabiskan 1 RU.
- Kueri Write/Insert: Menulis satu baris data baru membutuhkan sekitar 2-5 RU karena melibatkan proses replikasi konsensus Raft linstas node.
- Kueri Scan Berat: Melakukan scan tabel besar tanpa indeks yang memakan waktu CPU lama akan mengonsumsi ratusan RU sekaligus. Dengan model ini, biaya operasional database kita menjadi sangat murah saat traffic sepi, dan kita hanya membayar sesuai performa kueri yang sebenarnya dikonsumsi.
Kemampuan Geo-Partitioning untuk Latensi Rendah Global #
Salah satu fitur tercanggih CockroachDB untuk aplikasi skala global adalah Geo-Partitioning (lokalisasi data tingkat baris).
Pada database tradisional multi-region, semua data biasanya ditulis di satu wilayah utama (misal: Virginia, AS). Pengguna dari Singapura yang ingin mengakses data mereka harus menunggu sinyal internet menyeberangi samudera Pasifik dengan latensi ratusan milidetik.
Geo-Partitioning memecahkan masalah ini dengan mengizinkan kita membuat aturan partisi database berdasarkan nilai kolom baris data (misalnya berdasarkan kolom country atau region).
CockroachDB kemudian secara fisik akan memindahkan baris data milik pengguna Eropa ke pusat data di Frankfurt, data pengguna Asia ke Singapura, dan data pengguna Amerika ke Oregon.
- Latensi Rendah: Kueri baca-tulis pengguna lokal diproses langsung di region terdekat dengan latensi secepat milidetik (local read/write speed).
- Kepatuhan Regulasi (GDPR): Memudahkan kepatuhan hukum kedaulatan data (data residency) karena data pribadi warga negara Eropa dijamin tidak akan pernah keluar dari teritorial fisik Uni Eropa.
- Konsistensi Global: Meskipun data tersebar secara fisik di berbagai benua, database tetap menyajikan satu skema relasional terpadu yang konsisten secara global dengan transaksi ACID yang sah.
Contoh Implementasi Kode: Node.js (TypeScript) dengan pg-pool #
Mari kita buat contoh implementasi praktis aplikasi backend menggunakan Node.js, TypeScript, dan library @types/pg standar untuk terhubung ke CockroachDB Serverless secara aman, lengkap dengan penanganan otomatis untuk kegagalan transaksi akibat konflik serialisasi (serialization retries).
1. Instalasi Library Driver #
Pasang driver PostgreSQL dan library pembantu di proyek Node.js kita:
npm install pg
npm install --save-dev @types/pg
2. Kode Modul Database (cockroach.ts)
#
CockroachDB menggunakan tingkat isolasi transaksi Serializable (tertinggi). Di tingkat ini, jika dua transaksi paralel mencoba mengubah data yang saling tumpang tindih secara bersamaan, CockroachDB akan membatalkan salah satu transaksi dengan melemparkan error kode SQL 40001 (Serialization Failure) untuk melindungi integritas data. Aplikasi kita wajib mendeteksi kode error ini dan melakukan kueri ulang secara otomatis (retry loop).
// BENAR: Menggunakan Pg-pool standar dengan penanganan retry 40001
import { Pool, PoolClient } from 'pg';
// Inisiasi database connection pool
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: true, // Wajib bernilai true untuk validasi sertifikat SSL CockroachCloud
},
max: 20, // Batas maksimal koneksi pool
idleTimeoutMillis: 30000,
});
/**
* Fungsi utilitas untuk mengeksekusi transaksi dengan retry otomatis
* jika terjadi konflik serialisasi (SQLSTATE 40001)
*/
export async function executeTxWithRetry<T>(
txFunc: (client: PoolClient) => Promise<T>
): Promise<T> {
const client = await pool.connect();
let attempt = 0;
const maxAttempts = 5;
while (true) {
attempt++;
try {
// Memulai transaksi SQL
await client.query('BEGIN;');
// Eksekusi fungsi transaksi utama dari parameter
const result = await txFunc(client);
// Commit transaksi jika sukses
await client.query('COMMIT;');
return result;
} catch (error: any) {
// Membatalkan semua perubahan dalam transaksi saat ini
await client.query('ROLLBACK;');
// Cek apakah error disebabkan oleh kegagalan serialisasi CockroachDB (SQLSTATE 40001)
const isSerializationError = error.code === '40001';
if (isSerializationError && attempt < maxAttempts) {
const backoffDelay = attempt * 100; // Delay eksponensial singkat
console.warn(JSON.stringify({
severity: 'WARNING',
message: `Konflik transaksi terdeteksi (40001). Mencoba ulang transaksi (Percobaan ${attempt}/${maxAttempts}) setelah ${backoffDelay}ms...`
}));
await new Promise((resolve) => setTimeout(resolve, backoffDelay));
continue; // Lanjutkan loop untuk mencoba kembali transaksi
}
// Jika bukan error serialisasi atau limit retry tercapai, lempar error asli
console.error(JSON.stringify({
severity: 'ERROR',
message: `Transaksi gagal secara permanen pada percobaan ${attempt}: ${error.message}`,
stack: error.stack
}));
throw error;
} finally {
// Lepaskan koneksi kembali ke pool
if (attempt === 1 || errorOccurred(client)) {
client.release();
}
}
}
}
// Fungsi pembantu pengecekan kondisi client
function errorOccurred(client: PoolClient): boolean {
return (client as any)._connected === false;
}
/**
* Contoh logika bisnis transaksi: Transfer saldo antar akun pengguna
*/
export async function transferBalance(
fromAccountId: string,
toAccountId: string,
amount: number
): Promise<boolean> {
return executeTxWithRetry(async (client) => {
// 1. Ambil saldo pengirim dan lakukan penguncian baris (FOR UPDATE)
const fromAccountRes = await client.query(
'SELECT balance FROM accounts WHERE id = $1 FOR UPDATE;',
[fromAccountId]
);
if (fromAccountRes.rows.length === 0) {
throw new Error(`Akun pengirim ${fromAccountId} tidak ditemukan.`);
}
const currentBalance = fromAccountRes.rows[0].balance;
if (currentBalance < amount) {
throw new Error(`Saldo tidak mencukupi untuk melakukan transfer.`);
}
// 2. Potong saldo pengirim
await client.query(
'UPDATE accounts SET balance = balance - $1 WHERE id = $2;',
[amount, fromAccountId]
);
// 3. Tambah saldo penerima
await client.query(
'UPDATE accounts SET balance = balance + $1 WHERE id = $2;',
[amount, toAccountId]
);
return true;
});
}
Perbandingan: CockroachDB vs. Spanner vs. AWS Aurora #
Tabel di bawah merangkum perbandingan fitur arsitektur CockroachDB dengan dua alternatif database terdistribusi utama lainnya di industri.
| Parameter Evaluasi | CockroachDB (NewSQL) | Google Cloud Spanner | AWS Aurora (Global Database) |
|---|---|---|---|
| Engine SQL | PostgreSQL Dialect | SQL Standar & PostgreSQL (Spanner) | PostgreSQL / MySQL Asli |
| Separation of Compute-Storage | Ya (Stateless Compute + Pageserver) | Ya (Aurora Storage Engine) | Tidak (Compute & Storage menyatu di VM) |
| Scale-To-Zero | Ya (Instans otomatis tidur) | Tidak (Ada batas kapasitas minimal 0.5 ACU) | Tidak (VM menyala konstan) |
| Penskalaan Vertikal | Sangat Cepat (Detik, hot-plug) | Cepat (Detik, bertahap) | Manual (Perlu upgrade paket VM) |
| Database Branching | Ya (Instan via Copy-on-Write) | Tidak (Harus kloning snapshot lambat) | Terbatas (Local branching via CLI) |
| Konektivitas Edge | WebSockets & TCP | Hanya TCP (Butuh Proxy khusus) | WebSockets (PostgREST API) & TCP |
| Model Deployment Cloud | Multi-Cloud & On-Premise | Terkunci di GCP saja | Terkunci di AWS saja |
Best Practice untuk Performa Distributed SQL #
Mengoperasikan CockroachDB secara efisien menuntut pemahaman arsitektur terdistribusi agar kueri kita tidak membebani jaringan antarnode kluster:
1. Wajib Terapkan Pola Penanganan Retry Transaksi #
Isolasi serialisasi pada database NewSQL bekerja dengan asumsi optimis (optimistic concurrency control). Jika terjadi konflik penulisan data pada baris yang sama, database akan membatalkan satu kueri untuk menjaga integritas data.
- BENAR: Bungkus seluruh kueri transaksi aplikasi di dalam helper function retry loop (seperti contoh
executeTxWithRetrydi atas) untuk secara otomatis memicu eksekusi ulang kueri saat menerima kode error40001.
2. Hindari Penggunaan Kunci Primer Berurutan (Auto-Increment Serial) #
Pada database PostgreSQL tradisional, menggunakan tipe data SERIAL atau AUTO_INCREMENT pada primary key adalah hal yang wajar. Namun, pada CockroachDB, ini adalah anti-pattern fatal.
- JANGAN menggunakan kunci primer yang berurutan. Kunci yang berurutan (misal: 1, 2, 3, 4…) akan menyebabkan semua baris data baru jatuh ke dalam satu Range yang sama di bawah satu node fisik. Hal ini memicu masalah kemacetan hotspot (hotspot write) di mana satu node bekerja keras sedangkan node lainnya menganggur.
- BENAR: Gunakan tipe data UUIDv4 atau UUID acak sebagai primary key. Nilai UUID yang tersebar secara acak akan menjamin bahwa data transaksi baru didistribusikan secara merata ke seluruh Range dan node di dalam kluster, memaksimalkan throughput penulisan paralel.
3. Batasi Ukuran Transaksi SQL (Avoid Large Transactions) #
Melakukan transaksi besar yang memodifikasi jutaan baris sekaligus dalam satu blok BEGIN-COMMIT akan menahan kunci baris (lock table) dalam waktu yang sangat lama, meningkatkan risiko konflik serialisasi dengan transaksi lain, dan membebani kapasitas memori RAM node karena harus menampung seluruh log transaksi sementara.
- BENAR: Pecah transaksi modifikasi data skala besar menjadi beberapa transaksi batch kecil (misal: memodifikasi maksimal 5.000 baris per transaksi) untuk menjaga responsivitas dan stabilitas kluster.
4. Gunakan Indeks Secara Efektif untuk Kueri Terdistribusi #
Mengeksekusi kueri tanpa filter indeks primer (table scan) memaksa CockroachDB mengirim perintah kueri ke seluruh node fisik kluster untuk mencari data secara manual. Ini meningkatkan latensi jaringan secara signifikan.
- BENAR: Selalu buat indeks pada kolom yang sering digunakan di klausa
WHERE,JOIN, atauORDER BY. Indeks yang tepat memungkinkan range router langsung mengarahkan kueri ke node spesifik yang menyimpan data tersebut (point lookup) tanpa membuang bandwidth internal kluster.
5. Gunakan Connection Pooler Terdistribusi untuk Efisiensi #
Di dalam lingkungan serverless di mana ribuan fungsi ephemeral melakukan inisiasi koneksi ke database secara independen, overhead pembuatan koneksi TCP baru dan pertukaran enkripsi TLS dapat menurunkan throughput database secara signifikan.
- BENAR: Manfaatkan connection pooler seperti PgBouncer baik di sisi kluster CockroachDB Serverless maupun di middleware serverless kita. PgBouncer melakukan multiplexing banyak koneksi klien ke sejumlah kecil koneksi server persisten secara efisien, menghemat konsumsi memori dan mempercepat eksekusi query.
Ringkasan #
- CockroachDB adalah database NewSQL terdistribusi yang menggabungkan horizontal scaling NoSQL dengan transaksi ACID tingkat tertinggi.
- Range-Based Sharding otomatis membagi data menjadi partisi 64MB (Ranges) dan mendistribusikannya secara merata lintas node kluster.
- Konsensus Raft menjamin konsistensi replika data dan toleransi bencana di tingkat multi-zone hingga multi-region secara transparan.
- CockroachDB Serverless memfasilitasi model komputasi pay-as-you-go berbasis Request Units (RU) dengan fitur scale-to-zero.
- Gunakan UUIDv4 sebagai primary key untuk menghindari hotspot write dan menjamin penulisan data didistribusikan merata ke semua node.
- Wajib implementasikan retry handler di sisi aplikasi untuk menangani unhandled serialization errors (SQLSTATE 40001) secara otomatis.