Steven
Steven5 min lugemist

Katkenud ühendus ei tohiks kogu rakendust maha võtta — aga meie oma tegi seda

Kui meie backend kadus keset koosolekut võrgust, ei pannud see lihtsalt transkriptsiooni pausile — see pani kogu rakenduse kokku jooksma. Põhjuseks oli üksainus käsitlemata sündmus ja parandus oli kümme rida. See on väljalasete kobar, mis pani GeekBye sisselogituna ja ühendatuna püsima läbi asjade, mis teda varem tapsid.

Inseneeria
Töökindlus
Electron
GeekBye väljalasked
Katkenud ühendus ei tohiks kogu rakendust maha võtta — aga meie oma tegi seda

Mõned vead katkestavad ühe funktsiooni. Kõige hullemad võtavad maha kõik nende ümber. Ühes varases GeekBye ehituses, kui meie backend kadus võrgust, samal ajal kui sina olid keset koosolekut, ei jäänud transkriptsioon lihtsalt seisma — kogu rakendus jooksis kokku. Üks katkenud ühendus, ja kogu aken oli kadunud.

Väljalasete kobar, mis selle parandas — v1.6.8 kuni v1.6.11 — on meistriklass tänamatus töös nimega "püsi sisselogituna, püsi ühendatuna". Peamine parandus oli kümme rida. Siin on, mis need kümme rida olid, ja kõik muu, mis nendega koos välja tuli.

Kümme rida katkestuse ja kokkujooksmise vahel

Kokkujooksmine elas heli koodis. Kui transkriptsiooniseanss lammutas oma WebSocketid, kutsus see neil removeAllListeners() — mõistlik koristamine. Aga siin on see Node.js detail, mis muudab koristamise katastroofiks: kuulajata error sündmust ei ignoreerita. See visatakse uuesti püüdmata erindina.

Nii et kujuta ette sündmuste jada, kui backend kaob võrgust: socketil on error sündmus järjekorras ootamas, et vallanduda. Koristamine käivitab removeAllListeners(), eemaldades käsitleja, mis oleks selle püüdnud. Hetk hiljem järjekorras olev viga vallandub — tühjusesse. Node näeb error sündmust, mida keegi ei kuula, ja teeb ainsat, mida tema leping lubab: viskab selle püüdmata erindina, mis paneb protsessi kokku jooksma. Kasutaja backend luksus kaks sekundit ja tema rakendus kadus.

Parandus (v1.6.8) on peaaegu antiklimaatiline. Kohe pärast kuulajate eemaldamist lisa uuesti sihilikult tühi error käsitleja:

this.micWebSocket.removeAllListeners()
this.micWebSocket.on('error', () => {}) // absorb late error events

See tühi funktsioon on kogu mõte. See annab ootel olevale veale koha, kuhu maanduda — püüdja — nii et Nodel on kuulaja ja see ei viska kunagi midagi uuesti. Sama commit koristas ka pooleldi avatud socketid, kui ühenduskatse ebaõnnestub poolel teel, nii et untsu läinud ühendus ei saa jätta elusat socketti rippuma koos veaga, mis ootab vallandumist. Commiti kommentaar ütleb selle otse: "Ilma selleta muutuvad ootel olevad vead püüdmata erinditeks ja panevad rakenduse kokku jooksma." Kümme rida muutsid täieliku kokkujooksmise vaikseks, taastatavaks katkestuseks.

Õppetund on väärt rohkem kui parandus ise: iga kord, kui kutsud removeAllListeners() socketil või voos, mida sa võib-olla veel lammutad, lisa esmalt uuesti tühi error püüdja. Käsitlemata error sündmus on kokkujooksmine, mitte logirida.

Sisselogituna püsimine: värskenda ette ja värskenda põrke peale

Poolel "püsi ühendatuna" oli kaksik "püsi sisselogituna". Enne seda kobarat GeekBye autentimistoken lihtsalt aegus — mõne aja pärast visati sind ilma tseremooniata tagasi sisselogimisekraanile, keset töövoogu, ilma nähtava põhjuseta.

v1.6.8 parandas selle mõlemast suunast:

  • Ennetav: taimer värskendab tokeni enne selle aegumist, ajastatud aegumise miinus nutika puhvri peale (murdosa tokeni elueast, alt ja ülevalt piiratud). Sind on värskendatud, enne kui sa üldse märkaksid.
  • Reaktiivne: kui päring tuleb sellegipoolest tagasi 401-ga — kella nihe, vahelejäänud taimer, külm sülearvuti — värskendab klient ühe korra ja proovib algset päringut täpselt üks kord uuesti. Üks valve-lipp piirab seda korduskatset, nii et püsiv 401 ei saa kunagi keerduda lõpmatuks tsükliks.

