Saat Migrasi Database Membutuhkan Pipeline Sendiri
Anda memiliki pipeline CI/CD yang solid untuk aplikasi Anda. Kode dibangun, pengujian berjalan, dan deployment terjadi secara otomatis. Kemudian seseorang membuka pull request yang menambahkan kolom baru ke tabel pengguna. Tiba-tiba, pipeline yang rapi terasa salah. Menjalankan migrasi database dalam alur yang sama dengan kode aplikasi berarti salah satu dari dua hal: menahan deployment hingga migrasi selesai, atau menjalankan migrasi secara terpisah dan berharap tidak ada yang rusak.
Masalahnya adalah perubahan database berperilaku berbeda dari kode aplikasi. Build yang gagal berarti tidak ada deployment baru. Migrasi yang gagal dapat merusak data, mengunci tabel, atau meninggalkan database dalam keadaan tidak konsisten. Anda membutuhkan pipeline yang dirancang khusus untuk risiko spesifik dari perubahan skema, backfill data, dan verifikasi.
Mengapa Pipeline Aplikasi Tidak Cukup
Pipeline aplikasi mengikuti pola yang sederhana: build, test, deploy. Jika pengujian gagal, pipeline berhenti. Jika deployment gagal, Anda perbaiki masalahnya dan deploy ulang. Rollback biasanya hanya dengan men-deploy versi sebelumnya.
Migrasi database mematahkan model ini. Migrasi mengubah struktur atau isi data Anda. Rollback migrasi tidak sama dengan mengembalikan perubahan kode. Anda perlu menjalankan skrip terpisah untuk membatalkan perubahan skema, dan skrip itu mungkin gagal jika data sudah berubah. Anda juga perlu menangani backfill catatan lama dengan nilai baru, dan memverifikasi bahwa data konsisten setelah migrasi.
Memaksakan perubahan database ke dalam pipeline yang sama dengan kode aplikasi menyebabkan kompromi. Entah Anda melewatkan pengujian otomatis untuk migrasi, atau menjalankannya secara manual selama jendela pemeliharaan, atau menerima risiko menjalankan skrip yang belum diuji di produksi.
Pipeline Terpisah untuk Perubahan Database
Solusinya adalah membuat pipeline khusus untuk perubahan database, sepenuhnya terpisah dari pipeline aplikasi Anda. Pipeline ini memiliki tahapannya sendiri, gerbang persetujuannya sendiri, dan monitoringnya sendiri. Pipeline ini memperlakukan perubahan database sebagai deployment kelas satu, bukan efek samping dari rilis aplikasi.
Berikut cara kerja tahapan-tahapan tersebut secara berurutan.
Cuplikan YAML berikut menunjukkan bagaimana Anda dapat mendefinisikan tahapan-tahapan ini dalam workflow GitHub Actions:
name: Database Migration Pipeline
on:
pull_request:
paths:
- 'migrations/**'
jobs:
dry-run:
runs-on: ubuntu-latest
steps:
- run: ./scripts/dry-run.sh
migration:
needs: dry-run
runs-on: ubuntu-latest
steps:
- run: ./scripts/migrate.sh
backfill:
needs: migration
runs-on: ubuntu-latest
steps:
- run: ./scripts/backfill.sh
reconciliation:
needs: backfill
runs-on: ubuntu-latest
steps:
- run: ./scripts/reconcile.sh
rollback-test:
needs: reconciliation
runs-on: ubuntu-latest
steps:
- run: ./scripts/rollback.sh
- run: ./scripts/reconcile.sh
Setiap job berjalan hanya jika job sebelumnya berhasil, mencerminkan alur pipeline yang dijelaskan di atas.
Diagram alur berikut menunjukkan lima tahapan dan perkembangannya:
Tahap 1: Dry-Run
Setiap kali skrip migrasi baru masuk ke repositori Anda, pipeline menjalankan dry-run terhadap database staging. Skrip dieksekusi tetapi tidak benar-benar mengubah apa pun. Tujuannya adalah untuk menangkap kesalahan sintaks, dependensi yang hilang, atau masalah logika sebelum menyentuh data nyata.
Jika dry-run gagal, pipeline segera berhenti. Tim mendapat notifikasi, dan tidak ada tahap lebih lanjut yang dijalankan. Ini menangkap sebagian besar kesalahan umum sejak awal, saat biaya perbaikannya masih murah.
Tahap 2: Migration
Setelah dry-run berhasil, pipeline menjalankan migrasi yang sebenarnya terhadap database staging. Ini mengubah skema atau mentransformasi data, tetapi masih di lingkungan yang aman. Pipeline mencatat setiap langkah: waktu mulai, waktu selesai, jumlah baris yang terpengaruh, dan peringatan apa pun.
Log ini berfungsi sebagai jejak audit. Ketika sesuatu salah di kemudian hari, Anda dapat melacak dengan tepat apa yang terjadi selama migrasi. Anda juga memiliki catatan berapa lama setiap langkah berlangsung, yang membantu Anda memperkirakan waktu eksekusi di produksi.
Tahap 3: Backfill
Beberapa migrasi memerlukan pengisian data untuk catatan yang sudah ada. Misalnya, menambahkan kolom baru dengan nilai default mungkin perlu memperbarui jutaan baris yang sudah ada. Menjalankan ini sebagai satu pembaruan besar dapat mengunci tabel selama menit atau jam.
Pipeline menangani backfill dalam batch kecil, biasanya seribu baris per iterasi, dengan jeda singkat di antara batch. Ini menjaga database tetap responsif dan mengurangi risiko kunci yang berlangsung lama. Pipeline memonitor setiap batch untuk durasi dan tingkat kesalahan. Jika sebuah batch gagal, pipeline berhenti dan mengirimkan peringatan. Pipeline tidak otomatis mencoba ulang, karena kegagalan mungkin mengindikasikan masalah yang lebih dalam yang perlu diselidiki.
Tahap 4: Reconciliation
Setelah migrasi dan backfill selesai, pipeline menjalankan skrip rekonsiliasi. Ini membandingkan data sebelum dan sesudah migrasi. Perbandingan dapat memeriksa jumlah baris, checksum pada kolom tertentu, atau nilai agregat seperti total saldo di tabel transaksi.
Jika rekonsiliasi menemukan perbedaan yang tidak terduga, pipeline gagal. Tim harus menyelidiki sebelum melanjutkan. Tahap ini menangkap kerusakan data diam-diam, pembaruan parsial, atau kesalahan logika yang tidak menyebabkan crash tetapi tetap menghasilkan hasil yang salah.
Tahap 5: Rollback Test
Pipeline menjalankan skrip rollback untuk memverifikasi bahwa migrasi dapat dibatalkan dengan bersih. Setelah rollback, pipeline menjalankan rekonsiliasi lagi untuk memastikan data kembali ke keadaan semula.
Ini adalah tahap terpenting untuk membangun kepercayaan. Jika pengujian rollback berhasil di staging, Anda tahu Anda dapat dengan aman membatalkan migrasi di produksi jika terjadi kesalahan. Jika gagal, pipeline berhenti, dan migrasi tidak diizinkan untuk dilanjutkan ke produksi.
Menjalankan di Produksi
Setelah kelima tahap berhasil di staging, pipeline siap untuk produksi. Namun prosesnya tidak otomatis. Langkah persetujuan manual berada di antara staging dan produksi. Seseorang dengan pengetahuan database meninjau hasil dan menyetujui eksekusi produksi.
Di produksi, pipeline menjalankan urutan yang sama: dry-run, migration, backfill, reconciliation, dan rollback test. Perbedaannya adalah monitoring lebih ketat, dan pipeline dapat dihentikan di tengah tahap jika anomali muncul. Setiap tahap di produksi juga memiliki kemampuan rollback sendiri, sehingga Anda dapat membatalkan kapan saja tanpa meninggalkan database dalam keadaan rusak.
Daftar Periksa Praktis untuk Pipeline Database Anda
- Pisahkan pipeline database dari pipeline aplikasi
- Selalu jalankan dry-run sebelum migrasi sebenarnya
- Catat setiap langkah dengan stempel waktu dan jumlah baris
- Jalankan backfill dalam batch kecil dengan monitoring
- Tambahkan pemeriksaan rekonsiliasi setelah migrasi dan backfill
- Uji skrip rollback di staging sebelum produksi
- Wajibkan persetujuan manual untuk eksekusi produksi
- Pertahankan kemampuan untuk menghentikan pipeline di tengah tahap
Intisari
Migrasi database layak mendapatkan ketelitian yang sama seperti deployment aplikasi. Pipeline khusus dengan tahapan dry-run, backfill, rekonsiliasi, dan pengujian rollback memberi Anda keyakinan bahwa perubahan skema tidak akan secara diam-diam merusak data Anda. Tahapan tambahan ini menambah waktu pada setiap migrasi, tetapi menghemat lebih banyak waktu dengan mencegah insiden produksi dan pekerjaan pemulihan yang panik setelahnya. Perlakukan perubahan database Anda seperti deployment produksi, dan data Anda akan tetap konsisten di setiap pembaruan.