Vai al contenuto
eLearner.app
Modulo 11 · Lezione 2 di 442/57 nel corso~10 min
Lezioni del modulo (2/4)

Race Conditions

Capire come funziona un database in isolamento su un singolo terminale è semplice, ma in un reale scenario web, ci saranno centinaia di processi "Node/PHP/Go" concorrenti che inviano richieste. Qui nascono le famose Race Conditions.

Il problema del "Lost Update"

Il Lost Update capita quando due thread leggono la stessa vecchia riga, ci fanno un calcolo matematico nel server JavaScript e poi rimandano indietro l'UPDATE per salvare il risultato.

Scenario:

  • Il prodotto #5 ha 10 unità.
  • L'utente A lo compra (Server A legge "10", calcola matematicamente "9").
  • Nello stesso millisecondo l'utente B ne compra un altro prima che l'acquisto di A finisca la fase di COMMIT.
  • Il Server B legge anche lui "10", calcola "9".
  • Il Server A manda UPDATE: setta quantità = 9.
  • Il Server B manda UPDATE: setta quantità = 9.
  • Entrambi pensano di aver diminuito il numero con successo. Ma dovevano essere 8 unità finali! Un acquisto fantasma è stato perso.

Soluzione senza transazioni pesanti (Atomic Updates)

Invece di leggere e fare la matematica fuori, falli fare al DB durante l'update stessso che è sempre sincronizzato.

SQL
-- Sbagliato
UPDATE products SET stock = 9 WHERE id = 5;

-- Corretto e thread-safe nativo
UPDATE products SET stock = stock - 1 WHERE id = 5;

Anomalie più estreme (Dirty Reads & Phantoms)

  • Dirty Read: Leggi dati temporanei altrui che non sono stati ancora confermati da un COMMIT (e potrebbero andare in ROLLBACK di colpo invalidandoti).
  • Phantom Reads: Fai uno scan di tutte le righe, nel frattempo un secondo thread inserisce un record nuovo, poi fai un altro scan durante la medesima transazione e ti ritrovi magicamente con il nuovo record apparso dal nulla compromettendo report finanziari calcolati.
Esercizio#sql.m11.l2.e1
Tentativi: 0Caricamento…

Modifica atomicamente il record dei clienti per una piccola manipolazione stringa.\nPoniamo che vogliamo rinominare la 'city' di un customer per aggiungere un asterisco. \nFallo sfruttando il valore stesso della riga per chi ha id=10, concatenando ' - Old' al valore attuale (Usa la pipe || di concatenazione postgres).

Caricamento editor…
Mostra suggerimento

UPDATE customers SET city = city || ' - Old' WHERE id = 10;

Soluzione disponibile dopo 3 tentativi

Esercizio#sql.m11.l2.e2
Tentativi: 0Caricamento…

Un lost update classico avviene in pagamenti e prezzi.\nPoniamo un rincaro di emergenza: Aumenta il 'unit_price' della tabella 'order_items' (per il prodotto 1 dell'ordine 1) del 20% in modo atomico, ossia moltiplicandolo * 1.2 invece che inserendo il nuovo prezzo manualmente in JS precalcolato.

Caricamento editor…
Mostra suggerimento

UPDATE order_items SET unit_price = unit_price * 1.2 WHERE order_id = 1 AND product_id = 1;

Soluzione disponibile dopo 3 tentativi