Je kan je staging area op tig manieren inrichten. De hierboven beschreven methode serialiseert de transacties (1 staging area doet alle updates) en kost nogal wat performance. Leuk met weinig gebruikers maar niet echt geschikt voor taken met duizenden verschillende gebruikers. Ook kun je hierdoor in de problemen komen met transacties die achter elkaar binnen komen. Als de eerste faalt en de tweede is afhankelijk van de eerste kan de tweede ook falen. Feitelijk weet je dus pas zeker of je tweede commando geldig is als het eerste gecommit is in de productie data. De tweede transactie (en de gebruiker) moet dus wachten op de eerste.
Voorbeeld. Neem een user tabel met een unieke key op het sofinummer.
T1 Begin
T1 insert USER (ID, sofinummer, naam)
T1 select @@Identity
T1 Commit
T2 Begin
T2 Update USER Set Naam = "willekeurige naam" where ID is HetID.
T2 Commit
Goed, T1 voegt een gebruiker toe en retourneert het ID van die gebruiker.
Daarna update T2 de naam van de gebruiker. Niets mis mee en het zou moeten werken.
Helaas zit er in de basisdataset al een user met hetzelfde sofinummer. Transactie 1 wordt dus teruggedraaid bij het verplaatsen naar productie. Echter de gebruiker krijgt dit niet te zien bij het invoeren van de User. De invoer gaat immers naar de staging area en die bevat niet de werkelijke data. Maar Transactie 2 is daar wel van afhankelijk! Dit voorbeeld geeft aan dat je dus een hoop zaken die normaal de database controleert zelf zult moeten doen. Nóg erger wordt het als beide users zich in de staging area bevinden (bv werknemer voert per ongelijk twee keer dezelfde user in, twee werknemers doen hetzelfde enz enz.) Dan zijn controles op de productie data onmogelijk, want beide nieuwe gebruikers ontbreken in eerste instantie in die data. Ook in de staging area kun je lastig zien of de gebruiker bestaat. je weet namelijk slechts na controle van alle records in je stagingtabellen wat de huidige waarde van de gegevens is. Misschien was het sofinummer wel al verbetert.
Op deze manier zit je dus een hoop werk wat de databaseserver voor je kan doen handmatig over te doen. Lijkt me niet handig, noch efficient.
Je kan vaak om dit soort probleem heen werken. Echter
elke bekende methode heeft nadelen. Er is geen standaard methode die Oracle en IBM eventjes in hun engine kunnen implementeren om voorgoed van het locking probleem af te zijn.
Als ontwikkelaar dien je dus zélf voor een oplossing te zorgen. Serialiseren is daar één van (en is feitelijk wat jou "staging" methode doet) maar dat gaat ten kosten van de concurrency en performance. Leuk voor Data Warehousing maar compleet onacceptabel bij OLTP. Juist doordat dit geen simpele problemen zijn, dien je de fundamenten mbt concurrency te begrijpen voordat je een goed multi user systeem kan ontwerpen.
[...]
In theorie komt dit veel voor, maar in praktijk zijn er meestal niet echt lock issues, omdat de applicatie hierop voorzien is.
Ok, een voorbeeld : bestellingen bij "amazon.com".
Het enige dat ik me zou kunnen voorstellen, is dat bv de prijs van een artikel verandert, terwijl er een bestelling geplaatst wordt. Oplossing : voorzie in de tabel artikel een veld "GeldigTot", dat de datum bevat tot wanneer dit record geldig is, en maak een nieuw record aan voor de gewijzigde versie. Zo kan je ook effectief de transacties in het verleden verifiëren, en blijft je logische structuur consistent. Als je hiervan geen historiek bijhoudt, dan is het imho ook niet echt nodig om de gegevens te locken.
De truc is net om locks te vermijden, door in je app historieken in te bouwen.
Ook dit is weer een voorbeeld van om het probleem heen werken. Bij de ontwikkeling los je die problemen zoveel mogelijk op. Daar zijn dus vele manieren voor. Je noemt er ondertussen zelf al een hoop op (transactie terugdraaien, historische tabellen, staging,....) Juist door te begrijpen wat de potentiele problemen zijn en met kennis van de applicatie kun je de oplossing optimaliseren. Misschien is een Dirty Read in een situatie de oplossing. Maar dan moet je wel het verschil kennen tussen een transactie met Dirty Reads enabled en een transactie zonder die functies. Er zijn veel applicaties geschreven waarbij de performance belabberd was omdat er niet goed nagedacht was over de concurrency issues. Alarmnummer probeert deze problemen in een kort(?) document uit te leggen. En dat is zeker niet eenvoudig.
[...]
Zie voorbeeld hierboven. Die prijsverandering is slechts van toepassing op het moment dat die gecommit wordt. Alle transacties die ervoor opgestart zijn moeten imho met de oude prijs(-record) werken
Bedoel je gecommit in de productie data? Of gecommit in je staging data? Want alweer creëer je im mijn ogen meer problemen dan je oplost. De meeste mensen vinden dat een prijsverandering (of elke andere verandering) in een OLTP systeem doorgevoerd moet worden op het moment dat de gebruiker denkt dat die doorgevoerd is. De gebruiker voert een nieuwe prijs in (desnoods met een periode waarin die geldig is) en verwacht dat die wijziging na het opslaan doorgevoerd wordt. Niet dat die doorgevoerd wordt op een later moment als de view gesynchroniseerd wordt. De commit naar de view is vanuit het gebruikersperspectief het moment dat de nieuwe prijs in het systeem staat. Met een materialized view creeër je een periode tussen het invoeren van de gebruiker en het doorvoeren in de basistabel. Alle transacties die in de tussentijd worden opgestart krijgen dus de oude prijs terwijl de gebruiker de nieuwe prijs al heeft ingevoerd. Maar heel raar. De gebruiker krijgt wél de nieuwe prijs te zien als die de prijs nogmaals wil wijzigen (want hij begrijpt er natuurlijk geen bal van.) Tenminste dat hoop ik. Anders ziet de gebruiker zijn weiziging helemaal niet meer terug. Inconsequent en dus onwenselijk. Nu kun je jezelf indekken door in een TO de regel opnemen dat alle veranderingen aan producten pas na (bv) middennacht worden doorgevoerd, maar daar maak je geen vrienden mee.
Een Materialised View creëert data inconsistancy. Wat je in OLTP applicaties niet wilt is data inconsistancy. Als jij deze twee basiseisen kan combineren hoor ik het graag.