Blog ·
Actualizat la trei dimineața, intenționat
Actualizarea automată a agentului este pornită implicit la 03:00. Interesant era ce situații consumă o fantă și o noapte cu de două ori ora trei.
De Provibr
Agentul care rulează în rețeaua proprie a unui client primește o compilare nouă la câteva zile. Până în această rundă, actualizarea lui era un buton pe care cineva trebuia să apese, deci în practică se întâmpla când se nimerea ca cineva să se uite. Dorința era o singură propoziție: fiecare își poate stabili pe agent un orar de actualizare, mai multe momente pe zi, pornit implicit la trei dimineața.
Propoziția este ușoară. Ce a făcut din asta o rundă este că o repornire programată de infrastructură are multe feluri de a fi tăcut greșită, iar aproape toate arată bine din afară.
Valoarea implicită stă pe coloană, iar acolo stă toată dorința
„Pornit implicit pentru toată lumea” se citește în două feluri. Scris în codul aplicației se aplică agenților creați de acum înainte, ceea ce îi exclude tocmai pe clienții care au deja unul. Scris ca valoare implicită de coloană în migrare se aplică fiecărui rând existent într-o singură instrucțiune.
Este deci o valoare implicită de coloană: un boolean care pornește de la adevărat și un vector JSON care pornește cu o singură intrare 03:00. Măsurat după ce migrarea a rulat pe baza de date de dezvoltare: fiecare agent existent pornit, fiecare la trei, iar un agent proaspăt creat căruia nu i s-a transmis niciuna dintre cele două valori a primit aceeași stare. Ultima verificare este cea care dovedește că munca o face valoarea implicită și nu un script de însămânțare.
Momentele sunt oră de perete în fusul platformei, nu UTC. Nu există fus pe organizație, iar panoul afișează totul în acel singur fus, deci un orar păstrat în UTC ar aluneca cu o oră vara, iar cel care ar fi scris ora trei ar fi întrerupt la cinci.
Două straturi pe o singură expresie regulată
Un moment este HH:MM pe o grilă de cinci minute, iar tiparul acela există de două ori: o dată în validatorul de schemă, care respinge un câmp de formular cu un mesaj lizibil, și o dată ca restricție de bază de date, care respinge tot ce nu a trecut niciodată prin acel formular.
O restricție de verificare nu poate conține o subinterogare, deci „fiecare element al acestui vector este un moment valid” nu poate fi o probă de existență peste vectorul desfăcut. Ce poate fi este un predicat de cale JSON care selectează exact elementele greșite, învelit într-o probă că selecția aceea este goală.
Prima versiune a acelui predicat era subtil inutilă, și arăta completă:
$[*] ? ( !(@ like_regex "^([01][0-9]|2[0-3]):(00|05|10|15|20|25|30|35|40|45|50|55)$") )Potrivirea unei expresii regulate cu ceva ce nu este un șir nu este falsă în calea JSON. Este Unknown. Iar negarea lui Unknown este tot Unknown, și un filtru păstrează doar elementele pentru care predicatul este cu adevărat adevărat. Selecția elementelor greșite se întorcea deci goală pentru un vector de numere, iar restricția o accepta. Măsurat față de PostgreSQL 17.10, [3], [null] și [{"a":1}] au trecut toate trei.
Reparația este o clauză în față: elementele al căror tip nu este șir sunt greșite prin definiție. Toate trei sunt acum respinse. Este exact clasa de eroare care continuă să ne coste runde: o pază care nu prinde nimic și arată completă, și niciun test care să pice, pentru că nu era nimic față de care să pice.
Cinci pași, iar ordinea este runda
În fiecare minut un lucrător întreabă cine are orar, predă rândurile unui planificator pur care decide ce fantă este scadentă, apoi parcurge cinci pași pe agent. Rezervarea este pasul patru și nu primul, iar fiecare pas dinainte stă acolo dintr-un motiv propriu.
| Pas | Dacă răspunsul este nu |
|---|---|
| Există o compilare mai nouă? | Fanta este rezervată oricum, marcată ca nimic de făcut |
| Este agentul conectat? | Nu se rezervă nimic, iar minutul următor încearcă din nou |
| Există deja o actualizare în curs? | Nu se rezervă nimic; prima va onora totuși fanta |
| Rezervarea fantei | Decide un index unic; cel care pierde nu trimite nimic |
| Trimiterea comenzii | Aceeași funcție ca butonul din panou |
Primele două rânduri sunt contrarii asumate, iar asimetria aceea este decizia cea mai interesantă a rundei.
Nimic de făcut consumă fanta, pentru că răspunsul nu se mai poate schimba în interiorul unei fante: manifestul publicării de pe disc este același pentru toată fereastra, deci rejudecarea în fiecare minut costă interogări și nu aduce nimic. Și îi dă clientului ceva de văzut. O pagină de agent care spune „03:00, deja la zi” se deosebește de un orar care nu rulează. O listă goală nu.
Deconectarea nu consumă nimic, pentru că o fantă rezervată în timp ce gazda tocmai repornește este consumată pentru totdeauna, iar atunci clientul acela pierde o noapte în tăcere. Minutul următor încearcă din nou, până se închide fereastra de recuperare; după aceea agentul își ia pur și simplu fanta următoare.
Cincisprezece minute, și nu șase ore
Copiile de siguranță au în această platformă o fereastră de recuperare de șase ore. Aceasta este de cincisprezece minute, iar diferența stă în ce se strică atunci când ești generos.
O copie făcută la nouă dimineața în loc de trei rămâne o copie. Un agent care repornește la nouă dimineața în loc de trei este o întrerupere în plină zi de lucru, iar ora trei dimineața este tocmai ce a ales clientul ca să o evite. Cincisprezece minute acoperă lucrul pentru care există cu adevărat această fereastră, o poartă abia repornită sau câteva runde sărite, și nimic altceva.
Planificatorul se uită două zile calendaristice în urmă și nu una, iar asta nu este o scăpare. Un moment de 23:55 cu fereastră de recuperare cade, imediat după miezul nopții, în ziua calendaristică de ieri; privind doar la ziua de azi, fanta aceea nu ar veni niciodată prin construcție.
Două frâne, din două motive diferite
Cinci agenți pe organizație la fiecare tur, și douăzeci și cinci în total.
Frâna pe organizație privește lățimea de bandă a clientului și nu pe a noastră. Binarul are în jur de douăzeci și doi de megaocteți, o gazdă cu patruzeci de agenți îi are de regulă pe toți patruzeci la 03:00 pentru că aceasta este valoarea implicită, iar patruzeci de descărcări simultane ies pe aceeași legătură. Cinci pe minut întind o flotă de patruzeci pe opt minute, comod în interiorul ferestrei de cincisprezece.
Frâna globală există pentru că prima lucrează pe organizație: cu o sută de organizații ar însemna cinci sute de actualizări într-un minut. Restul vine în minutul următor, tot în interiorul ferestrei.
Peste toate acestea nu stă niciun steag de lansare, ceea ce la noi este neobișnuit. Comutatorul stă deja la client, pe coloană; un al doilea steag, la nivel de platformă, ar promite ce promite deja coloana. Ce există este o frână de urgență pentru noi: punerea intervalului de interogare pe zero oprește cronometrul fără să schimbe o singură setare de client.
Noaptea cu doi de trei
În ultima duminică din octombrie ora de perete 03:00 există de două ori, iar în ultima duminică din martie ora 02:30 nu există deloc. La amândouă răspunde o funcție auxiliară comună, necopiată în acest modul, tocmai pentru că un algoritm de oră de vară întreținut în două locuri se desparte o zi pe an.
Funcția aceea răspunde unei ore de perete ambigue cu primul dintre cele două momente, iar uneia inexistente cu momentul următor care există. Urmările sunt exact cele dorite. În noaptea lungă, ora 03:00 rulează o dată și nu de două ori; măsurat: o fantă. În noaptea scurtă, ora 02:30 devine pur și simplu 03:30 locale în loc să fie sărită, pentru că nesărirea este partea sigură a acelei monede. Amândouă sunt fixate prin teste.
Zilele sunt parcurse ca zile calendaristice și nu prin adunarea unui număr fix de milisecunde: adunarea a douăzeci și patru de ore peste o schimbare sare o zi sau o face de două ori.
Cui i se spune, și cât de des
Reconcilierea care raportează deznodămintele stă în lucrător și nu pe calea de trimitere, iar anunță doar orarul. Un clic manual avea deja pe cineva care se uita.
Anunțurile sunt grupate pe organizație, pentru că patruzeci de agenți în ziua unei publicări nu trebuie să devină patruzeci de rânduri într-o cutie poștală. Un eșec este comunicat cel mult o dată pe agent și pe zi calendaristică. Iar marca de timp care consemnează că s-a cântărit un anunț este scrisă și atunci când decizia a fost tăcerea, ca tăcerea să se deosebească de un lucrător care nu a ajuns niciodată acolo.
O actualizare programată nu apare intenționat ca operațiune rădăcină în sertarul de sarcini: la trei dimineața nu așteaptă nimeni, iar un sertar plin de rânduri nocturne este inutil pentru lucrul pentru care există. Urma sunt un rând de rulare, un eveniment și un anunț. Evenimentul acela are un fel al lui, pentru că cronologia nu are voie să pretindă că la trei clientul a pornit el însuși ceva.
Ce am nota
- „Implicit pentru toată lumea” este o valoare implicită de coloană. În codul aplicației înseamnă „pentru toată lumea de acum înainte”, iar asta îi exclude tocmai pe oamenii la care vă gândeați.
- Unknown nu este fals. În calea JSON, un predicat pe o valoare de tipul greșit nu este nici adevărat, nici fals, iar negarea nu ajută. Verificați mai întâi tipul.
- Decideți pentru fiecare situație dacă ea consumă fanta. Nimic de făcut și de neatins sunt răspunsuri opuse, iar amândouă sunt invizibile dacă le schimbați între ele.
- O fereastră de recuperare este o promisiune despre întrerupere, nu despre completitudine. Copiați numărul dintr-un sistem cu alt fel de a se strica și veți avea numărul greșit.
- Frânați acolo unde se simte cu adevărat. Patruzeci de agenți într-o rețea de client împart o legătură, iar aceea este limita, nu ruta noastră de descărcare.
- Rezolvați ora de vară o singură dată. Două copii ale aceluiași raționament se înțeleg până în ultima duminică din octombrie.
Pentru claritate despre ce este acest lucru: orarul este în producție și pornit implicit. Mai multe lucruri nu sunt dovedite. Lucrătorul nu a fost pus la treabă într-o poartă cu adevărat pornită, niciun agent real nu a executat încă o actualizare programată, anunțul de eșec și dedublarea lui zilnică nu au fost urmărite cap la cap, iar ecranul a fost recitit în două din cele nouă limbi.
Din munca de platformă a Provibr. De ce un agent refuză o actualizare pe care nu o poate verifica se află în cum se refuză singură o actualizare semnată, iar de ce agentul se conectează spre exterior în loc să asculte se află în un agent de ieșire în locul redirecționării de porturi.