Steven
Steven5 min lugemist

Turvakontroll, mis muutis meie rakenduse võimatuks sulgeda

Automaatne uuendus oli raskeim funktsioon, mille oleme kunagi välja lasknud — kuus väljalaset nelja päevaga, et takistada sel rakendust nurjata. Halvim viga oli see, mille tõime sisse ettevaatlikkusest: 500-millisekundiline "turva"-kontroll, mis muutis ebaõnnestunud uuenduse protsessiks, mida sa sõna otseses mõttes ei saanud sulgeda.

Inseneeria
Electron
Töökindlus
GeekBye väljalasked
Turvakontroll, mis muutis meie rakenduse võimatuks sulgeda

Iga töölauarakenduse arendaja alahindab automaatset uuendust täpselt üks kord. See tundub lahendatud probleemina — teek laadib alla uue versiooni ja taaskäivitab su rakenduse. Siis lased selle välja ja saad teada, et "taaskäivita oma rakendus" on üks ohtlikumaid asju, mida programmilt saab paluda, sest see juhtub täpselt hetkel, mil su rakendus lammutab iseennast ja tal on kõige väiksem veamarginaal.

GeekBye automaatne uuendus vajas stabiliseerumiseks kuut väljalaset nelja päevaga — v1.5.14 kuni v1.5.19. See on lugu selle lõigu halvimast veast, mille põhjustasime ise, üritades olla ettevaatlikud.

Kuus väljalaset ja see üks, millel oli tähtsust

Kaar algas argiselt. v1.5.14 parandas piinliku trükiviga-tüüpi vea: uuendusvoog osutas GitHubi repositooriumi nimele, mida polnud olemas, nii et uuendaja kontrollis 404-t. v1.5.15 lisas käsitsi nupu "Check for Updates" ja päris veateate. Siis algasid quitAndInstalli vead ja väljalasked tulid kiiresti — sest kui su uuendusmehhanism on katki, ei saa sa selle parandust välja lasta läbi uuendusmehhanismi. Iga iteratsioon on käsitsi taasinstallimise hasartmäng.

See, millel on tähtsust, on v1.5.18. Selle kogu sisu oli üks commit pealkirjaga, mille peale ma siiani võpatan: restore original quitAndInstall behavior to prevent unkillable app.

Kuidas "ettevaatlik olemine" rakenduse nurjas

Siin on olukord. Kui uuendus on alla laaditud, peaks Electroni quitAndInstall sulgema rakenduse ja vahetama sisse uue versiooni. Ühes varasemas väljalaskes muretses keegi — põhjendatult —, et tingimusteta sulgemine on riskantne. Mis siis, kui installimine annab vea? Kas poleks turvalisem sulgeda ainult siis, kui kõik näeb terve välja?

Nii kasvatas kood vahi, mis nägi mõistlik välja:

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)

Loogika: käivita installimine, oota pool sekundit ja sunni lõplik app.quit() välja ainult siis, kui lipp updateDownloaded on endiselt tõene — muidu hoia rakendus avatuna, et kasutaja ei jääks hätta.

Lõks on ühe rea kaugusel, veakäsitlejas. See käsitleja seadis this.updateDownloaded = false. Nii et kujuta ette ebaõnnestunud installimist: sündmus error vallandub ja tühjendab lipu. Kuid quitAndInstall oli juba alustanud lammutamist — see oli sulgenud aknad ja eemaldanud rakenduse sulgemiskuulajad. Siis ärkab 500 ms taimer, kontrollib nüüd vale lippu, otsustab "viga tuvastatud, hoia rakendus avatuna" ja jätab app.quit() vahele.

Nüüd on sul, just nimelt macOS-is, halvim võimalik olek. macOS ei sulge rakendust ainult sellepärast, et selle viimane aken sulgus — see on window-all-closed käitumine, millele iga Maci rakendus toetub. Nii et protsess on endiselt elus, kuid sellel pole akent, pole menüüriba teed ja selle sulgemiskuulajad on välja rebitud. Pole midagi, millele klõpsata. Cmd-Q-l pole millegagi rääkida. Ainus väljapääs on Force Quit Activity Monitorist. "Turva"-kontroll oli muutnud ebaõnnestunud uuenduse — taastatava tüli — zombiks, keda sa ei saanud tappa.

Parandus: lammutamine peab olema tingimusteta

Parandus v1.5.18-s on peaaegu agressiivselt igav, ja see ongi mõte. See eemaldab nutikuse:

  1. Eemalda window-all-closed ja before-quit kuulajad, mis võiksid segada.
  2. Hävita iga aken — window.destroy(), mitte window.close(). Sulgemist saab käsitleja vetostada; hävitamist ei saa. Kui sa pühendud väljalülitumisele, ei küsi sa viisakalt.
  3. Kutsu quitAndInstall.
  4. Kutsu app.quit() tingimusteta.

Ei mingit lippu, ei mingit taimerit, ei mingit "hoia see avatuna igaks juhuks". Sest tõde väljalülitumise tee kohta on see, et poolik väljalülitumine on halvem kui kumbki tulemus. Täielik sulgemine on korras. Täielikult avatuks jäämine on korras. Ainus olek, milleni sa ei tohi kunagi jõuda, on lammutatud, kuid endiselt töötav — ja täpselt sellesse olekusse võib sind tingimuslik sulgemine kinni jätta.

