Runtime lineage reconstrueert de geschiedenis; het kan dit niet voorkomen.
Elke belangrijke tool voor data lineage die tegenwoordig in productie is, werkt op dezelfde manier. Een query wordt uitgevoerd op het datawarehouse. De engine registreert de query. Een parser – soms de eigen query history API van het datawarehouse, soms een externe SQL-parser – ontleedt de query in bron- en doelkolommen en schrijft vervolgens de verbindingen naar een grafiek.
Dit is runtime lineage. OpenLineage collectors, de systeemtabellen van Databricks Unity Catalog, de ACCESS_HISTORY view van Snowflake, het manifest.json van dbt dat na dbt compile wordt gegenereerd – ze reconstrueren allemaal de afhankelijkheidsgrafiek op basis van artefacten die tijdens of na de uitvoering worden geproduceerd. De grafiek is accuraat voor wat er is uitgevoerd. Hij vertelt je niets over wat er vervolgens zal worden uitgevoerd.
Het verschil is belangrijk wanneer een upstream-bron een kolomtype wijzigt van INTEGER naar VARCHAR. Runtime lineage brengt dit aan het licht nadat het downstream-model faalt of, erger nog, de data stilletjes dwingt en onjuiste aggregaties produceert. Een financieel team haalt maandagochtend een omzetdashboard op. De cijfers lijken plausibel. Ze zijn echter onjuist. Niemand weet het totdat iemand drie dagen later handmatig een steekproefsgewijze controle uitvoert in het bronsysteem.
De runtime lineage is niet verbroken. Voor audit trails, compliance rapportage en inzicht in de historische dataflow blijft het essentieel. Maar om productiestoringen te voorkomen, kent het een structurele beperking: het vereist uitvoering om de grafiek te genereren. De defecte query moet worden uitgevoerd voordat je erachter komt dat deze defect is.
Compile-time lineage lost de grafiek op vóór de uitvoering.
Rocky, een op Rust gebaseerd SQL-controlepaneel dat deze week op Hacker News verscheen met 120 punten en 48 reacties, hanteert een andere aanpak. De compiler parseert transformatiemodellen die zijn geschreven in een .rocky DSL, lost afhankelijkheden op kolomniveau op aan de hand van gedeclareerde schema's en bouwt de lineage-grafiek op voordat een query het datawarehouse bereikt.
Het mechanisme werkt als een typechecker. Elk model declareert zijn inputs en outputs. De compiler doorloopt de DAG en vergelijkt elke gerefereerde kolom met de schema-definitie van het upstream-model. Als een kolom ontbreekt, hernoemd is of een incompatibel type heeft, mislukt de compilatie met een diagnostische code — E010 voor ontbrekende kolommen, E013 voor type-mismatches. Er wordt geen query verzonden. Er worden geen rijen geschreven. Er wordt geen rekenkracht verbruikt.
Het uitvoeren van rocky compile valideert de volledige DAG. Het uitvoeren van rocky lineage --column revenue traceert die ene kolom terug door aggregaties, joins en seeds naar de oorsprong. Het uitvoeren van rocky lineage-diff vergelijkt de opgeloste grafiek tussen twee Git-referenties en laat precies zien welke kolommen afhankelijkheden hebben gekregen of verloren in een pull-request.
Dit is niet helemaal nieuw. SQLMesh voert een vergelijkbare statische analyse uit met behulp van zijn eigen SQL-parser, waarbij de kolomherkomst tijdens de planning wordt bepaald in plaats van na de uitvoering. De aanpak van dbt zit daar tussenin: dbt compile lost Jinja op en produceert SQL, maar kolomherkomst in dbt is afhankelijk van de dbt Cloud metadata API of tools van derden zoals Elementary en SQLLineage die de gecompileerde SQL achteraf parsen.
Het Rocky-project voegt daar nog branch-isolatie aan toe. rocky branch create experiment-v2 voorziet in een apart schema in het datawarehouse, voert daar de aangepaste DAG uit en laat je de resulterende herkomstgrafiek vergelijken met de hoofdgrafiek. Zie het als een staging-omgeving voor je kolomafhankelijkheden, niet alleen voor je data.
Schemadrift is de stille fout die compileertijd-lineage daadwerkelijk detecteert.
Schema-afwijkingen zijn de meest voorkomende oorzaak van onzichtbare dataverlies in productiedatawarehouses. Een bronsysteem voegt een kolom toe. Een upstream-team wijzigt een DECIMAL(18,2) naar DECIMAL(38,10). Een CSV-seedbestand arriveert met headers in een andere volgorde. Geen van deze zaken leidt noodzakelijkerwijs tot queryfouten. Veel ervan leiden echter tot stille verslechtering van de datakwaliteit.
Neem bijvoorbeeld een Databricks-pipeline waarin een bronzen tabel gegevens opneemt van een SQL Server CDC-feed. De DBA van de bron wijzigt unit_price van DECIMAL(10,2) naar DECIMAL(10,4). De transformatie van brons naar zilver converteert naar DOUBLE voor aggregatie — zonder foutmelding. Het zilver-naar-goud-model rondt af op twee decimalen voor het financiële rapport. De extra precisie wordt in sommige rijen stilzwijgend weggelaten, maar veroorzaakt afrondingsverschillen in andere rijen. Omzetcijfers verschuiven met fracties van een cent per rij, wat oploopt tot duizenden centen over miljoenen transacties.
Met runtime lineage ontdek je dit wanneer de financiële afdeling een afstemmingsfout signaleert. Met compile-time lineage detecteert de compiler de typewijziging op de grens van het schemacontract. De data-contractfunctie van Rocky stelt je in staat om column unit_price: DECIMAL(10,2) required in de modeldefinitie te declareren. Wanneer het upstream-schema verandert, mislukt rocky compile voordat de pipeline wordt uitgevoerd.
De beperking zit hem in de reikwijdte. Compile-time lineage dekt alleen transformaties die zijn gedefinieerd in de eigen modellen van de tool. Als een Azure Data Factory-pipeline gegevens in een stagingtabel plaatst die Rocky als bron leest, valideert het schemacontract aan de hand van het gedeclareerde schema, maar het gedeclareerde schema is slechts zo actueel als de laatste keer dat het is bijgewerkt. Automatische schema-introspectie vanuit de warehousecatalogus dicht dit gat gedeeltelijk. Rocky ondersteunt dit via de adapterlaag voor Databricks (Unity Catalog), Snowflake en BigQuery, waarbij live schema's tijdens het compileren worden opgehaald.
Toch blijven alle gegevens die via een pad buiten de controle van de tool binnenkomen – een handmatige CSV-upload, een connector van een derde partij, een onbeheerde Spark-taak – buiten de grafiek die tijdens het compileren wordt gegenereerd. Dit is waar runtime-monitoring van pas komt.
Branch-isolatie maakt lineage diffing praktisch voor teams.
Kolomherkomst als statisch artefact is nuttig voor documentatie. Kolomherkomst als diffable artefact is nuttig voor codebeoordeling.
De traditionele workflow voor het valideren van een transformatiewijziging verloopt als volgt: een ontwikkelaar wijzigt een dbt-model, opent een pull request, een reviewer leest de SQL, traceert mentaal de kolomafhankelijkheden en keurt goed op basis van zijn of haar begrip van de DAG. Voor eenvoudige modellen werkt dit. Voor een DAG met meer dan 200 modellen en verwijzingen tussen databases is mentaal traceren onbetrouwbaar.
Rocky's branch-model pakt dit direct aan. Wanneer een ontwikkelaar rocky branch create feature-x uitvoert, maakt de tool een geïsoleerd schema (bijv. dev_feature_x) aan in de doeldatawarehouse. Alle modellen in de gewijzigde DAG compileren en worden uitgevoerd tegen dat schema. De herkomstgrafiek voor de branch wordt naast de hoofd-herkomstgrafiek opgeslagen. Het uitvoeren van rocky lineage-diff main..feature-x produceert een gestructureerde diff die laat zien welke kolommen nieuwe upstream-afhankelijkheden hebben gekregen, welke deze hebben verloren en welke van type zijn veranderd.
Dit transformeert lineage-review van een handmatige SQL-leesopdracht naar een gestructureerde diff. Een reviewer kan zien dat fact_orders.total_revenue nu afhankelijk is van stg_pricing.discount_rate terwijl dit voorheen niet het geval was, en zich afvragen of die afhankelijkheid opzettelijk is.
SQLMesh biedt vergelijkbare functionaliteit via de sqlmesh plan-opdracht, die een diff van de DAG berekent en de getroffen downstream-modellen weergeeft. De granulariteit verschilt: SQLMesh berekent diffs op modelniveau met lineage op kolomniveau beschikbaar in de gebruikersinterface, terwijl Rocky diffs op kolomniveau weergeeft in de CLI-output.
De praktische beperking is de kosten van het datawarehouse. Elke branch creëert echte tabellen in het datawarehouse. Voor lokale ontwikkeling met DuckDB als backend is dit gratis. Voor een Databricks-branch met 50 modellen en data van terabyte-formaat lopen de rekenkosten snel op. Rocky's SHALLOW CLONE-ondersteuning op Databricks verzacht dit door metadata in plaats van data te klonen, maar de functie staat nog steeds op de planning en is nog niet voor alle modeltypen beschikbaar.
Contracten worden tijdens het compileren gedetecteerd, al het andere tijdens de uitvoering.
Het bijhouden van de kolomherkomst tijdens het compileren voorkomt een specifieke klasse van fouten: schemaverschuivingen, typefouten, ontbrekende kolommen en verbroken contracten binnen de beheerde DAG. Deze fouten zijn reëel en komen vaak voor — alleen al schemaverschuivingen zijn verantwoordelijk voor een aanzienlijk deel van de incidenten in data pipelines bij organisaties die meer dan 50 modellen gebruiken.
Maar productie pipelines falen om redenen die geen enkele compiler kan voorzien. Een Databricks-cluster loopt vast door een ondergeprovisioneerd autoscalingbeleid. De referenties van de linked service van een ADF-pipeline verlopen. Een dbt Cloud-taak slaagt, maar produceert nul rijen omdat de brontabel tijdens een onderhoudsvenster is leeggehaald. Een Power BI dataset verversing mislukt omdat de gateway service niet meer reageert. Dit zijn runtime fouten en ze vereisen runtime detectie.
Hier spelen compileertijd- en runtime observatie een complementaire rol. Het bijhouden van de kolomherkomst tijdens het compileren laat zien welke kolommen worden beïnvloed als een schema verandert. Runtime monitoring laat zien dat de pipeline daadwerkelijk is mislukt, dat de verversing is vertraagd, dat het aantal rijen met 40% is gedaald ten opzichte van de vorige run.
MetricSign detecteert deze runtime fouten bij Power BI verversingen, ADF-pipelines, Databricks-taken en dbt-uitvoeringen. Gerelateerde incidenten worden gegroepeerd op basis van de hoofdoorzaak, zodat één verlopen inloggegeven niet 30 afzonderlijke waarschuwingen genereert voor 30 verschillende datasets. De kolomherkomstgrafiek van een tool zoals Rocky laat zien welke kolommen zich in de getroffen zone bevinden. Het runtime signaal van de monitoring bevestigt dat de fout daadwerkelijk heeft plaatsgevonden.
De conclusie voor de technische afdeling: implementeer compile-time lineage om schemavalidatie naar een eerder stadium in CI te verplaatsen. Blijf runtime monitoring gebruiken om fouten op te sporen die niet door statische analyse kunnen worden voorspeld. Geen van beide vervangt de ander. Samen overbruggen ze de kloof tussen 'deze DAG is structureel geldig' en 'deze DAG heeft daadwerkelijk correcte gegevens op tijd geproduceerd'.