Featured image of post Mengapa Data Anda Selamat Saat Listrik Padam di Tengah Penyimpanan: Rekayasa Konsistensi Crash

Mengapa Data Anda Selamat Saat Listrik Padam di Tengah Penyimpanan: Rekayasa Konsistensi Crash

Eksplorasi teknis mendalam tentang konsistensi crash (Crash Consistency) penyimpanan: mengapa menambahkan satu blok 4 KB memerlukan beberapa penulisan disk non-atomik, pengurutan ulang oleh page cache OS dan FTL SSD, batas fsck dan Soft Updates, Write-Ahead Logging (WAL) dan ARIES, mode journaling ext4, pohon Merkle Copy-on-Write (ZFS/Btrfs), serta jebakan fsync().

1. Pendahuluan: Ladang Ranjau di Balik Penambahan Satu Blok File 4 KB

Saat Anda menekan Ctrl+S di editor teks atau mengonfirmasi transfer bank, aplikasi Anda mengeluarkan panggilan sistem write(). Dari sudut pandang pemrogram aplikasi, menyimpan data tampak seperti satu langkah tunggal yang instan dan tak terpisahkan (atomic).

Namun, di balik layar kernel sistem operasi, sistem berkas (file system), mesin penyimpanan basis data, dan pengontrol SSD, tersembunyi salah satu masalah paling rumit dalam rekayasa sistem komputer: Masalah Konsistensi Crash (Crash Consistency Problem).

Mari kita tinjau operasi paling sederhana dalam sistem berkas UNIX klasik (seperti UFS atau ext2): menambahkan (append) satu blok data baru berukuran $4\,\text{KB}$ ($D_{\text{new}}$) ke akhir file yang sudah ada. Bahkan operasi sepele ini memerlukan pembaruan setidaknya tiga struktur yang terpisah secara fisik pada media penyimpanan:

  1. Bitmap Data (Data Bitmap $B$): mengubah bit alokasi untuk nomor blok fisik $k$ dari $0$ (bebas) menjadi $1$ (terpakai).
  2. Inode ($I$): memperbarui catatan metadata file—menambah ukuran file size, memperbarui waktu modifikasi mtime, dan menulis alamat blok fisik $k$ ke dalam larik penunjuk blok inode.
  3. Blok Data ($D_{\text{new}}$): menulis muatan aktual $4\,\text{KB}$ milik pengguna ke sektor fisik blok $k$.

Di sinilah kita menghadapi asimetri fisik mendasar perangkat keras: memori utama (DRAM) sangat cepat tetapi volatil (hilang saat listrik mati), sedangkan disk dan SSD NAND bersifat non-volatil tetapi tidak mampu memperbarui tiga lokasi yang berjauhan $(B, I, D_{\text{new}})$ secara bersamaan dalam satu tindakan fisik atomik. Jika listrik tiba-tiba padam di tengah ketiga penulisan ini ($2^3 - 2 = 6$ kemungkinan status penulisan parsial), sistem berkas akan bangun saat reboot dalam kondisi kerusakan parah:

Tersimpan di DiskHilang di RAMInkonsistensi Crash yang Terjadi & Tingkat Keparahan
Hanya $D_{\text{new}}$$B,\; I$Kehilangan Data Jinak: blok data baru ada di disk, tetapi baik inode maupun bitmap tidak mengetahuinya. Struktur sistem berkas tetap utuh.
Hanya $B$$I,\; D_{\text{new}}$Kebocoran Ruang (Space Leak / Blok Yatim): blok $k$ ditandai terpakai ($1$) di bitmap $B$, tetapi tidak ada inode yang menunjuk ke sana. Kapasitas tersebut bocor secara permanen.
Hanya $I$ (atau $I + D_{\text{new}}$)$B$Alokasi Ganda (Double Allocation — Kerusakan Metadata Katastrofik): inode $I$ menunjuk ke blok $k$, tetapi bitmap $B$ masih menandainya bebas ($0$). Kelak file kedua akan mengalokasikan blok $k$ dan kedua file saling menghancurkan data!
$I$ dan $B$$D_{\text{new}}$Kebocoran Data Usang (Stale Data Exposure — Kerentanan Keamanan Kritis): inode dan bitmap telah diperbarui, tetapi $D_{\text{new}}$ belum tertulis. Membaca file tersebut akan mengembalikan data lama milik pengguna lain yang sudah dihapus (seperti kata sandi atau kunci privat) yang sebelumnya menempati sektor $k$!

