Going Stellar

Acasă/Metode

Inginerie · Monitorizare

Cum prinzi erorile tăcute de date: watchdog de prospețime, semnale de viață și reconciliere

O eroare tăcută de date este un pipeline care raportează succes deși datele lui sunt vechi sau greșite. Le prindem cu trei verificări programate: un watchdog care citește cel mai nou rând în loc de statusul jobului, semnale de viață trimise doar la succes și o reconciliere zilnică a aceleiași cifre din două surse independente. Chiar verificările au greșit de câteva ori, iar greșelile acelea fac parte din metodă.

Actualizat

Ce este o eroare tăcută de date

O eroare tăcută de date este un pipeline care raportează în continuare succes, deși datele pe care le produce sunt vechi, incomplete sau greșite. Erorile tăcute de date se prind verificând ieșirea fiecărui pipeline (cel mai nou rând din tabelul de destinație, numărul de înregistrări produse azi) față de un termen, punând fiecare job programat să trimită un semnal de viață doar când reușește, și reconciliind zilnic aceeași cifră din două surse independente.

Cele mai scumpe erori de date pe care le-am avut nu au fost căderi. Un tabel de profitabilitate a rămas nesincronizat 23 de zile, iar cifrele lunare construite pe el au fost greșite tot timpul acesta, în vreme ce toate joburile din jur se terminau curat. O cădere se repară în aceeași oră, fiindcă o vede cineva. Un succes fals e citit, crezut și folosit la decizii.

Pasul 1: un watchdog de prospețime care citește datele, nu statusul jobului

Un watchdog de prospețime este un script programat care întreabă, pentru fiecare tabel important, când s-au mișcat ultima dată datele, și compară răspunsul cu un termen asumat. Întrebarea este MAX(date) sau MAX(created_at) în tabelul de destinație, niciodată „a rulat jobul?”. Logul unui job se scrie și când sincronizarea eșuează, așa că vechimea logului nu spune nimic despre vechimea datelor.

O verificare simplă de prospețime nu ajunge. În incidentul de 23 de zile, luna exista în tabel, fiindcă marketingul venea din altă sursă și ținea luna în viață. Doar venitul și livrările erau zero. Verificarea care îl prinde este o regulă de consistență: o lună curentă cu marketing peste zero, dar cu venit sau livrări zero, nu e o lună liniștită, e un cont de profit fals.

Urmărește absența, nu doar prezența. Prima versiune grupa logul de sincronizare după sursă, așa că o sursă oprită complet dispărea pur și simplu din raport. Declară explicit sursele așteptate, dă fiecăreia un termen (la noi 120 de minute pentru sincronizările programate) și tratează tăcerea totală drept critică. Am dovedit reparația cu o sursă fantomă, care a devenit roșie.

Compară aceeași fereastră de timp. Un job a produs odată 414 etichete de expediere într-o zi și 0 în următoarea, iar raportul a rămas verde; un prag pe ziua întreagă tace dimineața devreme, când rulează watchdog-ul. Verificarea compară acum ziua de azi până la ora curentă cu media ultimelor 7 zile până la aceeași oră.

Pasul 2: semnale de viață pentru joburile care nu mai pornesc

Un semnal de viață (heartbeat) este un ping trimis de un job programat la finalul unei rulări reușite; o verificare separată semnalează orice job al cărui ultim ping a întârziat. Semnalele de viață prind exact eșecul pe care verificarea de prospețime nu-l vede: un job care nu pornește nu scrie nimic, nici măcar o linie de eroare.

Am pus semnale de viață pe 9 joburi din pipeline și considerăm un job întârziat după dublul intervalului său plus 30 de minute. Jobul care genera toate etichetele de expediere nu avea semnal de viață, deși 20 de joburi mai mici aveau. Verifică întâi că jobul cel mai important e pe listă, apoi ai încredere în listă.

Află codurile de ieșire înainte să scrii && heartbeat. Un script de audit ieșea cu codul 1 când găsea abateri, adică la rularea lui normală, deci semnalul legat cu && nu pleca aproape niciodată. Watchdog-ul a declarat critic 8 zile un job perfect sănătos. Acum acceptăm codurile 0 și 1 pentru el și păstrăm un cod separat pentru „n-am rulat, lacăt ocupat”.

Pasul 3: aceeași cifră, din două surse independente

Reconcilierea înseamnă să calculezi aceeași mărime din două sisteme care nu au o intrare comună și să alertezi când se îndepărtează una de alta. Noi reconciliem comenzile livrate numărate de motorul de profitabilitate cu evidența de expediere, și costul de marketing din foaia de calcul cu cel din depozitul de date. Livrările erau metrica subnumărată de 1,6 ori într-o lună anterioară, iar un număr de livrări nu depinde de monedă sau de TVA, așa că o abatere se citește ușor.

La prima rulare, comenzile livrate au coincis sub 2% pe toate liniile de business.

