Konteks dan cakupan
Sebuah event lake menerima webhook dari beberapa vendor. Kolom-kolom inti bersifat stabil, ekstensi sering berubah, dan beberapa event berisi tanggal, timestamp, nilai biner, serta desimal. Rancang model penyimpanan Iceberg v3, bandingkan Variant dengan string JSON dan struct, serta berikan rencana migrasi dan penerimaan (acceptance).
Ini menguji pemodelan data semi-terstruktur, bukan keputusan untuk menempatkan setiap kolom ke dalam Variant. Spesifikasi Iceberg mendefinisikan Variant sebagai nilai yang struktur dan tipenya dapat bervariasi di seluruh baris dan file, dengan tipe primitif yang lebih kaya daripada JSON. Ini adalah kemampuan v3, sehingga versi format dan kompatibilitas reader menjadi bagian dari perancangan.
Hal yang diuji oleh pewawancara
Jawaban yang kuat mempromosikan kolom-kolom yang stabil dan sering difilter ke kolom tingkat atas (top-level) dan mempertahankan ekstensi yang jarang digunakan serta cepat berubah di dalam Variant. Jawaban tersebut membedakan array dan objek Variant dari list dan struct bertipe tetap, lalu membahas statistik, predicate pushdown, biaya proyeksi, dan dukungan engine.
Pewawancara juga mencari kontrak data, penamaan, konflik tipe, privasi, backfill, dan jalur fallback. Jawaban yang sangat baik menyediakan strategi dual-write atau view dari payload mentah ke kolom kanonikal dan menyatakan apa yang terjadi ketika reader lama tidak dapat memahami v3.
Pertanyaan klarifikasi
Kolom mana yang menjadi kunci dan filter
Konfirmasikan apakah tenant ID, tipe event, waktu event, dan kunci idempoten bersifat stabil dan sering dikueri. Kolom-kolom tersebut harus berupa kolom bertipe data, bukan path yang diparsing dari Variant pada setiap kueri.
Jaminan kueri apa yang dibutuhkan oleh ekstensi
Untuk pemutaran ulang audit (audit replay), Variant dapat mempertahankan bentuk aslinya. Untuk agregasi berlatensi rendah atau pemangkasan partisi (partition pruning), path yang telah divalidasi harus dijadikan kolom atau materialized view.
Apakah semua reader mendukung v3
Lakukan inventarisasi Spark, Flink, Trino, SDK layanan, dan tugas ekspor. Jika reader v2 masih ada, tentukan view kompatibilitas JSON, tabel v3 yang terisolasi, atau tunda upgrade.
Jawaban 30 detik
"Saya mempertahankan kolom yang stabil dan sering difilter sebagai kolom bertipe data dan hanya menempatkan ekstensi yang cepat berubah dan berfrekuensi rendah di dalam Variant. Variant mempertahankan lebih banyak tipe data daripada string JSON, tetapi tidak secara otomatis menyediakan statistik kolom atau pushdown. Saya menggunakan registri path, aturan kualitas, dan kolom termaterialisasi untuk mengontrol biaya. Sebelum melakukan upgrade ke v3, saya menginventarisasi reader, memberikan kompatibilitas view kepada pekerjaan v2 dengan kehilangan presisi eksplisit, serta mengukur byte pindaian, latensi, konflik tipe, dan keberhasilan backfill."
Solusi langkah demi langkah
Langkah 1: Pisahkan kolom kanonikal dari ekstensi
Tempatkan tenant ID, nama event, waktu event, sumber, dan kunci idempoten dalam kolom struct tingkat atas dengan tipe, opsionalitas, dan field ID yang konsisten. Tempatkan objek berfrekuensi rendah khusus vendor di dalam Variant, dengan mempertahankan versi sumber dan ID event mentah untuk replay dan audit.
Langkah 2: Bandingkan ketiga representasi
Struct tetap cocok untuk skema yang stabil, komputasi bertipe data, dan statistik kolom. String JSON kompatibel secara luas tetapi diparsing ulang untuk setiap kueri, dan semantik tanggal, desimal, serta biner bergantung pada parser. Variant memungkinkan objek dan array yang berubah-ubah ditambah tipe primitif yang lebih kaya, dengan konsekuensi pada dukungan engine, statistik, dan tata kelola.
Langkah 3: Tentukan kontrak Variant
Buat registri untuk path yang diizinkan: path, tipe yang diharapkan, sensitivitas, tim pemilik, versi saat pertama kali terlihat, dan apakah memenuhi syarat untuk dipromosikan. Tolak atau karantina tipe berisiko tinggi yang tidak dikenal alih-alih mengonversinya secara diam-diam menjadi string.
event_id: string
event_time: timestamptz
payload: variant
payload_registry:
vendor.order.total: decimal(18,2)
vendor.order.shipped_at: timestamptzRegistri mengatur kualitas data; registri tidak boleh melakukan hard-code setiap path Variant ke dalam skema tabel. Promosikan sebuah path melalui evolusi skema atau kolom termaterialisasi hanya setelah path tersebut menjadi dimensi kueri utama.
Langkah 4: Kontrol biaya kueri
Hindari penelusuran wildcard tanpa batas pada Variant dalam pemindaian besar. Bangun projected view atau kolom termaterialisasi untuk path yang stabil, lakukan partisi berdasarkan tipe event dan waktu, serta catat byte pindaian, CPU parsing, dan hit rate. Ambil sampel dan buat profil path yang tidak dikenal secara offline sebelum mempromosikannya.
Langkah 5: Tangani versi dan reader
Variant diizinkan di Iceberg v3. Sebelum rilis, periksa versi format setiap reader, pemetaan Parquet atau Avro, dan dukungan SDK. Pekerjaan v2 dapat menggunakan view kompatibilitas yang menserialisasikan Variant sebagai JSON, tetapi view tersebut harus mendokumentasikan kehilangan tipe dan presisi serta kolom-kolom yang tidak dapat lagi dikueri secara efisien.
Langkah 6: Rancang backfill, konflik, dan privasi
Lakukan backfill kolom kanonikal baru dari Variant sambil mempertahankan payload mentah dan versi transformasi. Jika satu path berubah dari desimal menjadi string, jangan menimpa secara diam-diam: buat versi untuk path tersebut, keluarkan metrik konflik, dan karantina data yang tidak valid bila diperlukan. Terapkan penyamaran (masking) tingkat kolom, penanganan penghapusan, dan audit akses ke Variant juga.
Langkah 7: Bangun matriks penerimaan (acceptance matrix)
Uji nilai null, tipe data campuran, array bersarang dalam, zona waktu, presisi, kolom tidak dikenal, reader lama, penulisan konkuren, dan percobaan ulang (retries). Lacak byte pindaian, CPU parsing, latensi p95, tingkat konflik tipe, konsistensi replay, dan tingkat keberhasilan reader v2/v3.
Model jawaban berkualitas tinggi
Saya tidak akan menyimpan setiap webhook sebagai string JSON. Tenant, tipe event, waktu, dan kunci idempoten menjadi kolom tingkat atas yang bertipe data; ekstensi vendor yang cepat berubah masuk ke Variant di bawah registri path, tipe, dan sensitivitas. Variant mempertahankan tanggal, timestamp, dan desimal lebih baik daripada string, tetapi saya tidak berasumsi bahwa setiap engine dapat melakukan pushdown predikat ke dalamnya secara efisien.
Sebelum melakukan upgrade ke Iceberg v3, saya menginventarisasi reader dan menyediakan view kompatibilitas JSON bagi pekerjaan v2 yang mencatat kehilangan presisi. Lapisan kueri mematerialisasi path bernilai tinggi dan memprofilkan path yang tidak dikenal. Konflik tipe masuk ke aliran karantina, dan proses backfill mempertahankan versi serta event mentah. Saya menerima rancangan tersebut hanya setelah mengukur byte pindaian, latensi p95, tingkat konflik, konsistensi replay, dan keberhasilan lintas engine.
Kesalahan umum
- Gejala → Menempatkan setiap kolom di Variant → Penyebab gagal → Filter utama kehilangan statistik bertipe data dan biaya kueri menjadi tidak dapat diprediksi → Solusi → Promosikan kolom yang stabil dan cadangkan Variant untuk ekstensi yang berubah-ubah.
- Gejala → Memperlakukan Variant sebagai string JSON → Penyebab gagal → Tanggal, desimal, dan nilai biner kehilangan semantik tipe → Solusi → Pertahankan tipe primitif di bawah registri.
- Gejala → Melakukan upgrade ke v3 tanpa pengujian reader → Penyebab gagal → Engine lama mungkin gagal membaca atau mengalami penurunan performa secara diam-diam → Solusi → Bangun matriks versi dan view kompatibilitas.
- Gejala → Memaksa konflik tipe menjadi string → Penyebab gagal → Agregasi dan batasan (constraints) di downstream menjadi rusak → Solusi → Buat versi untuk path atau karantina dan ukur konflik.
- Gejala → Menimpa payload mentah selama backfill → Penyebab gagal → Perbedaan konversi tidak dapat diputar ulang atau diaudit → Solusi → Pertahankan event mentah, versi transformasi, dan pekerjaan yang idempoten.
Pertanyaan lanjutan dan tanggapan
Pertanyaan lanjutan 1: Mengapa tidak mempertahankan string JSON dan memarsingnya saat kueri?
String memaksimalkan kompatibilitas, tetapi setiap kueri membayar biaya parsing dan presisi tipe bergantung pada parser. Hal itu dapat diterima untuk pemutaran ulang audit saja. Agregasi, filter, dan konsistensi lintas engine akan diuntungkan dengan memindahkan kontrak tipe ke Variant atau kolom kanonikal.
Pertanyaan lanjutan 2: Sebuah path bernilai numerik hari ini dan menjadi string besok. Apa yang Anda lakukan?
Gunakan registri untuk menolak atau mengarantina penulisan dan catat versi vendor. Jika kedua tipe tersebut sah, buat versi path atau tentukan union eksplisit; jangan biarkan query engine menebak.
Pertanyaan lanjutan 3: Reader lama hanya mendukung Iceberg v2. Bagaimana Anda melakukan migrasi?
Pertahankan tabel atau view yang kompatibel dengan v2 yang menserialisasikan Variant sebagai JSON dan mendokumentasikan kehilangan presisi. Lakukan shadow-read v3 dengan reader baru, lalu alihkan pekerjaan secara bertahap. Setiap pekerjaan mendeklarasikan versi format minimumnya di gerbang upgrade.
Pertanyaan lanjutan 4: Kapan sebuah path Variant harus menjadi kolom tingkat atas?
Promosikan ketika path tersebut stabil, sering diakses, memiliki konflik rendah, dan pemantauan profil menunjukkan pemindaian atau parsing yang lebih sedikit. Pertahankan Variant mentah untuk jendela verifikasi dan pemutaran ulang sebelum mempertimbangkan pembersihan.