Modulelessen (2/4)
Raceomstandigheden
Het is eenvoudig om te begrijpen hoe een database geïsoleerd op een enkele terminal werkt, maar in een echt webscenario zullen er honderden gelijktijdige "Node/PHP/Go"-processen zijn die verzoeken verzenden. Dit is waar de beroemde Race Condities worden geboren.
Het probleem "Verloren update".
Verloren update vindt plaats wanneer twee threads dezelfde oude rij lezen, er wat wiskunde op uitvoeren in de JavaScript-server en vervolgens de UPDATE terugsturen om het resultaat op te slaan.
Scenario:
- Product #5 heeft 10 eenheden.
- Gebruiker A koopt er een (Server A leest "10", berekent "9").
- In dezelfde milliseconde koopt gebruiker B een andere voordat de aankoop van A de COMMIT-fase beëindigt.
- Server B leest ook "10", berekent "9".
- Server A verzendt
UPDATE: set quantity = 9. - Server B verzendt
UPDATE: set quantity = 9. - Beiden denken dat ze het aantal succesvol hebben verlaagd. Maar de uiteindelijke waarde had 8 eenheden moeten zijn! Er is een fantoomaankoop verloren gegaan.
Oplossing zonder zware transacties (Atomic Updates)
In plaats van buiten te lezen en te rekenen, kunt u de DB dit laten doen tijdens de update zelf, die altijd gesynchroniseerd is.
-- Wrong
UPDATE products SET stock = 9 WHERE id = 5;
-- Correct and natively thread-safe
UPDATE products SET stock = stock - 1 WHERE id = 5;Meer extreme afwijkingen (Dirty Reads & Phantoms)
- Dirty Read: u leest de tijdelijke gegevens van iemand anders die nog niet zijn bevestigd door een COMMIT (en die plotseling naar ROLLBACK kunnen gaan, waardoor u ongeldig wordt).
- Phantom Reads: u scant alle rijen, terwijl een tweede thread een nieuw record invoegt, vervolgens voert u nog een scan uit tijdens dezelfde transactie en ontdekt u op magische wijze dat het nieuwe record uit het niets verschijnt, waardoor berekende financiële rapporten in gevaar komen.
Wijzig het klantrecord atomair voor een kleine tekenreeksmanipulatie.\nStel dat we de 'stad' van een klant willen hernoemen en er een asterisk aan toevoegen. \nDoe dit door gebruik te maken van de eigen waarde van de rij voor die met id=10, door ' - Oud' aan te sluiten op de huidige waarde (gebruik postgres' || aaneenschakelingspipe).
Toon hint
UPDATE klanten SET stad = stad || ' - Oud' WAAR id = 10;
Oplossing beschikbaar na 3 pogingen
Een klassieke verloren update vindt plaats in betalingen en prijzen.\nLaten we zeggen dat er een noodprijsverhoging is: verhoog de 'unit_price' van de tabel 'order_items' (voor product 1 van bestelling 1) atomair met 20%, d.w.z. vermenigvuldig deze * 1,2 in plaats van de nieuwe prijs in te voegen die handmatig vooraf is berekend in JS.
Toon hint
UPDATE order_items SET eenheidsprijs = eenheidsprijs * 1.2 WAAR order_id = 1 EN product_id = 1;
Oplossing beschikbaar na 3 pogingen