Steven
Steven5 min. skaitymo

Kaip transliuoti ataskaitą realiu laiku be mirgėjimo

Kai susitikimas baigiasi, GeekBye santrauka dabar užsipildo gyvai, o ne verčia jus spoksoti į besisukantį indikatorių. Kad srautinė sąsaja atrodytų rami, o ne trūkčiojanti, mirgėjimą teko spręsti dukart — kartą struktūruotiems laukams, kartą markdown — ir antrasis sprendimas buvo rendereris, kurį jau turėjome.

Inžinerija
Frontend
Srautinimas
GeekBye leidimai
Kaip transliuoti ataskaitą realiu laiku be mirgėjimo

Prieš šį leidimą susitikimo užbaigimas GeekBye reiškė spoksojimą į besisukantį indikatorių. Programa siųsdavo visą jūsų transkripciją analizei, laukdavo visos santraukos — įvertinimo, pagrindinių taškų, užduočių, visko — ir tada išmesdavo tai į ekraną iškart. Veikė, bet atrodė lėta, nes jūs spoksojote į nieką, kol ji dirbo.

GeekBye v1.6.13–v1.6.15 pavertė tai ataskaita, kuri įteka gyvai, laukas po lauko, kol yra generuojama. Įdomi dalis yra ne pats srautinimas — o viskas, ką turėjome padaryti, kad srautinimas neatrodytų trūkčiojantis. Ramus srautinimas ir kampuotas srautinimas yra ta pati funkcija su labai skirtingu įgyvendinimu.

Mirgėjimas, pirma dalis: neleisk kiekvienai porcijai purtyti React

Santrauka nėra vienas blokas — tai struktūruoti laukai (bendras įvertinimas, pagrindiniai taškai, užduotys ir taip toliau), kuriuos backend išleidžia po vieną per server-sent events. Naivus būdas tai atvaizduoti — atnaujinti React būseną kiekvieno įvykio metu, kai jis atkeliauja.

Padaryk taip ir gausi mirgėjimą. Kiekvienas atkeliaujantis laukas paleidžia savo pakartotinį atvaizdavimą; komponentai, rodę „tuščia", užsimontuoja, tada atsimontuoja, tada vėl užsimontuoja kaip „užpildyta"; išdėstymas trūkčioja su kiekvienu paketu. Ataskaita susirenka pati jums prieš akis blogiausiu įmanomu būdu — matomai, nervingai.

Pataisymas yra dviejų dalių disciplina. Pirma, kaupk atkeliaujančius laukus ref — kintamame laikiklyje, kuris nepaleidžia atvaizdavimų — ir publikuok į React būseną kartą per porciją su šviežia kopija, kad medis atsinaujintų tikslingai, o ne kiekvieno mikroįvykio metu. Antra, atvaizduok vieną stabilų komponentų medį visą laiką, ar srautinant, ar užbaigus, su vietoje esančiais užpildais laukams, kurie dar neatkeliavo:

// accumulate in a ref, publish once per chunk
partialRef.current = { ...partialRef.current, [field]: value }
setPartial({ ...partialRef.current })

// one tree, never swapped; missing fields show a placeholder in place
const report = isStreaming ? partial : saved
<Score>{report.overallScore ?? '…'}</Score>

Komponentas, rodantis įvertinimą, niekada neatsimontuoja — jis tik rodo , kol skaičius nusileidžia, o tada skaičių. Niekas netrūkčioja. O visiškai surinktas objektas pabaigoje vis tiek kešuojamas į vietinę duomenų bazę, tad srautinė sąsaja ir tvirtas saugojimas sugyvena nesikaudami.

Mirgėjimas, antra dalis: rendereris, kurį jau turėjome

Antrasis mirgėjimas yra subtilesnis ir gyvena pačiame markdown. Ataskaita atvaizduoja turtingą markdown — antraštes, paryškintą tekstą, sąrašus, kodo blokus. Bet markdown, atkeliaujantis srauto viduryje, bet kuriuo momentu yra nebaigtas. Sąrašas su vienu ženkleliu kol kas. Kodo blokas, kuris atidarytas, bet neuždarytas. Paryškinimo žymuo be poros kol kas.

Įprastas markdown rendereris iš naujo analizuoja visą eilutę su kiekvienu tokenu, ir ši nebaigta būsena kiekvieną kartą išanalizuojama į kažką kito — tad sąrašas mirga, kodo blokas blyksteli atsidarydamas ir užsidarydamas, išdėstymas šokinėja, kol tokenai baigiasi. Tai tas pats nervingas surinkimas kaip anksčiau, sluoksniu žemiau.

Pataisymas buvo beveik nejaukiai prieinamas: mes jau naudojome srautinimą suvokiantį markdown rendererį gyvam AI pokalbiui. Rendererį, sukurtą toleruoti nepilnus tokenus — atvaizduoti nebaigtą markdown stabiliai ir nusėsdinti jį tik tokenams baigiantis — o ne kaskart analizuoti nuo nulio. Ataskaitai tereikėjo panaudoti tą patį. Būtent šią problemą sprendėme pokalbiui mėnesiais anksčiau; „pataisymas" ataskaitai buvo suprasti, kad įrankį jau turime, ir nukreipti jį į antrą paviršių. Pakartotinis panaudojimas nugalėjo perstatymą.

Priedas: formatuotas kopijavimas yra iškarpinės formato uždavinys