Peen, aga kriitiline osa: mõlemad teed on mutexi taga. Kui kümme päringut tabab 401-i samal hetkel, ei käivita nad kümmet värskendust — nad kõik ootavad seda ühte käimasolevat värskendust ja proovivad siis uuesti. Ja kui värskendust tõesti päästa ei saa, aegub rakendus seansi sujuvalt — selge kümnesekundiline teade "sinu seanss aegus, palun logi uuesti sisse" — mitte vaikne viskamine sisselogimisekraanile.

Taasühendumine: lisa see kliendile, seejärel vii backendi

v1.6.9 muutis katkenud transkriptsiooniühenduse taastatavaks, mitte saatuslikuks. Kui backend teatas, et ülesvoolu kõneühendus oli katkenud, lõpetas klient saatusliku vea kuvamise ja ühendus hoopis uuesti — teadlikult rahuliku kasutuskogemusega: üks merevaigukollane teade "Taasühendumine…" (mitte üks iga katse kohta), roheline teade "Taasühendatud", kui see tagasi tuli, ja lõplik "palun taaskäivita" viga ainult pärast seda, kui iga katse oli ammendatud.

Ja siis midagi, mis mulle selle kobara juures meeldib: üks väljalase hiljem, versioonis v1.6.10, kustutasime selle kliendipoolse taasühendumise loogika — umbes 113 rida — ja viisime taasühendumise backendi, mis nüüd väljastab vaikseid "taasühendumise" olekuteateid, mida klient lihtsalt peegeldab. Ehitasime taastumise kliendile, otsustasime, et backend on õige omanik, ja viisime selle üle. See pole kõhklemine; see on raske probleemi aus elutsükkel. Töökindluse loogika, mis on jagatud ühenduse mõlema otsa vahel, on halb märk — vali omanik. (Backendist sai lõpuks selle lõplik omanik, ja see on taasühendumise lugu, mida jutustatakse artiklis miks su AI märkmetegija peatub halva Wi-Fi peal.)

Sama väljalase muutis salvestusaja piirangud inimlikuks: toore vea asemel hoiatusteade, mis laseb salvestusel jätkuda, ja selge sõnum "Salvestuse piir saavutatud", kui see tegelikult peatub — sõbralik lause esile toodud, sisemine eesliide eemaldatud.

Üks korduskatse, mis valitseb kõiki

Versiooniks v1.6.11 oli sama korduskatse-koos-taganemisega loogika kopeeritud kolme erinevasse kohta — autentimine, uuendused, seansi lammutamine. Nii et see konsolideeriti üheks mooduliks ühe jagatud reegliga selle kohta, mida üldse tasub korrata: mööduvad võrgutõrked (ühenduse lähtestus, ajalõpp, DNS-i luksumine, socketi katkemine) proovitakse eksponentsiaalse taganemisega uuesti; tõeline serveriviga kukub kohe läbi, sest tõelise 4xx kordamine lihtsalt raiskab kasutaja aega. Üks klassifikaator, mõned nimega eelseaded, kolm kutsujat sellele üle viidud.

Mida see kobar õpetas

  1. Käsitlemata error sündmus on kokkujooksmine, mitte logirida. Node viskab uuesti error sündmused, millel pole kuulajat. Eemalda kuulajad socketilt, mida sa võib-olla veel lammutad, ja pead uuesti lisama tühja error püüdja — muidu võtab üks katkenud ühendus kogu rakenduse endaga kaasa.
  2. Värskenda mandaate enne aegumist ja reaktiivselt 401 peale — mõlemal mutexiga. Ennetav hoiab sind sisselogituna; reaktiivne püüab servajuhtumid; mutex tähendab, et N samaaegset kutsujat põhjustab täpselt ühe värskenduse, ja korduskatse piir tähendab, et halb token ei saa lõputult tsükeldada.
  3. Proovi uuesti ainult mööduvate vigade puhul ja tsentraliseeri klassifikaator. "Kas seda tasub korrata?" on üks otsus, mis kuulub ühte kohta. Tõelise vea kordamine on lihtsalt aeglasem läbikukkumine.
  4. Otsusta, kellele kuulub taasühendumine. Lisasime selle kliendile, seejärel viisime backendi. Taastumise loogika, mis elab mõlemas otsas, on veagenraator; anna sellele üks kodu.

See on kolmas peatükk töökindluse loos, millest saab GeekBye v2. Eelmise peatüki jaoks vaata me kustutasime 5000 rida helikoodi ja transkriptid hakkasid ilmuma kaks korda (v1.6.0); selle kohta, kuhu taasühendumine lõpuks paika loksus, miks su AI märkmetegija peatub halva Wi-Fi peal; ja kogu kaare jaoks tarkvara täiuslikkuseni laevastamise anatoomia.

Seotud artiklid

Turvakontroll, mis muutis meie rakenduse võimatuks sulgeda
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
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