2. Mengapa Penulisan Tidak Mencapai Disk Sesuai Urutan Program Anda

Dalam arsitektur modern, urutan kronologis pemanggilan write() hampir tidak pernah sama dengan urutan fisik penulisan bit ke piringan magnetik atau sel flash NAND karena adanya empat lapisan buffer volatil dan pengurutan ulang (Reordering):

  flowchart TD
    APP["1. Ruang Pengguna Aplikasi<br/>Buffer Runtime Bahasa (stdio fwrite) / Buffer Pool DB"]
    PC["2. Page Cache Kernel OS (Dirty DRAM Pages)<br/>Write-Back Caching: Menggabungkan & Menunda Penulisan Selama Detik"]
    SCHED["3. Lapisan Blok OS & Penjadwal I/O (mq-deadline / BFQ)<br/>Mengurutkan Ulang Permintaan Berdasarkan LBA untuk Meminimalkan Latensi"]
    DEV["4. Cache DRAM Volatil Internal Perangkat & Antrean NCQ/NVMe<br/>Pengontrol FTL SSD Mengurutkan Ulang Penulisan Lintas Kanal NAND"]
    MEDIA[("5. Media Fisik Non-Volatil<br/>Sel Flash NAND / Piringan Magnetik (Hanya Ini yang Tahan Mati Listrik!)")]

    APP -->|"Syscall write()"| PC
    PC -->|"Flush Latar Belakang / fsync()"| SCHED
    SCHED -->|"Perintah NVMe / SATA"| DEV
    DEV -->|"Penghalang FLUSH CACHE / FUA"| MEDIA

Untuk menjamin urutan dan durabilitas, sistem harus mengeluarkan perintah penghalang perangkat keras eksplisit (NVMe Flush, SATA FLUSH CACHE EXT, atau bit FUA: Force Unit Access) yang memaksa SSD menguras buffer DRAM volatil internalnya ke sel NAND non-volatil.


3. Generasi 1: Pembaruan di Tempat, fsck, dan Soft Updates

  1. fsck (File System Consistency Check) dan Tembok Skalabilitas: Sistem berkas UNIX awal (FFS, ext2) memperbaiki inkonsistensi pasca-crash dengan memindai seluruh inode, bitmap, dan pohon direktori di disk sebelum mount (fsck). Karena waktu eksekusi fsck sebanding dengan total kapasitas disk $O(N_{\text{disk}})$, memindai penyimpanan multi-terabyte modern memakan waktu berjam-jam atau berhari-hari—meskipun hanya satu file $4\,\text{KB}$ yang sedang ditulis saat listrik mati!
  2. Soft Updates (Ganger & McKusick 1994): Digunakan di FreeBSD UFS, teknik ini menghindari jurnal dengan melacak grafik ketergantungan penunjuk secara ketat di memori dan memutus ketergantungan siklik melalui operasi roll-back dan roll-forward sementara di RAM sebelum menulis blok ke disk.

4. Tulang Punggung Penyimpanan Modern: Write-Ahead Logging (WAL) dan Journaling

Untuk memangkas waktu pemulihan dari $O(N_{\text{disk}})$ menjadi sebanding केवल dengan transaksi aktif saat crash $O(N_{\text{active}})$ (hanya birkaç milidetik!), sistem berkas modern (ext3/ext4, XFS, NTFS) dan basis data menggunakan Write-Ahead Logging (WAL) / Journaling:

  1. Journal Write: Sebelum menyentuh lokasi asli di disk (Home Locations), sistem menambahkan secara berurutan ke Journal sebuah blok deskriptor awal TxB (dengan TID) diikuti salinan blok yang diperbarui $(I, B, D_{\text{new}})$.
  2. Journal Commit (Titik Komit Atomik): Setelah penghalang flush perangkat keras memastikan langkah 1 mencapai media fisik, sistem menulis satu blok komit TxE (Transaction End). Penulisan satu sektor TxE ini adalah sakelar atomik yang mengesahkan seluruh transaksi!
  3. Checkpoint: Setelah TxE aman di disk, $(I, B, D_{\text{new}})$ disalin secara asinkron ke lokasi asli (Home Locations) di latar belakang.
  flowchart LR
    subgraph Journal ["Jurnal Melingkar di Disk (Sequential Append)"]
        TXB["1. TxB (TID=42)<br/>Blok Deskriptor"] --> JB["2. Bitmap B Tercatat"]
        JB --> JI["3. Inode I Tercatat"]
        JI --> JD["4. Data D_new Tercatat"]
        JD ==>|"★Penghalang Flush Perangkat Keras"| TXE["5. TxE (TID=42)<br/>Blok Komit Atomik"]
    end

    subgraph Home ["Lokasi Asli di Disk (Home Locations)"]
        HB["Bitmap Data Asli"]
        HI["Tabel Inode Asli"]
        HD["Blok Data Asli"]
    end

    TXE ==>|"6. Checkpoint (Tulis Asinkron)"| HB
    TXE ==>|"6. Checkpoint"| HI
    TXE ==>|"6. Checkpoint"| HD
  • Listrik Padam SEBELUM TxE: Lokasi asli belum tersentuh; saat reboot, catatan jurnal yang belum selesai cukup dibuang.
  • Listrik Padam SETELAH TxE: Saat reboot, manajer pemulihan cukup memutar ulang (Redo replay, bersifat idempoten) blok dari jurnal ke lokasi asli dalam hitungan milidetik. ext4 modern bahkan menghilangkan jeda penghalang pertama menggunakan Checksum Jurnal (CRC32C) di dalam TxE.

