Prompt dan konteks
Sebuah fact table Apache Iceberg menyimpan 18 bulan data pesanan saat batch job Spark, stream Flink, dan kueri Trino berjalan secara bersamaan. Tim ingin membagi customer_name menjadi first_name dan last_name serta mengubah partisi dari harian menjadi bulanan. File historis tidak dapat ditulis ulang seluruhnya, kueri lama harus tetap berfungsi, penulisan streaming tidak boleh berhenti, dan peluncuran yang gagal harus dapat di-rollback.
Wawancara ini menguji apakah kandidat memisahkan antara kompatibilitas skema, identitas field, tata letak partisi, snapshot commit, serta upgrade reader/writer. Iceberg mendokumentasikan field ID yang stabil serta evolusi skema dan partisi yang independen; jawaban yang kuat mengubah jaminan tersebut menjadi migrasi bertahap, bukan sekadar mengatakan "ubah tabel lalu lakukan backfill."
Apa yang dinilai oleh pewawancara
Mencari pemahaman terkait perbedaan antara nama dan identitas field, antara evolusi skema dan backfill historis, serta antara spesifikasi partisi baru dan pemindahan fisik file lama. Kandidat harus menjelaskan atomisitas snapshot, kompatibilitas engine, dan bukti rollback, termasuk apa yang terjadi ketika sebuah writer mengalami race condition dengan migrasi.
Pertanyaan klarifikasi
- Versi Spark, Flink, Trino, katalog, dan Iceberg mana yang membaca dan menulis tabel tersebut?
- Apakah
customer_namedapat di-parse dengan andal di berbagai bahasa, nama tunggal (mononim), dan batasan privasi? - Apakah klien lama bergantung pada posisi,
SELECT *, atau skema yang diserialisasi? - Bisakah reader mencampur spesifikasi partisi dan tetap menerapkan date predicate pushdown?
- Bagaimana koordinasi antara streaming checkpoint dan versi skema?
- Apakah katalog mendukung commit atomik, retensi snapshot, dan rollback berdasarkan ID?
Jawaban 30 detik
"Saya akan menginventarisasi kompatibilitas engine dan penggunaan kolom, menambahkan kolom baru menggunakan field ID Iceberg, dan mempertahankan kolom lama selama masa observasi. Evolusi partisi dilakukan secara terpisah: file baru menggunakan transformasi bulanan sementara file lama tetap dapat dibaca. Saya akan meluncurkan reader yang kompatibel, lalu writer, kemudian consumer; memvalidasi kueri, checkpoint, dan commit bersamaan, serta mempertahankan snapshot sebelumnya untuk rollback. Backfill historis adalah pekerjaan berversi, bukan perubahan skema implisit."
Solusi langkah demi langkah
Langkah 1: Tentukan matriks kompatibilitas dan kontrak perubahan
Catat skema, field ID, spesifikasi partisi saat ini, retensi snapshot, versi writer, dan versi reader. Buat kontrak terpisah untuk semantik kolom dan tata letak partisi agar penguraian (parsing), penggantian nama, dan penulisan ulang fisik tidak menjadi satu commit tunggal yang tidak jelas.
Perlakukan pemisahan sebagai tindakan aditif terlebih dahulu: tambahkan first_name dan last_name, pertahankan customer_name, serta tentukan aturan untuk spasi, mononim, nama multibahasa, dan kegagalan penguraian. Jangan secara diam-diam memberikan arti baru pada kolom lama. Rencanakan penghapusan hanya setelah setiap consumer selesai bermigrasi.
Langkah 2: Gunakan field ID, bukan posisi kolom
Iceberg mengikat field berdasarkan ID yang stabil. Catatan perubahan dapat terlihat seperti ini:
old: id=7 customer_name:string
new: id=21 first_name:string, id=22 last_name:string
old id=7 remains until consumers migrateJangan pernah menggunakan kembali ID 7 untuk arti yang berbeda setelah dihapus. Periksa juga child ID dari struct, map, dan list bersarang. CI harus membandingkan peta ID sebelum dan sesudah perubahan; penggantian nama saja bukan bukti evolusi yang aman.
Langkah 3: Pisahkan backfill dari penulisan online
Biarkan writer batch dan streaming mengisi kolom baru terlebih dahulu serta memublikasikan metrik kualitas penguraian. Lakukan backfill file historis per partisi di kemudian waktu. Baca snapshot atau watermark tetap, dan catat input snapshot, versi kode, jumlah baris, kegagalan penguraian, serta checksum dalam sebuah manifest. Jika commit Iceberg yang bersamaan mengalami konflik, baca ulang snapshot terbaru dan coba lagi; jangan pernah menimpa commit milik writer lain.
Jika nama historis tidak dapat diuraikan dengan andal, simpan null ditambah name_parse_status daripada mengarang data identitas. Backfill adalah komputasi berversi terpisah, bukan persyaratan dari perintah skema itu sendiri.
Langkah 4: Kembangkan spesifikasi partisi secara independen
Tambahkan spesifikasi partisi menggunakan transformasi bulan untuk data baru sementara file lama tetap menggunakan spesifikasi hari. Planner harus mengidentifikasi spesifikasi setiap file dan melakukan pruning dengan benar.
spec-0: day(ts) -> existing files
spec-1: month(ts) -> new filesUji volume pemindaian, pruning, dan perilaku file-file kecil pada tabel bayangan (shadow table). Consumer harus membaca metadata melalui katalog daripada menyusun path penyimpanan objek dari nama direktori.
Langkah 5: Tahapkan rilis reader dan writer
Terapkan reader yang mentoleransi kedua kolom terlebih dahulu, lalu writer yang mengisi kolom baru, dan baru kemudian consumer yang memerlukan field baru tersebut. Validasi pemulihan streaming checkpoint terhadap kedua snapshot. Catat dukungan spesifik setiap engine untuk penggantian nama, tipe bersarang, promosi tipe, transformasi, dan spesifikasi campuran; gunakan view atau upgrade engine jika dukungan belum tersedia.
Jadikan setiap perubahan sebagai snapshot kecil dan catat writer, katalog, peta ID, serta spesifikasi partisi. Hindari menggabungkan penghapusan kolom, perubahan tipe, dan penulisan ulang besar-besaran dalam jendela observasi yang sama.
Langkah 6: Terapkan gate pada commit dan pertahankan kemampuan rollback
Jalankan pemeriksaan struktural pada ID, tipe, dan spesifikasi; pemeriksaan data pada jumlah baris, rasio null, kegagalan penguraian, agregat, dan irisan tanggal; serta pemeriksaan perilaku pada SQL lama/baru, pemulihan checkpoint, konflik commit, dan pruning. Simpan sampel yang tidak terurai untuk ditinjau.
Simpan previous_snapshot_id dan new_snapshot_id sebelum rilis. Jika sebuah gate gagal, arahkan kembali katalog ke snapshot lama, pertahankan file baru beserta buktinya, jeda consumer yang memerlukan kolom baru, dan jalankan kembali partisi yang terpengaruh dari input tetap.
Contoh jawaban
"Pertama, saya akan menginventarisasi dukungan Spark, Flink, Trino, dan katalog untuk field ID, lalu menambahkan dua kolom baru sambil mempertahankan customer_name. Parser deterministik dan name_parse_status akan mengukur kualitas; backfill historis menggunakan snapshot tetap, versi kode, dan sebuah manifest. File baru menggunakan month(ts) sementara file lama menggunakan day(ts); metadata tabel menangani keduanya."
"Saya akan meng-upgrade reader yang kompatibel, lalu writer, kemudian consumer. Sebelum rilis, saya akan memvalidasi ID, nilai null, agregat, kueri lama dan baru, pemulihan stream, serta commit yang bersamaan. Saya akan mempertahankan snapshot sebelumnya dan me-rollback penunjuk katalog jika ada hard gate yang gagal."
Kesalahan umum
- Menggunakan posisi kolom sebagai identitas → byte lama salah dibaca → validasi field ID yang stabil.
- Memperlakukan penggantian nama sebagai kolom semantik baru → nilai lama mendapatkan arti baru → tambahkan, lakukan depresiasi (deprecate), lalu hapus.
- Memindahkan setiap file lama setelah mengubah spesifikasi → commit sangat besar dan rollback menjadi lemah → biarkan spesifikasi berdampingan dan tulis ulang secara selektif.
- Meng-upgrade reader khusus-kolom-baru terlebih dahulu → writer lama menghasilkan nilai null → reader yang kompatibel, writer, kemudian consumer.
- Hanya menguji perintah skema → kueri atau pemulihan stream masih gagal → jalankan structural gate, data gate, dan behavior gate.
- Melakukan backfill langsung ke snapshot aktif → tidak ada titik pemulihan yang bersih → gunakan output berversi dan snapshot ID.
Pertanyaan lanjutan dan tanggapan
Pertanyaan lanjutan 1: Mengapa field ID yang dihapus tidak boleh digunakan kembali?
File data lama masih berisi ID asli tersebut. Menggunakannya kembali membuat reader menginterpretasikan byte lama sebagai field semantik baru, jadi alokasikan ID baru dan pertahankan field lama sampai consumer dan replay historis selesai bermigrasi.
Pertanyaan lanjutan 2: Bisakah spesifikasi partisi lama dan baru berdampingan dengan aman?
Ya, jika metadata mencatat spesifikasi setiap file dan engine yang digunakan menerapkan transformasi serta predikat dengan benar. Verifikasi pruning, perilaku zona waktu, dan jumlah file kecil menggunakan kueri berskala produksi daripada menyimpulkan perilaku dari nama direktori.
Pertanyaan lanjutan 3: Bagaimana cara memulihkan diri dari konflik commit yang bersamaan?
Baca snapshot terbaru, pastikan input tetap valid, hitung ulang hanya pada partisi yang terpengaruh, dan kirimkan kembali. Jangan pernah memaksakan file metadata lama menimpa snapshot milik writer lain; konflik yang berulang memerlukan konkurensi yang lebih rendah atau irisan data yang lebih kecil.
Pertanyaan lanjutan 4: Kapan kolom lama dapat dihapus?
Setelah reader, writer, replay, audit, dan ekspor selesai bermigrasi, periode observasi stabil, dan snapshot yang dipertahankan mencakup jendela rollback. Inventarisasi consumer tersembunyi sebelum melakukan penghapusan.
Pertanyaan lanjutan 5: Apa yang harus dilakukan terhadap nama yang tidak dapat diuraikan?
Tulis null ditambah alasan dan referensi terkontrol ke nilai asli, pantau berdasarkan bahasa dan format, serta hindari memasukkan tebakan ke dalam field identitas. Perbaiki dalam versi berikutnya atau alur kerja peninjauan manual (human review).