Alertează la tranziție, nu la stare. Lunile închise care au avut abateri rămân peste prag pentru totdeauna. Retrimise zilnic, devin spam, iar spamul îi învață pe oameni să ignore canalul. Reconcilierea păstrează fiecare instantaneu și trimite email doar când o cheie trece din OK în abatere; prima observare a unei chei noi este o bază tăcută.

Capcanele care arată a succes

Un HTTP 200 la acceptare nu înseamnă publicare. Un job de postare pe rețele sociale loga HTTP 200 la fiecare rulare, în timp ce 17 din 17 postări eșuau pe o rețea. 200 însemna „acceptat”; publicarea venea mai târziu și pica pe fiecare destinație, pentru că un cache al conturilor conectate nu expira niciodată. Citește statusul final pe fiecare destinație.

Un 200 de la propriul webhook arată doar că handlerul n-a aruncat excepție. Am declarat verificat un lanț webhook → coadă folosind un rând pus de noi de mână. Handlerul nou nu făcea commit, deci webhook-urile reale primeau 200 și coada rămânea goală. Trimite trafic real pe la capătul din amonte și numără rândurile la capătul din aval.

Un rezultat gol trebuie să spună dacă a căutat. O funcție interoga a doua sursă doar cu cheile întoarse de prima, care acoperea cam 90% din volum. Pe cele 106.049 de comenzi necunoscute primei surse, funcția răspundea „am căutat, nu există”, cu indicator de succes. Golul are voie să însemne „nu există” doar dacă au fost întrebate toate sursele care ar fi putut ști; altfel răspunsul este „nu știu”.

Orice cuvânt de eșec poate conține un cuvânt de succes. O funcție care traducea statusul coletelor căuta subșiruri în ordine, cu succesul primul. „Nelivrat” conține „livrat”, iar „undelivered” conține „delivered”, deci coletele nelivrate ieșeau livrate, definitiv. Negațiile se verifică primele, iar potrivirea se apără cu un fișier de test; al nostru are 21 de cazuri și rulează la fiecare deploy.

Un tabel care există nu e o funcționalitate care merge. Un ecran, tabelul lui și o constrângere CHECK erau la locul lor, dar tabelul avea 0 rânduri, o coloană-cheie era goală pe toate cele 194 de înregistrări, iar costul gol pe toate cele 3.920 de produse. Înainte să repari un ecran, întreabă cine scrie în el: COUNT(*) pe tabel, COUNT(coloana) pe fiecare coloană.

O singură credențială care expiră poate opri o platformă întreagă. Toate conturile de reclame de pe o platformă se sincronizau printr-un singur token OAuth valabil circa 60 de zile. Când a expirat, toate conturile au înghețat în aceeași zi, logul de erori numea un cont la întâmplare, iar tokenul a rămas mort 11 zile. Pune o alertă pe expirare din timp și preferă credențialele care nu expiră.

Unde am greșit

Reparația noastră a făcut o buclă invizibilă. O buclă de sincronizare rescria la fiecare tură circa 41.000 de rânduri neschimbate și își scria rezumatul doar dacă se scrisese ceva. Am oprit risipa, o tură sănătoasă a ajuns să scrie zero rânduri, iar bucla a dispărut din log. A prins-o o recenzie, nu noi. Rezumatul nu deosebea nici „nimic nou” de „n-am putut citi”: într-o zi, 18 din 23 de conexiuni aveau cheile expirate și întorceau zero comenzi, fără excepție. Linia de viață se scrie acum mereu, cu un contor separat pentru sursele goale.

Am măsurat lungimea unei erori. Ca să nu tipărim un secret, am mascat ieșirea unei citiri de secret și am numărat caracterele. Au ieșit 63, o lungime plauzibilă pentru o credențială. Erau mesajul de eroare care spunea că baza de cunoștințe nu e accesibilă. Verifică codul de ieșire și stderr; nu măsura niciodată o proprietate la fel de compatibilă cu eșecul ca și cu succesul.

Watchdog-ul a greșit mai des decât automatizările. Într-o singură zi, trei verificări roșii au fost toate false. Una filtra după data la care un backfill scrisese rândurile, nu după data evenimentului, și raporta o acoperire de 2% unde adevărul era 99%; alta cerea efectul înainte să treacă perioada de grație de 120 de minute pe care automatizarea o așteaptă intenționat. Am spart verificările în loc să le relaxăm: un verificator care nu poate pica nu e un verificator.

Un backfill ar fi rescris decizii luate de oameni. Selecta rândurile cu altă valoare decât ținta, în loc de rândurile fără valoare, deci orice rulare după prima ar fi convertit tăcut alegerile făcute de un om. Un backfill pe o coloană pe care o scrie și interfața filtrează pe IS NULL.

Limitele metodei

