Saat Migrasi Database Bermasalah: Rollback vs Roll-Forward

Tim Anda baru saja menjalankan migrasi database di produksi. Lima menit kemudian, dashboard monitoring memerah. Tingkat error melonjak. Pengguna mulai melaporkan masalah. Sekarang Anda harus mengambil keputusan cepat: apakah Anda membatalkan perubahan, atau mendorong perbaikan lain ke depan?

Momen ini membedakan tim yang punya rencana dari tim yang panik. Dan jawabannya tidak pernah sesederhana "tinggal rollback." Tipe perubahan yang Anda buat, data yang terlibat, dan status sistem Anda semuanya menentukan jalur mana yang lebih aman.

Dua Jalur Pemulihan

Pada dasarnya ada dua cara untuk pulih dari migrasi database yang buruk. Keduanya bekerja secara berbeda, membawa risiko yang berbeda, dan berlaku untuk situasi yang berbeda.

Rollback berarti membalikkan migrasi yang baru saja Anda jalankan. Anda mengeksekusi down migration, yang merupakan kebalikan persis dari apa yang Anda lakukan. Jika Anda menambahkan kolom, down migration akan menghapusnya. Jika Anda mengubah tipe data, down migration akan mengubahnya kembali.

Roll-forward berarti membiarkan migrasi yang bermasalah tetap ada dan menulis migrasi baru yang memperbaiki masalah. Anda tidak mundur. Anda maju dengan koreksi.

Kedua strategi memiliki tempatnya masing-masing. Triknya adalah mengetahui mana yang cocok untuk situasi Anda sebelum Anda membutuhkannya.

Kapan Harus Rollback

Rollback paling cocok untuk perubahan yang aman untuk dibalikkan. Biasanya ini adalah operasi non-destruktif di mana membatalkan perubahan tidak menyebabkan kehilangan data atau korupsi.

Kandidat yang baik untuk rollback meliputi:

  • Menambahkan kolom nullable
  • Membuat index baru
  • Menambahkan tabel baru
  • Membuat view atau function

Perubahan ini bersifat aditif. Saat Anda membalikkannya, Anda menghapus sesuatu yang telah ditambahkan. Tidak ada data yang ada yang hilang atau rusak dalam prosesnya.

Pertimbangkan skenario di mana Anda menambahkan kolom last_login_at ke tabel users. Kolom tersebut nullable, jadi baris yang sudah ada tidak masalah. Setelah deployment, Anda menemukan bahwa kode aplikasi memiliki bug yang menulis timestamp yang salah. Rollback dengan menghapus kolom tersebut aman. Tidak ada data yang dirugikan karena kolom tersebut kosong atau berisi data yang tidak perlu Anda pertahankan.

Kapan Harus Roll-Forward

Roll-forward menjadi pilihan yang lebih baik ketika migrasi bersifat destruktif atau ketika membalikkannya akan menyebabkan lebih banyak kerusakan daripada masalah aslinya.

Situasi di mana roll-forward lebih aman:

  • Menghapus kolom atau tabel
  • Mengubah tipe data dengan cara yang menghilangkan presisi
  • Memodifikasi nilai data yang ada dalam skala besar
  • Menggabungkan tabel
  • Menghapus constraint NOT NULL yang diandalkan sistem lain

Bayangkan Anda menjalankan migrasi yang menghapus kolom legacy_status. Data di kolom itu sudah hilang. Menulis down migration yang menambahkan kolom itu kembali tidak akan mengembalikan datanya. Pengguna yang bergantung pada field status tersebut sekarang melihat nilai null. Langkah terbaik Anda adalah menulis migrasi baru yang membuat ulang kolom tersebut dan mengisinya dari backup atau dari log aplikasi.

Kasus umum lainnya: Anda mengubah kolom dari VARCHAR ke INTEGER, mengonversi nilai string menjadi angka. Rollback dengan mengubah tipe kembali ke VARCHAR berisiko karena nilai integer mungkin tidak dapat dikonversi kembali ke string dengan bersih. Nilai 42 menjadi "42", tetapi bagaimana dengan nilai yang terpotong atau dibulatkan selama konversi? Anda telah kehilangan informasi. Roll-forward memungkinkan Anda menulis migrasi yang hati-hati yang menangani kasus-kasus tepi ini secara eksplisit.

Menulis Down Migration yang Benar-Benar Berfungsi

Jika Anda memilih untuk mendukung rollback, down migration Anda memerlukan perhatian sungguhan. Mereka tidak bisa menjadi pembalikan mekanis dari up migration. Setiap down migration harus memperhitungkan data yang ada pada saat rollback.

Berikut adalah contoh konkret. Anda menambahkan kolom nullable last_login_at ke tabel users, tetapi kode aplikasi memiliki bug. Down migration yang aman dan perbaikan roll-forward akan terlihat seperti ini:

-- Down migration yang aman: hapus kolom, tetapi hanya setelah memeriksa keamanannya
BEGIN;

-- Langkah 1: Verifikasi tidak ada kode aplikasi atau view yang bergantung pada kolom ini
-- (Pemeriksaan ini dilakukan di pipeline deployment, bukan di SQL)

-- Langkah 2: Hapus kolom
ALTER TABLE users DROP COLUMN IF EXISTS last_login_at;

COMMIT;

-- Migrasi roll-forward: tambahkan kolom dengan nama yang benar
BEGIN;

-- Tambahkan kolom dengan nama dan tipe yang dimaksud
ALTER TABLE users ADD COLUMN last_login_at TIMESTAMP;

-- Opsional backfill dari log aplikasi atau backup
-- UPDATE users SET last_login_at = ... WHERE id IN (...);

COMMIT;

