In de 13 jaar dat ik ook ontwikkel werkzaamheden doe heb ik vaak data moeten importeren/exporteren en synchroniseren. Iedereen kan het, veel mensen doen het, maar er zit een wereld van valkuilen in. Ik weet het, want in elke ben ik wel een keer gestapt. Ze zeggen dat hij die slim is van zijn fouten leert, en hij die wijs is van andermans fouten leert…
Als je ooit een contacten lijst uit Outlook hebt geëxporteerd en later geïmporteerd in een CRM systeem, dan weet je dat het niet geheel triviaal is. Je moet velden mappen, er ontstaat vervuiling en dubbele records of kunt bepaalde data niet goed meenemen. Dit artikel gaat wat meer over het structureel verplaatsen van data van één bron naar een andere.
Do 1: Bouw een Staging Area
Als importeren structureel moet gebeuren, dan is een staging area onvermijdelijk. Een staging area is de ruimte tussen de ruwe brondata en uiteindelijke productie data. Hiermee zorg je ook dat het importeren van data uit meerdere stappen bestaat en dat je over eventuele problemen in de data kunt communiceren. Maar het zorgt er bijvoorbeeld ook voor dat er meerdere importeer acties tegelijk kunnen worden uitgevoerd die elkaar niet beïnvloeden. Het geeft controle aan het import proces. Heel kort door de bocht zorg ik dat een staging area (SA) een aantal eigenschappen heeft. Een tabel waarin de ruwe data geladen kan worden. Alle velden in deze tabel zijn van het type string. Dit is handig omdat je anders data waarin een datum veld niet goed is ingevuld, niet geladen kan worden (of een bedrag veld). Dit zorgt ervoor dat alle data geaccepteerd wordt (behalve als het aantal kolommen niet klopt, of een aangeleverd veld meer data tekens bevat dan je aankunt). Ook geef je elk record een batch id mee. Een sleutel waarmee je de import sessie uniek kunt identificeren. Dit maakt het mogelijk dat er meerdere import sessies gedaan kunnen worden die elkaar niet in de weg zitten en hou je meteen desgewenst geschiedenis vast. Voeg eventueel op record niveau ook nog een status veld toe en een waarschuwing en fout beschrijving. Na het inladen van de ruwe data voer je alle controles uit. Zijn datum velden wel in de juiste vorm? Bevatten getal velden punten en komma’s, is een bedrijfsnaam wel een bestaand bedrijf in de productie data, en bevat een record wel alle verplichte velden? Na deze controle kan er besloten worden of de data door mag naar de productie data of de tabel die voor de SA als output of “gouden data” geldt. Bekijk per scenario wat de meest zinvolle regel hierin is. Als er bijvoorbeeld “klanten” geïmporteerd worden, of orders, dan is één fout reden om de gehele batch af te keuren. Als je dit namelijk toelaat en alleen de goede records importeert, weet niemand meer welke orders er gemist worden en kun je ook niet opnieuw meer de import uitvoeren omdat je dan dubbele records krijgt. Beter geef je de ruwe data terug met de opmerkingen wat er niet klopt zodat dit kan worden aangepast (uiteraard hangt dit af van veel zaken en zit er wel ruimte in het verhaald alleen moet het hier om een bewuste keuze gaan). Als de SA gebouwd is om data door te geven aan een productie systeem, realiseer je dan dat de productie omgeving ook weer een SA nodig heeft om de data te importeren, want voor productie geldt weer do 2: Wantrouw alle data.
Do 2: Wantrouw alle data
En wantrouw het op alle vlakken! Het klinkt zo logisch, maar mijn ervaring is dat deze valkuil vaak over het hoofd gezien wordt. Er zijn veel niveaus waarop je data kunt wantrouwen. Bevat de data wel de juiste structuur? Zijn kolommen waarin je getallen verwacht wel getallen? Komt een unieke sleutel maar één keer voor? Is deze data niet dubbel aangeleverd? Welk datum-tijd formaat wordt gehanteerd?
Do 3: Als data incrementeel is moet je alle data verwerken en in de juiste volgorde
Als data structureel geïmporteerd wordt gaat dat in volledige sets (elke aanlevering bevat alle data) of incrementele sets (de eerste keer alle data, daarna alleen nieuwe records, wijzigingen en verwijderingen). Bij volledige sets kun je incomplete records of records die niet aan de voorwaarden voldoen verwijderen. Bij incrementeel is dat niet verstandig. Stel dat er later wel eventuele child records binnenkomen, dan worden deze genegeerd of verwijderd omdat er een parent record ontbreekt. Als je dit soort records wel meeneemt maar een status aangeeft (bijv. Incompleet), dan blijft het record genegeerd totdat het wel ineens een valide record wordt. Incrementeel heeft als voordeel dat er minder data verplaatst wordt, het nadeel is dat elk bestand ingelezen moet worden en altijd in de juiste volgorde. De kans is groot dat als je data importeert van meerdere tabellen er een parent child constructie in zit. Denk aan orders en klanten. Transacties en transactieregels, producten en eigenaren, contacten en relaties.
Do 4: Bestaat er afhankelijkheid met een andere database wees dan op je hoede
Als je de zaakjes goed op orde hebt kan er bij een grote fout makkelijk een back-up terug gezet worden voor je staging area. Alleen kan het zijn dat de eerder geschoonde data al in een opvolgend proces is gebruikt. Een SA is vaak de eerste stap in een hele keten van dataverwerking. Herken waar de afhankelijkheden van andere databases bestaan en als er een restore uitgevoerd moet worden, zorg ook dat de vervolgstappen in de keten dit doen, of hier rekening mee kunnen houden.
Do 5: Laat een probleem niet alle verwerking ophouden
Ik werk altijd met verzamelingen (batches) records. Elk record in een tabel wordt hiermee gelabeld. Het voordeel is dat hierdoor meerdere import procedures tegelijkertijd kunnen lopen en als er problemen zijn met één batch, dan stopt de verwerking van die batch, maar kunnen andere batches met records gewoon ingelezen worden. Dit noem ik de applicatiemotor. Door goede scheiding aan te brengen kan het probleem ook snel geïsoleerd worden en staat het de andere verwerking van data niet in de weg.
Do 6: Ga uit van falen
Al lijkt de import robuust, er komt altijd wel weer een bestand voorbij die de boel op zijn kop zet. Verminkte records, ongeldige tekens, zeer groot bestand die onverwacht een time-out veroorzaakt. Ga er altijd vanuit dat het mis gaat zodat je correct om kunt gaan met deze diverse scenario’s. Ook de vervolg stappen na de import zou eerst een aantal integriteit checks moeten doen voordat de data geaccepteerd wordt. Stel dat een bestand per ongeluk met een verkeerd klantnummer wordt aangeleverd, dan genereert dit ineens heel veel wijzigingen of nieuwe records. Zou kun je bijvoorbeeld een poortwachter inbouwen die bij 33% wijzigingen of nieuwe records de verwerking stil legt. Zoveel wijzigingen is onnatuurlijk en het inlezen van een verkeerd bestand kan desastreuze gevolgen hebben voor een systeem die daar geen rekening mee houdt.
Do 7: Data is vervuild en moet beheerd worden
Ik ben nog niet tegengekomen dat aangeleverde data perfect is. Dubbele records, verminkte records, ontbrekende verplichte velden, ongeldige tekens, verkeerde karakterset. Of niet bestaande klantnummers, puntjes en komma’s in bedrag velden, en ga zo maar door. Je zult dus altijd filters en checks moeten doen voordat data geaccepteerd wordt. Per situatie kan er (automatisch) beslist worden of een import bij fouten door mag gaan. Hoe geavanceerd het inlezen en controleren ook gaat, er loopt altijd wel iets vast. Zorg dus altijd dat een SA actief beheerd wordt. Iemand die de fouten signaleert of iets meet signaal mailtjes doet. Een bepaalde vorm van een interface is in mijn ogen noodzakelijk.
Do 8: Maak de staging area generiek (maar ken zijn duistere kant)
Het generiek maken van applicaties en code wordt vaak gezien als een doel op zich en als middel om kosten te besparen op ontwikkelwerkzaamheden en zodat niet-programmeurs functionaliteit kunnen toevoegen en wijzigen. Daarnaast kan de functionaliteit voor meer dingen ingezet worden dan oorspronkelijk bedoeld was en wordt de levenscyclus van de oplossing langer. Of dit nu een Staging Area is of een volledig ERP systeem. Het generiek maken kan deze voordelen inderdaad bieden, mits ze op de juiste manier worden toegepast. Daarbij is het zaak om vooral te kijken hoe de “echte wereld” eruit ziet. Het is niet ondenkbaar dat een import file door de tijd heen wijzigt, een veld bijvoegen of de validatie van een veld wijzigen zou geen code aanpassing moeten vergen. Door meerdere regels toe te kunnen passen ontstaat de vrijheid van validatie en uitbereiding. Het goed kunnen documenteren of inzichtelijk maken wat bepaalde regels nu doen en waarom ze bestaan is net zo belangrijk als de genericiteit zelf, anders is overdracht of in beheer nemen net zo gemakkelijk als op opnieuw bouwen zelf. Daarnaast is functionaliteit op een staging area een hellend vlak. Het wordt als snel technisch heel complex om bijvoorbeeld meerdere manier van aanleveren te faciliteren of de mapping naar relationele tabellen te faciliteren. Ook XML (wat in de basis generiek is) kan zeer veel ellende veroorzaken als er teveel vrijheid gegeven bij de inrichting. Als laatste valkuil hierin is het toepassen van business rules die eigenlijk oordelen over de functionele inhoud van data. Dit zou pas op een veel later niveau moeten plaatsvinden. Zie Do 9 & 10.
Do 9: Data is functioneel en onderhevig aan interpretatie
Data heeft betekenis en die betekenis kan voor verschillende mensen anders zijn. Ofwel “assumption is the mother of all f*ckups”. Zo ken ik een voorbeeld van dat elke rij in een table een drietal eigenschappen heeft: Created, Updated en Deleted. In feite is dit meta data over de import gegevens. Created is wanneer het record fysiek is aangemaakt, Updated wanneer het gewijzigd is en Deleted wanneer het logisch verwijderd is. Waarom logisch? Nouja, je geeft aan dat het record niet meer bestaat, toch bestaat het nog, maar moet het als verwijderd beschouwt worden. Dit heeft als voordeel dat je dus kunt zien wat er verwijderd is. Maar nu komt het filosofische vraagstuk die op meerdere manier interpreteert kan worden. Is een Delete nu ook een Update? Dus als het record logisch verwijderd wordt, dien je dan ook het de Updated kolom te vullen? Dan hangt af van het proces wat na de synchronisatie plaats vind en zo kan een veld meer betekenen en is dus een uitleg nodig.
Do 10: Geef geen betekenis aan data in de SA
Het is heel verleidelijk om in de Staging Area al betekenis aan data te geven. Om bijvoorbeeld aan te geven dat een klant actief is of wat de voorkeursvestiging is van een consument. Deze regels dienen niet (in de eerste) staging area plaats te vinden. Eén reden is dat bij een wijziging van de businessregels alle data opnieuw ingelezen moet worden. Of dat als er een andere data bron bijkomt (die bijvoorbeeld niet aangeleverd wordt via de staging area) deze niet gebruikt kan worden in de business rules. Doe van de staging area is om data te verplaatsen en te schonen.
Deze tien do’s zijn zeker niet de waarheid. Voor elk punt kan ook een goed tegenargument gebruikt worden, en het kan ook nog eens per situatie verschillen. Daarnaast zijn er altijd factoren als tijd, scope, budget, etc. Maar het is goed bij het realiseren van elke import tool of staging areas om de do’s er nog even tegenaan te houden. De uitleg bij de do’s zijn ook kort en niet begeleid van uitgebreide voorbeelden. Als je een vraagt hebt, stel hem dan gerust, en dit is uiteraard vrijblijvend. Als laatste “do” zou ik nog toe willen voegen: Laat evolutie het werk doen. Begin simpel en eenvoudig en als dat werkt verbeter het dan. Uit het niets een heel complex en ingewikkeld systeem ontwikkelen is een goed recept voor falen. Of zoals de Engels sprekenden het zo mooi uitdrukken: Epic fail.