Migrări de schemă cu agenți: backfill fără downtime

Migrarea generată e corectă, secvența de livrare nu: ce blochează, cum aplici expand–contract și cum scrii un backfill reluabil, în loturi.

10 minute de lectură

Agentul scrie migrarea în douăzeci de secunde: un ALTER TABLE, o coloană nouă, un index. Arată corect, trece revizuirea, intră în livrarea de vineri. Luni dimineață afli că ALTER-ul a ținut tabela blocată patruzeci de minute, iar aplicația a returnat erori tot timpul ăsta. Codul migrării era corect; secvența de livrare, nu. Diferența dintre cele două e singura care contează într-o bază de date cu trafic.

Migrări de schemă fără downtime: expand, migrare de date, contract — cu porți umane la fiecare pas

Articolul e despre cum folosești un agent la migrări de schemă și la backfill fără să oprești producția: ce blochează, de fapt, fiecare operațiune, ce tipar de livrare folosești și unde pui porțile umane. Exemplele sunt pe PostgreSQL și MariaDB.

Ce blochează, de fapt

Prima regulă: nu întreba modelul „e sigură migrarea asta?", ci verifică în documentația bazei tale ce blocaj ia fiecare operațiune. Sunt patru fapte care explică majoritatea incidentelor.

În PostgreSQL, implicit se ia cel mai restrictiv blocaj. Documentația e categorică: pentru ALTER TABLE „se obține un blocaj ACCESS EXCLUSIVE, în lipsa unei mențiuni explicite contrare". Cu el, orice citire sau scriere pe tabelă așteaptă.

Adăugarea unei coloane nu e mereu costisitoare — depinde de valoarea implicită. Când adaugi o coloană cu o valoare implicită non-volatilă, documentația explică faptul că valoarea e evaluată o singură dată și stocată în metadate, iar „valoarea va fi aplicată doar când tabela e rescrisă, ceea ce face ALTER TABLE foarte rapid chiar și pe tabele mari". În niciunul dintre aceste cazuri nu e nevoie de rescrierea tabelei. Reversul: o valoare implicită volatilă (de exemplu un apel de tip ceas), o coloană generată stocată, o coloană de identitate sau una cu tip domeniu cu constrângeri „vor determina rescrierea întregii tabele și a indexurilor ei".

Crearea unui index blochează scrierile. În mod normal, spune documentația, baza „blochează tabela împotriva scrierilor" pe durata construirii; celelalte tranzacții pot citi, dar inserările, actualizările și ștergerile „se vor bloca până la finalizarea construirii indexului". Varianta CREATE INDEX CONCURRENTLY evită blocarea, cu trei consecințe de știut: necesită două scanări ale tabelei și durează semnificativ mai mult; dacă apare o problemă în timpul scanării, comanda eșuează și lasă în urmă un index „invalid", ignorat la interogări, dar care consumă efort la fiecare actualizare; și nu poate rula într-un bloc de tranzacție.

În MariaDB, tu declari ce accepți. Clauza ALGORITHM spune care e cel mai puțin eficient algoritm pe care îl accepți — de la INSTANT, care operează „fără modificarea vreunui fișier de date", la COPY, algoritmul clasic cu tabelă temporară. Clauza LOCK controlează concurența: NONE „efectuează operațiunea fără a obține niciun blocaj pe tabelă", permițând toate operațiunile concurente; SHARED permite doar citiri; EXCLUSIVE nu permite nimic. Detaliul care transformă asta într-un instrument de siguranță: dacă algoritmul sau nivelul de blocare cerut nu e suportat, instrucțiunea dă eroare. Cu alte cuvinte, scriind ALGORITHM=INPLACE, LOCK=NONE în migrare, primești o eroare în loc de un incident.

Această regulă merită scrisă direct în instrucțiunile de proiect pe care le citește agentul — formatul și limitele unui astfel de fișier sunt în ghidul despre AGENTS.md.

Tiparul care face totul posibil: expand și contract

Schimbările incompatibile cu versiunea anterioară nu se fac dintr-o singură livrare. Tiparul se numește parallel change și e descris de Danilo Sato pe bliki-ul lui Martin Fowler, în trei faze:

  1. Expand — „extinzi interfața ca să suporte atât versiunea veche, cât și pe cea nouă". În termeni de bază de date: adaugi coloana nouă, lași vechea coloană la locul ei.
  2. Migrate — muți consumatorii pe versiunea nouă, în ritmul pe care ți-l permite realitatea.
  3. Contract — „elimini versiunea veche și modifici interfața astfel încât să suporte doar versiunea nouă".