Down migration aman karena kolomnya nullable dan aditif. Migrasi roll-forward memperbaiki masalah tanpa membalikkan perubahan skema.

Inilah yang membuat down migration berbahaya:

  • Ini mengasumsikan data dalam keadaan yang sama seperti saat up migration dijalankan
  • Ini mengabaikan baris yang ditambahkan atau dimodifikasi setelah up migration
  • Ini secara membabi buta membalikkan perubahan skema tanpa memeriksa integritas data

Down migration yang aman untuk menambahkan kolom NOT NULL dengan nilai default harus:

  1. Memverifikasi bahwa menghapus kolom tidak akan merusak query aplikasi
  2. Menangani baris apa pun yang dimasukkan setelah kolom ditambahkan
  3. Memastikan tidak ada relasi foreign key yang bergantung pada kolom tersebut

Untuk perubahan tipe data dari VARCHAR ke INTEGER, down migration perlu menangani nilai yang tidak memiliki representasi string yang bersih. Anda mungkin perlu melakukan cast integer kembali ke string, tetapi juga menangani NULL dan kasus tepi yang tidak dimiliki oleh nilai string asli.

Risiko Nyata yang Tidak Bisa Diabaikan

Rollback terdengar sederhana, tetapi membawa risiko serius yang baru diketahui tim setelah sesuatu berjalan salah.

Kehilangan data adalah risiko terbesar. Ketika migrasi menghapus kolom, data tersebut hilang. Tidak ada down migration yang bisa mengembalikannya kecuali Anda memiliki backup. Jika Anda tidak mengambil backup sebelum migrasi, rollback berarti menerima kehilangan data permanen.

Ketergantungan migrasi menciptakan jebakan tersembunyi. Jika migrasi kedua bergantung pada kolom yang ditambahkan oleh migrasi pertama, rollback ke sebelum migrasi pertama akan merusak segalanya. Aplikasi Anda mungkin crash karena mengharapkan kolom yang sudah tidak ada. Data Anda mungkin menjadi tidak konsisten karena baris mereferensikan nilai yang telah dihapus.

Roll-forward memiliki risikonya sendiri. Yang terbesar adalah waktu. Anda perlu menulis migrasi baru, melewati pipeline, dan men-deploy-nya. Selama waktu itu, aplikasi Anda berjalan dengan keadaan yang rusak. Pengguna mengalami error. Tim Anda berada di bawah tekanan untuk memperbaikinya dengan cepat, yang meningkatkan kemungkinan membuat kesalahan lain.

Roll-forward juga memerlukan pengetahuan yang akurat tentang status database saat ini. Anda tidak bisa menulis perbaikan berdasarkan asumsi. Anda perlu tahu persis seperti apa data saat ini, bukan seperti apa saat migrasi dirancang.

Membuat Keputusan Sebelum Anda Membutuhkannya

Waktu terburuk untuk memutuskan antara rollback dan roll-forward adalah ketika produksi sedang bermasalah. Pada saat itu, Anda stres, waktu terus berjalan, dan penilaian Anda terganggu.

Pendekatan yang lebih baik adalah mengklasifikasikan setiap migrasi sebelum dijalankan. Tetapkan kategori pemulihan untuk setiap migrasi:

  • Aman untuk di-rollback: Perubahan aditif seperti kolom baru, tabel, atau index
  • Memerlukan backup sebelum rollback: Perubahan yang memodifikasi data yang ada atau menghapus kolom nullable
  • Hanya roll-forward: Perubahan destruktif seperti menghapus kolom, mengubah tipe data, atau menggabungkan tabel

Dokumentasikan klasifikasi ini di file migrasi Anda atau di runbook deployment Anda. Ketika sesuatu berjalan salah, tim Anda membaca klasifikasi dan menjalankan strategi yang telah ditentukan. Tidak perlu debat. Tidak perlu ragu-ragu.

Daftar Periksa Keputusan Cepat

Sebelum Anda menjalankan migrasi apa pun di produksi, ajukan pertanyaan-pertanyaan ini:

Diagram pohon keputusan berikut dapat membantu Anda menerapkan daftar periksa di bawah tekanan:

flowchart TD A[Migrasi gagal] --> B{Perubahan aditif?} B -- Ya --> C[Pertimbangkan rollback] B -- Tidak --> D[Pilih roll-forward] C --> E{Risiko kehilangan data?} E -- Rendah --> F[Rollback aman] E -- Tinggi --> G[Roll-forward saja] D --> H{Sinkron kode-skema?} H -- Ya --> I[Tulis migrasi perbaikan] H -- Tidak --> J[Perbaiki kode dulu, lalu migrasi]
  • Apakah perubahannya aditif atau destruktif?
  • Dapatkah down migration mengembalikan status persis sebelumnya, termasuk data?
  • Apakah Anda memiliki backup terverifikasi yang diambil sebelum migrasi?
  • Apakah down migration sudah diuji di lingkungan staging?
  • Apakah migrasi ini bergantung pada migrasi lain yang berjalan sebelumnya?
  • Berapa biaya downtime saat Anda menulis perbaikan roll-forward?

Jika Anda tidak bisa menjawab semua ini, jangan jalankan migrasi di produksi dulu.

Intisari Konkret

Rollback dan roll-forward bukanlah strategi yang dapat dipertukarkan. Keduanya berlaku untuk tipe perubahan yang berbeda dan membawa risiko yang berbeda. Tim yang menangani insiden database dengan baik bukanlah mereka yang paling cepat mengetik SQL. Mereka adalah tim yang memikirkan pemulihan sebelum migrasi dijalankan. Mereka mengklasifikasikan perubahan mereka, menguji down migration mereka, dan memiliki backup yang siap. Ketika dashboard memerah, mereka tidak panik. Mereka menjalankan rencana yang sudah mereka buat.