Untuk menghindari penulisan ganda setiap blok data ($2\times$ Write Amplification), mode bawaan data=ordered pada ext4 hanya mencatat metadata $(I, B)$ di jurnal, tetapi mewajibkan blok data $D_{\text{new}}$ selesai ditulis ke lokasi aslinya terlebih dahulu SEBELUM mengizinkan blok komit metadata TxE ditulis ke jurnal. Dalam basis data relasional, protokol ARIES (Mohan et al., 1992) mengelola kebijakan Steal / No-Force melalui pageLSN dan tiga fase: Analysis, Redo (Repeating History), serta Undo (dengan Compensation Log Records, CLRs).


5. Generasi 3: Copy-on-Write (CoW) dan Pohon Merkle (ZFS / Btrfs)

Sistem berkas generasi baru (ZFS, Btrfs, APFS) melarang penimpaan blok di tempat (In-Place Overwrite) dan mengadopsi Copy-on-Write (CoW / Shadow Paging):

  1. Setiap blok data yang diubah $D_{2,\text{new}}$ dan inode induknya $I_{v2}$ selalu ditulis ke ruang bebas baru, membiarkan blok lama $D_{2,\text{old}}$ tetap utuh.
  2. Setelah seluruh cabang baru aman di disk, satu penulisan atomik memperbarui blok akar (Uberblock di ZFS) dari $U_1$ ke $U_2$.
  3. Keunggulan: Pemulihan instan $0$ detik tanpa pemutaran ulang jurnal, Snapshot $O(1)$ tanpa biaya salin, dan penyembuhan mandiri (Self-Healing) terhadap kerusakan bit (Bit Rot) melalui Pohon Merkle dengan checksum kriptografi 256-bit di setiap penunjuk induk.

6. Jebakan fsync() bagi Pengembang dan Pengujian Injeksi Kesalahan

Bahkan pada sistem berkas berjurnal atau CoW, mengganti file lewat rename("config.json.tmp", "config.json") tanpa memanggil fsync() terlebih dahulu akan menghasilkan file kosong berukuran $0$ byte pasca-mati listrik di bawah Delayed Allocation (ext4), karena perubahan nama metadata dikomit sebelum halaman data di cache ditulis ke disk!

Protokol 6 Langkah Lengkap untuk Penyimpanan File Atomik Tahan-Crash: (1) write() ke file sementara $\to$ (2) fsync(fd) pada file sementara $\to$ (3) close(fd) $\to$ (4) rename() atomik $\to$ (5) buka direktori induk dan panggil fsync(dir_fd) untuk menyimpan entri direktori $\to$ (6) close(dir_fd).

Sebagaimana dibuktikan oleh insiden fsyncgate PostgreSQL pada tahun 2018 (ketika kernel Linux menandai halaman kotor sebagai Clean setelah kegagalan I/O EIO saat fsync(), yang затем diperbaiki dengan errseq_t), rekayasa penyimpanan modern mengandalkan kerangka kerja injeksi kesalahan sistematis (Fault Injection) seperti ALICE dan CrashMonkey untuk menguji seluruh permutasi pengurutan ulang perangkat keras terhadap pemadaman listrik.

comments powered by Disqus