Di Andrew Tan
Le variazioni di schema e i cambiamenti a monte che interrompono il sistema sono la principale causa di fallimenti silenziosi dei dati. Ecco come evitare di essere colti di sorpresa.
Il campo che ha rotto tutto
Una società fintech che conosco aveva un'integrazione con un processore di pagamenti che funzionava perfettamente da due anni. Centinaia di migliaia di transazioni al giorno, pipeline solida, team finanziario soddisfatto. Poi una mattina i rapporti di riconciliazione hanno iniziato a produrre assurdità . Le cifre dei ricavi erano sbagliate di ordini di grandezza. Conteggi negativi dove avrebbero dovuto esserci positivi.
La causa principale ha richiesto sei ore per essere trovata. Il loro processore di pagamenti aveva cambiato silenziosamente un campo — amount — da una stringa a un intero. Nessun incremento di versione. Nessuna guida alla migrazione. Nessuna email. L'hanno semplicemente spinto. La logica di conversione della pipeline stava eseguendo operazioni su stringhe su quel campo, tagliando i centesimi, analizzando i codici di valuta, e quando improvvisamente ha ricevuto interi, non ha dato errore. Ha continuato a funzionare e ha prodotto numeri che sembravano abbastanza plausibili da superare i controlli automatici ma erano completamente sbagliati.
Sei ore di tempo degli ingegneri. Due giorni di riconciliazione del team finanziario. Una conversazione molto scomoda con il CFO. Tutto perché un fornitore ha cambiato un tipo di campo.
Ho visto variazioni di questa storia più volte di quante ne possa contare. Il sistema di origine cambia senza preavviso. La pipeline non si blocca. Fa qualcosa di peggio: continua a funzionare e produce spazzatura.
Perché questo non appare nel tuo monitoraggio
Ecco la parte controintuitiva. La maggior parte delle pipeline è progettata per rilevare i fallimenti di elaborazione, non i fallimenti di schema.
Ricevi avvisi quando un lavoro non viene eseguito. Ricevi avvisi quando la latenza aumenta. Ricevi avvisi quando il database di destinazione rifiuta una scrittura. Quello che di solito non ricevi sono avvisi quando l'input cambia forma e la tua logica di elaborazione va silenziosamente fuori strada.
La ragione è strutturale. La validazione dello schema tende a essere un ripensamento. I team scrivono prima la logica della pipeline, si preoccupano della validazione in seguito, poi non la implementano mai effettivamente perché la pipeline è già in esecuzione e toccarla sembra rischioso.
Quindi finisci con un sistema che è resistente ai fallimenti dell'infrastruttura e completamente cieco alle violazioni del contratto dei dati.
Tre classi di cambiamenti a monte
Non tutti i cambiamenti a monte sono ugualmente pericolosi. Tre categorie:
I cambiamenti additivi sono quelli amichevoli. Appare un nuovo campo opzionale. Un array guadagna un tipo di elemento extra. Il sistema di origine cresce senza rompere la struttura esistente. La tua pipeline generalmente gestisce bene questi cambiamenti — ignori semplicemente ciò che non ti serve. Basso rischio.
I cambiamenti che interrompono sono il nemico ovvio. Un campo richiesto scompare. Un tipo di dato cambia. Un campo viene rinominato. Questi tendono a causare fallimenti evidenti, il che è in realtà positivo. La tua pipeline si blocca, ricevi un avviso, lo risolvi. Doloroso ma rilevabile.
I cambiamenti silenziosi sono i peggiori. Questi sono cambiamenti che non causano fallimenti. Il campo è ancora lì. Il tipo è ancora tecnicamente compatibile. Ma la semantica è cambiata. Un campo status che conteneva "active" e "inactive" ora contiene "enabled" e "disabled". I tuoi controlli nulli passano ancora. I tuoi conteggi di righe sembrano normali. I tuoi dashboard si popolano ancora. Tutto è sbagliato.
I cambiamenti silenziosi sono quelli che finiscono nelle conversazioni con il CFO.
Dove fallisce la maggior parte delle pipeline
L'istinto è di aggiungere più validazione al livello di elaborazione. Controlla i tipi prima di convertire. Verifica che i campi richiesti siano presenti. Esegui controlli di forma all'ingestione.
Questo aiuta, ma manca il problema principale. La validazione al livello di elaborazione cattura gli errori di tipo. Non cattura la deriva semantica. Non cattura il caso in cui un campo è presente, correttamente tipizzato, e completamente sbagliato nel significato.
La vera difesa è a un livello completamente diverso: il contratto tra il sistema di origine e la tua pipeline.
Un contratto definisce non solo la struttura dei dati ma le aspettative intorno ad essi. Il campo X è una stringa che rappresenta un importo denominato in valuta in unità minori. Il campo Y è un enum con esattamente questi quattro valori. Il campo Z è un timestamp in UTC, mai nullo, sempre entro gli ultimi 90 giorni.
Quando definisci quel contratto esplicitamente e lo testi a ogni ingestione, catturi i cambiamenti silenziosi prima che corrompano i dati a valle. Catturi il caso in cui amount è ancora una stringa ma ora rappresenta l'importo totale piuttosto che l'importo in unità minori. Catturi il caso in cui l'enum guadagna un quinto valore che la tua pipeline non ha mai visto.
Test del contratto al livello della pipeline
Il test del contratto è ben consolidato nei microservizi (Pact è il framework più comune), ma è sottoutilizzato nelle pipeline di dati. L'idea di base è semplice: definisci la forma e la semantica del tuo input come un contratto formale, quindi esegui test contro quel contratto ogni volta che i dati fluiscono.
In pratica, questo significa tre cose.
Una specifica di schema che va oltre i tipi di campo. Includi intervalli di valori attesi, vincoli di cardinalità , la relazione tra i campi. Non solo amount: integer ma amount: positive integer, max 10,000,000, rappresenta centesimi.
Un livello di monitoraggio che esegue questi controlli continuamente, non solo all'avvio della pipeline. Un controllo fallito per esecuzione va bene. Diecimila controlli falliti su 500.000 record è il tuo allarme di cambiamento silenzioso.
Una soglia di avviso calibrata sui tuoi dati. Se lo 0,1% dei record storicamente ha transaction_id nullo (anomalo ma reale), imposta il tuo avviso all'1%. Quando il tasso di nullità raggiunge l'1%, qualcosa è cambiato. Quando raggiunge il 15%, qualcosa è sicuramente cambiato.
Vale la pena implementarlo? Sì, con una sola avvertenza: scrivi i tuoi contratti al punto di integrazione, non dopo il fatto. Adattare i contratti a una pipeline esistente è doloroso perché devi fare il reverse-engineering di ciò che pensavi fosse vero. Il momento di documentare le tue aspettative è quando costruisci per la prima volta l'integrazione, mentre la conoscenza è fresca.
Difendersi da ciascuna classe
Cambiamenti additivi: semplicemente ignorali. Costruisci la tua pipeline per estrarre solo ciò di cui hai bisogno. Non interromperti su campi inaspettati.
Cambiamenti che interrompono: fallisci in modo chiaro e veloce. Un crash evidente con un messaggio di errore chiaro è meglio della corruzione silenziosa. Validazione rigorosa all'ingestione sui campi da cui dipendi.
Cambiamenti silenziosi: è qui che il test del contratto si dimostra utile. Traccia le distribuzioni, non solo la struttura. Se la distribuzione dei valori del campo status cambia improvvisamente dal 90% "active" / 10% "inactive" al 50/50, qualcosa è cambiato a monte. Forse è un cambiamento legittimo del business. Forse è un cambiamento semantico nel modo in cui classificano i conti. In ogni caso, vuoi saperlo.
Come lo gestisce layline.io
La sfida con la maggior parte dei framework di pipeline è che i cambiamenti di schema richiedono cambiamenti nella pipeline. Aggiorni una mappatura di campo, ridistribuisci il lavoro, speri che nient'altro si rompa. Il ciclo di feedback tra "cambiato a monte" e "pipeline adattata" è di ore o giorni.
In layline.io, il modello di elaborazione separa il lavoro logico (cosa vuoi fare con i dati) dal formato fisico (in che forma arrivano i dati). L'evoluzione dello schema, nuovi campi, campi rinominati, cambiamenti di tipo, possono essere gestiti tramite configurazione piuttosto che codice. La tua logica opera su campi logici mappati dallo schema fisico, quindi quando lo schema fisico cambia, aggiorni la mappatura senza riscrivere la pipeline.
Questo non elimina la necessità di test del contratto. Vuoi comunque sapere quando arriva qualcosa di inaspettato. Ma riduce drasticamente il raggio d'azione. Un cambiamento a monte che avrebbe richiesto una riscrittura e ridistribuzione della pipeline diventa un aggiornamento di configurazione che richiede minuti.
Significa anche che puoi eseguire integrazioni sia batch che streaming contro lo stesso sistema di origine con la stessa gestione dello schema. Quando il processore di pagamento cambia un campo, lo risolvi una volta — non separatamente per la tua pipeline di real-time fraud detection e il tuo lavoro batch notturno di riconciliazione.
La lezione pratica
Inizia con i contratti che non hai. Scegli le tue tre integrazioni a monte più critiche, quelle in cui un cambiamento silenzioso dei dati causerebbe il maggior danno, e scrivi cosa ti aspetti effettivamente da ciascun campo. Non solo il tipo. L'intervallo, i valori consentiti, il significato semantico.
Poi costruisci un monitoraggio che catturi le deviazioni da quelle aspettative. Non perfetto, non ampio, solo le integrazioni più critiche prima.
La maggior parte dei team opera senza contratti espliciti fino a quando qualcosa si rompe. I team che operano con essi scoprono i cambiamenti a monte a loro condizioni, non alle 3 del mattino con dati corrotti.
Andrew Tan è un imprenditore seriale e fondatore di layline.io, costruendo infrastrutture di elaborazione dati aziendali che gestiscono carichi di lavoro sia batch che in tempo reale su larga scala.
