Pulihkan sistem setelah crash
Hubungkan urutan write-ahead log, asumsi fsync, data yang belum tersimpan, checkpoint, dan replay menjadi kontrak durabilitas yang bisa diuji.
Pertanyaan
Setelah put dijawab sukses lalu mesin mati, data mana yang dijamin masih ada, dan asumsi apa di balik jaminan itu?
Setelah baca ini, kamu bisa menyatakan kontrak durabilitas dengan jelas, menjelaskan peran fsync, WAL, dan checkpoint, lalu menghitung apa yang dipulihkan untuk titik crash tertentu.
Prasyarat dan contoh
Kamu sudah baca M09.01 Bangun dictionary berbasis file, jadi paham record ber-CRC dan replay. Kamu juga kenal transaksi dan durabilitas dari M05 serta acknowledgment tulis di replikasi M08.
Layanan menerima put(k, v), menulis ke file, lalu membalas 200 OK. Dua menit kemudian listrik padam. Setelah boot, 3 write terakhir yang sudah dibalas OK nggak ada. Kodenya memanggil write() dengan benar, jadi bug-nya bukan di situ.
write() hanya menyerahkan byte ke kernel. Byte itu berada di page cache di RAM. Kalau prosesmu mati, kernel tetap menuliskannya. Kalau kernel atau mesinnya mati, byte itu hilang.
Mekanisme
1. Tiga tempat data berada. (a) Buffer di proses, (b) page cache OS, (c) media yang benar-benar menyimpan. write() memindahkan dari (a) ke (b). fsync() meminta OS membawa (b) ke (c), dan menunggu perangkat mengonfirmasi. Crash proses hanya kehilangan (a). Crash OS atau mati listrik kehilangan (a) dan (b).
2. Write-ahead log. Sebelum mengubah struktur utama (misalnya halaman B-tree atau memtable), tulis dulu record perubahan ke log sekuensial, lalu fsync, baru balas klien. Log kecil dan sekuensial itu murah di-fsync. Struktur utama yang besar boleh ketinggalan dan diperbaiki dengan memutar ulang log.
3. Kontrak durabilitas. Tulis eksplisit, misalnya:
fsyncper write:OKberarti data ada di media, kalau perangkat jujur soal flush.fsynctiap 100 ms:OKberarti data ada di OS. Mesin mati bisa kehilangan sampai 100 ms terakhir.- Tanpa
fsync: aman dari crash proses, tidak aman dari crash mesin.
Biayanya nyata. Pada SSD server dengan perlindungan daya, satu fsync bisa 0,1-1 ms, kira-kira 1.000-10.000 fsync per detik. Pada HDD dengan putaran 7.200 rpm, sekitar 100-200 per detik. Karena itu ada group commit: kumpulkan write yang tiba bersamaan, satu fsync untuk semuanya. Dengan 50 write per batch dan fsync 1 ms, kamu dapat sampai 50.000 write per detik tanpa melonggarkan kontrak.
4. Replay. Saat startup, baca WAL dari awal (atau dari checkpoint) dan terapkan tiap record yang CRC-nya valid, berhenti di record rusak pertama. Replay harus idempoten: menerapkan record yang sama dua kali memberi hasil sama, karena kamu nggak tahu persisnya record mana yang sudah mendarat di struktur utama.
5. Checkpoint. Tanpa checkpoint, WAL tumbuh dan replay makin lama. Checkpoint memaksa struktur utama sampai ke disk (flush + fsync), lalu mencatat "semua record sampai LSN 48.000 sudah aman di struktur utama". WAL sebelum LSN itu boleh dibuang. Urutannya penting: struktur utama di-fsync dulu, baru penanda checkpoint ditulis, baru WAL lama dibuang. Kalau urutannya terbalik, crash di tengah menghilangkan data.
6. Detail yang sering terlewat. File baru atau hasil rename baru dijamin tahan setelah direktori-nya di-fsync juga. Dan kalau fsync mengembalikan error, jangan coba lagi lalu berasumsi sukses: pada beberapa sistem, halaman kotor yang gagal ditulis bisa ditandai bersih sehingga percobaan kedua "berhasil" padahal datanya hilang. Perlakukan sebagai kegagalan fatal.
Saat rancangan ini gagal
- Disk atau layer virtual yang mengabaikan flush. Fsync kembali sukses padahal data masih di cache perangkat. Kontrakmu hanya sekuat perangkat di bawahnya.
- Torn write di ujung WAL. Record terakhir hanya sebagian sampai. CRC menangkapnya, dan kamu harus siap memotong log di situ.
- Checkpoint tanpa urutan benar. WAL dibuang sebelum halaman utama benar-benar tahan. Crash di antaranya menghilangkan data yang sudah di-ack.
- Replay yang nggak idempoten. Record "tambah 1 ke counter" diterapkan dua kali menghasilkan 2. Simpan nilai absolut atau nomor urut (LSN) per halaman.
- Contoh tandingan: tim mengubah fsync per write jadi per detik "karena throughput naik 20x" tanpa memberi tahu produk. Saat node mati, 1 detik write pembayaran yang sudah di-ack hilang. Angka throughput benar, tapi kontrak yang dijual ke pemakai berubah diam-diam.
Latihan
WAL berisi record LSN 1 sampai 10. Sistem fsync tiap 4 record: setelah record 4, 8. Record 9 dan 10 sudah di-write() dan di-ack ke klien, belum di-fsync. Checkpoint terakhir mencakup LSN 4.
- Mesin mati listrik setelah record 10. Record mana yang dipulihkan, dan record mana yang di-ack tapi hilang?
- Bagaimana jika yang mati hanya prosesnya (OS tetap hidup)?
- Ubah satu hal agar tidak ada record ter-ack yang hilang saat mati listrik, dan sebut biayanya.
Lihat pembahasan
-
Mati listrik: record 1-8 sudah di-fsync, jadi dipulihkan. Replay mulai dari setelah checkpoint (LSN 5) sampai 8. Record 9 dan 10 berada di page cache saja, jadi hilang. Kedua record itu sudah di-ack, jadi ini pelanggaran dari sudut pandang klien, walau sesuai kontrak "fsync tiap 4 record".
-
Hanya proses mati: page cache OS tetap utuh dan akan ditulis kernel. Replay membaca record 5-10 dan memulihkan semuanya. Klien nggak kehilangan apa pun.
-
Perbaikan: jangan ack sebelum fsync (fsync per write, atau group commit yang ack setelah fsync batch). Biayanya latensi tiap write bertambah satu fsync (sekitar 0,1-1 ms di SSD berdaya terlindungi, jauh lebih besar di HDD), dan throughput bergantung pada kemampuan batching. Kamu juga harus tetap mengasumsikan perangkat jujur soal flush.
Terkait
- Sebelumnya: M09.01 Bangun dictionary berbasis file.
- Lanjut ke M09.03 Bandingkan struktur penyimpanan.
- Acknowledgment tulis di replikasi (M08) dan transaksi (M05) memakai ide sama di level berbeda. Lab L09 dan proyek B05 belum tersedia.
Catatan
- Asumsi: satu node, filesystem lokal seperti ext4 atau XFS, SSD atau HDD biasa. Angka fsync adalah rentang kasar dan wajib diukur di perangkatmu.
- Yang nggak dibahas: perilaku persis tiap filesystem (mode
data=ordered,O_DSYNC,O_DIRECT) dan RAID controller dengan baterai. - Perilaku fsync yang gagal sudah dibahas luas di komunitas PostgreSQL, tapi detailnya bergantung versi kernel.
- Terakhir ditinjau: 26 Oktober 2026.