Schema Migrations di Production: Zero-Downtime Deployments
Daftar Isi
- Pendahuluan
- Kenapa Migration di Production Berisiko?
- Pola Expand and Contract
- Alur Zero-Downtime Migration yang Praktis
- Contoh: Rename Column dengan Aman
- Backfill Tanpa Membuat Sistem Down
- Rollback Plan
- Kesalahan Umum
- Checklist
- FAQ
- Kesimpulan
Pendahuluan
Schema migration terlihat sederhana di local development. Kamu menambah column, rename field, mengubah type, menjalankan migration, lalu selesai. Di production, situasinya berbeda. Database sedang melayani traffic asli, versi aplikasi lama mungkin masih berjalan, background job masih menulis data, dan table besar bisa berisi jutaan row.
Migration yang aman bukan cuma soal mengubah schema. Yang lebih penting adalah mengubah schema sambil aplikasi tetap berjalan.
Zero-downtime migration bukan berarti tidak ada risiko sama sekali. Maksudnya, perubahan dirancang agar read dan write tetap bisa berjalan saat sistem berpindah dari bentuk lama ke bentuk baru.
Kenapa Migration di Production Berisiko?
Banyak migration gagal karena bentuk database dan ekspektasi aplikasi tidak sinkron.
Misalnya, deployment menghapus column ketika instance aplikasi lama masih membacanya. Atau migration me-rename column sebelum semua writer diperbarui. Kadang SQL-nya benar, tetapi query mengunci table besar terlalu lama sampai request ikut tertahan.
Risiko yang sering muncul:
- table lock yang terlalu lama
- versi aplikasi lama dan baru berjalan bersamaan
- background job masih memakai asumsi lama
- backfill besar membuat database melambat
- index belum ada untuk query path baru
- rollback plan hanya memikirkan code, bukan data
Intinya: migration production harus kompatibel dengan beberapa versi aplikasi.
Pola Expand and Contract
Pola paling aman untuk banyak perubahan schema adalah expand and contract.
Expand berarti kamu menambahkan bentuk schema baru tanpa langsung menghapus bentuk lama. Aplikasi bisa membaca dan menulis ke dua bentuk selama masa transisi.
Contract berarti kamu menghapus bentuk lama hanya setelah sistem tidak lagi bergantung padanya.
| Fase | Yang dilakukan | Contoh |
|---|---|---|
| Expand | Tambahkan struktur baru | Tambah display_name, tetap simpan name |
| Migrate | Pindahkan atau duplikasi data | Backfill display_name dari name |
| Switch | Ubah read dan write | Aplikasi membaca display_name |
| Contract | Hapus struktur lama | Drop name setelah terverifikasi |
Pola ini memang terasa lebih lambat daripada satu migration besar. Tapi setiap langkah bisa dicek, sehingga risikonya jauh lebih terkendali.
Alur Zero-Downtime Migration yang Praktis
Migration production yang aman biasanya dilakukan lewat beberapa langkah kecil.
- Tambahkan schema baru dengan cara yang backward-compatible.
- Deploy code yang bisa menulis ke field lama dan baru.
- Backfill data lama dalam batch kecil.
- Verifikasi count, nilai null, dan perilaku aplikasi.
- Pindahkan read ke field baru lewat feature flag atau konfigurasi.
- Berhenti menulis ke field lama.
- Hapus schema lama setelah sistem stabil.
Bagian pentingnya: setiap deployment tetap aman meskipun service atau worker lain masih memakai versi sebelumnya.
Contoh: Rename Column dengan Aman
Rename column secara langsung bisa berbahaya karena code lama mungkin masih memakai nama lama.
Tidak aman:
ALTER TABLE users RENAME COLUMN name TO display_name;
Pendekatan yang lebih aman:
ALTER TABLE users ADD COLUMN display_name text;
Lalu lakukan backfill:
UPDATE users
SET display_name = name
WHERE display_name IS NULL;
Selama masa transisi, aplikasi bisa menulis ke dua column:
| Aksi | Column lama name | Column baru display_name |
|---|---|---|
| User update profile | write | write |
| Versi aplikasi lama membaca | read | ignore |
| Versi aplikasi baru membaca | fallback | read |
| Setelah cutover | stop writing | read/write |
Setelah yakin aman, baru hapus column lama:
ALTER TABLE users DROP COLUMN name;
Langkah terakhir ini sebaiknya dilakukan di deployment terpisah, bukan bersamaan dengan perubahan yang memperkenalkan field baru.
Backfill Tanpa Membuat Sistem Down
Backfill sering menjadi bagian paling berisiko. Meng-update jutaan row dalam satu transaction bisa membuat lock, replication lag, CPU tinggi, dan alert production berisik.
Lebih aman memakai batch kecil:
UPDATE users
SET display_name = name
WHERE display_name IS NULL
AND id > 10000
AND id <= 20000;
Lalu ulangi untuk range berikutnya.
Contoh progress:
| Batch | Range ID | Row ter-update | Durasi |
|---|---|---|---|
| 1 | 1-10.000 | 9.842 | 1,2s |
| 2 | 10.001-20.000 | 9.911 | 1,3s |
| 3 | 20.001-30.000 | 9.877 | 1,2s |
Jaga ukuran batch supaya traffic normal tetap sehat. Kalau perlu, beri jeda antar batch.
Rollback Plan
Rollback bukan cuma “deploy ulang versi sebelumnya”. Perubahan database sering lebih sulit dibalik daripada perubahan aplikasi.
Sebelum migration, tanyakan:
- Apakah code lama masih bisa berjalan setelah migration ini?
- Jika code baru bermasalah, apakah ia bisa mengabaikan column baru?
- Apakah data sedang disalin, diubah, atau dihapus?
- Apakah backfill yang gagal bisa dilanjutkan?
- Metric apa yang menjadi sinyal untuk berhenti?
Untuk perubahan yang berisiko, rollback yang baik sering berupa menonaktifkan penggunaan path baru, bukan langsung membalik semua perubahan schema.
Kesalahan Umum
Menggabungkan schema dan code change dalam satu langkah
Jika schema dan code harus berubah persis di waktu yang sama, deployment menjadi rapuh. Pecah perubahan menjadi langkah yang kompatibel.
Menghapus column lama terlalu cepat
Column lama sebaiknya tetap ada sampai kamu yakin tidak ada aplikasi, worker, report, atau script yang masih memakainya.
Melupakan background job
Job sering memakai code path lama atau memproses data yang tertunda. Masukkan job ke dalam migration plan.
Backfill terlalu agresif
Backfill cepat yang membuat production melambat bukan keberhasilan. Lebih baik stabil dan bertahap daripada cepat tapi berisik.
Tidak menyiapkan observability
Pantau database load, query latency, lock waits, replication lag, error rate, dan perilaku aplikasi selama migration.
Checklist
- Buat schema change pertama tetap backward-compatible.
- Hindari destructive change di deployment yang sama dengan code baru.
- Gunakan expand and contract untuk rename, split, dan type change.
- Backfill dalam batch kecil.
- Buat backfill bisa dilanjutkan jika gagal.
- Verifikasi data dengan count dan spot check.
- Masukkan worker dan scheduled job ke rencana.
- Tentukan rollback behavior sebelum migration berjalan.
- Pantau lock, latency, dan replication lag.
- Drop schema lama hanya setelah path baru stabil.
FAQ
Apakah semua migration perlu expand and contract?
Tidak. Perubahan kecil yang additive, seperti menambah nullable column, sering cukup aman. Gunakan expand and contract saat code lama dan baru bisa berbeda asumsi terhadap schema.
Apakah menambah index aman di production?
Tergantung database dan command-nya. Di PostgreSQL, CREATE INDEX CONCURRENTLY sering lebih aman untuk table besar karena tidak memblokir write, tetapi tetap punya aturan dan risiko sendiri.
Apakah migration sebaiknya otomatis saat deploy?
Untuk migration additive yang sederhana, automation bisa berjalan baik. Untuk migration berisiko, banyak tim memilih langkah manual yang terkontrol dengan monitoring dan stop condition yang jelas.
Bagaimana tahu column sudah aman dihapus?
Cek application code, background job, report, dashboard, script manual, log, dan query monitoring. Kalau memungkinkan, simpan column lama selama satu release cycle sebelum dihapus.
Kesimpulan
Production migration adalah pekerjaan engineering, bukan sekadar file SQL. Tujuannya adalah menggerakkan database dan aplikasi bersama-sama tanpa memaksa perubahan besar dalam satu waktu.
Gunakan langkah yang backward-compatible, lakukan perubahan data besar secara bertahap, verifikasi tiap fase, dan tunda cleanup yang destructive sampai sistem terbukti tidak lagi membutuhkan bentuk lama.
Artikel Terkait
Lanjutkan membaca topik yang masih satu konteks.
PostgreSQL Advanced: Memahami Window Functions dan CTEs
Pahami PostgreSQL window functions dan CTEs lewat contoh SQL untuk ranking, running total, query yang lebih rapi, dan analytics yang mudah dibaca.
NoSQL vs Relational: Memilih Database yang Tepat untuk Proyek Kamu
Kerangka praktis untuk memilih database relational atau NoSQL berdasarkan bentuk data, query, konsistensi, dan kebutuhan operasional.
Panduan GraphQL API: Schema, Query, dan Mutation
Pelajari dasar GraphQL API lewat schema, query, mutation, resolver, error handling, dan pola praktis untuk data aplikasi.
Database Indexing Strategy: B-Trees, Hash Indexes, dan Lainnya
Pelajari cara memilih, memvalidasi, dan merawat database index tanpa memperlambat write atau menebak-nebak performa.