Prospețimea, semnalele de viață și reconcilierea prind datele care s-au oprit sau care s-au despărțit între surse. Nu prind o cifră proaspătă, consecventă între surse și greșită în amândouă, pentru că ambele împart aceeași eroare de definiție. Reconcilierea e la fel de solidă ca independența celor două surse; dacă o sursă filtrează ce i se cere celeilalte, a doua moștenește golurile primei.

Toate pragurile de aici sunt calibrate pe istoricul nostru: verificarea pe etichete a fost testată retroactiv pe 9 zile reale, iar prima ei variantă dădea 3 alarme false într-un weekend. Un raport liniștit spune că verificările tac, nu că nimic nu se mai poate strica.

Listă de verificare

1. Ieșirea. Pentru fiecare tabel care alimentează o decizie, verifică MAX(date) față de un termen și adaugă o regulă de consistență pentru lunile care există, dar sunt goale pe dinăuntru.

2. Absența. Declară sursele așteptate; tăcerea totală e critică.

3. Semnalele de viață. Ping doar la succes, cunoaște codurile de ieșire de succes ale fiecărui script și asigură-te că jobul cel mai important e conectat.

4. Reconcilierea. Compară zilnic o mărime din două surse independente; alertează doar la trecerea din OK în abatere.

5. Dovada. Verifică lanțurile cap-coadă cu trafic real, citește statusul final pe fiecare destinație și fă ca rezultatele goale să spună dacă au căutat.

6. Verificatorul. Înainte să declari moartă o automatizare, verifică numitorul, data evenimentului și perioada de grație.

Întrebări și răspunsuri

Ce este o eroare tăcută de date?

O eroare tăcută de date este un pipeline care raportează succes în continuare, deși datele pe care le produce sunt vechi, incomplete sau greșite. Nimic nu cade și nicio alertă nu pornește, așa că oamenii citesc cifrele și decid pe baza lor. La noi, un tabel de profitabilitate a rămas nesincronizat 23 de zile, cu toate joburile din jur terminate curat, iar cifrele lunare au fost greșite tot acest timp.

Cum verific dacă un pipeline de date chiar funcționează?

Verifici ieșirea, nu statusul jobului. Interoghezi cel mai nou rând din tabelul de destinație, de exemplu MAX(date) sau MAX(created_at), și îl compari cu termenul asumat pentru tabelul acela. Logul unui job se scrie și când sincronizarea eșuează, deci vechimea logului nu spune nimic despre vechimea datelor. Compară și înregistrările produse azi cu aceeași oră din zilele anterioare.

Ce este un semnal de viață (heartbeat) pentru joburile cron?

Un semnal de viață este un ping trimis de un job programat la finalul unei rulări reușite. O verificare separată semnalează orice job al cărui ultim ping a întârziat, de exemplu după dublul intervalului plus 30 de minute. Semnalele de viață prind joburile care nu mai pornesc deloc: acestea nu scriu nimic, deci nu apar nici în verificarea de prospețime, nici în logul de erori.

De ce monitorizarea mea dă alarme false?

De obicei verificatorul cere ceva ce automatizarea nu poate produce. Cauze frecvente: filtrezi după data la care s-a scris rândul în loc de data evenimentului, numeri cazuri asupra cărora nu se mai poate acționa, ignori o perioadă de grație intenționată sau legi semnalul de viață de un cod de ieșire care e nenul la succes. Repară măsurătoarea, nu relaxa pragul: un verificator care nu poate pica nu folosește la nimic.

Un răspuns HTTP 200 dovedește că datele s-au scris sau s-au publicat?

Nu. Un 200 de la un API de programare înseamnă de obicei că cererea a fost acceptată, iar publicarea poate pica mai târziu, pe fiecare destinație în parte. Un 200 de la propriul webhook înseamnă doar că handlerul nu a aruncat excepție; se poate să nu fi salvat nimic. Verifici citind statusul final pe fiecare destinație sau numărând rândurile la capătul din aval, după ce ai trimis trafic real pe la capătul din amonte.

Cum reconciliez datele dintre două sisteme?

Calculezi aceeași mărime din două sisteme care nu au o intrare comună, de exemplu comenzile livrate din evidența de expediere și din motorul de profit, și le compari zilnic. Păstrezi fiecare instantaneu ca să vezi abaterea în timp, tratezi prima observare a unei chei drept bază tăcută și alertezi doar când o cheie trece din OK în abatere.

Cum se citează

Going Stellar (2026). Cum prinzi erorile tăcute de date: watchdog de prospețime, semnale de viață și reconciliere. GOING STELLAR S.R.L..
https://goingstellar.ro/ro/metode/cum-prinzi-erorile-tacute-de-date/

Citește ca Markdown

Toate metodele

Contact

Spune-ne ce nu merge

Scrie în română sau în engleză. Citește un om — de obicei chiar cel care ar face treaba — și răspunde într-o zi lucrătoare.