Jepsen: NATS 2.12.1
(jepsen.io)- 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
fsyncsetiap 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=alwaysatau 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)
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
.blkJetStream 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%
- Contoh: pada node
- NATS sedang menyelidiki masalah tersebut
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,n5rusak →n3terpilih sebagai leader lalu menghapus seluruhjepsen-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
fsyncsetiap 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=alwaysatau 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
- 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
.blkdan snapshot rusak, dapat terjadi pesan hilang pada sebagian node atau penghapusan seluruh stream - Karena interval default
fsyncpanjang, ada risiko kehilangan data yang sudah diakui saat beberapa node gagal bersamaan - Jepsen mengusulkan penggunaan
fsync=alwaysatau 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
Komentar Hacker News
Sekarang saya jadi penasaran apakah AI suatu hari bisa membaca dokumentasi proyek dan memprediksi kemungkinan kehilangan data hanya dari kalimat-kalimat marketing
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
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
Ini referensi yang sangat bagus → Jepsen Glossary
Saya baru menemukan aphyr.com belakangan ini, dan berharap ada banyak insight di sana
Setelah itu jepsen.io berkembang menjadi proyek profesional, dan mulai dijalankan secara serius sekitar 10 tahun lalu
Apakah alasannya untuk menaikkan performa benchmark? Pada cluster kecil, pengaturan seperti ini sering menjadi penyebab masalah
Banyak aplikasi tidak membutuhkan durability penuh, jadi lazy fsync bisa berguna
Namun menjadikannya nilai default memang bisa diperdebatkan
Sepertinya ini bisa diatasi dengan batching seperti TCP corking
Kegagalan akibat lazy fsync biasanya tidak terjadi serentak di sebagian besar node
Kelebihan: mendukung stream tanpa batas dengan durability setingkat object storage
Kekurangan: belum punya fitur consumer group
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
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
Misalnya tingkat isolasi transaksi default PostgreSQL adalah read committed
Redis juga secara default melakukan fsync setiap 1 detik
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
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
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)
Sepertinya lebih baik pakai Redpanda saja
Kalau dilakukan batch flush secara berkala, latensi memang akan naik tetapi throughput mungkin bisa tetap terjaga
Ini mirip dengan cara menggabungkan putaran Paxos
Setelah satu putaran selesai, batch berikutnya harus langsung dimulai