Feed delta Anda melewatkan commit yang terlambat: bangun kursor yang tepercaya
Satu baris tiba terlambat karena tanda air bukan batas commit. Telusuri kegagalannya, lalu tetapkan cakupan, batas, retensi, dan kelanjutan yang aman.
Inilah kegagalan yang saya lihat terjadi dua kali, di dua mesin berbeda. Sebuah ekspor berjalan pukul 10:00 dan mencatat stempel waktu modifikasi terakhir tertinggi yang dilihatnya. Satu transaksi yang mulai pukul 09:58 masih terbuka, commit pukul 10:03, dan membawa stempel waktu dari saat ia dimulai. Ekspor berikutnya hanya meminta baris yang lebih baru dari 10:00 dan tidak pernah melihatnya. Ukuran transfernya sangat baik dan datanya salah.
Telusuri ekspor yang kehilangan satu baris
Ikuti urutan peristiwa, bukan SQL-nya. Aplikasi menulis waktu modifikasi baris saat transaksi dimulai, bukan saat commit. Eksportir membaca tanda air jam dinding sementara transaksi itu masih terbuka. Lalu transaksi itu commit dengan waktu modifikasi yang lebih tua daripada tanda air yang tersimpan. Batch berikutnya dari konsumen dimulai setelah tanda air, jadi baris itu tertinggal di belakang kursor dan tidak akan pernah dipilih lagi.
Tidak ada yang mencatat galat, karena tidak ada yang gagal. Itulah yang membuat kelas bug ini mahal: feed delta cepat, jumlah baris tampak masuk akal, dan baris yang hilang muncul berminggu-minggu kemudian sebagai tiket rekonsiliasi yang tidak bisa direproduksi siapa pun.
Nomor urut memiliki bentuk cacat yang sama karena alasan berbeda. ID monoton memberi tahu urutan nilai dialokasikan, bukan urutan transaksi di-commit. Transaksi yang mengambil nomor rendah bisa commit setelah transaksi yang mengambil nomor tinggi, jadi kursor berbasis urutan alokasi juga bisa meninggalkan lubang.
Lima janji yang harus dipenuhi feed
Sebelum memilih transportasi, mintalah kontrak lima klausul dari sistem sumber. Jika ada klausul yang hilang, Anda punya heuristik, bukan feed perubahan.
- Cakupan: sisipan, pembaruan, dan penghapusan, masing-masing dengan kunci stabil yang diperlukan untuk menerapkannya.
- Batas: apa arti nilai kursor dan apa isi batch yang konsisten.
- Konsistensi: bagaimana tabel terkait dibaca tanpa mencampur keadaan sumber yang tidak kompatibel.
- Retensi: berapa lama konsumen yang terputus dapat melanjutkan dari posisi tersimpannya.
- Inisialisasi ulang: snapshot penuh yang didukung saat posisi itu tidak lagi valid.
Mekanisme yang didukung dapat memenuhi kelimanya. Dokumentasi SQL Server tentang pelacakan perubahan adalah satu contoh terbit konkret: ia menetapkan versi valid minimum, mencatat penghapusan maupun perubahan, dan menjelaskan transaksi snapshot yang mencakup validasi, pengambilan versi, serta kueri perubahan terkait. Itu contoh apa yang harus dituntut dari sumber, bukan sintaksis yang bisa Anda tempel di mesin lain.
Rancang titik periksa sebagai bagian dari pipeline
Posisi tersimpan konsumen adalah mesin keadaan yang sebenarnya, jadi tuliskan sebagai demikian. Kursor hanya boleh maju melalui alur kerja yang sudah menerapkan batch dan sudah commit transaksi tujuan.
cursor = durable_checkpoint() # last fully applied position
batch = extract(cursor, upper_bound) # one consistent batch
apply(batch) # idempotent at the target
commit()
checkpoint(batch.upper_bound) # only now
Jika pengiriman gagal di tengah jalan, rekonsiliasi batch yang sama alih-alih mengarang posisi sumber baru. Ekstraksi ulang dengan batas atas yang lebih baru adalah cara penerapan sebagian menjadi celah permanen. Aturan cermin juga berlaku di sisi lain: penerapan idempoten di tujuan mencegah duplikat dan tidak melakukan apa pun untuk perubahan yang tidak pernah dilihat ekstraksi.
Penghapusan butuh catatan, bukan kesimpulan
Membandingkan baris yang masih ada tidak dapat memberi tahu konsumen kunci mana yang hilang. Sumber yang tidak pernah mencatat penghapusan meninggalkan Anda dengan dua opsi tidak sempurna: menyimpan riwayat kunci cukup lama untuk di-diff, atau menjalankan rekonsiliasi penuh eksplisit sesuai jadwal. Keduanya keputusan desain dengan biaya, dan tidak ada yang terwujud kecuali seseorang memilikinya.
Retensi adalah keputusan jenis yang sama. Setiap feed perubahan yang didukung punya jendela; nyatakan itu dalam kontrak dan beri peringatan saat titik periksa konsumen mendekatinya. Konsumen yang terbangun setelah gangguan dua minggu seharusnya membaca galat jelas "perlu inisialisasi ulang" alih-alih diam-diam menerima batch sebagian.
Lalu ukur apakah delta itu sepadan
Kembali membandingkan setiap kunci dan hash kadang benar, dan perlu diketahui biayanya. Anda masih mentransfer informasi tentang setiap baris, hash tidak memberi Anda snapshot konsisten lintas tabel, dan skema hash membawa pertanyaannya sendiri tentang representasi dan tabrakan. Lebih sedikit byte dengan batas yang dapat diverifikasi adalah tujuannya; lebih sedikit byte yang diam-diam menjatuhkan perubahan yang sudah di-commit sama sekali bukan delta.
Kolom modifikasi terakhir adalah petunjuk. Kursor yang bertahan dari commit terlambat, penghapusan, dan gangguan panjang adalah kontrak.