Back home

Keandalan runtime Rust/Wasm memerlukan penanganan panik dan pembatalan pemulihan

Setelah instance Wasm bersama mulai menerima panggilan untuk waktu yang lama, kerusakan akan meningkat dari kegagalan tunggal menjadi masalah pemulihan status dan isolasi kesalahan.

Wasm pada awalnya dapat dengan mudah dianggap sebagai lapisan porting: kode dapat diprogram, halaman dapat dijalankan, kinerjanya oke, dan segala sesuatunya tampak hampir sama. Memang mulai sulit, biasanya setelah demo selesai. Setelah modul seperti editor, perender, dan parser dokumen berpindah dari eksperimen satu halaman ke runtime tetap jangka panjang, model kesalahan akan segera berubah.

Saat ini, kepanikan dan pembatalan tidak lagi menjadi cabang pengecualian di lapisan bahasa. Apa yang mereka putuskan adalah: apakah instance ini dapat terus menerima pekerjaan berikutnya, apakah status dalam memori terkontaminasi, apakah lapisan host harus segera membuang instance tersebut, dan apakah kumpulan instance perlu diisi. Ketika tim lapangan memindahkan kernel yang sudah lama berjalan di container asli ke Web, lapisan perubahan inilah yang paling mudah diremehkan.

Setelah Demo dilewati, model kesalahan baru saja dimulai.

Gangguan dalam satu panggilan tidak sulit untuk dipahami. Klik tombol memicu panggilan Wasm. Jika gagal, kesalahan akan dilaporkan untuk operasi tersebut. Segarkan halaman dan coba lagi. Biayanya masih terkendali.

Masalah terjadi setelah runtime mulai menggunakan kembali contoh. Ketika instance Wasm yang sama terus-menerus membuka banyak dokumen, menerima beberapa putaran peristiwa masukan, dan melewati beberapa panggilan jembatan JS, cakupan pengaruh panik dan pembatalan tidak lagi berhenti pada tindakan saat ini. Kegagalan yang tidak lengkap dapat menurunkan permintaan berikutnya.

Risiko seperti itu seringkali tidak terekspos pada hari pertama. Pada tahap pertama, Anda biasanya hanya melihat laporan kesalahan yang tersebar: kegagalan rendering sesekali, ekspor tertentu terhenti, dan dokumen tertentu berada dalam kondisi yang salah setelah ditutup dan dibuka kembali. Jika Anda memeriksa lebih lanjut, petunjuknya secara bertahap akan menyatu dengan fenomena yang sama: meskipun kegagalan terjadi dalam rantai panggilan, kerusakan tetap terjadi pada contoh bersama.

Pada titik ini, fokus pembahasannya bukan lagi “Apakah kode Rust akan panik”, tetapi “Apakah runtime ini memenuhi syarat untuk terus melayani panggilan berikutnya setelah panik”.

Kepanikan dapat ditangkap, pembatalan hanya dapat mengubah keadaan.

Hal terpenting yang harus dipisahkan di Rust/Wasm adalah dua semantik kegagalan yaitu panik dan batalkan.

Panic juga mempunyai kesempatan untuk melepas lelah kembali sepanjang batasan yang telah ditetapkan. Selama lapisan pengikat dan lapisan host menyetujui metode pemulihan terlebih dahulu, panggilan saat ini dapat gagal, dan status lain dalam instance juga dapat dipertahankan. membatalkan bukanlah cara yang tepat sama sekali. Artinya eksekusi saat ini telah mencapai kondisi yang tidak dapat dipulihkan. Jika Anda terus menggunakan instance yang sama untuk menerima permintaan, pada dasarnya Anda bertaruh bahwa memori dan sumber daya tidak akan rusak di tengah jalan.

Setelah keduanya tercampur saat runtime, masalah pasti akan terjadi pada pemrosesan selanjutnya:

  • Swallow batalkan sebagai pengecualian normal, dan kumpulan instance akan terus menggunakan kembali objek yang telah kehilangan kredibilitas.
  • Perlakukan semua kepanikan seolah-olah instance tersebut harus dihancurkan, dan throughput akan dikurangi secara tidak perlu
  • Host JS hanya mengetahui “panggilan gagal”, tetapi tidak tahu apakah harus mencoba lagi, kehilangan instance, atau memutus sesi saat ini

Ini juga merupakan hal yang paling realistis tentang keandalan runtime Wasm: semantik pemulihan harus ditentukan terlebih dahulu sebelum isolasi dan penjadwalan selanjutnya dapat diterapkan.

Jika lapisan pengikatan tidak menyediakan semantik pemulihan, lapisan host akan mengambil status buruk dan terus menerima pekerjaan.

Tempat paling berbahaya untuk masalah semacam ini bukanlah pada kode bisnis, tetapi pada lapisan pengikat yang sepertinya telah “sudah ditangani”. Lapisan host sering kali hanya melihat objek kesalahan yang muncul dan mencatatnya sebagai kegagalan panggilan normal. Lognya ada di sana dan halaman tidak langsung mogok, tetapi sistem mungkin meninggalkan kondisi buruk di dalam instance.

