Masalah dan Kapan Ini Berlaku
Sebuah aplikasi web memiliki empat jenis respons GET:
/index.htmlmemiliki URL yang stabil, berubah pada setiap deployment, dan mereferensikan aset statis saat ini./assets/app.8f3c.jsmemiliki hash konten di nama filenya, sehingga perubahan konten juga mengubah URL-nya./api/catalogadalah katalog produk publik. Server memilih representasi dariAccept-Language, dan produk mentolerir data basi (staleness) sekitar 60 detik./api/cartdipersonalisasi untuk pengguna yang terautentikasi dan tidak boleh digunakan kembali oleh CDN atau pengguna lain.
Setelah sebuah rilis, beberapa pengguna tetap menggunakan HTML lama dan meminta file JavaScript lama yang telah dihapus oleh deployment. Katalog terkadang muncul dalam bahasa yang salah. Tim juga menganggap no-cache sebagai "jangan simpan", sehingga perbaikan yang diajukan tidak mendeskripsikan perilaku yang diinginkan. Jelaskan bagaimana cache privat browser, cache bersama CDN, freshness, revalidasi, dan cache keys berinteraksi. Kemudian rancang header respons, urutan deployment, dan metode verifikasi untuk keempat respons tersebut.
Nilai 60 detik adalah asumsi wawancara, bukan pengukuran produksi dari sebuah sumber. Cakupan dasar mencakup HTTP cache. Service Worker Cache, cache memori aplikasi, dan cache data framework akan muncul dalam pertanyaan lanjutan. Ini adalah pertanyaan frontend, full-stack, atau performa web yang keahlian intinya adalah menganalisis perilaku browser dan platform HTTP, sehingga kategorinya adalah frontend.
Apa yang Dievaluasi oleh Pewawancara
Sinyal pertama adalah apakah kandidat menyusun kebijakan dari empat pertanyaan: bolehkah respons disimpan, siapa yang boleh menggunakannya kembali, berapa lama ia tetap segar (fresh), dan apa yang terjadi setelah ia menjadi basi (stale)? Menghafal direktif Cache-Control sering kali mencampuradukkan penyimpanan dengan penggunaan kembali langsung. Jawaban yang kuat mendefinisikan batasan data sebelum menulis header.
Sinyal kedua adalah pembedaan yang tepat antara no-cache dan no-store. no-cache mengizinkan penyimpanan tetapi mewajibkan validasi dengan server asal (origin) sebelum setiap penggunaan kembali. no-store menginstruksikan cache untuk tidak menyimpan respons. Menerapkan no-store pada setiap respons dinamis akan menghilangkan manfaat conditional-request dan beberapa kapabilitas browser; direktif ini hanya boleh digunakan jika persyaratannya memang melarang penyimpanan HTTP-cache.
Sinyal ketiga adalah model freshness dan validasi yang benar. Respons yang masih segar biasanya dapat digunakan kembali tanpa menghubungi server upstream. Ketika respons sudah basi atau validasi diperlukan, klien dapat mengirimkan If-None-Match atau If-Modified-Since. Jika representasi tidak berubah, server mengembalikan 304 Not Modified; cache memperbarui metadata dan menggunakan kembali body yang disimpan.
Terakhir, pewawancara mencari penalaran terkait deployment dan cache-key. Header aset statis yang sempurna tidak dapat memperbaiki proses rilis di mana HTML lama menunjuk ke file yang sudah dihapus. Menghilangkan Vary: Accept-Language juga dapat membuat cache bersama menggunakan representasi bahasa Mandarin untuk permintaan bahasa Inggris ke URL yang sama. Jawaban yang kuat mencakup header, masa pakai aset, varian representasi, dan pengujian yang dapat direproduksi.
Pertanyaan Klarifikasi Sebelum Menjawab
- Apakah keempat respons melewati CDN?
s-maxagemengontrol cache bersama dan tidak mengatur freshness browser privat. Tanpa CDN, kebijakan API publik bisa lebih sederhana. - Apakah bahasa katalog berasal dari URL atau request header?
/api/catalog?locale=zh-CNsudah menggunakan URI yang berbeda sebagai bagian dari cache key. URL tunggal yang representasinya bergantung padaAccept-Languagememerlukan fieldVaryyang sesuai. - Bolehkah browser menyimpan respons keranjang dan memvalidasinya nanti? Jika cache privat satu pengguna boleh menyimpannya, gunakan
private, no-cache. Jika kebijakan keamanan atau produk melarang penyimpanan HTTP-cache sama sekali, gunakanno-store. - Berapa lama aset lama ber-hash dapat tetap tersedia? Penghapusan langsung akan merusak tab lama, navigasi riwayat (history), deployment bertahap, dan versi rollback. Retensi harus mencakup periode di mana entry point lama masih dapat diakses ditambah jendela rollback.
- Berapa tingkat kebasian (staleness) data katalog yang diizinkan? "Maksimal 60 detik" harus membedakan freshness normal dari penggunaan data basi ekstra selama revalidasi atau kesalahan. Jika konten yang lebih lama tidak pernah dapat diterima,
stale-while-revalidatedanstale-if-errortidak dapat memperpanjang penggunaan kembali. - Apakah Service Worker terpasang? Service Worker dapat mencegat permintaan dan mengembalikan entri Cache Storage sebelum HTTP cache normal berpartisipasi. Ini harus diperiksa sebagai lapisan terpisah.
Kerangka Jawaban 30 Detik
"Untuk setiap respons, pertama-tama saya memutuskan apakah ia boleh disimpan, siapa yang boleh menggunakannya kembali, berapa lama ia segar, dan bagaimana ia divalidasi setelah itu. Aset JavaScript dengan content hash mendapatkan max-age satu tahun ditambah immutable, dengan jaminan bahwa bita pada URL yang sama tidak pernah berubah. HTML mendapatkan no-cache dan ETag, sehingga browser boleh menyimpannya tetapi memvalidasinya pada penggunaan kembali normal. Katalog publik dapat memberikan s-maxage=60 kepada CDN, menambahkan ETag, dan menggunakan Vary: Accept-Language ketika URL yang sama melayani beberapa bahasa. Keranjang setidaknya adalah private; gunakan private, no-cache jika penyimpanan browser diizinkan, dan no-store hanya jika penyimpanan itu sendiri dilarang. Deploy aset baru sebelum HTML dan pertahankan file lama ber-hash. Kemudian uji respons 200 tanpa cache, fresh hit, revalidasi 304, varian bahasa, dan pemutaran ulang (replay) HTML lama."
Pembahasan Mendalam Langkah demi Langkah
Langkah 1: Modelkan penyimpanan, penggunaan kembali, freshness, dan validasi secara terpisah
HTTP caching bukanlah satu sakelar tunggal. Jalur permintaan membutuhkan setidaknya empat keputusan:
- Penyimpanan:
no-storemelarang penyimpanan. Respons lain tunduk pada metode, status, otorisasi, dan aturan caching eksplisit. - Cakupan penggunaan kembali: Browser adalah cache privat yang biasanya melayani satu pengguna. CDN dan proxy adalah cache bersama (shared cache).
privatemencegah penggunaan kembali oleh cache bersama, sedangkanpublicdapat secara eksplisit mengizinkannya. - Freshness:
max-age=Nbiasanya menentukan berapa lama cache privat dan bersama dapat menggunakan kembali respons secara langsung. Di cache bersama,s-maxage=Nmenimpamax-age. Usia respons saat ini juga mencerminkan metadata sepertiDatedanAge. - Perilaku setelah basi: Cache dapat mengirimkan conditional request menggunakan validator. Representasi yang tidak berubah menghasilkan 304 dan penggunaan kembali body yang disimpan; representasi yang berubah menghasilkan 200 dengan konten baru.
Oleh karena itu, "cache hit" mendeskripsikan dua biaya yang berbeda. Fresh hit tidak memerlukan konfirmasi upstream. Revalidated hit masih menimbulkan round trip jaringan dan kerja validasi, meskipun menghindari transfer seluruh body respons. Jawaban wawancara harus menyebutkan jenis hit mana yang dimaksud.
Langkah 2: Berikan masa pakai freshness yang panjang untuk aset statis ber-hash
Jika proses build menjamin bahwa konten yang berubah menerima URL baru, aset statis dapat mengembalikan:
Cache-Control: public, max-age=31536000, immutable31536000 detik setara dengan satu tahun. immutable menyatakan bahwa resource tidak akan berubah saat masih segar dan dapat menghindari validasi yang tidak perlu pada beberapa jalur reload. Bagian yang penting bukanlah angka satu tahun tersebut, melainkan sepasang invarian:
- Bita pada URL ber-hash tertentu tidak pernah berubah.
- Versi baru menggunakan URL baru yang direferensikan oleh HTML baru.
Menimpa file pada URL berumur panjang yang sama akan membuat klien memiliki konten lama. Menghapus file lama ber-hash juga merusak tab lama, riwayat browser, edge node yang diperbarui bertahap, dan rilis rollback yang mungkin masih memintanya. Proses rilis yang tangguh akan mengunggah aset baru terlebih dahulu, memverifikasi bahwa aset tersebut dapat diakses, memublikasikan HTML setelahnya, dan mempertahankan aset lama ber-hash sampai entry point lama dan versi rollback tidak lagi dapat diakses.
Langkah 3: Revalidasi URL HTML yang stabil pada setiap penggunaan kembali normal
HTML entri tidak dapat secara alami mengubah URL-nya pada setiap representasi, sehingga ia dapat mengembalikan:
Cache-Control: no-cache
ETag: "index-v184"Browser boleh menyimpan dokumen, tetapi penggunaan kembali normal memerlukan validasi. Permintaan selanjutnya mencakup:
If-None-Match: "index-v184"Server mengembalikan 304 jika representasi tidak berubah, atau 200 dengan HTML baru dan ETag baru setelah rilis. ETag mengidentifikasi versi dari suatu representasi. ETag tidak harus berupa hash konten, tetapi harus berubah ketika representasi berubah. Respons juga dapat membawa Last-Modified. Jika permintaan berisi If-None-Match dan If-Modified-Since, kondisi ETag yang lebih tepat diprioritaskan di bawah semantik HTTP.
no-cache bukan berarti "jangan simpan". Gunakan no-store hanya jika produk mengharuskan HTML tidak masuk ke HTTP cache sama sekali, dengan konsekuensi hilangnya revalidasi 304. Fitur back/forward cache pada browser juga dapat memulihkan snapshot halaman tanpa melalui jalur revalidasi HTTP biasa, sehingga masalah "tombol Back menampilkan halaman lama" tidak dapat didiagnosis hanya dari Cache-Control.
Langkah 4: Kontrol perilaku browser dan CDN secara independen untuk API publik
Misalkan katalog biasanya mentolerir keterlambatan 60 detik. Browser harus melakukan konfirmasi pada setiap permintaan, sedangkan CDN dapat langsung menggunakan kembali respons hingga 60 detik:
Cache-Control: public, max-age=0, s-maxage=60
ETag: "catalog-v927"
Vary: Accept-LanguageSetiap direktif memiliki fungsi terpisah:
max-age=0memungkinkan browser menyimpan respons tetapi langsung membuatnya basi, sehingga penggunaan berikutnya masuk ke jalur validasi.s-maxage=60memungkinkan cache bersama menggunakannya kembali secara langsung selama 60 detik.ETagmemungkinkan browser atau CDN membuat conditional request setelah basi.Vary: Accept-Languagemenginstruksikan cache untuk menyertakan request header bahasa saat memilih respons yang disimpan.
Jika produk menerima data basi selama 30 detik lagi sebagai kompensasi atas latensi validasi yang lebih rendah, tambahkan stale-while-revalidate=30. Direktif standar ini memengaruhi setiap cache yang mendukungnya, bukan hanya CDN. Jika hanya perilaku CDN yang ingin diubah, gunakan pengaturan khusus CDN eksplisit dari penyedia yang dipilih. Ketika jaminan bisnis menyatakan konten tidak boleh lebih lama dari 60 detik, jangan tambahkan jendela basi ini. Ini adalah kompromi eksplisit antara freshness dan latensi yang lebih rendah, bukan optimasi bawaan.
Sering kali lebih terprediksi untuk menempatkan lokal (locale) di URL, seperti /api/catalog?locale=zh-CN. Pencatatan log, pemanasan (warming), pembatalan (invalidation), dan cache keys kemudian menjadi eksplisit, dan respons tidak lagi memerlukan Accept-Language untuk membedakan representasi pada satu URL. Hindari menambahkan Vary: User-Agent secara sembarangan; tingginya jumlah nilai yang mungkin ada dapat merusak efektivitas cache bersama.
Langkah 5: Lindungi keranjang belanja yang dipersonalisasi
Keranjang tidak boleh disajikan dari cache bersama ke pengguna lain. Jika browser boleh menyimpan respons pengguna saat ini tetapi harus mengonfirmasinya sebelum digunakan kembali, kembalikan:
Cache-Control: private, no-cache
ETag: "cart-v19"private membatasi penyimpanan hanya pada cache privat. no-cache memerlukan validasi sebelum digunakan kembali. Jangan mencoba mengisolasi pengguna dengan Vary: Cookie: nilai cookie memiliki kardinalitas yang sangat tinggi dan menciptakan risiko cache-key serta privasi. Respons yang dipersonalisasi harus secara eksplisit mencegah penggunaan kembali bersama.
Jika persyaratan keamanannya adalah "respons tidak boleh ditulis ke HTTP cache mana pun", gunakan:
Cache-Control: no-storeTidak perlu menambahkan private, no-cache, max-age=0. Direktif yang tumpang tindih atau lebih restriktif tidak membuat kebijakan lebih aman; hal itu justru membuat niatnya lebih sulit diaudit. Selain itu, menambahkan no-store ke respons mendatang tidak menghapus salinan segar yang disimpan sebelumnya. Respons insiden mungkin memerlukan CDN purge, URL berversi, masa kedaluwarsa entri lama, atau mekanisme pembersihan browser yang ditargetkan.
Langkah 6: Hubungkan kebijakan cache dengan protokol deployment
Kegagalan JavaScript yang terhapus tidak dapat diperbaiki hanya dengan header. Protokol deployment harus:
- Mengunggah setiap aset baru yang ber-hash.
- Memverifikasi dari lokasi edge produksi bahwa setiap aset dapat diakses dan memiliki tipe MIME, kompresi, dan header yang benar.
- Memublikasikan HTML yang mereferensikan URL baru tersebut.
- Mempertahankan aset lama ber-hash selama jendela tab lama, rilis bertahap, dan rollback.
- Memulihkan HTML lama selama rollback saat aset lamanya masih ada.
- Menghapus aset hanya setelah tidak ada lagi entry point yang dapat diakses yang mereferensikannya.
Jika beberapa node menghasilkan HTML, ETag mereka juga harus sesuai dengan representasinya. ETag yang berbeda untuk konten yang identik akan menghasilkan respons 200 yang tidak perlu. Menggunakan kembali strong ETag yang sama untuk konten yang berbeda dapat menghasilkan respons 304 yang salah. Kompresi, bahasa, dan perubahan representasi lainnya juga memerlukan cache keys yang benar atau penanganan Vary.
Langkah 7: Verifikasi urutan permintaan alih-alih satu kali reload
Bangun matriks permintaan di DevTools browser atau dengan curl:
curl -I "$ORIGIN/index.html"
curl -I -H 'If-None-Match: "index-v184"' "$ORIGIN/index.html"
curl -I -H 'Accept-Language: zh-CN' "$ORIGIN/api/catalog"
curl -I -H 'Accept-Language: en-US' "$ORIGIN/api/catalog"Atur ORIGIN ke origin situs target sebelum menjalankan perintah. Verifikasi semua hal berikut:
- Permintaan awal mengembalikan 200, header yang dimaksudkan, dan validator.
- Aset statis tidak menghubungi upstream saat masih segar, dan URL yang diubah mengambil file baru.
- Conditional request untuk HTML yang tidak berubah mengembalikan 304, sedangkan deployment menghasilkan 200 dengan ETag baru.
Agemeningkat pada CDN hit katalog, dan validasi terjadi setelah masa freshness bersama berakhir.- Dua permintaan bahasa menerima body yang berbeda dan respons menyertakan
Vary. - Keranjang tidak memiliki konfigurasi yang mengizinkan penggunaan kembali oleh cache bersama.
- Memutar ulang (replay) HTML lama tetap mengambil aset ber-hash yang direferensikannya dengan respons 200.
- DevTools "Disable cache" berguna untuk diagnosis jaringan tetapi tidak menggantikan pengujian jalur cache yang sebenarnya.
Uji reload biasa, force reload, tab baru, navigasi back/forward, dan jalur dengan Service Worker secara terpisah. Force reload mengubah direktif cache permintaan, sehingga hanya menguji jalur tersebut tidak mereproduksi perilaku pengguna normal.
Contoh Jawaban Berkualitas Tinggi
"Saya tidak akan memulai dengan mendaftar header. Untuk setiap respons, saya akan menjawab empat pertanyaan: bisakah disimpan, siapa yang bisa menggunakannya kembali, berapa lama ia segar, dan bagaimana ia divalidasi setelah itu?
File JavaScript menggunakan content hash, sehingga URL-nya berubah bersamaan dengan bitanya. Saya akan mengembalikan public, max-age=31536000, immutable dan menjadikan 'jangan pernah menimpa konten di URL yang sama' sebagai invarian rilis. HTML entri memiliki URL yang stabil, jadi saya akan menggunakan no-cache dengan ETag. Dokumen ini dapat disimpan, tetapi penggunaan kembali normal memvalidasinya; konten yang tidak berubah mendapatkan 304, sedangkan konten yang berubah mendapatkan HTML baru dan ETag baru. no-cache mengizinkan penyimpanan. no-store adalah direktif yang melarangnya.
Katalog publik mengizinkan keterlambatan sekitar 60 detik. Saya akan menggunakan max-age=0 untuk browser dan s-maxage=60 untuk CDN. Jika produk menerima data basi selama 30 detik lagi sebagai kompensasi menyembunyikan latensi revalidasi, saya akan menambahkan stale-while-revalidate=30; jika tidak, saya akan menghilangkannya. Jika URL /api/catalog yang sama bervariasi berdasarkan Accept-Language, ia memerlukan Vary: Accept-Language. Menempatkan lokal di URL bahkan lebih mudah dipahami.
Keranjang belanja secara eksplisit bersifat privat bagi pengguna. Jika penyimpanan dan validasi browser dapat diterima, saya akan mengembalikan private, no-cache. Jika kebijakan melarang penyimpanan, saya hanya akan mengembalikan no-store. Saya tidak akan mengizinkan respons yang dipersonalisasi berada di cache bersama atau menggunakan cookie berkardinalitas tinggi sebagai cache key bersama.
HTML lama yang meminta bundle yang sudah dihapus juga mengungkap bug protokol deployment. Saya akan mengunggah aset baru terlebih dahulu, memublikasikan HTML kedua, dan mempertahankan aset lama ber-hash selama jendela akses lama dan rollback. Verifikasi saya akan menguji respons 200 awal, fresh hit, 304 berbasis ETag, 200 pasca-deploy, dua representasi bahasa, batasan cache bersama untuk keranjang, dan replay HTML lama—bukan hanya satu kali force reload."
Kesalahan Umum
- Mengartikan
no-cachesebagai tidak disimpan → direktif ini mengizinkan penyimpanan dan mewajibkan validasi sebelum penggunaan kembali → gunakanno-storeuntuk melarang penyimpanan, atau pertahankanno-cacheditambah validator untuk memanfaatkan 304. - Memberikan masa pakai freshness satu tahun untuk setiap resource → HTML dengan URL stabil dapat terus menunjuk ke rilis lama → khususkan masa freshness panjang untuk aset berbasis hash konten dan validasi dokumen entri.
- Menghapus aset lama ber-hash segera setelah rilis → tab lama, riwayat, dan rilis rollback mungkin masih mereferensikannya → publikasikan aset sebelum entry point dan pertahankan selama jendela akses masih ada.
- Menetapkan ETag tanpa kebijakan freshness → cache tidak memiliki kejelasan masa pakai penggunaan kembali langsung dan dapat melakukan validasi yang tidak perlu → definisikan freshness dan validasi secara bersamaan.
- Menganggap respons 304 gratis tanpa biaya → ini menghemat body respons tetapi tetap memakan biaya round trip dan pekerjaan validasi → pilih masa pakai freshness ketika latensi dan toleransi data membenarkannya.
- Melayani beberapa bahasa di satu URL tanpa
Vary→ cache bersama dapat menggunakan kembali representasi yang salah → letakkan bahasa di URL atau tambahkanVary: Accept-Languageke cache key. - Menggunakan
Vary: Cookieuntuk mengisolasi keranjang → kunci berkardinalitas tinggi mengurangi hit rate dan meningkatkan risiko privasi → cegah penggunaan kembali bersama denganprivateatauno-store. - Mengasumsikan direktif
no-storeyang baru ditambahkan akan menghapus entri lama → header baru tidak dapat mencapai respons segar yang masih digunakan kembali secara langsung → lakukan purge, beri versi baru, atau tunggu entri lama kedaluwarsa. - Hanya menguji dengan DevTools force reload → force reload mengubah direktif cache permintaan → uji navigasi biasa, fresh hits, revalidasi, pemulihan riwayat, dan jalur Service Worker.
Pertanyaan Lanjutan
Lanjutan 1: Bolehkah katalog menyajikan data basi ketika CDN atau origin mengalami kegagalan?
Bagi toleransi bisnis menjadi dua jendela: maksimal 60 detik selama operasi normal, dan kelonggaran terpisah selama terjadi respons 5xx dari origin. Jika data yang lebih lama dapat diterima selama error, evaluasi stale-if-error dengan durasi terbatas. Jika harga atau inventaris tidak boleh basi, tampilkan pesan kesalahan sebagai gantinya. stale-while-revalidate menyembunyikan latensi revalidasi di latar belakang; stale-if-error mengizinkan penggunaan kembali data basi selama terjadi kegagalan. Keduanya menyelesaikan masalah yang berbeda.
Lanjutan 2: Kapan server harus menggunakan strong atau weak ETag?
Strong ETag berarti dua representasi identik secara bita-demi-bita dan tepat digunakan jika range request yang presisi atau identitas bita sangat penting. Weak ETag diawali dengan W/ dan merepresentasikan kesetaraan semantik terlepas dari kemungkinan perbedaan bita, yang cocok untuk perubahan pemformatan yang tidak esensial dalam output yang dirender server. Validasi cache dapat menggunakan keduanya, tetapi kontrol konkurensi If-Match dan range requests memerlukan pemeriksaan aturan perbandingan kuat (strong-comparison).
Lanjutan 3: Mengapa DevTools menampilkan "from memory cache" atau "from disk cache" tanpa menunjukkan 304?
Respons yang masih segar dapat digunakan kembali tanpa conditional request apa pun, sehingga tidak ada respons 304. Memori dan disk mendeskripsikan pilihan penyimpanan yang dilakukan oleh browser, bukan semantik HTTP yang terpisah. Periksa Cache-Control, Age, apakah permintaan benar-benar dikirim, dan usia respons saat ini alih-alih menyimpulkan seluruh kebijakan hanya dari satu label DevTools.
Lanjutan 4: Mengapa perubahan response headers tidak berpengaruh setelah menambahkan Service Worker?
Service Worker mungkin mengembalikan respons Cache Storage lama sebelum permintaan jaringan terjadi, sehingga HTTP cache biasa tidak pernah berpartisipasi. Periksa fetch handler, versi Cache Storage, pembersihan saat aktivasi, dan timing klaim klien. Beri versi pada nama cache atau manifes resource, lalu verifikasi bagaimana halaman yang dikontrol oleh Service Worker lama diperbarui.
Lanjutan 5: Bagaimana HTML yang dipersonalisasi dan mereferensikan aset ber-hash harus di-cache?
HTML dapat menggunakan Cache-Control: private, no-cache untuk mencegah penggunaan kembali bersama dan melakukan validasi sebelum digunakan kembali oleh cache privat. Aset statis ber-hash yang identik untuk setiap pengguna tetap dapat menggunakan caching publik jangka panjang. Halaman yang terautentikasi dan resource statisnya tidak memerlukan satu kebijakan bersama yang sama; tentukan batasannya berdasarkan apakah masing-masing representasi dipersonalisasi atau tidak.
Lanjutan 6: Bagaimana sebuah deployment dapat menghapus aset statis lama secara aman?
Bangun himpunan referensi dari HTML saat ini, HTML yang mendukung rollback, manifes rute, dan catatan rilis, lalu tambahkan jendela aksesibilitas halaman lama maksimum. Aset ber-hash memenuhi syarat untuk penghapusan tertunda hanya setelah tidak ada entry point yang dapat dipublikasikan mereferensikannya, jendela rollback telah berakhir, dan log akses menunjukkan tidak ada permintaan yang valid. Tugas pembersihan harus dapat dihentikan dan mempertahankan beberapa rilis terbaru agar satu keputusan yang salah tidak merusak proses rollback.