„Ingineria datelor este dezvoltarea, implementarea și întreținerea sistemelor și proceselor care primesc date brute și produc informație de calitate, consistentă, care susține cazuri de utilizare din aval, precum analiza și învățarea automată.”Joe Reis, Matt Housley · Fundamentals of Data Engineering · 2022 · Fundamentals of Data Engineering (2022), cap. 1 — definiția ingineriei datelor
O rulare repetată pentru aceeași fereastră trebuie să dea același rezultat — asta e idempotența.
ETL transformă datele într-o unealtă separată înainte să le încarce; ELT le încarcă brute și le transformă cu SQL în depozit, unde calculul e ieftin, SQL-ul stă în git și proveniența se citește din interogări. Ordinea nouă face extragerea subțire (copiază fidel) și transformarea bogată (modelează, testează, documentează). Orchestrarea e graful de sarcini: dependențe, program, reîncercări, alerte, o vedere a ce a rulat și ce nu. Parametrul unei rulări e fereastra de timp, niciodată „acum” — altfel rularea de ieri nu se mai poate repeta. Idempotența e proprietatea care face totul reparabil: rularea repetată pentru aceeași fereastră produce exact aceleași rânduri. Se obține ștergând și reîncărcând partiția, sau prin merge pe cheie — niciodată prin adăugare oarbă, care dublează datele la a doua rulare. Cu ea, un backfill e doar o buclă peste ferestre vechi, iar o eroare de la 3 dimineața se rezolvă cu o reluare, nu cu o investigație. Încărcările incrementale poartă un watermark și o fereastră pentru datele care sosesc târziu; fișierele mici se compactează; totul se testează în CI, ca orice cod.
Gândește-te la un pipeline ca la o funcție, nu ca la un eveniment unic. Dacă îi dai ca parametru o fereastră de timp, aceeași intrare produce mereu aceeași ieșire. Asta schimbă natura defecțiunilor. Un pipeline care adaugă orbește rânduri e ca un casier care tipărește bonul de două ori și încasează ambele sume. Unul idempotent e ca un formular online: poți apăsa trimite din nou, sistemul îți arată aceeași confirmare. Reparabilitatea devine atunci o decizie de design, nu o speranță. Iar dacă fiecare rulare e o funcție pură pe o fereastră, apare o întrebare firească: cine decide ordinea și momentul acestor apeluri?
Pasajul precedent s-a oprit la o întrebare: cine ordonează aceste apeluri? Răspunsul e orchestratorul. El ține graful sarcinilor, le declanșează când dependențele sunt gata și reia ce a picat. Detaliul esențial: el nu spune niciodată „rulează acum”, ci „rulează fereastra de ieri”. Așa o rulare eșuată poate fi repetată mâine cu aceeași intrare. Gândește-te la un panificator care coace pe comenzi, nu când i se pare: fiecare comandă are o dată, deci o comandă ratată de marți se poate coace miercuri, identică. Cu ferestre ca parametri, un backfill devine banal: aceeași rețetă, aplicată bucată cu bucată peste trecut. Rămâne de văzut ce garantează că repetarea nu strică ceva deja corect.
Pasajele anterioare au arătat cine ordonează rulările. Acum: ce mecanisme fac o rulare repetabilă în practică. Există două rețete principale. Prima: ștergi partiția ferestrei și o reîncarci de la zero, ca să înlocuiești o foaie dintr-un caiet, nu să lipești una peste alta. A doua: faci merge pe cheie, adică actualizezi rândurile existente și inserezi doar cele noi, ca un registru în care corectezi o linie, nu rescrii tot registrul. Ce e interzis e adăugarea oarbă, care dublează datele la a doua rulare. Mai rămâne o problemă: datele care sosesc târziu. De asta încărcarea incrementală poartă un watermark și o fereastră de întârziere, ca o cutie poștală care așteaptă scrisori după ce coșul a trecut. Dar cum verifici că toate aceste mecanisme chiar funcționează înainte de producție?
Repetabil și corect sunt lucruri diferite. O rulare poate produce aceleași rânduri de zece ori și tot să greșească. De aceea transformările se testează ca orice cod: aserțiuni care verifică dacă cheile sunt unice, dacă sumele se potrivesc, dacă niciun câmp obligatoriu nu e gol. Un test eșuat oprește pipeline-ul înainte ca datele proaste să ajungă la rapoarte. Gândește-te la controlul de calitate dintr-o fabrică de conserve: fiecare cutie e verificată înainte de etichetare, nu după ce e pe raft. Iar testele trăiesc în același depozit cu codul, rulate automat la fiecare schimbare, ca o rețetă care e gustată la fiecare modificare de ingredient. Așa reparabilitatea devine încredere verificată, nu promisiune. Rămâne o ultimă bucată: cine vede tot acest proces și cum își dă seama cineva ce s-a întâmplat la 3 dimineața.
Pasajele anterioare au acoperit mecanica. Rămâne ultima întrebare: cine ține transformarea vie? Când SQL-ul stă în depozitul de versiuni, granița dintre inginerul de date și analist se estompează. Inginerul asigură țevile: ferestre, idempotență, orchestrare. Analistul modelează sensul: ce e un client activ, ce înseamnă o vânzare. E ca într-o bucătărie: unul ține aragazul funcțional, celălalt decide rețeta, iar amândoi semnăază felul final. De aici apare nevoia de contracte de date: coloanele, tipurile și pragurile de calitate devin promisiuni scrise, pe care avalul le poate verifica. Un raport lunar de facturare nu mai pică liniștit pentru că cineva a redenumit o coloană; contractul strigă mai întâi. Asta închide cercul: pipeline-ul nu mai e o țeavă întreținută în tăcere, ci un produs cu utilizatori, promisiuni și un responsabil. Iar următorul pas al lecturii e să alegi unde se oprește transformarea ta.
Pasajele de până acum au tratat depozitul ca pe o cameră infinită. În practică, alegi unde se oprește transformarea și unde începe stocarea goală. Datele brute rămân intacte, deci cresc necontrolat: un magazin online care copiază fidel fiecare click ajunge la mii de fișiere mici pe zi, iar fiecare interogare le deschide pe toate. De asta se compactează periodic, ca să aranjezi o grămadă de bonuri mici într-un singur dosar lunar. Apare și întrebarea inversă: ce transformi în sursă, înainte de copiere? Regula practică: în sursă doar ce e prea voluminos sau prea sensibil, restul în depozit, unde e vizibil și testabil. Filtrarea jurnalelor de acces la graniță e ok; calcularea veniturilor acolo e o capcană, pentru că scapă de sub ochii tuturor. Următoarea întrebare e cine plătește toate aceste alegeri.
Parametrul unei rulări nu e niciodată „acum”, ci o fereastră de timp. Dacă pipeline-ul citește ora curentă, rularea de ieri nu mai poate fi repetată, pentru că „acum” s-a schimbat. Cu ferestre, o rulare e doar o funcție cu argumente: ianuarie 5 produce aceleași rânduri ori de câte ori o apeși. Asta transformă backfill-ul dintr-o operațiune eroică într-o buclă banală peste ferestre vechi, rulată în paralel. Gândește-te la un contabil care reface situația lunii martie: lucrează din cifrele din martie, nu din cele de azi. Apoi apare întrebarea firească: cât de des rulezi fiecare fereastră, și cine decide programul?
Pasajele au arătat ferestrele și testele. Rămâne mecanica concretă a idempotenței: cum faci o rulare repetabilă la nivel de rând. Două căi există. Prima: ștergi partiția ferestrei și o reîncarci de la zero. A doua: faci merge pe cheie, adică actualizezi rândul vechi dacă cheia există și inserezi doar dacă nu. Ambele produc aceleași rânduri la a doua rulare. Cea de-a treia cale, adăugarea oarbă, e capcana: un pipeline incremental care doar inserează va dubla comenzile de marți dacă rulează de două ori. Gândește-te la un coș de cumpărături: dacă clientul mai adaugă un produs, îi actualizezi coșul, nu îi creezi un coș nou lângă cel vechi. Dar merge-ul are o condiție: cheia trebuie să fie stabilă și completă. O cheie care se schimbă în timp, precum adresa unui client, transformă actualizarea în istorie pierdută. Atunci alegi conștient: rând curent sau istorie cu valabilitate. Iar alegerea cheii decide dacă pipeline-ul tău poate fi reparat sau doar curățat manual.
Idempotența nu e o virtute la care speri, ci o proprietate pe care o verifici. Iei aceeași fereastră, rulezi de două ori, și compari rezultatul: identic sau nu. Cea mai simplă metodă e ștergerea și reîncărcarea partiției, ca să înlocuiești o foaie dintr-un caiet, nu să lipești alta peste. Când reîncărcarea completă e prea scumpă, folosești merge pe cheie: rândul nou înlocuiește vechiul, nu i se adaugă lângă. Adăugarea oarbă pare mai rapidă, dar a doua rulare dublează vânzările din martie, iar un raport de venituri devine mincinos fără să pară stricat. De asta testul de idempotență stă în integrarea continuă, lângă testele de cod. Cum îl aplici totuși la volume uriașe, unde reîncărcarea completă costă prea mult?
De ce contează Un pipeline care nu poate fi rerulat fără să dubleze datele transformă fiecare incident într-o operațiune manuală de curățenie.