Avertismentul din articol e cel pe care îl ignoră majoritatea echipelor: pe durata migrării „furnizorul trebuie să suporte două versiuni diferite", iar dacă faza de contract nu se execută, „s-ar putea să ajungi într-o stare mai proastă decât cea de la început". Tradus: o coloană „temporară" lăsată în schemă doi ani e datorie tehnică, nu compromis inteligent.

Tiparul e o decizie de arhitectură, nu un truc de migrare: presupune că aplicația poate rula, o perioadă, în două versiuni compatibile simultan. Evoluția schemei, replicarea și compatibilitatea între versiuni sunt tratate în cursul Arhitectura Sistemelor AI la Scară.

Aplicat pe o redenumire de coloană — cazul clasic în care echipele fac downtime degeaba:

Pas Bază de date Aplicație
1 adaugi coloana nouă, fără constrângeri nimic
2 scrii în ambele coloane, citești din cea veche
3 backfill în loturi pentru rândurile vechi
4 citești din coloana nouă, continui să scrii în ambele
5 scrii doar în coloana nouă
6 ștergi coloana veche

Șase livrări mici, fiecare reversibilă, în locul uneia care „ar trebui să meargă".

Backfill-ul: partea pe care o subestimează toată lumea

Un UPDATE care atinge patruzeci de milioane de rânduri într-o singură tranzacție e o rețetă pentru blocaje, creștere necontrolată a jurnalului de tranzacții și o repornire imposibilă la jumătate.

Cerințele unui script de backfill care rezistă:

  • În loturi mici, cu identificatori ordonați, nu cu decalaj: fiecare lot își reține ultimul identificator procesat.
  • Reluabil. Dacă moare la 60%, repornirea continuă, nu o ia de la capăt.
  • Idempotent. Rularea de două ori pe același interval produce același rezultat.
  • Cu pauze controlate între loturi, ca replicile să nu rămână în urmă.
  • Cu observabilitate: câte rânduri rămân, în cât timp, cu ce rată de eroare.
  • Oprit ușor. Un comutator care îl oprește fără să lase date inconsistente.

Un agent scrie bine un astfel de script, pentru că e cod repetitiv cu tipare cunoscute. Ce nu decide agentul: dimensiunea lotului potrivită pentru sistemul tău, fereastra de rulare și dacă backfill-ul poate rula în timpul zilei.

Ce faci cu agentul și ce rămâne la tine

Ce delegi agentului și ce rămâne decizie umană într-o migrare de schemă

Deleghi cu încredere: generarea fișierului de migrare în formatul folosit de proiect, scriptul de backfill cu toate proprietățile de mai sus, scriptul de verificare a parității între vechi și nou, migrarea inversă, actualizarea documentației și a testelor.

Nu deleghi: decizia privind blocajele acceptabile, ordinea livrărilor, fereastra de rulare, momentul fazei de contract și ștergerea efectivă a coloanei sau a tabelei. Sunt decizii cu consecințe ireversibile, iar un model nu poartă răspunderea pentru ele.

Instrucțiunile care schimbă rezultatul. Într-un proiect cu trafic, adaugă în fișierul de instrucțiuni reguli explicite pe care agentul le respectă la fiecare migrare:

## Migrări de bază de date

- Orice migrare declară explicit algoritmul și nivelul de blocare acceptate.
- Nu se acceptă operațiuni care rescriu tabela; dacă sunt necesare,
  se semnalează în descrierea pull request-ului, nu se execută tăcut.
- Indexurile se creează fără blocarea scrierilor, într-o migrare separată.
- Coloanele nu se șterg în aceeași livrare cu adăugarea înlocuitorului.
- Fiecare migrare are migrare inversă testată.
- Backfill-ul stă în script separat, nu în migrare.

Reguli de acest tip nu sunt garanții — instrucțiunile rămân context, nu configurație impusă — deci perechea lor obligatorie e o verificare automată în integrarea continuă care respinge migrările care nu le respectă. Distincția între ghidaj și regulă care blochează e detaliată în ghidul despre izolarea agenților de programare.

Patru operațiuni care produc cele mai multe incidente

Toate par banale în textul migrării și niciuna nu e.

