Menjalankan Migrasi Database di Production Tanpa Khawatir
Pipeline deployment sudah hijau. Perubahan kode sudah direview dan disetujui. Lingkungan staging terlihat baik-baik saja. Lalu tibalah momen yang paling mendebarkan bagi setiap engineer: menjalankan migrasi terhadap database production.
Di sinilah risiko sesungguhnya berada. Perubahan skema yang berjalan sempurna di laptop Anda bisa mengunci tabel production selama beberapa menit. Migrasi data yang berjalan lancar dengan seribu baris bisa memakan waktu berjam-jam jika dihadapkan pada satu juta baris. Dan ketika sesuatu berjalan salah, aplikasi mulai mengembalikan error ke pengguna nyata.
Masalahnya tidak hanya teknis. Ini juga tentang waktu, koordinasi, dan mengetahui kapan harus berhenti.
Kapan Menjalankan Migrasi
Tidak seperti deployment aplikasi yang sering bisa dilakukan kapan saja, migrasi database memiliki konsekuensi langsung terhadap performa query dan ketersediaan data. Waktu teraman untuk menjalankannya adalah saat traffic sedang rendah. Banyak tim menyebutnya downtime window, tapi nama itu menyesatkan. Downtime window tidak berarti aplikasi harus offline. Artinya Anda telah menyepakati periode ketika Anda bisa melakukan perubahan yang mungkin menyebabkan gangguan, dan Anda telah siap menghadapi kemungkinan tersebut.
Pertanyaan sebenarnya adalah: dapatkah migrasi Anda berjalan sementara aplikasi masih melayani traffic? Jika Anda mendesain migrasi dengan hati-hati, jawabannya sering kali ya. Tapi itu tergantung pada pemahaman Anda tentang apa yang dilakukan database saat menjalankan perintah tertentu.
Masalah Locking
Sumber masalah paling umum selama migrasi production adalah locking. Saat Anda menjalankan perintah seperti ALTER TABLE, sebagian besar database akan mengunci tabel untuk mencegah perubahan lain selama operasi berlangsung. Jika perintah itu memakan waktu lama, setiap query dari aplikasi yang menyentuh tabel tersebut akan menunggu atau gagal. Pengguna mulai melihat halaman lambat atau pesan error.
Beberapa database menawarkan cara untuk mengurangi locking. PostgreSQL mendukung CREATE INDEX CONCURRENTLY, yang membangun indeks tanpa memblokir operasi tulis. MySQL memiliki opsi serupa untuk operasi tertentu. Tapi tidak semua perubahan bisa dilakukan tanpa lock. Menambahkan kolom dengan nilai default, mengubah tipe kolom, atau menghapus kolom seringkali memerlukan exclusive lock.
Kuncinya adalah mengetahui apa yang didukung database Anda sebelum menulis migrasi. Periksa dokumentasi untuk versi database Anda. Uji migrasi pada salinan data production Anda, bukan hanya sampel kecil. Ukur berapa lama lock akan bertahan dalam kondisi realistis.
Sebagai contoh, berikut adalah pola aman untuk menambahkan kolom dengan nilai default di PostgreSQL, menghindari exclusive lock yang lama:
-- Langkah 1: Tambahkan kolom sebagai nullable (cepat, tanpa rewrite default)
ALTER TABLE users ADD COLUMN display_name text;
-- Langkah 2: Backfill kolom dalam batch kecil
UPDATE users SET display_name = username WHERE display_name IS NULL LIMIT 1000;
-- Ulangi sampai tidak ada baris tersisa
-- Langkah 3: Set NOT NULL (cepat, tanpa rewrite data)
ALTER TABLE users ALTER COLUMN display_name SET NOT NULL;
Pecah Migrasi Besar Menjadi Langkah-Langkah Kecil
Kesalahan umum adalah mencoba melakukan terlalu banyak hal dalam satu migrasi. Misalnya, menambahkan kolom baru, mengisinya dengan data dari kolom lain, lalu menghapus kolom lama dalam satu langkah. Ini menciptakan operasi berdurasi panjang yang menahan lock selama seluruh proses.
Pendekatan yang lebih aman adalah memecah pekerjaan menjadi beberapa migrasi yang lebih kecil:
- Tambahkan kolom baru tanpa nilai default.
- Jalankan background job untuk mengisi kolom baru dalam batch.
- Verifikasi data sudah lengkap dan benar.
- Hapus kolom lama dalam migrasi terpisah.
Setiap langkah bisa diverifikasi sebelum melanjutkan ke langkah berikutnya. Jika ada yang salah di langkah kedua, Anda tidak kehilangan apa pun. Anda bisa memperbaiki data dan mencoba lagi. Pendekatan ini memakan waktu lebih lama, tapi risikonya jauh lebih kecil.
Bangun Safety Checks ke Dalam Pipeline
Pipeline Anda tidak boleh menjalankan migrasi secara buta. Pipeline perlu memeriksa status database saat ini sebelum melakukan perubahan. Sebelum migrasi dimulai, pipeline bisa memeriksa query yang berjalan lama. Jika ada query yang sudah berjalan lebih dari beberapa detik, migrasi harus menunggu. Query tersebut mungkin sedang menahan lock yang akan memblokir migrasi, atau migrasi bisa memblokir mereka.
Diagram alir berikut mengilustrasikan proses safety check yang dijelaskan di atas:
Selama migrasi, pipeline harus memonitor:
- Durasi eksekusi: berapa lama setiap statement berjalan
- Waktu tunggu lock: apakah query lain menunggu lock
- Tingkat error: error apa pun dari database atau aplikasi
Jika salah satu metrik ini melewati ambang batas, pipeline harus menghentikan migrasi dan memberi tahu tim. Ini bukan tentang menjadi terlalu hati-hati. Ini tentang memiliki mekanisme yang jelas untuk mencegah masalah kecil menjadi insiden production.
Apa yang Terjadi Setelah Migrasi Selesai
Migrasi selesai tanpa error. Pipeline menunjukkan hijau. Tapi pekerjaan belum selesai.
Aplikasi Anda mungkin masih memiliki koneksi database lama yang terbuka. Connection pool menyimpan cache koneksi, dan koneksi yang di-cache tersebut mungkin masih memegang referensi ke skema lama. Beberapa ORM menyimpan cache query plan yang tidak lagi cocok dengan skema baru. Masalah ini tidak selalu muncul segera. Mereka bisa menyebabkan error halus beberapa menit atau jam kemudian.
Banyak tim me-restart aplikasi setelah migrasi, atau setidaknya menguras koneksi lama dan membiarkan koneksi baru dibuat. Yang lain menunggu beberapa menit sambil memonitor log dan metrik sebelum mendeklarasikan migrasi berhasil. Pendekatan yang tepat tergantung pada stack aplikasi Anda dan cara kerja connection pooling.
Checklist Praktis untuk Migrasi Production
Sebelum menjalankan migrasi di production, lalui checklist ini:
- Uji migrasi pada salinan data production, bukan hanya database development
- Ukur berapa lama setiap statement berjalan dan lock apa yang diperolehnya
- Verifikasi versi database mendukung alternatif lock-free untuk operasi Anda
- Periksa query yang berjalan lama sebelum memulai migrasi
- Siapkan monitoring untuk waktu tunggu lock dan tingkat error selama migrasi
- Tentukan kondisi abort yang jelas: metrik atau error apa yang memicu penghentian
- Siapkan rencana rollback: cara mengembalikan migrasi jika ada yang salah
- Beri tahu tim sebelum dan sesudah migrasi berjalan
- Rencanakan untuk me-restart aplikasi atau menyegarkan koneksi setelah migrasi
Kesimpulan
Menjalankan migrasi database di production bukan tentang menghindari risiko. Ini tentang memahami risiko, memecahnya menjadi bagian-bagian yang bisa dikelola, dan memiliki sinyal yang jelas kapan harus berhenti. Migrasi terbaik adalah yang tidak diperhatikan siapa pun karena berjalan mulus. Yang kedua terbaik adalah yang dihentikan lebih awal sebelum menyebabkan kerusakan nyata. Yang terburuk adalah yang berjalan sampai selesai tapi merusak aplikasi dengan cara yang butuh waktu berjam-jam untuk terdeteksi. Bangun pipeline Anda untuk mengincar kategori pertama, tapi selalu siap untuk kategori kedua.