Replication #

Bayangkan situasi ini: sistem kamu sedang melayani ribuan user secara bersamaan, campuran antara user yang sedang checkout, user yang browsing katalog, dan tim internal yang menjalankan laporan bulanan. Semua ini menabrak satu database server yang sama. Write dari proses checkout bersaing dengan read dari laporan yang full-table-scan. Latency naik, antrian query menumpuk, dan di suatu titik database mulai menjawab lambat — atau tidak menjawab sama sekali.

Ini bukan masalah hardware yang kurang kencang. Ini adalah masalah arsitektur: satu server dipaksa melayani semua jenis beban sekaligus, tanpa pemisahan. Database replication adalah jawaban untuk masalah ini. Dengan menyalin data dari satu server ke server lain dan mendistribusikan beban berdasarkan jenis operasi, sistem bisa melayani lebih banyak user, lebih tahan terhadap kegagalan, dan lebih mudah diskalakan tanpa harus terus-menerus meng-upgrade hardware.

Tapi replication bukan sekadar “duplikasi data supaya aman”. Ia membawa serangkaian kompleksitas baru — replication lag, read after write problem, failover yang tidak mulus, migrasi schema yang berbahaya — yang kalau tidak dipahami dengan baik, bisa menghasilkan bug yang jauh lebih sulit dilacak dibanding bug biasa.

Apa Itu Database Replication #

Database replication adalah mekanisme untuk menyalin dan menyinkronkan data dari satu database server — disebut primary (atau master) — ke satu atau lebih database server lain — disebut replica (atau standby, slave).

Perubahan yang terjadi di primary — INSERT, UPDATE, DELETE, bahkan perubahan schema — direkam dalam binary log, lalu dikirimkan ke replica untuk dieksekusi ulang. Hasilnya, replica menjadi salinan dari primary yang selalu (atau hampir selalu) up-to-date.