Adăugarea unei constrângeri „nu poate fi nul" pe o tabelă mare. Baza trebuie să verifice fiecare rând existent, iar verificarea se face sub blocaj. Varianta sigură e în doi timpi: adaugi constrângerea ca „nevalidată", faci curățenia în date în loturi, apoi o validezi separat, când validarea nu mai blochează scrierile.

Adăugarea unei chei străine. Aceeași problemă, amplificată: se verifică relația pentru toate rândurile și se ia blocaj și pe tabela referită. Ordinea contează — întâi te asiguri că datele sunt curate, abia apoi adaugi constrângerea.

Schimbarea tipului unei coloane. Cea mai înșelătoare dintre toate, pentru că un ALTER de o linie poate declanșa rescrierea completă a tabelei și a indexurilor ei. Tiparul expand–contract rezolvă și cazul ăsta: coloană nouă cu tipul corect, scriere dublă, backfill, comutare, ștergere.

Indexul unic pe date care conțin duplicate. Construirea eșuează la jumătate, după ce a consumat resurse, iar în varianta concurentă lasă în urmă un index invalid. Verifici duplicatele înainte, cu o simplă interogare de grupare, nu descoperi problema în timpul livrării.

Regula comună celor patru: operațiunea care validează date existente e cea periculoasă, nu cea care schimbă structura. Când ceri unui agent o migrare, cere-i explicit să marcheze în descriere dacă operațiunea verifică rânduri existente — e întrebarea care prinde din timp trei din cele patru cazuri de mai sus.

Repetiția pe o copie

Regula care elimină aproape toate incidentele: nicio migrare pe o tabelă mare nu se livrează fără o repetiție pe o copie restaurată din producție, cu date anonimizate.

Ce măsori la repetiție: cât durează efectiv, ce blocaje ia, cât crește jurnalul de tranzacții, cât rămân în urmă replicile, cât spațiu suplimentar consumă. Cu cifrele astea, decizia „rulăm marți la 3 dimineața" devine un calcul, nu o intuiție.

Tot acolo verifici și planul de revenire. Întrebarea corectă nu e „ce facem dacă eșuează", ci „ce facem dacă reușește pe jumătate": o migrare care a adăugat coloana și a picat la backfill lasă sistemul într-o stare pe care trebuie să o știi dinainte.

Lucrul efectiv cu agentul pe depozit — permisiuni, hooks, verificări automate înainte de integrare — e tratat în cursul Claude Code Mastery.

Secvența, pe scurt

  1. Scrii intenția în limbaj natural și constrângerile (trafic, fereastră, ce nu se poate opri).
  2. Ceri agentului migrarea, migrarea inversă, scriptul de backfill și scriptul de verificare — separat.
  3. Verifici manual blocajele: ce ia fiecare instrucțiune, în versiunea ta de bază.
  4. Repeți pe copie restaurată, cu volum real, și notezi cifrele.
  5. Livrezi expand, apoi migrarea aplicației, apoi backfill-ul, apoi comutarea citirilor.
  6. Aștepți o perioadă de observație — zile, nu ore.
  7. Livrezi contract și ștergi ce nu mai e folosit.
  8. Închizi bucla: notezi în fișierul de instrucțiuni ce te-a surprins.

Dacă problema ta nu e structura, ci viteza interogărilor pe structura existentă, diagnosticul se face altfel — cu planuri de execuție, nu cu migrări; metoda e în ghidul despre optimizarea SQL cu AI.

Întrebări frecvente

Î: Pot lăsa agentul să scrie și să ruleze migrări? R: Să le scrie, da. Să le ruleze în producție, nu. Migrările sunt operațiuni cu efecte ireversibile asupra datelor, iar decizia privind momentul și blocajele acceptabile rămâne umană. Practica sănătoasă e ca migrările generate să intre printr-un pull request separat, cu un revizor desemnat, și să fie rulate de pipeline-ul obișnuit de livrare.

Î: De ce a ținut ALTER TABLE tabela blocată, deși am adăugat doar o coloană? R: Cel mai probabil din cauza valorii implicite. În PostgreSQL, o valoare implicită non-volatilă e stocată în metadate și nu cere rescrierea tabelei, dar o valoare volatilă, o coloană generată stocată sau una de identitate determină rescrierea întregii tabele și a indexurilor. În plus, ALTER TABLE ia implicit cel mai restrictiv blocaj, deci chiar și o operațiune scurtă poate crea o coadă dacă tabela e ocupată.

Î: Cum adaug un index pe o tabelă mare fără să opresc scrierile? R: În PostgreSQL, cu CREATE INDEX CONCURRENTLY, știind că durează mai mult, cere două scanări și nu poate rula într-o tranzacție; dacă eșuează, lasă un index invalid care trebuie șters și recreat. În MariaDB, declari ALGORITHM și LOCK=NONE, iar dacă operațiunea nu suportă nivelul cerut, primești o eroare în loc de un blocaj neașteptat.

Î: Când șterg coloana veche? R: După ce nicio versiune aflată în rulare nu o mai citește și nu o mai scrie, plus o perioadă de siguranță care acoperă revenirile posibile la versiunea anterioară. Faza de contract e obligatorie — articolul care descrie tiparul avertizează că, dacă nu o execuți, ajungi într-o stare mai proastă decât cea inițială — dar e ultima, nu prima.

Î: Backfill-ul poate rula în timpul zilei? R: Depinde de sistemul tău, iar răspunsul se obține la repetiția pe copie, nu prin estimare. Ce contează: dimensiunea lotului, pauzele dintre loturi, cât rămân în urmă replicile și dacă tabela e implicată în tranzacții lungi. Un backfill bine scris poate rula oricând, pentru că e oprit ușor și reluabil.

Concluzie

Un agent scrie migrări corecte sintactic în câteva secunde și economisește timp real la partea repetitivă: scriptul de backfill, verificările, migrarea inversă, documentația. Ce nu poate face e să decidă cât blocaj suportă producția ta la ora zece dimineața.

Reguli scrise în proiect, verificări automate care resping ce nu le respectă, o repetiție pe copie și tiparul expand–migrate–contract: patru lucruri plictisitoare care transformă migrările dintr-un eveniment temut într-o operațiune de rutină.


Surse: PostgreSQL — ALTER TABLE · PostgreSQL — CREATE INDEX · MariaDB — InnoDB online DDL · Danilo Sato — Parallel Change (martinfowler.com)

Articol informativ, publicat la 19 septembrie 2026. Comportamentul instrucțiunilor de modificare a schemei diferă între versiuni și motoare de stocare — verifică documentația versiunii tale înainte de a rula o migrare în producție.

Autor

Echipa editorială Cursuri AI

Redacție dedicată AI-ului aplicat, în limba română. Actualizăm articolul când se schimbă modelele, prețurile sau legislația la care face referire.

  • Surse oficiale citate în text
  • Revizuit înainte de publicare

Cursul care continuă acest articol

Arhitectura Sistemelor AI la Scară: Ghid Complet Enterprise (2026 Edition)

  • 21 lecții
  • ~25h de conținut
  • IT Pro

Ai citit teoria. În curs o aplici: lecții structurate pe module, exerciții și quiz-uri cu feedback imediat, Profesorul AI integrat în fiecare lecție și progres salvat automat. La final primești o atestare privată de finalizare.

Din programa cursului

  1. Fundamente Arhitectură AI Enterprise
  2. Core Runtime și Orchestrare
  3. Date, RAG și Caching
  4. Scalare și Performanță

+ încă 3 module în programa completă

499 lei pe lună pentru acest curs, TVA 21% inclus · sau 1.999 lei pe lună pentru toate cele 25 de cursuri IT Pro (vezi ce înveți în tot parcursul).

Abonament lunar, cu reînnoire automată · anulezi oricând din contul tău · conținut digital cu acces imediat, vezi condițiile de retragere. Atestarea confirmă parcurgerea cursului și este privată — nu este diplomă și nu este calificare recunoscută de stat.

Ți-a plăcut articolul? Lasă o apreciere sau salvează-l pentru mai târziu.
Newsletter

Articole noi despre AI, o dată pe săptămână

Fără zgomot: un singur email pe săptămână, cu articolele noi și cursul asociat, când există.

Comunitate

Întrebări & sugestii

Ce au întrebat cititorii despre acest articol — și răspunsurile echipei Cursuri AI.

Mesajele sunt verificate de un moderator înainte de publicare.

Fii primul care lasă o întrebare sau o sugestie pe acest articol.

Catalogul complet

50 de cursuri AI în română, cu exerciții, quiz-uri și Profesor AI

Un curs: 499 lei pe lună · pachet Non-IT: 1.499 lei pe lună · pachet IT Pro: 1.999 lei pe lună. Toate cu TVA inclus, începutul primei lecții se citește fără cont.