Veel kaks õppetundi, mille sama nädal andis

Suletamatu viga on pealkiri, kuid kuue väljalaske rabelemine karastas kaks muud harjumust, mida tasub varastada.

Filtreeri oma krahhitelemeetriat täpsete allkirjade, mitte laiade märksõnade järgi. Rabelemise keskel avastasime, et meie veateavitus oli seadistatud kõrvale heitma kõike, mis sisaldas sõnu nagu permission, token või microphone — katse müra vähendada, mis vaikselt neelas tõelisi krahhe, mis juhtusid neid sõnu mainima. Rebisime laiad filtrid välja ja asendasime need täpsete keeldumisstringidega (konkreetne teade, mille macOS väljastab, kui luba tagasi lükatakse) ja konkreetsete ajutiste võrgukoodidega nagu ERR_NETWORK_CHANGED. Müra vähendamine ja vigade peitmine on sama nupp, keeratud vastupidistesse suundadesse; kui sa filtreerid tunde järgi, filtreerid välja just selle, mida sul oli vaja näha.

Iga automaatne tee vajab käsitsi pääseteed. Automaatne uuendus on oma olemuselt parimale-pingutusele suunatud — võrgud tõrguvad, installimised ebaõnnestuvad. Nii sai iga tõrkerežiim inimliku varuvariandi: käsitsi nupu "Check for Updates", eksponentsiaalse tagasilibisemisega kordused, taaskontrolli taimeri ja — reserveeritud just juhtumiks, kui kasutaja käsitsi katse ebaõnnestus — lihtsas keeles teate "kustuta rakendus ja installi uuesti veebisaidilt". Automaatne tee on mugavus; käsitsi tee on garantii.

Peamine mõte

  1. Vaht pöördumatu toimingu ümber on ohtlikum kui toiming ise. Tingimuslik sulgemine üritas takistada halval uuendusel rakendust sulgeda ja tekitas selle asemel oleku, mis on halvem kui sulgemine või mittesulgemine. Väljalülitumise ja installimise teed peaksid olema tingimusteta ja idempotentsed — mitte kunagi seotud muudetava lipuga, mille teine käsitleja saab su alt välja lüüa.
  2. macOS-is "pole aknaid" ei ole "pole rakendust". Iga lammutusloogika peab arvestama platvormiga, kus aknata protsess töötab edasi. Testi tõrketeed päris operatsioonisüsteemis, mitte ainult õnnelikku teed.
  3. Funktsiooni, mille lased välja läbi uuendussüsteemi, ei saa testida läbi uuendussüsteemi. See asümmeetria on põhjus, miks automaatne uuendus väärib paranoilist, tingimusteta, tugevalt käsitsi kontrollitud koodi. Sa saad selle lihtsat teed pidi parandada alles pärast seda, kui see juba töötab.

See on töökindluse töö varaseim peatükk, millest lõpuks sai GeekBye v2. Selle kohta, kuhu see tee viis, vaata mida versioon 2 tegelikult nõuab (v2.0.0) ja kogu kaart artiklis tarkvara täiuseni väljalaskmise anatoomia.

Seotud artiklid

Tarkvara täiuslikuna tarnimise anatoomia: kuidas koodiülevaatus tabas selle, mida testid ei suutnud
Steven
Steven5 min lugemist

Tarkvara täiuslikuna tarnimise anatoomia: kuidas koodiülevaatus tabas selle, mida testid ei suutnud

Kogu GeekBye v2 sarja vältel kordub üks ja seesama: parandus läbib arendaja masinas iga testi ning siis tõestab koodiülevaatus, et peaaegu kõigi teiste jaoks oleks see läbi kukkunud. See on üheksa väljalaske taga olev töövoog — ülevaatuse värav, esmalt-parandus tabamised ja testi-enne-tarnimist distsipliin, mis muudab "minu masinas töötab" fraasiks "see töötab".

Inseneeria
Protsess
Koodiülevaatus
Üks CSS-i muutuja, viis ülevaatusringi ja Swifti tööriistakett, mis valetas
Steven
Steven6 min lugemist

Üks CSS-i muutuja, viis ülevaatusringi ja Swifti tööriistakett, mis valetas

GeekBye v2.0.7 muutis kogu overlay reguleeritavalt läbipaistvaks — mis kõlab nagu üherealine CSS-i muudatus ja seda kindlasti ei olnud. Tegelik lugu on selles, mida koodiülevaatus tabas: kaks pinda, mis keeldusid tuhmumast, salvestusriba, mis nägi sama läbipaistvuse juures vale välja, ja kompileeritud binaar, mis kõikus 672 baiti edasi-tagasi, sest meie enda dokumentatsioon ütles ülevaatajale vale Swifti versiooni.

Inseneeria
Disain
Ehitus
Päev, mil meie rakendus tegi endale DDoS-i
Steven
Steven4 min lugemist

Päev, mil meie rakendus tegi endale DDoS-i

Ootel olevate üleslaadimiste kuhi, mis käivitumisel korraga vabaks lasti, muutis iga GeekBye kliendi väikeseks teenusetõkestuse (DoS) rünnakuks meie enda serverite vastu. Parandus — ja ühenduse elususe redel, mille see meid ehitama sundis — on üks kasulikumaid asju, mida v2 meile õpetas.

Töökindlus
Võrgundus
Inseneritöö