Arsitektur dasar replication:

  ┌────────────────────────────────────────────────┐
  │                   Aplikasi                     │
  └──────────────────┬─────────────────────────────┘
                     │
          ┌──────────▼──────────┐
          │    Write / Read     │
          │    (critical read)  │
          └──────────┬──────────┘
                     │
          ┌──────────▼──────────┐
          │   Primary Database  │  ← satu-satunya yang menerima write
          └──────────┬──────────┘
                     │  binary log
          ┌──────────▼──────────┐
          │    Replication      │
          │    (async / sync)   │
          └──────────┬──────────┘
                     │
        ┌────────────┼────────────┐
        │            │            │
  ┌─────▼─────┐ ┌───▼──────┐ ┌──▼───────┐
  │ Replica A │ │ Replica B│ │ Replica C│
  │ (API read)│ │(reporting│ │ (backup) │
  └───────────┘ └──────────┘ └──────────┘

Prinsip dasarnya sederhana: semua write masuk ke primary, sebagian besar read dilayani oleh replica. Tapi implementasinya penuh dengan nuansa yang menentukan apakah replication menjadi aset atau beban.


Mengapa Replication Diperlukan #

High Availability dan Failover #

Tanpa replication, database adalah single point of failure. Ketika primary database down — entah karena hardware failure, OOM killer, disk penuh, atau kesalahan operasi — seluruh aplikasi ikut down sampai server pulih atau diganti. Ini bisa berlangsung menit sampai jam.

Dengan replication, ketika primary down, salah satu replica bisa dipromosikan menjadi primary baru. Prosesnya tidak instan dan tidak bebas risiko, tapi jauh lebih cepat dari memulihkan server dari nol. Di sistem dengan SLA tinggi, perbedaan antara “down 2 menit” dan “down 45 menit” adalah perbedaan yang sangat nyata bagi user dan bagi bisnis.

Skenario failover:

  Kondisi normal:
  Primary ──────────────────→ Replica A
                               Replica B

  Primary down:
  Primary [✗]                  Replica A ← dipromosikan jadi primary baru
                               Replica B ← mulai replicate dari primary baru

  Aplikasi redirect koneksi ke primary baru → downtime diminimalkan

Read Scalability #

Mayoritas aplikasi web bersifat read-heavy. Halaman produk, feed, dashboard, history transaksi — semua ini adalah operasi read. Write (checkout, update profil, submit form) jauh lebih jarang dibanding read.

Tanpa replication, semua read dan write bersaing di satu server. Dengan replication, write tetap ke primary sementara read didistribusikan ke replica. Kamu bisa menambah replica baru untuk menambah kapasitas read tanpa menyentuh primary — horizontal scaling yang relatif murah dan cepat.

Tanpa replication — semua beban di satu server:
  1000 read/detik  ┐
  50   write/detik ├─→ Primary (kewalahan)
  5    report/detik┘

Dengan replication — beban terdistribusi:
  50   write/detik ──────────────────→ Primary
  800  read/detik  ──────────────────→ Replica A + B
  5    report/detik ─────────────────→ Replica C (dedicated reporting)

Isolasi Beban Query Berat #

Query analitik dan reporting sering melakukan full table scan, aggregate besar, atau join kompleks yang mengunci resource dalam waktu lama. Jika query seperti ini dijalankan di primary, ia bisa membuat latency semua query lain naik drastis — bahkan query write yang seharusnya cepat.

Dengan replica yang didedikasikan untuk reporting, query berat bisa berjalan sepuas-puasnya tanpa mengganggu operasional utama. Replica reporting bahkan bisa dikonfigurasi berbeda dari replica API — misalnya dengan lebih banyak RAM untuk buffer pool, atau index tambahan yang spesifik untuk kebutuhan analitik.

Disaster Recovery #

Replica bisa ditempatkan di availability zone atau region yang berbeda dari primary. Jika terjadi kegagalan infrastruktur skala besar — data center mati, bencana alam, atau kesalahan operasi yang menghancurkan seluruh data di primary — replica di region lain menjadi satu-satunya harapan pemulihan.

Tanpa replica offsite, disaster recovery hanya bergantung pada backup — yang artinya restore dari file, yang bisa memakan waktu jam atau bahkan hari untuk database berukuran besar.


Tiga Jenis Replication #

Cara data disinkronkan dari primary ke replica menentukan trade-off antara konsistensi dan performa. Ada tiga model utama.

Synchronous Replication #

Primary menganggap sebuah write berhasil hanya setelah semua replica mengkonfirmasi bahwa mereka sudah menerima dan menulis data tersebut. Write tidak dikembalikan ke aplikasi sampai seluruh replica selesai.

Synchronous replication — alur write:

  Aplikasi ──→ Primary ──→ Replica A (tunggu konfirmasi)
                       └──→ Replica B (tunggu konfirmasi)
                                │
                       setelah semua konfirmasi
                                │
  Aplikasi ←── "write berhasil" ┘

Kelebihan: tidak ada replication lag, semua replica selalu up-to-date, tidak ada risiko kehilangan data jika primary crash setelah write.

Kekurangan: latency write meningkat secara langsung sesuai dengan latency jaringan ke replica yang paling lambat. Jika satu replica lambat atau tidak responsif, seluruh write terhambat. Di sistem dengan replica di region berbeda (latency tinggi), ini bisa menjadi masalah serius.

Kapan digunakan: sistem finansial dengan zero data loss requirement, atau situasi di mana konsistensi data jauh lebih penting dari kecepatan write.

Asynchronous Replication #

Primary menganggap write berhasil segera setelah menyimpan data ke dirinya sendiri. Data dikirim ke replica di background, tanpa menunggu konfirmasi.

Asynchronous replication — alur write:

  Aplikasi ──→ Primary ──→ "write berhasil" ──→ Aplikasi
                  │
                  └──→ (background) ──→ Replica A
                                   └──→ Replica B

Kelebihan: latency write sangat rendah, primary tidak bergantung pada ketersediaan atau kecepatan replica.

Kekurangan: ada jeda waktu antara write di primary dan tersedianya data di replica — ini disebut replication lag. Jika primary crash tepat setelah write tapi sebelum data terkirim ke replica, data tersebut hilang.

Kapan digunakan: sebagian besar aplikasi web — e-commerce, media sosial, SaaS — di mana sedikit lag pada replica masih bisa diterima dan kecepatan write lebih diprioritaskan.

Semi-Synchronous Replication #

Kompromi antara keduanya: primary menunggu konfirmasi dari minimal satu replica sebelum menganggap write berhasil. Tidak harus semua replica, cukup satu.

Perbandingan tiga jenis replication:

┌──────────────────┬────────────────┬──────────────┬──────────────────────┐
│ Jenis            │ Latency Write  │ Data Safety  │ Kapan Digunakan      │
├──────────────────┼────────────────┼──────────────┼──────────────────────┤
│ Synchronous      │ Tinggi         │ Sangat Tinggi│ Finansial, kritikal  │
│ Asynchronous     │ Rendah         │ Sedang       │ Mayoritas web app    │
│ Semi-Synchronous │ Menengah       │ Tinggi       │ Balance antara keduanya│
└──────────────────┴────────────────┴──────────────┴──────────────────────┘

Masalah Umum yang Harus Dipahami #

Replication bukan solusi tanpa trade-off. Ada beberapa masalah yang konsisten muncul di sistem yang menggunakan replication — dan memahaminya adalah prasyarat untuk menggunakan replication dengan benar.

Replication Lag #

Replication lag adalah jeda waktu antara saat sebuah write terjadi di primary dan saat perubahan itu tersedia di replica. Dalam kondisi normal, lag bisa serendah beberapa milidetik. Tapi dalam kondisi tertentu, lag bisa membengkak hingga detik, menit, bahkan jam.

Penyebab paling umum: replica menerima binary log dari primary secara real-time, tapi eksekusinya di replica bersifat serial — satu per satu — sementara di primary write bisa terjadi secara concurrent. Jika primary menerima banyak write sekaligus, replica bisa ketinggalan.

Penyebab lain yang sering diabaikan: query berat yang berjalan di replica itu sendiri. Jika ada laporan yang sedang berjalan di replica dan mengunci banyak resource, eksekusi binary log dari primary bisa terhambat.

-- Cara mengecek replication lag di MySQL
SHOW SLAVE STATUS\G

-- Perhatikan kolom:
-- Seconds_Behind_Master: lag dalam detik (0 = tidak ada lag)
-- Slave_IO_Running: apakah thread penerima binary log berjalan
-- Slave_SQL_Running: apakah thread eksekusi query berjalan
-- Last_Error: pesan error jika replication berhenti

Read After Write Problem #

Ini adalah salah satu bug yang paling membingungkan user: user mengubah sesuatu, lalu halaman di-refresh, dan perubahan itu tidak muncul. Atau lebih buruk — konfirmasi muncul “data berhasil disimpan”, tapi data yang terlihat di halaman berikutnya masih data lama.

Ini terjadi ketika aplikasi melakukan write ke primary, tapi read berikutnya diarahkan ke replica yang belum menerima perubahan tersebut.

Read after write problem — alur yang bermasalah:

  1. User update nama: "Ali" → "Budi"
  2. Aplikasi: UPDATE ke Primary ✓
  3. Aplikasi: SELECT dari Replica (lag 200ms)
  4. Replica belum punya perubahan → mengembalikan "Ali"
  5. User melihat namanya masih "Ali" padahal sudah diubah

  Hasil: user panik, submit ulang, data mungkin duplikat.

Ada beberapa pendekatan untuk menangani ini, masing-masing dengan trade-off berbeda:

Pendekatan 1 — Read from primary setelah write:
  Setelah operasi write, arahkan read berikutnya ke primary
  selama beberapa detik (atau sampai replica catch-up).
  ✓ Simpel, tidak butuh infrastruktur tambahan.
  ✗ Menambah beban primary untuk operasi read.

Pendekatan 2 — Track replication position:
  Simpan posisi binary log terakhir yang ditulis.
  Sebelum read dari replica, cek apakah replica sudah melewati posisi itu.
  ✓ Presisi, tidak overkill.
  ✗ Butuh mekanisme tracking dan cek tambahan.

Pendekatan 3 — Cache hasil write:
  Setelah write, simpan hasil baru di cache (Redis).
  Read berikutnya ambil dari cache dulu sebelum ke database.
  ✓ Cepat, tidak menambah beban database.
  ✗ Butuh cache layer, ada kompleksitas invalidation.

Query Locking di Replica #

Replica bukan hanya server baca pasif — ia juga menjalankan semua query dari binary log primary. Jika kamu menjalankan query yang melakukan locking di replica (SELECT ... FOR UPDATE, atau transaction panjang), itu bisa memblokir eksekusi binary log dan membuat lag membengkak.

-- ANTI-PATTERN: query locking di replica
-- Ini menghambat proses replication dan menyebabkan lag
SELECT * FROM orders WHERE status = 'pending' FOR UPDATE;

-- BENAR: query locking harus selalu ke primary
-- Replica hanya untuk read biasa tanpa locking
SELECT * FROM orders WHERE status = 'pending';
-- Jika butuh locking, arahkan koneksi ke primary secara eksplisit

Migrasi Schema di Sistem dengan Replication #

Migrasi schema — terutama ALTER TABLE untuk tabel besar — adalah salah satu operasi paling berbahaya di sistem dengan replication. ALTER TABLE yang membutuhkan rebuild tabel bisa memblokir semua read dan write di primary selama proses berlangsung. Sementara itu, replica harus mengeksekusi operasi yang sama dari binary log, yang bisa menyebabkan lag ekstrem.

Jangan pernah menjalankan ALTER TABLE pada tabel besar di jam sibuk tanpa menggunakan tool online schema migration seperti pt-online-schema-change (Percona) atau gh-ost (GitHub). Tool ini melakukan perubahan schema secara bertahap tanpa memblokir tabel, dan aman digunakan di sistem dengan replication.

Best Practice Replication #

Pisahkan Koneksi Write dan Read di Level Aplikasi #

Pemisahan ini adalah fondasi dari seluruh arsitektur replication. Tanpa pemisahan yang eksplisit di kode aplikasi, semua query akan masuk ke primary dan replica tidak pernah digunakan.

# ANTI-PATTERN: satu connection pool untuk semua query
# Semua query — read maupun write — masuk ke primary
db = create_connection(host="primary-db")

def get_user(user_id):
    return db.query("SELECT * FROM users WHERE id = ?", user_id)

def update_user(user_id, name):
    db.execute("UPDATE users SET name = ? WHERE id = ?", name, user_id)

# ──────────────────────────────────────────────────────────────────

# BENAR: pisahkan connection pool berdasarkan jenis operasi
db_primary = create_connection(host="primary-db")    # untuk write
db_replica  = create_connection(host="replica-db")   # untuk read

def get_user(user_id):
    # read → replica
    return db_replica.query("SELECT * FROM users WHERE id = ?", user_id)

def update_user(user_id, name):
    # write → primary
    db_primary.execute("UPDATE users SET name = ? WHERE id = ?", name, user_id)

def get_user_after_update(user_id):
    # read setelah write → primary (hindari read after write problem)
    return db_primary.query("SELECT * FROM users WHERE id = ?", user_id)

Di level infrastruktur, pertimbangkan menggunakan database proxy seperti ProxySQL atau AWS RDS Proxy yang bisa melakukan routing otomatis berdasarkan jenis query — read ke replica, write ke primary — tanpa harus mengubah kode aplikasi.

Jangan Gunakan Replica untuk Data yang Harus Real-Time dan Konsisten #

Ada kategori read yang tidak boleh diarahkan ke replica, terlepas dari berapa kecilnya lag yang ada. Ini adalah read yang menjadi dasar untuk keputusan bisnis yang irreversible, atau yang langsung mengikuti sebuah write.

Gunakan PRIMARY untuk:
  ✓ Login dan verifikasi session
  ✓ Cek saldo sebelum transaksi
  ✓ Validasi stok sebelum checkout
  ✓ Read yang langsung mengikuti write oleh user yang sama
  ✓ Operasi yang melibatkan locking (SELECT ... FOR UPDATE)

Gunakan REPLICA untuk:
  ✓ Halaman daftar produk, katalog
  ✓ History transaksi (tanpa locking)
  ✓ Dashboard dan statistik non-kritis
  ✓ Feed, notifikasi, aktivitas
  ✓ Laporan dan export data
  ✓ Query analitik dan full table scan

Dedikasikan Replica Berdasarkan Jenis Beban #

Satu replica untuk semua jenis read masih menciptakan kompetisi beban — query API yang butuh latency rendah bersaing dengan query reporting yang berjalan lama.

Topologi dengan dedicated replica:

  Primary
    │
    ├──→ Replica A (API read)
    │       └── Konfigurasi: banyak connection, max_execution_time ketat
    │
    ├──→ Replica B (reporting & analitik)
    │       └── Konfigurasi: buffer pool besar, index tambahan untuk reporting
    │
    └──→ Replica C (backup & monitoring)
            └── Tidak melayani query aplikasi, hanya untuk backup otomatis

Dengan pemisahan ini, query laporan bulanan yang berjalan 10 menit tidak akan mempengaruhi latency API sama sekali — karena mereka berjalan di replica yang berbeda.

Monitor Replication Lag Secara Aktif #

Lag yang membengkak adalah early warning sign bahwa ada masalah — entah di primary (terlalu banyak write), di replica (query berat menyumbat eksekusi), atau di jaringan antara keduanya. Lag yang tidak dimonitor adalah lag yang baru ketahuan ketika user sudah mengeluh.

Metrik yang wajib dimonitor:

  Seconds_Behind_Master  → lag replication dalam detik
                           Alert jika > 5 detik untuk sistem kritikal
                           Alert jika > 30 detik untuk sistem biasa

  Slave_IO_Running       → thread penerima binary log
                           Harus selalu "Yes"

  Slave_SQL_Running      → thread eksekusi query dari binary log
                           Harus selalu "Yes"

  Relay_Log_Space        → ukuran relay log yang belum dieksekusi
                           Membesar = replica sedang ketinggalan

Jika lag konsisten tinggi, langkah investigasi yang benar adalah:

Lag tinggi → diagnosa:

  Cek Seconds_Behind_Master
      │
      ├─ Nilai besar dan terus naik?
      │       └─→ Replica tidak bisa catch up
      │           Kemungkinan: write di primary terlalu banyak,
      │           atau ada query berat di replica yang memblokir SQL thread
      │
      ├─ Slave_SQL_Running = No?
      │       └─→ Ada error saat eksekusi binary log
      │           Cek Last_Error untuk detail
      │
      └─ Slave_IO_Running = No?
              └─→ Koneksi antara primary dan replica terputus
                  Cek network, firewall, atau credential

Rancang Failover Sebelum Dibutuhkan #

Failover yang tidak direncanakan adalah failover yang kacau. Ketika primary tiba-tiba down di tengah malam, bukan saat itu untuk mulai memikirkan bagaimana cara mempromosikan replica.

Checklist kesiapan failover:

  □ Dokumentasi langkah-langkah promosi replica sudah ada dan up-to-date
  □ Proses failover sudah pernah dicoba di staging (bukan hanya di teori)
  □ Aplikasi bisa dikonfigurasi ulang untuk mengarah ke primary baru
    dalam waktu kurang dari 2 menit
  □ Ada monitoring alert yang akan membangunkan engineer jika primary down
  □ Semua engineer yang on-call tahu cara membaca SHOW SLAVE STATUS
  □ Ada runbook yang jelas untuk setiap skenario kegagalan

Alat seperti Orchestrator (MySQL) atau fitur bawaan seperti AWS RDS Multi-AZ bisa mengotomasi sebagian besar proses failover — tapi pemahaman manual tentang apa yang terjadi di baliknya tetap penting.


Anti-Pattern yang Harus Dihindari #

✗ Anti-pattern 1: mengirim semua query ke primary "biar aman"
  Replica tidak pernah digunakan, semua keuntungan replication hilang.
  Primary tetap menjadi bottleneck meski sudah ada replica.

✓ Solusi: pisahkan secara eksplisit — write ke primary, read ke replica.
  Tentukan kategori mana yang boleh ke replica dan mana yang harus ke primary.

──────────────────────────────────────────────────────────────────────────

✗ Anti-pattern 2: membaca data kritis dari replica tanpa antisipasi lag
  Cek saldo dari replica → saldo mungkin belum update → double spend.
  Verifikasi stok dari replica → stok mungkin sudah habis → oversell.

✓ Solusi: operasi kritis (cek saldo, validasi stok, verifikasi session)
  harus selalu membaca dari primary.

──────────────────────────────────────────────────────────────────────────

✗ Anti-pattern 3: menjalankan query locking di replica
  SELECT ... FOR UPDATE di replica memblokir SQL thread replication.
  Lag membengkak, semua read dari replica menjadi stale.

✓ Solusi: query yang membutuhkan locking harus ke primary.
  Replica hanya untuk read tanpa locking.

──────────────────────────────────────────────────────────────────────────

✗ Anti-pattern 4: ALTER TABLE di jam sibuk tanpa online migration
  ALTER TABLE pada tabel besar memblokir seluruh tabel.
  Replication lag bisa membengkak hingga jam.
  User mengalami error atau timeout selama proses berlangsung.

✓ Solusi: gunakan pt-online-schema-change atau gh-ost untuk perubahan
  schema pada tabel besar. Jadwalkan di jam dengan trafik rendah.

──────────────────────────────────────────────────────────────────────────

✗ Anti-pattern 5: tidak memonitor replication lag sama sekali
  Lag yang membengkak baru ketahuan setelah user mengeluh.
  Pada saat itu, replica mungkin sudah tertinggal sangat jauh
  dan butuh waktu lama untuk catch up.

✓ Solusi: pasang monitoring dan alert pada Seconds_Behind_Master.
  Tetapkan threshold yang jelas dan tindakan yang harus diambil.

Checklist Review Replication #

ARSITEKTUR:
  □ Write selalu ke primary, tidak ada write ke replica
  □ Read sudah dipisah berdasarkan jenis (API, reporting, backup)
  □ Ada replica di availability zone atau region yang berbeda
  □ Topologi replication terdokumentasi dan up-to-date

ROUTING QUERY:
  □ Connection pool terpisah untuk primary dan replica di aplikasi
  □ Operasi kritis (cek saldo, validasi stok, login) menggunakan primary
  □ Query locking (SELECT FOR UPDATE) hanya ke primary
  □ Read after write sudah ditangani (primary read / cache / tracking)

MONITORING:
  □ Seconds_Behind_Master dimonitor dengan alert yang jelas
  □ Slave_IO_Running dan Slave_SQL_Running dimonitor
  □ Ada notifikasi jika replication berhenti (thread tidak berjalan)
  □ Ada dashboard yang menampilkan lag semua replica secara real-time

FAILOVER:
  □ Runbook failover sudah ada dan pernah dipraktikkan
  □ Proses promosi replica ke primary terdokumentasi step-by-step
  □ Aplikasi bisa dikonfigurasi ulang untuk primary baru dengan cepat
  □ Semua engineer on-call familiar dengan prosedur failover

MIGRASI SCHEMA:
  □ ALTER TABLE pada tabel besar menggunakan online schema migration tool
  □ Migrasi dijadwalkan di luar jam sibuk
  □ Lag replication dimonitor selama dan setelah migrasi
  □ Ada rollback plan jika migrasi gagal

Ringkasan #

  • Replication memisahkan beban write dan read — primary menerima semua write, replica melayani sebagian besar read. Tanpa pemisahan yang eksplisit di kode aplikasi, replication tidak memberikan manfaat apapun.
  • Ada tiga model replication — synchronous (konsisten tapi lambat), asynchronous (cepat tapi ada lag), dan semi-synchronous (kompromi). Pilih berdasarkan toleransi terhadap data loss dan kebutuhan latency.
  • Replication lag adalah keniscayaan di asynchronous replication — desain aplikasi harus mengasumsikan bahwa replica bisa tertinggal, bukan mengasumsikan replica selalu up-to-date.
  • Read after write problem terjadi ketika read setelah write diarahkan ke replica yang belum menerima perubahan — tangani dengan membaca dari primary setelah write, atau menggunakan cache.
  • Operasi kritis harus selalu ke primary — cek saldo, validasi stok, verifikasi session, dan semua operasi yang menjadi dasar keputusan bisnis irreversible tidak boleh membaca dari replica.
  • Dedikasikan replica berdasarkan jenis beban — replica untuk API read, replica untuk reporting, replica untuk backup. Jangan campur semua beban dalam satu replica.
  • Query locking tidak boleh di replicaSELECT ... FOR UPDATE dan transaction panjang di replica memblokir SQL thread replication dan membuat lag membengkak.
  • Migrasi schema butuh perlakuan khusus — gunakan online schema migration tool untuk tabel besar, jangan jalankan ALTER TABLE biasa di jam sibuk.
  • Monitor lag secara aktifSeconds_Behind_Master harus punya alert threshold yang jelas. Lag yang tidak dimonitor adalah lag yang baru ketahuan setelah user mengeluh.
  • Rancang failover sebelum dibutuhkan — runbook, latihan, dan otomasi failover harus sudah siap sebelum primary pertama kali down di produksi.

← Sebelumnya: Data Integrity   Berikutnya: Locking →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact