
Pemeriksaan Keselamatan yang Menjadikan Aplikasi Kami Mustahil Ditutup
Kemas kini automatik ialah ciri paling sukar yang pernah kami keluarkan — enam keluaran dalam empat hari untuk menghalangnya daripada merosakkan aplikasi. Pepijat paling teruk ialah yang kami sendiri perkenalkan ketika cuba berhati-hati: pemeriksaan "keselamatan" 500 milisaat yang menukar kemas kini gagal menjadi proses yang benar-benar tidak boleh anda tutup.
Setiap pembangun aplikasi desktop memandang remeh kemas kini automatik tepat sekali sahaja. Ia kelihatan seperti masalah yang sudah selesai — sebuah pustaka memuat turun versi baharu dan memulakan semula aplikasi anda. Kemudian anda mengeluarkannya, dan anda belajar bahawa "mulakan semula aplikasi anda" ialah salah satu perkara paling berbahaya yang boleh diminta daripada sesebuah program, kerana ia berlaku tepat pada saat aplikasi anda sedang meruntuhkan dirinya sendiri dan mempunyai margin ralat yang paling sedikit.
Kemas kini automatik GeekBye mengambil enam keluaran dalam empat hari — v1.5.14 hingga v1.5.19 — untuk stabil. Ini ialah kisah pepijat paling teruk dalam rentetan itu, yang kami sendiri sebabkan kerana cuba berhati-hati.
Enam keluaran, dan satu yang penting
Rentetan ini bermula dengan hal biasa. v1.5.14 membetulkan pepijat memalukan sekelas salah taip: suapan kemas kini menghala ke nama repo GitHub yang tidak wujud, jadi pengemas kini memeriksa satu 404. v1.5.15 menambah butang manual "Check for Updates" dan mesej ralat yang sebenar. Kemudian pepijat quitAndInstall mula muncul, dan keluaran datang dengan pantas — kerana apabila mekanisme kemas kini anda rosak, anda tidak boleh mengeluarkan pembetulannya melalui mekanisme kemas kini itu. Setiap lelaran ialah pertaruhan pemasangan semula manual.
Yang penting ialah v1.5.18. Keseluruhan kandungannya ialah satu commit dengan tajuk yang masih membuat saya mengernyit: restore original quitAndInstall behavior to prevent unkillable app.
Bagaimana "berhati-hati" merosakkan aplikasi
Inilah persediaannya. Apabila sesebuah kemas kini telah dimuat turun, quitAndInstall daripada Electron sepatutnya menutup aplikasi dan menggantikannya dengan versi baharu. Dalam keluaran terdahulu, seseorang — dengan munasabah — bimbang bahawa keluar tanpa syarat itu berisiko. Bagaimana jika pemasangan itu ralat? Bukankah lebih selamat untuk keluar hanya jika semuanya kelihatan sihat?
Maka kod itu menumbuhkan pengawal yang kelihatan munasabah:
autoUpdater.quitAndInstall(false, true)
setTimeout(() => {
if (this.updateDownloaded)
app.quit() // only quit if the update is still "good"
else console.log('Error detected — keeping app open')
}, 500)
Logiknya: cetuskan pemasangan, tunggu setengah saat, dan paksa app.quit() terakhir hanya jika bendera updateDownloaded masih benar — jika tidak, biarkan aplikasi terbuka supaya pengguna tidak terkandas.
Perangkapnya hanya sebaris jauh, dalam pengendali ralat. Pengendali itu menetapkan this.updateDownloaded = false. Jadi bayangkan pemasangan yang gagal: peristiwa error tercetus dan mengosongkan bendera. Tetapi quitAndInstall sudah mula meruntuhkan — ia telah menutup tetingkap dan membuang pendengar penutup aplikasi. Kemudian pemasa 500 ms terjaga, memeriksa bendera yang kini palsu, memutuskan "ralat dikesan, biarkan aplikasi terbuka," dan melangkau app.quit().
Kini anda mempunyai, khususnya pada macOS, keadaan paling teruk yang mungkin. macOS tidak menutup aplikasi hanya kerana tetingkap terakhirnya ditutup — itulah gelagat window-all-closed yang menjadi sandaran setiap aplikasi Mac. Jadi proses itu masih hidup, tetapi ia tiada tetingkap, tiada laluan bar menu, dan pendengar penutupnya telah dicabut. Tiada apa-apa untuk diklik. Cmd-Q tiada apa-apa untuk diajak bercakap. Satu-satunya jalan keluar ialah Force Quit daripada Activity Monitor. Pemeriksaan "keselamatan" itu telah menukar kemas kini yang gagal — gangguan yang boleh dipulihkan — menjadi zombi yang tidak boleh anda matikan.
Pembetulannya: perobohan mestilah tanpa syarat
Pembetulan dalam v1.5.18 hampir membosankan secara agresif, dan itulah maksudnya. Ia membuang kepintaran itu:
- Buang pendengar
window-all-closeddanbefore-quityang boleh mengganggu. - Musnahkan setiap tetingkap —
window.destroy(), bukanwindow.close(). Menutup boleh diveto oleh pengendali; memusnahkan tidak boleh. Apabila anda komited untuk mematikan, anda tidak meminta dengan sopan. - Panggil
quitAndInstall. - Panggil
app.quit()tanpa syarat.
Tiada bendera, tiada pemasa, tiada "biarkan terbuka untuk berjaga-jaga." Kerana kebenaran tentang laluan penutupan ialah penutupan separuh siap lebih teruk daripada kedua-dua hasil. Keluar sepenuhnya tidak mengapa. Kekal terbuka sepenuhnya tidak mengapa. Satu-satunya keadaan yang anda tidak boleh capai ialah diruntuhkan tetapi masih berjalan — dan itulah tepatnya keadaan yang penutupan bersyarat boleh membuat anda terkandas.
Dua pelajaran lagi yang diajar oleh minggu yang sama
Pepijat yang tidak boleh dimatikan ialah tajuk utamanya, tetapi kelam-kabut enam keluaran itu mengeraskan dua lagi tabiat yang berbaloi dicontohi.
Tapis telemetri ranap anda mengikut tandatangan tepat, bukan kata kunci yang luas. Di tengah-tengah kelam-kabut itu kami mendapati pelaporan ralat kami dikonfigurasikan untuk membuang apa-apa yang mengandungi perkataan seperti permission, token, atau microphone — satu percubaan memangkas gangguan yang secara senyap menelan ranap sebenar yang kebetulan menyebut perkataan itu. Kami mencabut penapis pukal itu dan menggantikannya dengan rentetan penolakan yang tepat (mesej khusus yang macOS keluarkan apabila sesuatu kebenaran dinafikan) dan kod rangkaian sementara yang khusus seperti ERR_NETWORK_CHANGED. Pengurangan gangguan dan penyembunyian pepijat ialah tombol yang sama diputar ke arah bertentangan; jika anda menapis mengikut gerak hati, anda akan menapis keluar perkara yang anda perlu lihat.
Setiap laluan automatik memerlukan jalan keluar manual. Kemas kini automatik pada dasarnya ialah usaha terbaik — rangkaian tersekat, pemasangan gagal. Jadi setiap mod kegagalan mendapat sandaran berperikemanusiaan: butang manual "Check for Updates", cubaan semula dengan backoff eksponen, pemasa pemeriksaan semula, dan — dikhaskan khusus untuk kes di mana percubaan manual seorang pengguna gagal — mesej berbahasa mudah "padam aplikasi dan pasang semula daripada laman web". Laluan automatik ialah kemudahan; laluan manual ialah jaminannya.
Kesimpulannya
- Satu pengawal di sekeliling tindakan yang tidak boleh dibalikkan lebih berbahaya daripada tindakan itu sendiri. Penutupan bersyarat itu cuba menghalang kemas kini buruk daripada menutup aplikasi, dan sebaliknya mencipta keadaan yang lebih teruk daripada keluar mahupun tidak keluar. Laluan penutupan dan pemasangan sepatutnya tanpa syarat dan idempoten — tidak sekali-kali disandarkan pada bendera boleh ubah yang boleh dibalikkan oleh pengendali lain di luar kawalan anda.
- Pada macOS, "tiada tetingkap" bukan bermakna "tiada aplikasi." Sebarang logik perobohan mesti mengambil kira platform di mana proses tanpa tetingkap terus berjalan. Uji laluan kegagalan, pada OS sebenar, bukan hanya laluan yang lancar.
- Ciri yang anda keluarkan melalui sistem kemas kini tidak boleh diuji melalui sistem kemas kini. Ketidaksimetrian itulah sebabnya kemas kini automatik layak mendapat kod yang paranoid, tanpa syarat, dan disahkan secara manual dengan berat. Anda hanya boleh membetulkannya dengan cara mudah selepas ia sudah berfungsi.
Ini ialah bab paling awal kerja kebolehpercayaan yang akhirnya menjadi GeekBye v2. Untuk melihat ke mana jalan itu menuju, lihat apa yang sebenarnya diperlukan untuk sebuah versi 2 (v2.0.0) dan keseluruhan rentetannya dalam anatomi mengeluarkan perisian hingga ke tahap kesempurnaan.