Setiap malam job backup berjalan dan laporannya menyala hijau — sampai suatu hari restore benar-benar dibutuhkan dan ternyata hasil backup tiga bulan terakhir tidak bisa dipakai. Skenario itu bukan khayalan: kredensial yang kedaluwarsa, storage yang penuh diam-diam, database yang terluput dari scope, atau ketidakcocokan versi tool — semuanya membuat backup tampak “berhasil” setiap malam tanpa pernah benar-benar pulih. Karena itu ada satu prinsip yang dipegang luas di industri: backup yang tidak pernah diuji restore-nya bukan backup. Chapter ini menyusun prosedur uji berkala yang lengkap — lalu menjalankan lapisan verifikasinya secara nyata di server latihan.

Peta materi Chapter ini:

  • Sumber kegagalan senyap pada backup dan alasan hanya restore yang bisa membuktikannya.
  • Prosedur uji lima langkah, dari restore ke environment terpisah sampai smoke test aplikasi.
  • Checklist verifikasi berlapis dan jadwal uji yang realistis.

Sumber Kegagalan Senyap #

Job backup adalah otomasi, dan otomasi gagal dengan cara yang paling senyap. Tabel berikut merangkum pola kegagalan yang paling sering ditemukan di lapangan:

Pola kegagalanGejalaTerdeteksi oleh
Kredensial kedaluwarsaJob gagal, tetapi notifikasi masuk folder spamUji restore + pemantauan job
Storage penuhBackup berhenti setengah jalan tanpa galat tegasPemeriksaan ukuran hasil + uji restore
Scope terluputDatabase baru tidak masuk daftar backupPerbandingan show dbs vs isi backup
Ketidakcocokan versimongorestore baru menolak dump lawas (atau sebaliknya)Satu-satunya penangkap: uji restore
Berkas terpotong saat transferChecksum tidak cocokUji restore + verifikasi checksum

Perhatikan kolom terakhir: hampir semua pola berujung pada satu jawaran yang sama. Laporan “backup selesai” hanya membuktikan bahwa perintah selesai — bukan bahwa data bisa kembali.

Prosedur Uji Lima Langkah #

Uji restore yang benar meniru skenario pemulihan sungguhan, bukan sekadar membuka berkas backup:

  1. Restore ke environment terpisah — server atau database uji tersendiri, tidak pernah menimpa produksi; di sinilah kecocokan versi tool dan server ikut teruji.
  2. Verifikasi struktur — semua collection hadir, jumlah document per collection cocok, daftar index identik.
  3. Verifikasi isi — query sampel pada document kunci, plus agregat pembanding (total, rata-rata) terhadap baseline produksi.
  4. Smoke test aplikasi — arahkan aplikasi versi uji ke data hasil restore, lalu jalankan alur inti: login, baca data, satu transaksi.
  5. Catat hasilnya — tanggal, durasi restore (ini RTO aktual kita!), temuan, dan tindak lanjut; catatan ini menjadi bukti audit sekaligus bahan perbaikan.

Langkah keempat sering dilewatkan padahal paling jujur: aplikasi bisa gagal membaca data yang secara jumlah “sudah cocok” — misalnya karena tipe field berubah atau validator menolak document hasil restore.

Praktik: Restore ke Environment Uji dan Verifikasi #

Mari kita jalankan lapisan-lapisan verifikasi di server latihan. Pertama, siapkan “produksi” — database latihan_backup lengkap dengan index:

js
use latihan_backup
db.dropDatabase()
db.buku.insertMany([
  { _id: 1, judul: "Laut Bercerita", harga: 98000, stok: 12 },
  { _id: 2, judul: "Bumi", harga: 89000, stok: 20 },
  { _id: 3, judul: "Atomic Habits", harga: 115000, stok: 8 }
])
db.penjualan.insertMany([
  { _id: 101, bukuId: 1, jumlah: 1 },
  { _id: 102, bukuId: 2, jumlah: 2 },
  { _id: 103, bukuId: 1, jumlah: 1 }
])
db.buku.createIndex({ judul: 1 })

Output yang Dihasilkan:

text
switched to db latihan_backup
{ ok: 1, dropped: 'latihan_backup' }
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }
{ acknowledged: true, insertedIds: { '0': 101, '1': 102, '2': 103 } }
judul_1

Kedua, jalankan “restore” ke environment uji. Pada praktik sungguhan, mongorestore yang memasukkan document dan membangun index dari berkas .metadata.json; di lingkungan latihan kita simulasikan hasil akhirnya di database latihan_uji — collection yang sama, isi yang sama, index yang sama:

js
use latihan_uji
db.dropDatabase()
db.buku.insertMany([
  { _id: 1, judul: "Laut Bercerita", harga: 98000, stok: 12 },
  { _id: 2, judul: "Bumi", harga: 89000, stok: 20 },
  { _id: 3, judul: "Atomic Habits", harga: 115000, stok: 8 }
])
db.penjualan.insertMany([
  { _id: 101, bukuId: 1, jumlah: 1 },
  { _id: 102, bukuId: 2, jumlah: 2 },
  { _id: 103, bukuId: 1, jumlah: 1 }
])
db.buku.createIndex({ judul: 1 })

Output yang Dihasilkan:

text
switched to db latihan_uji
{ ok: 1, dropped: 'latihan_uji' }
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }
{ acknowledged: true, insertedIds: { '0': 101, '1': 102, '2': 103 } }
judul_1

Ketiga — inti chapter ini — verifikasi berlapis secara otomatis. Setiap baris membandingkan environment uji dengan sumbernya, lalu mencatat LULUS atau GAGAL:

js
const produksi = db.getSiblingDB("latihan_backup")
const uji = db.getSiblingDB("latihan_uji")
const cek = [
  ["jumlah document buku", produksi.buku.countDocuments({}), uji.buku.countDocuments({})],
  ["jumlah document penjualan", produksi.penjualan.countDocuments({}), uji.penjualan.countDocuments({})],
  ["total stok",
    produksi.buku.aggregate([{ $group: { _id: null, t: { $sum: "$stok" } } }]).toArray()[0].t,
    uji.buku.aggregate([{ $group: { _id: null, t: { $sum: "$stok" } } }]).toArray()[0].t],
  ["jumlah index buku", produksi.buku.getIndexes().length, uji.buku.getIndexes().length]
]
cek.forEach(([nama, asal, hasil]) =>
  print(nama + ": " + (asal === hasil ? "LULUS" : "GAGAL") + " (asal " + asal + ", hasil " + hasil + ")"))

Output yang Dihasilkan:

text
jumlah document buku: LULUS (asal 3, hasil 3)
jumlah document penjualan: LULUS (asal 3, hasil 3)
total stok: LULUS (asal 40, hasil 40)
jumlah index buku: LULUS (asal 2, hasil 2)

Empat lapis lolos. Terakhir, smoke test ala aplikasi: satu query yang benar-benar dipakai aplikasi — pencarian buku berdasar judul — dan kita lihat hasilnya utuh:

js
uji.buku.find({ judul: "Bumi" }, { _id: 0, judul: 1, harga: 1, stok: 1 })

Output yang Dihasilkan:

text
[ { judul: 'Bumi', harga: 89000, stok: 20 } ]

Pola skrip cek di atas layak diadopsi apa adanya ke pipeline uji Anda: ganti database-nya, tambahkan baris untuk setiap angka yang dianggap kritis oleh bisnis (jumlah order hari ini, total saldo, dsb.), dan jalankan otomatis setiap kali restore uji selesai.

Checklist dan Jadwal #

Checklist lengkap untuk satu sesi uji restore:

LapisYang diperiksaContoh alat/perintahKriteria lulus
StrukturCollection dan index hadirgetCollectionNames(), getIndexes()Identik dengan sumber
JumlahDocument per collectioncountDocuments({})Cocok dengan baseline
IsiDocument kunci dan tipe fieldfind() + $typeField utuh, tipe benar
AgregatTotal field kritisaggregate([{$group: ...}])Cocok dengan baseline
AplikasiAlur inti aplikasiSmoke test / uji end-to-endFitur inti berjalan
WaktuDurasi restore + verifikasiCatat waktu mulai-selesaiDalam anggaran RTO

Dan jadwalnya menyesuaikan risiko — makin rapat untuk sistem makin kritis:

FrekuensiCakupanPelaksana
MingguanRestore satu collection + verifikasi jumlahOtomatis (script)
BulananRestore penuh ke staging + seluruh checklist + smoke testEngineer on-call
Saat perubahan besarFull drill: upgrade versi, pindah region, ganti strategi backupSeluruh tim
Pasca insidenReview: bagian restore yang lambat atau gagalRetro tim

Dua kebiasaan penutup yang membedakan tim siap dari yang tidak: hasil setiap uji didokumentasikan (tanggal, durasi, temuan, tindak lanjut) sebagai jejak audit, dan runbook restore disimpan di tempat yang bisa dijangkau seluruh tim on-call — bukan hanya di kepala satu orang.

Rangkuman #

  • Backup gagal secara senyap: kredensial kedaluwarsa, storage penuh, scope terluput, versi tak cocok — dan hampir semuanya hanya bisa ditangkap oleh uji restore sungguhan.
  • Prosedur lima langkah: restore ke environment terpisah, verifikasi struktur, verifikasi isi, smoke test aplikasi, catat hasil dan RTO aktual.
  • Verifikasi berlapis bisa diautomasi sepenuhnya — pola skrip cek kita membandingkan jumlah, agregat, dan index terhadap sumber, lalu mencetak LULUS/GAGAL per baris.
  • Jadwal realistis: mingguan untuk spot check otomatis, bulanan untuk drill penuh, dan selalu setelah perubahan besar atau insiden.