Backfill Data Lama Tanpa Mengganggu Database Produksi
Anda baru saja menerapkan migrasi baru yang menambahkan kolom last_login_at ke tabel user. Perubahan skema berjalan mulus. Tapi sekarang Anda melihat data: setiap pengguna yang sudah ada memiliki nilai null di kolom tersebut. Riwayat login mereka dari minggu lalu, bulan lalu, tahun lalu — semuanya tidak terlihat oleh field baru ini.
Inilah saatnya Anda membutuhkan backfill.
Apa Sebenarnya Arti Backfill
Backfill adalah proses mengisi data yang sudah ada sebelum migrasi diterapkan. Ini bukan tentang memindahkan data ke struktur baru — itu tugas skrip migrasi. Backfill adalah tentang memperbarui data lama agar sesuai dengan aturan baru yang kini diikuti sistem Anda.
Situasi di mana backfill menjadi diperlukan sangat umum:
- Anda menambahkan kolom baru, tetapi baris yang sudah ada tidak memiliki nilai untuk kolom tersebut.
- Anda mengubah cara penyimpanan alamat, dari satu kolom teks menjadi kolom jalan, kota, dan kode pos yang terpisah.
- Anda memperkenalkan kalkulasi baru, seperti skor risiko untuk transaksi, tetapi transaksi masa lalu belum pernah diberi skor.
Dalam setiap kasus, datanya ada di sana, valid tetapi tidak lengkap. Sistem tahu apa yang harus dilakukan dengan data baru yang masuk, tetapi data lama terjebak dalam format sebelumnya.
Mengapa Anda Tidak Bisa Memproses Semuanya Sekaligus
Pendekatan naif adalah menjalankan satu query yang memperbarui semua baris sekaligus. Untuk tabel kecil dengan beberapa ratus baris, itu berfungsi dengan baik. Untuk tabel dengan jutaan baris, itu adalah bencana yang menunggu untuk terjadi.
Satu pembaruan masif mengunci baris, menghabiskan log transaksi, dan memperlambat semua query lain yang mengenai tabel yang sama. Jika aplikasi Anda melayani pengguna selama backfill, pengguna tersebut akan mengalami timeout, respons lambat, atau permintaan gagal. Database bahkan bisa kehabisan memori atau ruang disk saat mencoba menangani operasi tersebut.
Solusinya adalah memproses data dalam potongan kecil yang terkontrol.
Batch Processing: Teknik Inti
Alih-alih memperbarui satu juta baris dalam satu kali, Anda memperbarui sepuluh ribu baris setiap kali, berhenti sejenak, lalu memproses batch berikutnya. Ini disebut batch processing, dan ini adalah fondasi backfill yang aman.
Berikut cara kerjanya dalam praktik:
Diagram alir berikut mengilustrasikan loop backfill lengkap, termasuk pemeriksaan idempotensi dan throttling:
-- Memproses satu batch baris yang masih perlu di-backfill
UPDATE users
SET last_login_at = (
SELECT MAX(login_time)
FROM login_history
WHERE login_history.user_id = users.id
)
WHERE last_login_at IS NULL
LIMIT 10000;
Setelah ini berjalan, Anda periksa berapa banyak baris yang terpengaruh. Jika sesuai dengan ukuran batch, Anda tunggu beberapa detik dan jalankan lagi. Jika mengembalikan lebih sedikit baris, backfill hampir selesai.
Memilih Ukuran Batch yang Tepat
Tidak ada ukuran batch universal yang cocok untuk semua database. Ukuran yang tepat tergantung pada:
- Seberapa kuat server database Anda.
- Seberapa besar beban yang diberikan aplikasi pada database.
- Seberapa kompleks logika pembaruannya.
- Seberapa banyak ruang log transaksi yang tersedia.
Mulailah dengan ukuran konservatif, misalnya 5.000 baris. Jalankan beberapa batch dan pantau metrik database: penggunaan CPU, I/O disk, latensi query dari sisi aplikasi. Jika database menanganinya dengan mudah, gandakan ukuran batch. Jika Anda melihat lonjakan latensi atau kontensi kunci, potong ukuran menjadi setengahnya.
Tujuannya adalah menemukan ukuran batch yang selesai dalam beberapa detik tanpa menimbulkan dampak yang terlihat pada query lain. Batch yang memakan waktu tiga puluh detik mungkin terlalu besar untuk sistem produksi dengan beban normal.
Throttling: Memberi Ruang Bernapas pada Database
Ukuran batch mengontrol seberapa banyak pekerjaan yang terjadi dalam satu unit. Throttling mengontrol berapa banyak waktu yang berlalu antar unit.
Setelah setiap batch selesai, tambahkan jeda yang disengaja sebelum memulai batch berikutnya. Jeda ini memungkinkan database menyelesaikan penulisan yang tertunda, melepaskan kunci, dan melayani query lain tanpa persaingan dari backfill Anda.
Throttle tipikal mungkin dua hingga lima detik antar batch. Selama jam sibuk, Anda mungkin meningkatkannya menjadi sepuluh atau lima belas detik. Selama window pemeliharaan, Anda mungkin menguranginya menjadi satu detik atau menghilangkannya sama sekali.
Throttle adalah katup pengaman Anda. Jika ada yang salah — lonjakan lalu lintas aplikasi mendadak, query lambat dari tim lain, peringatan replikasi lag — Anda dapat meningkatkan jeda dan membiarkan sistem stabil sebelum melanjutkan.
Membuat Backfill Idempoten
Skrip backfill harus aman dijalankan beberapa kali. Jika sebuah batch gagal di tengah jalan, atau jika Anda perlu memulai ulang seluruh proses, menjalankan skrip yang sama lagi seharusnya tidak menghasilkan data duplikat atau error.
Idempotensi untuk backfill biasanya berarti salah satu dari dua hal:
- Periksa sebelum tulis: Hanya perbarui baris yang masih memiliki nilai null atau nilai lama.
- Gunakan logika upsert: Sisipkan atau perbarui berdasarkan apakah baris sudah memiliki data baru.
Untuk contoh last_login_at, query di atas sudah idempoten karena hanya menargetkan baris di mana kolom tersebut masih null. Jika sebuah batch gagal setelah memperbarui 5.000 baris, eksekusi berikutnya akan melewati baris-baris tersebut dan melanjutkan dengan sisanya.
Untuk backfill yang lebih kompleks, seperti menghitung ulang nilai turunan, Anda dapat menambahkan kolom timestamp processed_at. Skrip backfill memeriksa apakah processed_at null sebelum memproses setiap baris. Setelah diproses, timestamp diatur, dan eksekusi berikutnya melewati baris tersebut.
Logging: Detail yang Tidak Terpikirkan Siapa Pun Sampai Rusak
Ketika backfill berjalan selama berjam-jam, Anda perlu tahu di mana posisinya dan apakah masih berfungsi dengan benar. Catat setiap batch:
- Nomor batch dan rentang waktu.
- Jumlah baris yang diproses.
- Durasi batch.
- Setiap error yang ditemui.
- Kemajuan saat ini sebagai persentase atau jumlah baris.
Log ini memiliki dua tujuan. Pertama, jika backfill berhenti secara tidak terduga, Anda dapat melanjutkan dari batch terakhir yang selesai, bukan memulai dari awal. Kedua, ketika backfill selesai, Anda memiliki catatan tentang apa yang sebenarnya terjadi, yang membantu debugging dan audit.
Entri log sederhana mungkin terlihat seperti ini:
2025-03-15 14:32:01 | Batch 47 | Diproses 10.000 baris | Durasi 3,2d | Tidak ada error
2025-03-15 14:32:06 | Batch 48 | Diproses 10.000 baris | Durasi 3,1d | Tidak ada error
2025-03-15 14:32:11 | Batch 49 | Diproses 10.000 baris | Durasi 3,5d | Tidak ada error
Daftar Periksa Backfill Praktis
Sebelum Anda menjalankan backfill di produksi, periksa daftar ini:
- Ukuran batch telah diuji di lingkungan staging dengan volume data serupa.
- Interval throttle dikonfigurasi dan dapat disesuaikan tanpa perubahan kode.
- Skrip idempoten — menjalankannya dua kali menghasilkan hasil yang sama.
- Logging menangkap kemajuan batch, error, dan waktu.
- Rencana rollback ada: Anda dapat membalikkan backfill jika ada yang salah.
- Monitoring tersedia untuk mendeteksi degradasi kinerja database.
- Dry run telah dijalankan pada salinan data produksi.
Kesimpulan
Backfill bukanlah skrip sekali pakai yang Anda tulis dan lupakan. Ini adalah operasi terkontrol yang menghormati fakta bahwa database Anda melayani pengguna saat Anda mengubah data mereka. Batch processing dan throttling bukanlah optimasi — mereka adalah persyaratan minimum untuk melakukan pekerjaan ini dengan aman. Tanpa keduanya, Anda hanya berjarak satu query besar dari insiden produksi.