1 poin oleh GN⁺ 2025-12-09 | 1 komentar | Bagikan ke WhatsApp
  • Jepsen memverifikasi durabilitas dan konsistensi sistem messaging terdistribusi NATS JetStream dalam berbagai kondisi gangguan
  • Hasil pengujian menunjukkan kehilangan data dan fenomena split-brain pada kerusakan file (.blk, snapshot) serta simulasi kegagalan daya
  • Secara default JetStream menjalankan fsync setiap 2 menit, sehingga pesan yang baru saja diakui bisa tetap belum tertulis ke disk
  • Crash OS pada satu node saja dapat memicu kehilangan data dan ketidaksesuaian replika
  • Jepsen merekomendasikan agar NATS mengubah default ke fsync=always atau mendokumentasikan risiko kehilangan data secara eksplisit

1. Latar belakang

  • NATS adalah sistem streaming populer untuk memublikasikan dan berlangganan pesan sebagai stream
    • JetStream menggunakan algoritma konsensus Raft untuk mereplikasi data dan menjamin pengiriman minimal satu kali (at-least-once)
  • Dalam dokumentasi, JetStream mengklaim konsistensi linearizable dan selalu tersedia, tetapi menurut teorema CAP kedua kondisi itu tidak dapat dipenuhi secara bersamaan
  • Menurut dokumentasi NATS, stream 3 node dapat menoleransi hilangnya 1 server, dan stream 5 node dapat menoleransi hilangnya 2 server
  • Pesan dianggap “berhasil disimpan” pada saat server mengakui permintaan publish
  • Untuk konsistensi data dibutuhkan mayoritas (quorum) node; pada klaster 5 node, minimal 3 harus berjalan agar pesan baru bisa disimpan

2. Desain pengujian

  • Jepsen melakukan pengujian dengan klien JNATS 2.24.0 dan lingkungan kontainer Debian 12 LXC
    • Sebagian pengujian menggunakan image Docker resmi NATS di lingkungan Antithesis
  • Satu stream JetStream tunggal dikonfigurasi (replikasi 5), lalu disuntikkan gangguan seperti penghentian proses, crash, partisi jaringan, packet loss, dan kerusakan file
  • Menggunakan filesystem LazyFS untuk mensimulasikan kegagalan daya yang menyebabkan hilangnya write yang belum di-fsync
  • Setiap proses memublikasikan pesan unik, lalu setelah pengujian selesai diverifikasi di semua node apakah pesan yang sudah diakui benar-benar ada
  • Jika pesan hanya ada pada sebagian node, kasus itu diklasifikasikan sebagai divergence (ketidaksesuaian replikasi)
Iklan

3. Hasil utama

