Saat Data Lama Bertemu Skema Baru: Backfill dan Verifikasi Rekam Jejak Lama
Anda baru saja menambahkan kolom baru ke tabel database yang sudah berjalan di produksi selama tiga tahun. Aplikasi sekarang menulis ke struktur lama dan baru. Tapi bagaimana dengan jutaan baris yang dibuat sebelum perubahan ini? Baris-baris itu duduk dengan nilai NULL di kolom baru, menyimpan data yang secara teknis tidak lengkap.
Inilah momen di mana banyak tim menyadari bahwa migrasi skema bukan hanya tentang menambahkan kolom. Ini tentang memastikan setiap rekaman yang ada mengejar aturan baru. Proses mengisi data lama ke dalam struktur baru disebut backfill. Dan jika Anda melakukannya secara sembarangan, Anda bisa mengunci tabel produksi selama berjam-jam, memperlambat aplikasi, dan membuat frustrasi semua orang yang menunggu kueri selesai.
Mengapa Backfill Bukan Sekadar UPDATE Sederhana
Pendekatan naif adalah menjalankan satu kueri UPDATE besar yang mengisi kolom baru untuk setiap baris yang NULL. Jika tabel Anda memiliki beberapa ribu baris, ini mungkin selesai dalam hitungan detik. Tapi jika Anda berurusan dengan puluhan juta baris, satu kueri itu bisa menahan kunci tabel untuk waktu yang lama. Kueri lain mengantre. Aplikasi melambat. Pengguna mulai melihat timeout.
Backfill perlu direncanakan dengan perhatian yang sama seperti operasi produksi lainnya. Langkah pertama adalah mendefinisikan dengan tepat data apa yang perlu diisi. Misalkan Anda menambahkan kolom status yang nilainya berasal dari dua kolom yang ada: is_active dan deleted_at. Logika backfill Anda harus cocok dengan aturan yang sama yang digunakan kode aplikasi baru saat menulis rekaman baru. Jika aplikasi mengatur status menjadi 'active' ketika is_active benar dan deleted_at null, backfill Anda harus melakukan hal yang sama.
Pemrosesan Batch: Cara yang Aman
Alih-alih satu kueri besar, proses data dalam batch kecil. Ambil seribu atau sepuluh ribu baris yang masih memiliki nilai NULL di kolom baru, perbarui, lalu jeda sebentar sebelum pindah ke batch berikutnya. Ini memberi database waktu untuk bernapas dan membiarkan kueri lain berjalan tanpa menunggu.
Beberapa database memiliki dukungan bawaan untuk pembaruan batch. PostgreSQL, misalnya, memungkinkan Anda menggunakan UPDATE ... WHERE ... LIMIT dengan subkueri. MySQL tidak mendukung LIMIT langsung di UPDATE, tetapi Anda dapat menggunakan loop dalam prosedur tersimpan atau menangani batching dari skrip migrasi Anda. Kuncinya adalah menjaga setiap batch cukup kecil sehingga durasi kunci tetap dapat diterima untuk beban kerja Anda.
Berikut adalah pola kasar yang berfungsi di banyak database:
Diagram alir berikut mengilustrasikan pendekatan iteratif yang aman ini:
Sebagai contoh konkret, misalkan Anda menambahkan kolom status yang berasal dari is_active dan deleted_at. UPDATE batch di PostgreSQL mungkin terlihat seperti ini:
UPDATE your_table
SET status = CASE
WHEN is_active = true AND deleted_at IS NULL THEN 'active'
WHEN is_active = false AND deleted_at IS NULL THEN 'inactive'
ELSE 'deleted'
END
WHERE id IN (
SELECT id
FROM your_table
WHERE status IS NULL
LIMIT 1000
);
Subkueri dalam memilih batch kecil kunci utama di mana kolom baru masih NULL. UPDATE luar hanya menyentuh baris-baris itu, menjaga kunci tetap pendek. Ulangi ini dalam satu lingkaran sampai tidak ada NULL yang tersisa.
while ada baris dengan NULL di new_column:
pilih batch kunci utama di mana new_column IS NULL
perbarui baris-baris itu dengan nilai yang dihitung
tidur untuk interval pendek
Interval tidur bukan tentang menjadi lambat. Ini tentang memberi kesempatan bagi operasi lain untuk berjalan. Bahkan 100 milidetik antar batch dapat membuat perbedaan besar dalam bagaimana database berperilaku di bawah beban.
Dua Sumber Kebenaran Selama Backfill
Sementara backfill berjalan, aplikasi terus menulis data baru ke struktur lama dan baru. Ini berarti Anda sekarang memiliki dua sumber kebenaran untuk data logis yang sama:
- Data yang ditulis oleh aplikasi (selalu konsisten karena aplikasi menulis di kedua tempat sekaligus)
- Data yang diisi oleh proses backfill (perlu cocok dengan logika aplikasi dengan tepat)
Keduanya harus menghasilkan nilai yang sama untuk input yang sama. Jika logika backfill Anda memiliki bug, atau jika logika aplikasi berubah antara saat backfill dimulai dan selesai, Anda akan berakhir dengan data yang tidak konsisten. Inilah mengapa verifikasi tidak opsional.
Verifikasi: Lebih dari Sekadar Memeriksa NULL
Setelah backfill selesai, insting alami adalah menjalankan pemeriksaan cepat: "Apakah masih ada nilai NULL yang tersisa?" Itu memberi tahu Anda bahwa kolom sudah terisi, tetapi tidak memberi tahu Anda apakah nilainya benar.
Verifikasi yang tepat berarti membandingkan nilai yang dihitung dengan nilai yang diharapkan untuk setiap baris. Anda membaca kolom lama, menghitung apa yang seharusnya menjadi nilai baru, dan membandingkannya dengan apa yang sebenarnya disimpan di kolom baru. Setiap ketidakcocokan perlu diselidiki.
Ketidakcocokan bisa berasal dari beberapa sumber:
- Bug dalam logika backfill yang menghitung nilai berbeda dari aplikasi
- Data yang berubah antara saat backfill memprosesnya dan saat verifikasi berjalan
- Aplikasi menulis nilai yang berbeda karena logikanya diperbarui di tengah migrasi
- Kondisi balapan di mana sebuah baris diperbarui oleh aplikasi setelah backfill tetapi sebelum verifikasi
Setiap ketidakcocokan perlu dilacak kembali ke akar penyebabnya. Terkadang itu adalah perbaikan sederhana. Terkadang itu mengungkapkan masalah yang lebih dalam dalam rencana migrasi.
Verifikasi Progresif
Anda tidak perlu memverifikasi seluruh tabel dalam satu kali proses. Mulailah dengan sampel acak. Pilih seribu baris, bandingkan nilainya, dan lihat apakah semuanya cocok. Jika sampel lolos, lanjutkan ke sampel yang lebih besar. Jika itu lolos, jalankan verifikasi penuh.
Untuk tabel besar, verifikasi penuh tidak berarti membaca setiap baris satu per satu. Anda dapat menulis kueri yang menghitung ketidakcocokan tanpa mengambil semua data. Sesuatu seperti:
SELECT COUNT(*) FROM table
WHERE computed_value != actual_new_value
Jika hitungannya nol, Anda bersih. Jika tidak nol, Anda tahu berapa banyak baris yang perlu perhatian, dan Anda dapat menyelidikinya dalam batch.
Ketika Verifikasi Lolos
Setelah verifikasi mengonfirmasi bahwa setiap baris dalam struktur baru cocok dengan nilai yang diharapkan, Anda memiliki keyakinan bahwa data konsisten. Tapi migrasi belum selesai. Aplikasi masih menulis ke kedua struktur. Langkah selanjutnya adalah berhenti membaca dari skema lama dan sepenuhnya mengandalkan yang baru. Transisi itu adalah fase terpisah dengan risiko dan langkah verifikasinya sendiri.
Backfill dan verifikasi adalah fondasi yang membuat transisi itu aman. Tanpa mereka, Anda beralih ke skema baru dengan data yang tidak lengkap atau tidak benar. Dengan mereka, Anda tahu persis apa yang Anda miliki.
Daftar Periksa Praktis
- Tentukan logika yang tepat untuk menghitung nilai baru, cocokkan dengan kode aplikasi
- Pilih ukuran batch yang menjaga durasi kunci dapat diterima untuk database Anda
- Implementasikan batching dengan jeda antar batch
- Jalankan backfill dalam jendela pemeliharaan atau periode lalu lintas rendah
- Verifikasi dengan sampel acak sebelum verifikasi penuh
- Selidiki setiap ketidakcocokan sebelum melanjutkan
- Dokumentasikan logika backfill dan hasil verifikasi untuk referensi di masa mendatang
Intisari Konkret
Backfill bukanlah tugas teknis yang Anda buru-buru selesaikan. Ini adalah langkah di mana Anda membuktikan bahwa logika migrasi Anda bekerja pada data nyata dalam skala besar. Jika Anda melewatkan verifikasi, Anda mempercayai bahwa kode backfill Anda sempurna dan tidak ada data yang berubah selama proses. Kepercayaan itu jarang dibenarkan. Jalankan backfill dalam batch, verifikasi hasilnya secara sistematis, dan hanya pindah ke fase berikutnya ketika Anda memiliki bukti bahwa setiap rekaman benar.