Yang perlu diperbaiki sebenarnya bukan sekedar try/catch, melainkan tindakan penanganan setelah kegagalan. Logika serupa dengan berikut ini baru saja mulai memasuki desain keandalan:

async function runWithRecovery(instance, input) {
  try {
    return await instance.exports.handle(input)
  } catch (error) {
    if (isAbort(error)) {
      pool.replace(instance.id)
    }
    throw error
  }
}

Fokus kode ini bukan pada sintaksis, namun pada penilaian sederhana: apakah kegagalan saat ini telah menandai instance ini sebagai objek tidak tepercaya. Jika jawabannya ya, tindakan pemulihan tidak boleh berhenti pada kesalahan yang terjadi, namun harus terus berlanjut hingga penghapusan instance, rekonstruksi sumber daya, dan pemotongan aliran permintaan.

Selama lapisan ini tidak didefinisikan dengan jelas, sistem akan tampak menangani kesalahan, namun apa yang sebenarnya dilakukannya adalah mengembalikan runtime yang berpotensi rusak ke jalur produksi.

Instance yang dibagikan akan memperbesar masalah pemulihan menjadi masalah strategi penyatuan

Setelah Wasm dimasukkan ke dalam produk nyata, jarang hanya ada “satu contoh sampai halaman ditutup”. Yang lebih umum adalah kumpulan instans, kumpulan pekerja, atau dokumen latar depan dan tugas latar belakang yang berbagi sekumpulan sumber daya runtime. Pada tahap ini, biaya pemulihan akibat kepanikan dan pembatalan akan secara langsung mengubah strategi pengumpulan.

Jika inisialisasi instance mahal, sistem secara alami akan cenderung menggunakannya kembali sesering mungkin. Namun begitu penggunaan kembali dilakukan, isolasi kesalahan harus ditingkatkan secara bersamaan:

  • Status mana yang hanya dapat digantung dalam satu panggilan, dan akan dibuang bersama panggilan tersebut setelah kegagalan
  • Cache mana yang diizinkan untuk disimpan di seluruh panggilan, dan cache mana yang harus sepenuhnya dibatalkan setelah pembatalan terjadi
  • Setelah instance diganti, bagaimana tugas yang antri akan dimigrasikan? Apakah percobaan ulang akan menimbulkan efek samping dua kali?

Ini bukanlah jawaban yang akan dikirimkan secara otomatis oleh lapisan bahasa. Itu adalah desain runtime.

Oleh karena itu, jika pembahasan mengenai keandalan Rust/Wasm hanya berhenti pada “dapatkah kepanikan ditangkap?”, maka mudah untuk meremehkan masalahnya. Yang benar-benar memperlebar kesenjangan biaya pemeliharaan adalah apakah kumpulan instance dapat mempertahankan batas kepercayaan yang jelas setelah terjadi kegagalan.

Batasan yang berlaku sangat terkait dengan siklus hidup

Rangkaian desain restorasi ini tidak diperlukan untuk setiap proyek Wasm.

Jika modul hanya merupakan alat offline satu kali, atau seluruh instance didaur ulang ketika halaman dihancurkan, maka perbedaan antara panik dan batal akan tetap ada, namun manfaat pemulihan akan jauh lebih kecil. Seringkali cukup dengan menyegarkan halaman secara langsung dan menjalankan kembali tugas secara langsung.

Setelah sistem memiliki karakteristik berikut, semantik pemulihan akan dengan cepat berubah dari “item optimasi” menjadi “item infrastruktur”:

  • Instance berada dalam jangka waktu yang lama dan tidak dimusnahkan bersamaan dengan siklus hidup satu halaman
  • Contoh yang sama terus menerus melakukan beberapa putaran panggilan
  • Lapisan hosting perlu menggunakan pooling sebagai ganti waktu startup dan throughput
  • Lindungi status sesi, status cache, dan tugas antri setelah kegagalan

Saat tim lapangan memindahkan kemampuan asli ke Web, batasan ini kemungkinan besar akan ditemui. Hubungan isolasi yang awalnya dibuat secara default dalam proses Aplikasi sering kali harus diisi lagi setelah mencapai batas host JS/Wasm.

Wasm memudahkan kode asli untuk masuk ke browser, tetapi tidak membawa semantik pemulihan runtime. Segera setelah sistem mulai berbagi instance, menggunakan kembali status, dan menerima panggilan jangka panjang, kepanikan dan pembatalan harus diperlakukan sebagai dua peristiwa runtime yang berbeda. Yang pertama peduli tentang bagaimana mengakhiri panggilan saat ini, dan yang kedua peduli tentang apakah instance ini dapat terus ada di pool. Jika penilaian ini tidak dibuat terlebih dahulu, semakin sukses transplantasi kode, semakin sulit pula menangani kegagalan berikutnya.