3.1 Kehilangan seluruh data pada NATS 2.10.22 (#6888)

  • Ditemukan fenomena seluruh stream JetStream hilang hanya karena crash proses sederhana
  • Setelah muncul error "No matching streams for subject", pemulihan tidak terjadi selama berjam-jam
  • Penyebabnya meliputi pembalikan snapshot leader, penghapusan status Raft, dan lain-lain, lalu diperbaiki pada versi 2.10.23

3.2 Kehilangan data saat file .blk rusak (#7549)

  • Jika file .blk JetStream mengalami error satu bit atau pemotongan (truncation), dapat terjadi kehilangan ratusan ribu write yang sudah diakui
    • Contoh: dari 1.367.069 kasus, 679.153 hilang
  • Bahkan jika hanya sebagian node yang rusak, dapat terjadi kehilangan data skala besar dan split-brain
    • Contoh: pada node n1, n3, n5, kehilangan pesan mencapai 78%
  • NATS sedang menyelidiki masalah tersebut
Iklan

3.3 Penghapusan seluruh data saat file snapshot rusak (#7556)

  • Jika file snapshot dalam data/jetstream/$SYS/_js_/ rusak, node menilai stream sebagai orphaned lalu menghapus seluruh data
  • Meski hanya sedikit node yang rusak, hal itu dapat membuat mayoritas klaster tidak tercapai dan stream tidak tersedia secara permanen
  • Contoh: node n3, n5 rusak → n3 terpilih sebagai leader lalu menghapus seluruh jepsen-stream
  • Jepsen menyoroti risiko node yang rusak menjadi leader saat pemilihan leader

3.4 Kehilangan data karena konfigurasi default fsync (#7564)

  • Secara default JetStream hanya menjalankan fsync setiap 2 menit, sementara pesan langsung diakui
    • Akibatnya, pesan yang baru saja diakui bisa masih belum tertulis ke disk
  • Saat terjadi kegagalan daya atau crash kernel, pesan yang sudah diakui selama puluhan detik bisa hilang
    • Contoh: dari 930.005 kasus, 131.418 hilang
  • Bahkan kegagalan pada satu node yang terjadi berurutan dapat menyebabkan seluruh stream terhapus
  • Perilaku ini nyaris tidak disebutkan dalam dokumentasi
  • Jepsen merekomendasikan mengubah default ke fsync=always atau menambahkan peringatan eksplisit tentang risiko kehilangan data

3.5 Split-brain akibat satu crash OS (#7567)

  • Hanya dengan kegagalan daya atau crash kernel pada satu node saja, tetap dapat terjadi kehilangan data dan ketidaksesuaian replikasi
  • Dalam struktur leader-follower, jika sebagian node sudah mengakui write yang baru ter-commit di memori lalu terjadi gangguan,
    mayoritas node dapat kehilangan write tersebut dan melanjutkan dengan status baru
  • Dalam pengujian, satu kegagalan daya memicu split-brain yang persisten
    • Terlihat bahwa tiap node kehilangan rentang pesan yang sudah diakui namun berbeda-beda
    Iklan
  • Jepsen mengutip kasus serupa pada Kafka dan menekankan bahwa risiko yang sama juga ada pada sistem berbasis Raft

4. Diskusi dan kesimpulan

  • Masalah kehilangan seluruh data pada 2.10.22 telah diselesaikan di 2.10.23
  • Pada 2.12.1, kehilangan data dan split-brain akibat kerusakan file dan crash OS masih tetap terjadi
  • Saat file .blk dan snapshot rusak, dapat terjadi pesan hilang pada sebagian node atau penghapusan seluruh stream
  • Karena interval default fsync panjang, ada risiko kehilangan data yang sudah diakui saat beberapa node gagal bersamaan
  • Jepsen mengusulkan penggunaan fsync=always atau pemberitahuan risiko yang jelas dalam dokumentasi
  • Klaim JetStream sebagai sistem yang “selalu tersedia” mustahil menurut teorema CAP, sehingga dokumentasi perlu diperbaiki
  • Jepsen menegaskan bahwa keberadaan bug bisa dibuktikan, tetapi ketiadaan masalah keamanan tidak bisa dibuktikan

4.1 Peran LazyFS

  • LazyFS digunakan untuk mensimulasikan hilangnya write yang belum di-fsync
  • Saat kegagalan daya, berbagai error storage seperti kerusakan write parsial (torn write) juga dapat direproduksi
  • Dalam riset terkait When Amnesia Strikes (VLDB 2024), bug serupa juga dilaporkan pada PostgreSQL, Redis, ZooKeeper, dan lainnya

4.2 Tugas ke depan

  • Verifikasi kehilangan pesan pada level konsumer tunggal, urutan pesan, serta jaminan Linearizable/Serializable belum dilakukan
  • Jaminan pengiriman tepat satu kali (exactly-once) juga menjadi topik riset berikutnya
  • Saat menambah atau menghapus node, ditemukan kesalahan dokumentasi dan langkah health check wajib yang hilang (#7545)
  • Prosedur aman untuk perubahan konfigurasi klaster masih belum jelas

1 komentar

 
GN⁺ 2025-12-09
Komentar Hacker News
  • Setiap kali seseorang melewatkan teori yang rumit lalu membuat sistem seperti ini, saya melihat aphyr merobohkannya
    Sekarang saya jadi penasaran apakah AI suatu hari bisa membaca dokumentasi proyek dan memprediksi kemungkinan kehilangan data hanya dari kalimat-kalimat marketing
    • Rasanya seperti mengelus janggut panjang sambil mengangguk
      Orang-orang selalu bilang “teori itu dilebih-lebihkan” atau “ngoprek lebih baik daripada pendidikan formal”, tapi pada akhirnya mereka malah menjegal diri sendiri di ruang masalah yang sudah terdokumentasi
    • Saya juga pernah menyuruh LLM melakukan hal serupa, dan hasilnya cukup berguna
  • Rasanya NATS mengabaikan teori CAP
    • Ini seperti komentar yang diremehkan
  • Saya selama ini memakai NATS untuk pub/sub in-memory, dan untuk bagian itu hasilnya luar biasa
    Detail scaling yang halus juga ditangani dengan baik
    Tapi saya belum pernah memakai persistence, dan tidak menyangka akan serapuh ini
    Fakta bahwa ia rentan bahkan terhadap kerusakan satu bit file saja cukup mengejutkan
  • Sebagai bahan terkait, Jepsen dan Antithesis baru-baru ini merilis glosarium sistem terdistribusi
    Ini referensi yang sangat bagus → Jepsen Glossary
  • Saya penasaran dengan perbedaan konten antara aphyr.com/tags/jepsen dan jepsen.io/analyses
    Saya baru menemukan aphyr.com belakangan ini, dan berharap ada banyak insight di sana
    • Jepsen awalnya dimulai sebagai seri blog pribadi
      Setelah itu jepsen.io berkembang menjadi proyek profesional, dan mulai dijalankan secara serius sekitar 10 tahun lalu
  • Saya heran kenapa pengaturan “Lazy fsync by Default” itu ada
    Apakah alasannya untuk menaikkan performa benchmark? Pada cluster kecil, pengaturan seperti ini sering menjadi penyebab masalah
    • Bukan cuma latensi, ada juga peningkatan throughput
      Banyak aplikasi tidak membutuhkan durability penuh, jadi lazy fsync bisa berguna
      Namun menjadikannya nilai default memang bisa diperdebatkan
    • Saya selalu penasaran kenapa fsync harus ditunda seperti itu
      Sepertinya ini bisa diatasi dengan batching seperti TCP corking
    • Ini salah satu hal yang dimungkinkan oleh sistem terdistribusi
      Kegagalan akibat lazy fsync biasanya tidak terjadi serentak di sebagian besar node
    • Ya, ini memang pilihan demi peningkatan performa
    • Menargetkan durability melalui replikasi dan distribusi, sambil mengambil throughput yang didapat dari lazy fsync
  • Sebagai alternatif serverless untuk JetStream, saya merekomendasikan s2.dev
    Kelebihan: mendukung stream tanpa batas dengan durability setingkat object storage
    Kekurangan: belum punya fitur consumer group
    • Saya penasaran apakah mereka pernah menjalankan pengujian Jepsen
  • Masalahnya adalah NATS secara default hanya melakukan fsync setiap 2 menit, lalu langsung mengembalikan ack
    Jika beberapa node mengalami gangguan pada saat yang sama, bisa terjadi kehilangan data yang sudah di-commit
    Ini mengingatkan saya pada marketing “web scale” dari masa awal MongoDB
    Saya pikir nilai default harus selalu menjadi opsi yang paling aman
    • NATS dengan jelas menyatakan bahwa ia hanya menjamin ketersediaan cluster
      Justru itu yang saya sukai, karena saya bisa merancang sistem di atas asumsi tersebut
      Saat saya memakainya pada 2018, performanya bagus dan pengelolaannya juga mudah
    • Kebanyakan DB modern juga default-nya tidak sepenuhnya aman
      Misalnya tingkat isolasi transaksi default PostgreSQL adalah read committed
      Redis juga secara default melakukan fsync setiap 1 detik
    • Cluster Redis mengembalikan ack hanya setelah direplikasi ke banyak node
      Bahkan pada Redis standalone, pengaturan ack setelah fsync juga dimungkinkan, tetapi buffering OS membuat jaminan penuh tetap sulit
      Pada akhirnya, yang penting adalah memahami dengan tepat arti ack
    • Sebagian besar sistem memilih kompromi antara kecepatan dan durability
      Jika hanya memaksakan nilai default yang aman, performa akan turun drastis dan beban tuning manual berpindah ke pengguna
      Misalnya tingkat isolasi default Postgres juga lemah sehingga bisa memunculkan race condition
      Referensi: tulisan uji Hermitage
    • Masalahnya, fsync hanya menyediakan pilihan yang ekstrem
      Di era SSD, tahap tengah seperti group-commit menghilang, dan sekarang biaya perpindahan syscall menjadi bottleneck
      Interval 2 menit itu terlalu panjang (perbedaan fdatasync vs fsync juga perlu dipertimbangkan)
  • Sejujurnya saya memang sudah menduganya, tapi tidak menyangka akan separah ini
    Sepertinya lebih baik pakai Redpanda saja
  • Saya penasaran apakah peringatan performa fsync di NATS bisa diperbaiki
    Kalau dilakukan batch flush secara berkala, latensi memang akan naik tetapi throughput mungkin bisa tetap terjaga
    • Bukan dengan interval tetap, melainkan mengantrekan permintaan tulis saat fsync sedang berlangsung lalu memprosesnya bersama pada batch berikutnya
      Ini mirip dengan cara menggabungkan putaran Paxos
      Setelah satu putaran selesai, batch berikutnya harus langsung dimulai