Kai ataskaita ėmė gerai atrodyti, žmonės norėjo ją įklijuoti — į dokumentą, į el. laišką — ir išlaikyti formatavimą bei ekrano nuotraukas. Instinktas yra serializuoti ataskaitą į markdown eilutę ir tikėtis, kad paskirties vieta ją atvaizduos. Paprastai neatvaizduoja.

Tikras atsakymas yra tas, kad iškarpinė laiko kelias reprezentacijas iškart. Tad įrašome dvi: HTML versiją su nepaliestu formatavimu ir ekrano nuotraukomis, įterptomis kaip inline paveikslėliai, ir teksto atsarginį variantą paskirtims, kurios nepriima HTML. Įklijuok į formatuotą redaktorių ir gausi suformatuotą ataskaitą su paveikslėliais; įklijuok į paprastą teksto laukelį ir gausi švarų tekstą. „Formatuotas kopijavimas" niekada nebuvo serializavimo problema — tai buvo pasirink-teisingą-iškarpinės-formatą problema.

Tas pats leidimas taip pat suteikė ataskaitai pokalbio stiliaus žymas Aš / Jie su dviem kalbėtojais, sulygintais į priešingas puses, kad transkripcija skaitytųsi kaip pokalbis, kuriuo ji ir buvo, ir perkėlė jūsų pačių ekrano nuotraukas į jų pusę, kad derėtų. (Vienas leidimas šiame lange, v1.6.14, buvo gryna perstatymo versija — jokios istorijos čia nėra, ir sąžininga tai pasakyti.)

Trys dalykai, kurių mus išmokė srautinė sąsaja

  1. Buferizuok srautinius laukus, publikuok kartą per porciją. Leisti kiekvienam server-sent event varyti savo būsenos atnaujinimą ir yra tas mirgėjimas. Kaupiklis ref plius viena publikacija per porciją, į medį, kuris niekada neatsimontuoja, paverčia nervingą surinkimą ramiu užpildymu.
  2. Bet kas, kas srautinamas, reikalauja srautinimą suvokiančio rendererio. Rendereris, iš naujo analizuojantis visą eilutę su kiekvienu tokenu, mirgės ties nebaigtu markdown. Naudok sukurtą nepilnam įvedimui — o jei tokį jau turi kitam paviršiui, panaudok jį pakartotinai, prieš statydamas antrą.
  3. Formatuotas kopijavimas yra apie iškarpinės formatus, ne apie serializavimą. Įrašyk text/html su įterptais paveikslėliais ir atsarginį text/plain. Iškarpinė buvo sukurta nešti abu; naudokis tuo.

Tai penktas patikimumo ir tobulinimo istorijos, virstančios GeekBye v2, skyrius. Ankstesnį skyrių žr. ausinių įkišamų klaida (v1.6.12); tą, kur ta pati server-sent events idėja vėliau tapo ištisu atsarginiu transportu, transkripcija gyvai, kai ugniasienė blokuoja WebSockets (v2.0.8); o visą lanką — programinės įrangos pristatymo tobulumo anatomiją.

Susiję straipsniai

Nutrūkęs ryšys neturėtų nulaužti visos programos — bet mūsiškė nulauždavo
Steven
Steven5 min. skaitymo

Nutrūkęs ryšys neturėtų nulaužti visos programos — bet mūsiškė nulauždavo

Kai mūsų serverio dalis nulūžo pačiame susitikimo įkarštyje, ji ne tik sustabdė transkripciją — ji nulaužė visą programą. Priežastis buvo vienas neapdorotas įvykis, o pataisymas tilpo į dešimt eilučių. Tai leidimų grupė, dėl kurios GeekBye lieka prisijungęs ir susietas per tuos dalykus, kurie anksčiau jį žudydavo.

Inžinerija
Patikimumas
Electron
Programinės įrangos tobulinimo anatomija: kaip kodo peržiūra pagavo tai, ko testai negalėjo
Steven
Steven5 min. skaitymo

Programinės įrangos tobulinimo anatomija: kaip kodo peržiūra pagavo tai, ko testai negalėjo

Per visą GeekBye v2 seriją kartojasi tas pats: pataisa praeina kiekvieną testą kūrėjo kompiuteryje, o paskui kodo peržiūra įrodo, kad ji būtų sugedusi beveik visiems. Tai devynių leidimų darbo eiga — peržiūros vartai, pagavimai pataisos etape ir „patikrink prieš išsiuntimą" disciplina, kuri „man veikia" paverčia „veikia".

Inžinerija
Procesas
Kodo peržiūra
Vienas CSS kintamasis, penki peržiūros ratai ir Swift įrankių grandinė, kuri melavo
Steven
Steven6 min. skaitymo

Vienas CSS kintamasis, penki peržiūros ratai ir Swift įrankių grandinė, kuri melavo

GeekBye v2.0.7 padarė visą perdangą reguliuojamai permatomą — kas skamba kaip vienos eilutės CSS pakeitimas, tačiau tikrai juo nebuvo. Tikroji istorija yra tai, ką pagavo kodo peržiūra: du paviršiai, kurie atsisakė blukti, įrašų juosta, kuri prie to paties permatomumo atrodė ne taip, ir sukompiliuotas dvejetainis failas, kuris svyravo 672 baitais, nes mūsų pačių dokumentai peržiūrėtojui nurodė neteisingą Swift versiją.

Inžinerija
Dizainas
Kūrimas