yaml-cpp diam-diam menerima kunci duplikat pada mapping YAML dan hanya menyimpan nilai terakhir: tanpa galat, tanpa peringatan, tanpa indikasi bahwa nilai sebelumnya dibuang. Spesifikasi YAML secara eksplisit menyebut perilaku ini tidak terdefinisi, namun setiap parser besar membuat pilihannya sendiri. Panduan ini menjelaskan apa yang dilakukan yaml-cpp, bagaimana parser lain berbeda, skenario nyata apa yang menghasilkan duplikat, dan cara mendeteksinya sebelum menimbulkan bug kehilangan data senyap di produksi.
Apa itu kunci duplikat di YAML?
Kunci duplikat terjadi ketika string kunci yang sama muncul lebih dari sekali pada level yang sama dalam satu mapping YAML. Pada bahasa seperti JSON hal ini juga tidak terdefinisi, tetapi terlihat jelas secara visual. Di YAML, tempat mapping membentang beberapa baris dan file bisa berisi ratusan baris, kunci duplikat mudah dimasukkan tanpa sengaja dan sama mudahnya terlewat saat peninjauan.
Seperti apa bentuk duplikat
Bentuk paling sederhana adalah pengulangan langsung: kunci didefinisikan di bagian atas mapping lalu didefinisikan ulang di bawahnya, kadang dengan nilai berbeda. Ini paling sering terjadi karena kesalahan salin-tempel, refaktor yang belum selesai, atau penggabungan potongan konfigurasi dari sumber berbeda. Nama kuncinya identik byte per byte (huruf besar-kecil sama, spasi sama) dan hanya muncul dua kali dalam blok mapping yang sama.
- Kesalahan salin-tempel: blok kunci diduplikasi saat menambahkan bagian baru berdasarkan bagian yang sudah ada
- Penggantian nama yang belum tuntas: kunci diganti namanya tetapi yang asli tidak dihapus, sehingga keduanya ada di file
- Penggabungan konfigurasi: dua fragmen YAML disatukan dan keduanya kebetulan mendefinisikan kunci level atas yang sama
- Pembatalan komentar: kunci yang dikomentari diaktifkan kembali tanpa menghapus pengganti aktif di bawahnya
- Ekspansi template: generator atau mesin template mengeluarkan kunci yang sama dua kali dari cabang kondisi berbeda
Catatan
Apa yang sebenarnya dikatakan spesifikasi YAML
Spesifikasi YAML 1.2 membahas kunci duplikat secara langsung dan tanpa ambiguitas: kunci tersebut tidak diizinkan dalam mapping YAML yang valid. Bagian 3.2.1.3 menyatakan bahwa kunci mapping harus unik di dalam mapping tersebut. Setiap dokumen yang memuat kunci duplikat secara teknis tidak sesuai spesifikasi.
Isi sebuah node mapping adalah himpunan tak berurutan dari pasangan node kunci/nilai, dengan batasan bahwa setiap kunci bersifat unik.
Tidak terdefinisi bukan berarti penguraian tidak valid
Nuansa pentingnya adalah meski spesifikasi menyebut kunci duplikat tidak sesuai, ia tidak mewajibkan parser menolaknya dengan galat keras. Sebaliknya, spesifikasi menyebut perilakunya tidak terdefinisi, artinya setiap implementasi parser bebas menangani duplikat sesuai pilihannya. Itulah sebabnya yaml-cpp, PyYAML, js-yaml, dan parser lain menerima duplikat tanpa melempar galat, meski dokumen hasilnya secara teknis YAML tidak valid.
Mengapa ini penting dalam praktik
"Perilaku tidak terdefinisi" dalam sebuah spesifikasi berarti aplikasi Anda bergantung pada detail implementasi yang bisa berubah antar versi pustaka. yaml-cpp saat ini memakai "nilai terakhir menang", tetapi tidak ada jaminan dalam spesifikasi untuk itu. Versi mendatang bisa berubah menjadi "nilai pertama menang", melempar pengecualian, atau mengembalikan node galat, dan semua perubahan itu tetap sesuai spesifikasi. Kode yang tanpa sengaja bergantung pada perilaku penyelesaian kunci duplikat rapuh secara definisi.
Peringatan
Perilaku yaml-cpp secara rinci
yaml-cpp adalah pustaka pengurai YAML untuk C++ yang paling banyak dipakai dan pilihan default di banyak aplikasi C++ serta mesin game. Saat yaml-cpp menemukan kunci duplikat dalam sebuah mapping, ia mengurai kedua kemunculan tetapi hanya menyimpan yang terakhir pada pohon Node yang dihasilkan. Nilai sebelumnya ditimpa dan hilang permanen dari struktur hasil penguraian.
Aturan "nilai terakhir menang"
Dalam implementasi yaml-cpp, setiap kunci pada mapping disimpan dalam daftar terurut pasangan kunci-nilai. Saat kunci duplikat diurai, yaml-cpp mencari di daftar yang ada untuk kunci yang cocok. Jika ditemukan, nilai tersimpan diganti dengan nilai baru. Node nilai sebelumnya dilepaskan. Dari sudut pandang aplikasi, memanggil `node["key"]` mengembalikan nilai yang terakhir didefinisikan seolah hanya ada satu definisi.
Tidak ada keluaran diagnostik secara default
yaml-cpp tidak mengeluarkan peringatan, pesan log, atau pengecualian saat menimpa kunci duplikat. Penguraian berhasil dengan `YAML::Node` yang tampak sepenuhnya normal. Tidak ada flag yang bisa Anda periksa setelah penguraian untuk mengetahui bahwa duplikat telah diselesaikan secara senyap. Satu-satunya cara mendeteksinya adalah memeriksa teks mentah sebelum penguraian, yang persis dilakukan detektor kunci duplikat khusus.
Perilaku konsisten di semua gaya mapping
yaml-cpp menerapkan "nilai terakhir menang" secara konsisten, baik mapping memakai gaya blok (kunci pada baris terpisah) maupun gaya alir dengan kurung kurawal. Mapping bersarang ditangani secara independen: duplikat hanya dibandingkan dalam level mapping yang sama, bukan di seluruh pohon dokumen. Kunci yang muncul pada dua mapping bersaudara dengan kedalaman anidasi berbeda tidak dianggap duplikat.
Detektor kunci duplikat YAML
Tempelkan dokumen YAML Anda dan temukan semua kunci duplikat di setiap level anidasi seketika: melaporkan nomor baris dan kedua nilai yang bersaing agar Anda memperbaikinya sebelum sampai ke yaml-cpp.
Bagaimana parser lain menangani duplikat
Karena spesifikasi YAML membiarkan perilaku kunci duplikat tidak terdefinisi, setiap ekosistem parser membuat keputusannya sendiri. Perbedaannya antar bahasa cukup besar sehingga file YAML yang lolos secara senyap di satu pipeline bisa gagal keras di pipeline lain. Memahami lanskapnya membantu Anda menulis YAML yang portabel.
| Parser / Pustaka | Bahasa | Perilaku pada kunci duplikat |
|---|---|---|
| yaml-cpp | C++ | Nilai terakhir menang: senyap, tanpa peringatan |
| PyYAML | Python | Nilai terakhir menang: senyap, tanpa peringatan |
| ruamel.yaml (ketat) | Python | Melempar DuplicateKeyError bila dikonfigurasi |
| js-yaml | JavaScript | Nilai terakhir menang: senyap, tanpa peringatan |
| gopkg.in/yaml.v3 | Go | Mengembalikan galat: duplicate map key |
| go-yaml v2 | Go | Nilai terakhir menang: senyap, tanpa peringatan |
| Psych (default) | Ruby | Melempar Psych::BadAlias / galat pada versi baru |
| SnakeYAML | Java | Nilai terakhir menang: senyap (dapat dikonfigurasi) |
| YamlDotNet | C# / .NET | Nilai terakhir menang: senyap, tanpa peringatan |
| libfyaml | C | Mengeluarkan peringatan; perilaku dapat dikonfigurasi |
Kesimpulan praktisnya tegas: `yaml.v3` di Go memperlakukan duplikat sebagai galat keras, sementara yaml-cpp, PyYAML, dan js-yaml menerimanya secara senyap. File konfigurasi YAML yang berfungsi di aplikasi C++ Anda dengan yaml-cpp bisa langsung gagal ketika file yang sama diproses oleh layanan Go atau linter Python yang ketat di pipeline CI.
Tips
Skenario nyata yang menyebabkan duplikat
Sebagian besar kunci duplikat tidak disengaja. Kunci itu muncul melalui pola yang dapat diprediksi dalam cara pengembang menulis dan memelihara file konfigurasi YAML. Mengetahui penyebab umumnya membantu Anda menangkapnya di sumbernya.
Pertumbuhan file konfigurasi seiring waktu
File konfigurasi berumur panjang menumpuk perubahan dari banyak kontributor. Kunci yang didefinisikan berbulan-bulan lalu di dekat bagian atas file didefinisikan ulang oleh kontributor baru yang tidak menyadari kunci itu sudah ada. Ini sangat umum pada file `values.yaml` Helm, ConfigMap Kubernetes, dan file variabel Ansible, yang bisa memuat ratusan kunci tersebar di file terlalu panjang untuk ditinjau seluruhnya.
Menggabungkan potongan konfigurasi dari tim berbeda
Ketika dua tim independen atau mikrolayanan menyumbang ke konfigurasi YAML bersama, kunci level atas yang sama bisa didefinisikan keduanya. File hasil gabungan memuat kedua definisi, dan yang muncul terakhir menang secara senyap. Ini sumber umum bug penimpaan spesifik lingkungan, tempat nilai dari tim yang salah berlaku di produksi.
Pola mengomentari lalu mengganti
Seorang pengembang mengomentari `timeout: 30` dan menambahkan `timeout: 60` tepat di bawahnya sebagai pengganti. Kemudian seseorang menghapus karakter komentar dari baris lama, mungkin dalam pencarian-dan-ganti global atau karena formatter editor salah konfigurasi, dan kedua nilai menjadi aktif. Yang terakhir menang, tetapi mana yang terakhir bergantung pada posisi masing-masing baris dalam file.
Bug template atau pembuatan kode
Pipeline CI/CD dan alat infrastructure-as-code sering menghasilkan YAML secara programatik. Bug pada logika template, misalnya cabang kondisi yang tidak mengecualikan kunci yang sudah dikeluarkan cabang lain dengan benar, bisa menghasilkan YAML yang tampak valid tetapi memuat duplikat senyap. File hasilnya lolos penguraian yaml-cpp, dan nilai yang salah dipakai di produksi tanpa galat tercatat.
Peringatan
Mendeteksi dan mencegah duplikat
Kunci duplikat mudah dideteksi dengan peralatan yang tepat. Tantangannya adalah menangkapnya sebelum mencapai parser produksi, bukan setelah kehilangan data senyap terjadi. Alur berikut mencakup deteksi pada setiap tahap, dari penulisan sampai deployment.
Langkah 1: deteksi sebelum commit dengan Detektor kunci duplikat YAML
Detektor kunci duplikat YAML memindai seluruh dokumen YAML Anda, termasuk mapping bersarang pada setiap kedalaman, dan melaporkan setiap kunci duplikat beserta nomor barisnya serta nilai yang ditimpa dan yang bertahan. Tempelkan file Anda sebelum commit untuk menangkap masalah seketika. Tanpa unggahan, tanpa pendaftaran, dan file tidak pernah meninggalkan browser Anda.
Langkah 2: linting di editor dengan yamllint
Untuk tim yang bekerja dengan file YAML setiap hari, `yamllint` dengan aturan `key-duplicates` yang disetel ke `enable` menangkap duplikat pada setiap penyimpanan. Pengguna VS Code dapat memasang ekstensi YAML (Red Hat) yang mengintegrasikan yamllint secara otomatis. Menambahkan yamllint ke hook pra-commit dan pipeline CI berarti duplikat tidak pernah sampai ke peninjauan kode tempat mereka bisa terlewat.
Langkah 3: penguraian mode ketat di rangkaian tes Anda
Meski kode produksi Anda memakai yaml-cpp, Anda bisa menambahkan validasi saat pengujian dengan parser ketat. Uraikan setiap file konfigurasi YAML dengan `yaml.v3` dari Go atau ruamel.yaml dari Python dalam mode ketat sebagai bagian dari rangkaian tes Anda. Parser ini memberi galat pada duplikat, sehingga Anda mendapat kegagalan tes yang tegas alih-alih bug runtime senyap. Setelah menjalankan pemeriksaan duplikat, gunakan Validator anchor dan alias YAML untuk memastikan pula bahwa penggunaan anchor dan alias Anda bersih.
Detektor kunci duplikat YAML
Temukan semua kunci duplikat di dokumen YAML mana pun seketika: setiap level anidasi dipindai, nomor baris dilaporkan, dan kedua nilai ditampilkan berdampingan.
Pencegahan: praktik struktural terbaik
- Urutkan kunci secara alfabetis: urutan alfabetis membuat deteksi duplikat menjadi sepele saat peninjauan kode
- Gunakan anchor YAML untuk nilai bersama: alih-alih menduplikasi blok, definisikan anchor sekali lalu rujuk dengan alias
- Terapkan yamllint di CI: pipeline yang gagal jauh lebih kuat sebagai sinyal daripada komentar di peninjauan kode
- Tinjau diff konfigurasi besar secara menyeluruh: periksa tampilan file lengkap, bukan hanya baris yang berubah, saat meninjau perubahan konfigurasi
- Jaga file tetap pendek: pisahkan file konfigurasi besar menjadi subfile yang fokus untuk mengurangi luas permukaan duplikat
Kunci merge, anchor, dan jebakan terkait
Kunci merge YAML (`<<`) serta sistem anchor dan alias adalah mekanisme sah untuk menggunakan ulang nilai dalam sebuah dokumen. Memahami bagaimana keduanya berinteraksi dengan deteksi kunci duplikat mencegah positif palsu pada peralatan Anda dan membantu menggunakannya dengan aman.
Cara kerja kunci merge
Kunci merge `<<` memberi tahu parser YAML agar memasukkan pasangan kunci-nilai dari mapping ber-anchor ke mapping saat ini. Ini bukan kunci duplikat: `<<` adalah indikator cadangan dalam spesifikasi YAML 1.1 dan ekstensi yang didukung luas pada 1.2. Saat kunci merge mengimpor kunci yang sudah ada di mapping tujuan, definisi eksplisit pada tujuan lebih diprioritaskan daripada nilai hasil merge. Ini perilaku yang disengaja dan dapat diprediksi, berbeda dari kunci duplikat yang tidak disengaja.
Anchor dan deteksi duplikat
Anchor YAML (`&name`) dan alias (`*name`) bukan duplikat. Anchor mendefinisikan node yang dapat digunakan ulang; alias merujuk padanya. Keduanya bisa muncul berkali-kali dalam dokumen tanpa menimbulkan pelanggaran kunci duplikat. Validator anchor dan alias YAML secara khusus memeriksa bahwa setiap alias mengarah ke anchor yang dideklarasikan dan tidak ada referensi melingkar, masalah yang berbeda dari kunci duplikat.
Ketika kunci merge menghasilkan duplikat semu
Kunci merge dapat menciptakan sesuatu yang tampak seperti duplikat jika mapping dasar ber-anchor dan mapping tujuan sama-sama mendefinisikan kunci yang sama. Ini bukan bug: spesifikasi menetapkan bahwa kunci eksplisit lebih diprioritaskan daripada kunci hasil merge. Namun, sebagian linter kunci duplikat melaporkannya sebagai galat. Jika Anda melihat positif palsu di yamllint untuk konfigurasi berbasis `<<`, pastikan Anda memakai kunci merge dengan benar sebelum membungkam peringatannya. Untuk file `values.yaml` Helm yang kompleks dan banyak memakai anchor, membandingkan versi dengan Penyorot perbedaan untuk konfigurasi JSON/YAML memudahkan menemukan perubahan pada level kunci di pull request.
Catatan
Poin penting
- yaml-cpp memakai nilai terakhir menang untuk kunci duplikat: nilai sebelumnya ditimpa secara senyap tanpa galat atau peringatan.
- Spesifikasi YAML 1.2 menyatakan secara eksplisit bahwa kunci duplikat tidak diizinkan dan menyebut perilakunya tidak terdefinisi.
- Perilaku parser sangat beragam: `yaml.v3` dari Go memberi galat pada duplikat, sementara PyYAML dan js-yaml seperti yaml-cpp menyimpan nilai terakhir secara senyap.
- Kunci kritis keamanan seperti `admin` atau `enabled` adalah target paling berbahaya: duplikat dapat memberi atau mencabut akses secara tidak terlihat.
- Gunakan Detektor kunci duplikat YAML untuk memindai file YAML mana pun dan menemukan duplikat di setiap level anidasi sebelum deployment.
- Tambahkan `yamllint` dengan `key-duplicates: enable` ke pipeline CI Anda untuk pencegahan otomatis pada setiap commit.
- Kunci merge YAML (`<<`) dan anchor bukan duplikat: keduanya mekanisme penggunaan ulang yang disengaja dengan aturan prioritas yang terdefinisi.