1. Pengenalan CockroachDB
CockroachDB (sering disingkat CRDB) adalah distributed SQL database yang dirancang untuk menyediakan skalabilitas horizontal, konsistensi kuat (strong consistency), dan toleransi terhadap kegagalan (survivability). CockroachDB dibangun di atas konsep yang mirip dengan Google Spanner — menggabungkan kekuatan SQL relasional dengan arsitektur distributed yang resilien.
Nama "Cockroach" diinspirasi dari kecoa yang dikenal sangat sulit dibunuh — filosofi yang sama diterapkan pada database ini. CockroachDB bisa bertahan dari kegagalan node, bahkan kegagalan seluruh data center, tanpa kehilangan data atau ketersediaan.
execution engine
isolation, timestamps
leaseholder routing
on-disk format
1.1 Keunggulan Utama CockroachDB
- Distributed SQL: Menulis SQL standar PostgreSQL sambil mendapat skalabilitas distributed database
- Serializable Isolation: Level isolasi tertinggi — menjamin konsistensi data tanpa anomali
- Automatic Sharding: Data otomatis di-split menjadi ranges dan didistribusikan ke seluruh node
- Multi-Active Availability: Bisa membaca dan menulis bahkan saat sebagian node mati
- Geo-Partitioning: Data bisa ditempatkan di region tertentu untuk performa dan compliance
- PostgreSQL Compatible Wire Protocol: Menggunakan driver PostgreSQL yang sudah ada
1.2 Kapan Menggunakan CockroachDB?
| Gunakan CockroachDB | Pertimbangkan Alternatif |
|---|---|
| Aplikasi multi-region global | Single region, skala kecil |
| Butuh strong consistency | Cukup eventual consistency |
| High availability kritis | Bisa toleransi downtime singkat |
| Transaksi ACID kompleks | Data model key-value sederhana |
| Skalabilitas horizontal otomatis | Vertical scaling cukup |
2. Instalasi & Setup Cluster
CockroachDB bisa dijalankan dalam mode single-node untuk development atau multi-node cluster untuk production. Setiap node berjalan sebagai satu binary yang sama — tidak ada perbedaan antara master dan worker.
2.1 Instalasi CockroachDB
# Download CockroachDB binary terbaru wget -qO- https://binaries.cockroachdb.com/cockroach-v24.1.0.linux-amd64.tgz | tar xvz # Pindahkan ke PATH sudo cp -i cockroach-v24.1.0.linux-amd64/cockroach /usr/local/bin/ # Verifikasi instalasi cockroach version # Instal client library (opsional, untuk bahasa tertentu) # Python: pip install psycopg2-binary sqlalchemy # Node.js: npm install pg
2.2 Memulai Single-Node Cluster
# Buat direktori untuk data
mkdir -p ~/cockroach-data
# Mulai single-node cluster (insecure mode untuk development)
cockroach start-single-node \
--insecure \
--store=~/cockroach-data \
--listen-addr=localhost:26257 \
--http-addr=localhost:8080 \
--background
# Buka SQL shell
cockroach sql --insecure --host=localhost:26257
# Di dalam SQL shell, buat database dan tabel:
CREATE DATABASE mydb;
USE mydb;
CREATE TABLE users (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name STRING NOT NULL,
email STRING UNIQUE NOT NULL,
region STRING DEFAULT 'us-east1',
created_at TIMESTAMPTZ DEFAULT now()
);
INSERT INTO users (name, email, region) VALUES
('Budi', 'budi@example.com', 'ap-southeast-1'),
('Andi', 'andi@example.com', 'eu-west-1'),
('Sari', 'sari@example.com', 'us-east1');
SELECT * FROM users;
2.3 Multi-Node Cluster Lokal
# Node 1 cockroach start \ --insecure \ --store=node1 \ --listen-addr=localhost:26257 \ --http-addr=localhost:8080 \ --join=localhost:26257,localhost:26258,localhost:26259 \ --background # Node 2 cockroach start \ --insecure \ --store=node2 \ --listen-addr=localhost:26258 \ --http-addr=localhost:8081 \ --join=localhost:26257,localhost:26258,localhost:26259 \ --background # Node 3 cockroach start \ --insecure \ --store=node3 \ --listen-addr=localhost:26259 \ --http-addr=localhost:8082 \ --join=localhost:26257,localhost:26258,localhost:26259 \ --background # Initialize cluster (hanya sekali) cockroach init --insecure --host=localhost:26257 # Cek status cluster cockroach node status --insecure --host=localhost:26257
CockroachDB juga menyediakan layanan cloud di cockroachlabs.cloud dengan tier serverless yang gratis hingga 10 GiB storage dan 50M Request Units/bulan. Ini sangat cocok untuk development dan prototyping tanpa perlu setup cluster sendiri.
3. Serializability & Konsistensi
CockroachDB menjamin serializable isolation — level isolasi transaksi terturut yang memastikan hasil eksekusi transaksi concurrent sama dengan eksekusi sekuensial tertentu. Ini artinya tidak ada dirty reads, non-repeatable reads, maupun phantom reads.
3.1 Bagaimana Serializability Bekerja
CockroachDB menggunakan kombinasi dari:
- Multi-Version Concurrency Control (MVCC): Setiap write membuat versi baru dengan timestamp, bukan mengoverwrite data lama
- Hybrid Logical Clock (HLC): Kombinasi wall clock dan logical counter untuk ordering yang konsisten di seluruh cluster
- Write Intentions: Write pertama-tama membuat "intent" yang berfungsi sebagai lock optimistic
- Read Refreshes: Read yang menemukan data lebih baru akan me-refresh timestamp-nya
-- CockroachDB SELALU dalam serializable isolation -- Tidak perlu SET TRANSACTION ISOLATION LEVEL -- Contoh: Transfer saldo dengan jaminan konsistensi -- Sesi 1: BEGIN; SELECT balance FROM accounts WHERE id = 'acc-1'; -- 1000 SELECT balance FROM accounts WHERE id = 'acc-2'; -- 500 UPDATE accounts SET balance = balance - 100 WHERE id = 'acc-1'; UPDATE accounts SET balance = balance + 100 WHERE id = 'acc-2'; COMMIT; -- Jika ada Sesi 2 yang mencoba update data yang sama -- di saat bersamaan, salah satu akan mengalami -- serialization conflict dan otomatis di-retry -- Mengecek isolation level (selalu SERIALIZABLE): SHOW TRANSACTION ISOLATION LEVEL; -- Retry logic di aplikasi (penting!) -- CockroachDB mengembalikan error 40001 (serialization failure) -- ketika transaksi perlu di-retry
3.2 Transaction Contention & Retry
Karena serializability, transaksi yang berkompetisi untuk data yang sama bisa mengalami contention. CockroachDB menyediakan mekanisme retry otomatis di beberapa driver, tetapi umumnya developer perlu mengimplementasikan retry logic sendiri.
import psycopg2
from psycopg2 import errors
import time
def execute_with_retry(conn, func, max_retries=5):
"""Execute function dalam transaction dengan retry logic."""
for attempt in range(max_retries):
try:
with conn:
with conn.cursor() as cur:
result = func(cur)
return result
except errors.SerializationFailure:
conn.rollback()
wait = 0.1 * (2 ** attempt) # exponential backoff
print(f"Retry attempt {attempt + 1}, waiting {wait}s...")
time.sleep(wait)
raise Exception("Transaction failed after max retries")
# Contoh penggunaan
conn = psycopg2.connect("postgresql://root@localhost:26257/mydb")
def transfer_balance(cur):
cur.execute("""
UPDATE accounts SET balance = balance - 100
WHERE id = 'acc-1'
""")
cur.execute("""
UPDATE accounts SET balance = balance + 100
WHERE id = 'acc-2'
""")
return True
execute_with_retry(conn, transfer_balance)
Untuk mengurangi contention, hindari transaksi yang mengupdate baris yang sama secara bersamaan. Gunakan teknik seperti: mengurutkan akses ke tabel secara konsisten, menggunakan SELECT FOR UPDATE, membagi hot rows, atau mengubah data model untuk menghindari bottleneck.
4. Ranges & Data Distribution
CockroachDB membagi semua data menjadi ranges — potongan-potongan data dari key space yang berurutan. Setiap range berukuran default 512 MiB dan direplikasi (default 3 replika) menggunakan Raft consensus untuk memastikan konsistensi.
4.1 Bagaimana Ranges Bekerja
Replika di Node 1, 2, 3
Replika di Node 2, 3, 1
Replika di Node 3, 1, 2
- Leaseholder: Setiap range memiliki satu node yang menjadi leaseholder — menerima semua read dan write untuk range tersebut
- Raft Leader: Bertanggung jawab untuk mereplikasi write ke follower replicas
- Auto-Rebalancing: CockroachDB otomatis memindahkan range ketika node ditambah/dihapus
- Range Splitting: Range otomatis di-split jika terlalu besar atau terlalu aktif
4.2 Mengelola Ranges
-- Melihat informasi ranges
SHOW RANGES FROM TABLE users;
-- Melihat distribusi ranges di seluruh node
SELECT
range_id,
start_key,
end_key,
lease_holder,
replicas
FROM crdb_internal.ranges
ORDER BY range_id;
-- Melihat statistik ranges
SELECT
range_id,
key_bytes,
val_bytes,
live_bytes,
lease_holder
FROM crdb_internal.range_stats
LIMIT 20;
-- Mengubah ukuran range (untuk testing/scenario tertentu)
ALTER TABLE users CONFIGURE ZONE USING
range_min_bytes = 134217728, -- 128 MiB
range_max_bytes = 536870912; -- 512 MiB
-- Manual split (biasanya tidak perlu karena otomatis)
ALTER TABLE users SPLIT AT VALUES ('M');
-- Manual merge (menggabungkan range kecil)
ALTER TABLE users UNSPLIT AT VALUES ('M');
-- Melihat hot ranges
SELECT
range_id,
lease_holder,
reads_per_second,
writes_per_second
FROM crdb_internal.ranges_no_leases r
JOIN crdb_internal.node_metrics m ON r.lease_holder = m.node_id
ORDER BY reads_per_second DESC
LIMIT 10;
4.3 Index & Range Interaction
Setiap tabel dan index di CockroachDB dipecah menjadi range terpisah. Pemilihan primary key sangat mempengaruhi distribusi data:
-- HINDARI: Auto-increment ID bisa menyebabkan hot spot
-- Semua write masuk ke range terakhir
CREATE TABLE bad_example (
id SERIAL PRIMARY KEY,
data STRING
);
-- LEBIH BAIK: Hash-sharded primary key
CREATE TABLE orders (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
customer_id INT NOT NULL,
total DECIMAL(10,2),
created_at TIMESTAMPTZ DEFAULT now()
);
-- ATAU: Gunakan hash-sharded index untuk kolom sequential
CREATE INDEX idx_orders_created ON orders (created_at)
USING HASH;
-- Untuk tabel besar, pertimbangkan composite key dengan locality
CREATE TABLE events (
region STRING,
created_at TIMESTAMPTZ,
event_id UUID DEFAULT gen_random_uuid(),
payload JSONB,
PRIMARY KEY (region, created_at, event_id)
);
5. Locality & Zone Configuration
Locality memungkinkan Anda memberi tahu CockroachDB tentang topologi fisik cluster — region mana setiap node berada. Informasi ini digunakan untuk menempatkan replika data secara strategis, mengoptimalkan latensi baca, dan memenuhi persyaratan kepatuhan data (data residency).
5.1 Menentukan Locality Saat Startup
# Node di US East cockroach start \ --insecure \ --store=node-us-east \ --listen-addr=us-east.cockroach.internal:26257 \ --join=node-us-east:26257,node-eu-west:26257,node-ap-south:26257 \ --locality=region=us-east,zone=us-east-1a \ --background # Node di EU West cockroach start \ --insecure \ --store=node-eu-west \ --listen-addr=eu-west.cockroach.internal:26257 \ --join=node-us-east:26257,node-eu-west:26257,node-ap-south:26257 \ --locality=region=eu-west,zone=eu-west-1a \ --background # Node di AP South cockroach start \ --insecure \ --store=node-ap-south \ --listen-addr=ap-south.cockroach.internal:26257 \ --join=node-us-east:26257,node-eu-west:26257,node-ap-south:26257 \ --locality=region=ap-south,zone=ap-south-1a \ --background # Initialize cluster cockroach init --insecure --host=node-us-east:26257
5.2 Zone Configuration
-- Konfigurasi zona untuk tabel: replika di setiap region
ALTER TABLE users CONFIGURE ZONE USING
num_replicas = 3,
constraints = '{+region=us-east: 1, +region=eu-west: 1, +region=ap-south: 1}',
lease_preferences = '[[+region=us-east]]';
-- Konfigurasi database-level
ALTER DATABASE mydb CONFIGURE ZONE USING
num_replicas = 3,
constraints = '{+region=us-east: 1, +region=eu-west: 1}';
-- Melihat zone configuration
SHOW ZONE CONFIGURATION FOR TABLE users;
SHOW ZONE CONFIGURATION FOR DATABASE mydb;
-- Partitioning berdasarkan region
ALTER TABLE users PARTITION BY LIST (region) (
PARTITION us_east VALUES IN ('us-east'),
PARTITION eu_west VALUES IN ('eu-west'),
PARTITION ap_south VALUES IN ('ap-south')
);
-- Setelah partition, atur placement setiap partisi
ALTER PARTITION us_east OF TABLE users CONFIGURE ZONE USING
constraints = '{+region=us-east: 2}',
lease_preferences = '[[+region=us-east]]';
ALTER PARTITION eu_west OF TABLE users CONFIGURE ZONE USING
constraints = '{+region=eu-west: 2}',
lease_preferences = '[[+region=eu-west]]';
ALTER PARTITION ap_south OF TABLE users CONFIGURE ZONE USING
constraints = '{+region=ap-south: 2}',
lease_preferences = '[[+region=ap-south]]';
CockroachDB bisa menggunakan locality-optimized search untuk mengurangi latensi cross-region reads. Dengan mengatur ALTER DATABASE ... SET LOCALITY REGIONAL BY ROW, read untuk baris tertentu akan di-route ke node terdekat terlebih dahulu.
6. Changefeeds (Change Data Capture)
Changefeeds memungkinkan Anda memantau dan mengirimkan perubahan data secara real-time ke sistem eksternal seperti Kafka, cloud storage, atau webhook. Ini sangat berguna untuk event sourcing, data replication, cache invalidation, dan analytics pipeline.
6.1 Membuat Changefeed
-- Aktifkan rangefeed di cluster
SET CLUSTER SETTING kv.rangefeed.enabled = true;
-- Changefeed ke Kafka
CREATE CHANGEFEED FOR TABLE users, orders
INTO 'kafka://kafka-broker:9092?topic_prefix=cdc_'
WITH updated, resolved, envelope = 'wrapped';
-- Changefeed ke cloud storage (S3, GCS, Azure)
CREATE CHANGEFEED FOR TABLE users
INTO 's3://my-bucket/cdc/users?AWS_ACCESS_KEY_ID={key}&AWS_SECRET_ACCESS_KEY={secret}'
WITH format = 'parquet', compression = 'gzip';
-- Changefeed ke webhook
CREATE CHANGEFEED FOR TABLE orders
INTO 'webhook-https://my-api.com/cdc-webhook?insecure_tls_skip_verify=true'
WITH webhook_auth_header = 'Bearer my-secret-token',
webhook_client_timeout = '5s',
envelope = 'wrapped';
-- Changefeed dengan filter dan transformasi
CREATE CHANGEFEED FOR TABLE orders
INTO 'kafka://kafka-broker:9092'
WITH updated, envelope = 'wrapped',
format = 'json',
key_in_value;
-- Changefeed hanya untuk kolom tertentu (cdc_prev berguna untuk audit)
CREATE CHANGEFEED FOR TABLE orders (id, status, total)
INTO 'kafka://kafka-broker:9022?topic_prefix=order_changes'
WITH updated, resolved;
6.2 Memantau & Mengelola Changefeeds
-- Melihat semua changefeed jobs
SHOW JOBS WHEN COMPLETE SELECT * FROM [SHOW CHANGEFEED JOBS];
-- Melihat status changefeed
SELECT job_id, status, created, description
FROM [SHOW CHANGEFEED JOBS];
-- Pause changefeed
PAUSE JOB 12345;
-- Resume changefeed
RESUME JOB 12345;
-- Cancel changefeed
CANCEL JOB 12345;
-- Melihat changefeed yang sedang aktif
SELECT
job_id,
job_type,
status,
fraction_completed,
high_water_timestamp
FROM crdb_internal.jobs
WHERE job_type = 'CHANGEFEED';
-- Mengubah sink URI changefeed
ALTER CHANGEFEED 12345 SET sink_uri = 'kafka://new-broker:9092';
-- Menambah tabel ke changefeed
ALTER CHANGEFEED 12345 ADD TABLE new_table;
6.3 Format Output Changefeed
| Format | Deskripsi | Cocok Untuk |
|---|---|---|
json | JSON standar, mudah dibaca | Webhook, debugging |
avro | Binary format dengan schema registry | Kafka, analytics |
csv | Comma-separated values | Export, reporting |
parquet | Columnar format | Data warehouse, S3 |
7. Multi-Region Deployment
CockroachDB menyediakan fitur multi-region yang powerful melalui survival goals dan table localities. Ini memungkinkan Anda menentukan bagaimana data direplikasi antar region dan seberapa resilien terhadap kegagalan region.
7.1 Mengatur Database Multi-Region
-- Pastikan node sudah diberi locality saat startup
-- Set database sebagai multi-region
ALTER DATABASE mydb SET PRIMARY REGION "us-east1";
ALTER DATABASE mydb ADD REGION "eu-west1";
ALTER DATABASE mydb ADD REGION "ap-southeast-1";
-- Set survival goal: survive region failure
ALTER DATABASE mydb SURVIVE REGION FAILURE;
-- Melihat region yang terdaftar
SHOW REGIONS FROM DATABASE mydb;
-- Tabel global: dibaca cepat dari semua region, ditulis dari region utama
CREATE TABLE products (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name STRING NOT NULL,
price DECIMAL(10,2)
) LOCALITY GLOBAL;
-- Tabel regional by row: setiap baris dikaitkan dengan region tertentu
CREATE TABLE user_accounts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email STRING NOT NULL,
crdb_region crdb_internal_region NOT NULL DEFAULT gateway_region(),
name STRING NOT NULL
) LOCALITY REGIONAL BY ROW;
-- Baris akan otomatis disimpan di region yang sesuai
INSERT INTO user_accounts (email, name) VALUES ('budi@id.com', 'Budi');
-- Data ini akan disimpan di region AP (jika koneksi dari sana)
-- Membaca dari region spesifik
SELECT * FROM user_accounts
WHERE crdb_region = 'ap-southeast-1';
-- Tabel regional by table: semua data di satu region
CREATE TABLE local_configs (
key STRING PRIMARY KEY,
value STRING
) LOCALITY REGIONAL BY TABLE IN REGION "us-east1";
7.2 Memahami Survival Goals
| Survival Goal | Replika Minimum | Toleransi | Write Latency |
|---|---|---|---|
ZONE FAILURE (default) | 3 | 1 zone mati | Rendah |
REGION FAILURE | 5 (across 3+ regions) | 1 region mati | Lebih tinggi |
7.3 Multi-Region Best Practices
-- Gunakan gateway_region() untuk otomatis mengaitkan baris
-- dengan region dari koneksi
INSERT INTO user_accounts (email, name, crdb_region)
VALUES ('user@local.com', 'Local User', gateway_region());
-- Cross-region join tetap bisa dilakukan
-- tapi coba hindari untuk latensi rendah
SELECT
u.name,
u.crdb_region,
o.total
FROM user_accounts u
JOIN orders o ON u.id = o.user_id
WHERE u.crdb_region = gateway_region();
-- Gunakan GLOBAL table untuk data yang jarang berubah
-- dan dibutuhkan di semua region (lookup table)
CREATE TABLE countries (
code STRING PRIMARY KEY,
name STRING NOT NULL
) LOCALITY GLOBAL;
-- Statistik untuk monitoring cross-region
SELECT
u.crdb_region,
COUNT(*) as user_count,
AVG(o.total) as avg_order
FROM user_accounts u
JOIN orders o ON u.id = o.user_id
GROUP BY u.crdb_region;
8. Optimasi & Best Practices
8.1 Query Performance
-- Analisis query plan
EXPLAIN SELECT * FROM users WHERE region = 'us-east';
-- EXPLAIN dengan detail
EXPLAIN (VERBOSE, TYPES)
SELECT u.name, COUNT(o.id)
FROM users u JOIN orders o ON u.id = o.user_id
GROUP BY u.name;
-- EXPLAIN ANALYZE (mengeksekusi query dan menampilkan statistik)
EXPLAIN ANALYZE
SELECT * FROM orders WHERE total > 100 ORDER BY created_at DESC;
-- Melihat statistik tabel
SHOW STATISTICS FOR TABLE users;
-- Mengumpulkan statistik manual
ANALYZE users;
-- Melihat index usage
SELECT
ti.index_name,
ti.index_type,
ts.total_reads,
ts.last_read
FROM crdb_internal.table_indexes ti
JOIN crdb_internal.index_usage_statistics ts
ON ti.index_id = ts.index_id AND ti.table_id = ts.table_id
WHERE ti.descriptor_name = 'users';
8.2 Cluster Monitoring
-- Node liveness
SELECT node_id, address, is_live
FROM crdb_internal.gossip_nodes;
-- Replication status
SELECT
store_id,
node_id,
range_count,
lease_count,
total_bytes
FROM crdb_internal.kv_store_status;
-- Cluster settings yang berguna
SET CLUSTER SETTING server.time_until_store_dead = '10m';
SET CLUSTER SETTING kv.snapshot_rebalance.max_rate = '64 MiB';
SET CLUSTER SETTING kv.rangefeed.enabled = true;
-- SQL activity monitoring
SELECT
query,
count,
service_lat_avg,
service_lat_p99
FROM crdb_internal.node_statement_statistics
ORDER BY count DESC
LIMIT 20;
8.3 Checklist Production
| Aspek | Rekomendasi |
|---|---|
| Cluster Size | Minimum 3 node, ideal 5+ untuk production |
| Replication | 3 replika default, 5 untuk critical data |
| Backups | Scheduled backup ke cloud storage setiap jam |
| Monitoring | Prometheus + Grafana, atau CockroachDB Cloud Console |
| Security | SSL/TLS, RBAC, audit logging |
| Connection Pool | Gunakan PgBouncer atau HikariCP |
| Version | Upgrade secara berkala, baca release notes |
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang CockroachDB: