data

Change Data Capture: cum propagi modificările din Postgres în restul stivei

CDC captează fiecare insert, update și delete dintr-o bază de date și le propagă în timp aproape real. Două abordări, tooling real, capcane operaționale.

Cuprins

Change Data Capture (CDC) este tehnica prin care fiecare modificare produsă într-o bază de date, indiferent că e un insert, un update sau un delete, este capturată și propagată către alte sisteme în timp aproape real. În loc să interoghezi periodic tabele pentru „ce s-a schimbat de ultima oară", citești direct fluxul de modificări din jurnalul bazei de date.

Nevoia apare când ai mai mult de un sistem care trebuie să reflecte aceleași date. Un search index, un data warehouse, un cache, un microserviciu cu stocare proprie. Fiecare dintre ele poate fi ținut la curent fie printr-un job batch care rulează periodic, fie prin CDC, care propagă modificările pe măsură ce se produc.

Ce este Change Data Capture mai exact?

CDC este un pattern de integrare a datelor, nu un produs specific. Ideea de bază: în loc să ceri bazei de date „dă-mi toate rândurile modificate după timestamp X", citești un flux ordonat de evenimente de modificare produs de baza de date însăși. Fiecare eveniment descrie o singură operație: ce tabelă, ce tip de modificare (INSERT / UPDATE / DELETE), valorile de dinainte și de după.

Rezultatul este că sistemele downstream nu mai au nevoie să știe cum să interogheze schema originală. Primesc un flux de evenimente structurate și îl consumă în ordinea producerii. Dacă un consumator pică și se reconectează mai târziu, poate relua fluxul de la ultimul offset confirmat, fără să piardă modificări.

Principalele use case-uri:

  • Replicare cross-region. O replică Postgres dintr-un alt datacenter se menține la zi prin replicare logică, care este CDC la nivel de motor.
  • Sincronizare cu un search index. Elasticsearch sau Typesense primesc documentele noi sau modificate imediat ce sunt scrise în Postgres, fără un job de reindex nocturn.
  • Alimentarea unui data warehouse. Tabelele de fapte din data warehouse se actualizează incremental, nu prin load complet zilnic. Combinat cu un pipeline ETL sau ELT, reduce fereastra de date vechi de la ore la minute.
  • Event-driven architecture. Modificările din baza de date devin evenimente pe un stream consumat de alte servicii, fără coupling direct între ele.

Care sunt mecanismele principale: trigger-based vs log-based?

Există două abordări fundamentale, cu compromisuri diferite:

Trigger-based CDC. Pui triggere pe tabele, iar la fiecare INSERT / UPDATE / DELETE, triggerul scrie o înregistrare într-o tabelă de audit. Un proces extern citește periodic acea tabelă și propagă modificările. Simplu de implementat, funcționează pe orice bază de date care suportă triggere. Dezavantajul: overhead la scriere (fiecare modificare declanșează logică suplimentară în tranzacție), tabelele de audit cresc rapid, și completeness-ul depinde de faptul că triggere există pe toate tabelele relevante.

Log-based CDC (WAL în Postgres, binlog în MySQL). Citești direct jurnalul de tranzacții al bazei de date. În Postgres, acesta este Write-Ahead Log (WAL): toate modificările sunt scrise mai întâi în WAL înainte să fie aplicate pe disc. Prin logical replication, Postgres decodifică WAL-ul și îl expune ca flux de evenimente structurate. Niciun overhead la scriere, completeness garantat (orice modificare care ajunge pe disc trece prin WAL), și include operații pe care triggerele le ratează uneori (TRUNCATE, modificări DDL cu efect de date).

Log-based câștigă pe toate criteriile de performanță și completeness pentru volume semnificative. Trigger-based rămâne util când nu ai acces la jurnalul tranzacțional sau când vrei un audit selectiv cu logică custom.

Ce tooling există: Debezium, replicare logică Postgres, MySQL binlog?

Ecosistemul este matur și acoperă principalele baze de date relaționale și documentare:

  • Debezium. Standardul open-source pentru CDC log-based. Rulează ca un set de conectori Kafka Connect și suportă Postgres, MySQL, MongoDB, SQL Server, Oracle, DB2. Fiecare conector citește jurnalul bazei de date și publică evenimentele pe topicuri Kafka. De acolo, orice consumator Kafka (Elasticsearch sink, JDBC sink, aplicații custom) le preia în ordinea producerii. Debezium este alegerea implicită când ai nevoie să alimentezi mai mulți consumatori dintr-un singur stream.
  • Replicarea logică nativă Postgres. Fără infrastructură suplimentară: creezi o publication pe instanța sursă și un subscription pe instanța destinație (tot Postgres). Funcționează bine pentru replicare single-destination; limitarea apare când vrei să alimentezi sisteme non-Postgres sau când ai nevoie de transformări înainte de destinație. Conectorul Postgres al Debezium folosește intern această facilitate.
  • MySQL binlog. Echivalentul WAL-ului în MySQL. Debezium îl citește prin conectorul MySQL; alternativa nativă este replicarea master-slave MySQL clasică. Binlog-ul trebuie activat explicit (binlog_format=ROW) și poate umple discul rapid dacă retenția nu este configurată.
  • Kafka Connect fără Debezium. Există conectori JDBC care fac CDC trigger-based sau timestamp-based direct pe Kafka Connect, fără să citească WAL-ul. Mai simplu de configurat, dar cu limitările trigger-based descrise mai sus.

Cum decizi când ai nevoie de CDC?

CDC adaugă complexitate operațională reală: un nou tip de infrastructură de monitorizat, replication slots de gestionat, schema changes de coordonat cu consumatorii. Decizia merită gândită înainte de implementare. Pentru pipelines de date la scara celor din PromoAzi (24 de milioane de înregistrări reîmprospătate noapte de noapte), întrebarea cheie este dacă latența unui job batch este acceptabilă sau dacă modificările trebuie propagate în timp real.

Câteva întrebări care ajută la calibrare:

  • Ce latență este acceptabilă? Dacă un job batch de 15 minute este suficient, CDC nu justifică complexitatea. Dacă ai nevoie de date în secunde (search index, replică de citire), CDC este justificat.
  • Câți consumatori ai? Un singur consumator poate fi alimentat cu replicare logică nativă sau cu un job periodic. Când consumatorii cresc (data warehouse + search index + microserviciu), un stream CDC centralizat reduce coupling-ul.
  • Poți citi direct din producție? Dacă nu (latență, securitate, izolare de workload), CDC pe o replică de citire este alternativa naturală.
  • Cum gestionezi DELETE-urile? Un job batch cu WHERE updated_at > ? nu vede rândurile șterse. CDC le capturează explicit ca evenimente DELETE, esențial pentru sisteme care trebuie să reflecte ștergerile.

Care sunt capcanele operaționale: schema changes, lag, replay?

  • Schema changes care strică consumatorii. Adaugi o coloană NOT NULL fără default pe o tabelă sursă: consumatorul Debezium, configurat cu schema dinaintea modificării, poate să se oprească sau să interpreteze greșit evenimentele noi. Regula de bază: orice modificare DDL pe o tabelă sursă CDC trebuie coordonată cu consumatorii înainte de aplicare. Schema Registry (parte din ecosistemul Kafka) ajută la versioning explicit.
  • Replication slot care acumulează WAL. Un replication slot previne Postgres-ul să șteargă WAL-ul până când consumatorul confirmă că l-a procesat. Dacă un consumator pică și nu se reconectează, Postgres reține WAL indefinit. Pe un VPS cu disc limitat, asta poate umple discul și bloca complet baza de date. Monitorizarea sloturilor inactive este obligatorie, iar sloturile neutilizate mai mult de câteva ore trebuie investigate imediat. Acest comportament este documentat și în secțiunea de monitorizare Postgres din stack-ul de observabilitate.
  • Lag la repornire și initial snapshot. Debezium face un snapshot complet la primul start, înainte să citească WAL-ul. Pe tabele mari (zeci de milioane de rânduri), snapshot-ul poate dura ore. Planifică fereastra de maintenance sau folosește snapshot incremental dacă versiunea Debezium îl suportă.
  • Replay idempotent la consumator. La reconectare, un consumator poate reprocesa evenimente deja aplicate. Consumatorii CDC trebuie să fie idempotenti: aplicarea aceluiași eveniment de două ori trebuie să producă același rezultat. Fără idempotență, dubletele produc corupție de date la destinație.
  • Ordinea evenimentelor între tabele. CDC garantează ordinea pe o singură tabelă, nu și ordinea relativă între tabele. Consumatorii care depind de consistență cross-tabelă trebuie să gestioneze această fereastră de inconsistență explicit.

Întrebări frecvente

CDC vs ETL clasic, care e diferența principală?

ETL clasic mișcă date în loturi periodice; CDC capturează fiecare modificare pe măsură ce se produce, în timp aproape real. Un job ETL rulat la fiecare oră înseamnă că datele tale de downstream sunt vechi cu până la 60 de minute. CDC reduce această fereastră la secunde sau zecimi de secundă, cu costul unei complexități operaționale mai mari.

Pot folosi CDC cu orice bază de date?

Nu orice bază de date expune un jurnal tranzacțional accesibil extern. Postgres, MySQL, MongoDB și SQL Server au suport matur pentru CDC log-based. SQLite nu are WAL accesibil extern. Bazele de date cloud (Aurora, Cloud SQL) adaugă restricții suplimentare: replication slots pot consuma resurse pe host și trebuie configurate explicit.

Debezium vs replicare logică nativă Postgres, care aleg?

Replicarea logică nativă este mai simplă dacă destinația e tot Postgres; Debezium dacă ai mai mulți consumatori sau dacă destinația e Kafka, Elasticsearch sau un data warehouse. Replicarea logică nativă funcționează cu CREATE PUBLICATION și un subscriber Postgres de pe altă instanță. Debezium adaugă un strat de transformare și routing, util când un singur stream trebuie să alimenteze sisteme heterogene.

Ce se întâmplă cu un replication slot neutilizat?

WAL-ul se acumulează pe disc până când consumatorul preia sau slotul este șters manual. Dacă un consumator Debezium pică și nu se reconectează, Postgres păstrează WAL-ul pentru acel slot indefinit. Pe un VPS cu disc limitat, asta poate umple discul și bloca întreaga bază de date. Monitorizează pg_replication_slots și setează o alertă pe pg_wal_lsn_diff.

CDC are sens pentru un startup mic cu o singură bază de date?

De obicei nu, pentru că complexitatea operațională depășește beneficiul la scară mică. Dacă toate serviciile tale citesc din aceeași bază de date Postgres, nu ai nevoie de CDC. Îl adaugi când apare primul sistem care nu poate citi direct din Postgres (un search index, un data warehouse, un microserviciu cu stocare proprie) și când latența unui job batch de 10-15 minute devine inacceptabilă.