Come prevenire le condizioni di gara in un'applicazione web?

27

Considera un sito di e-commerce, in cui Alice e Bob stanno entrambi modificando le schede di prodotto. Alice sta migliorando le descrizioni, mentre Bob sta aggiornando i prezzi. Iniziano a modificare Acme Wonder Widget allo stesso tempo. Bob finisce prima e salva il prodotto con il nuovo prezzo. Alice impiega un po 'più di tempo ad aggiornare la descrizione e quando finisce, salva il prodotto con la sua nuova descrizione. Purtroppo, ha anche sovrascritto il prezzo con il vecchio prezzo, che non era previsto.

Nella mia esperienza, questi problemi sono estremamente comuni nelle app web. Alcuni software (ad esempio il software wiki) hanno una protezione contro questo - di solito il secondo salvataggio fallisce con "la pagina è stata aggiornata mentre stavi modificando". Ma la maggior parte dei siti web non ha questa protezione.

Vale la pena notare che i metodi del controller sono sicuri per i thread. Solitamente usano transazioni di database, che li rendono sicuri nel senso che se Alice e Bob cercano di salvare nello stesso momento, non causeranno corruzione. Le condizioni di competizione derivano da Alice o Bob che hanno dati obsoleti nel loro browser.

Come possiamo prevenire tali condizioni di gara? In particolare, mi piacerebbe sapere:

  • Quali tecniche possono essere utilizzate? per esempio. tenere traccia del tempo dell'ultima modifica. Quali sono i pro e i contro di ciascuno.
  • Che esperienza utente utile?
  • In che framework è integrata questa protezione?
posta paj28 25.11.2014 - 11:10
fonte

4 risposte

15

Hai bisogno di "leggere le tue scritture", il che significa che prima di scrivere una modifica, devi leggere di nuovo il record e controllare se sono state apportate modifiche da quando lo hai letto l'ultima volta. Puoi farlo da campo a campo (a grana fine) o in base a un timestamp (a grana grossa). Mentre fai questo controllo hai bisogno di un blocco esclusivo sul record. Se non sono state apportate modifiche, è possibile annotare le modifiche e rilasciare il blocco. Se il record è cambiato nel frattempo, si interrompe la transazione, si rilascia il blocco e si avvisa l'utente.

    
risposta data 25.11.2014 - 13:54
fonte
9

Ho visto 2 modi principali:

  1. Aggiungi il timestamp dell'ultimo aggiornamento della pagina che l'uso sta modificando in un input nascosto. Quando si effettua il commit del timestamp viene verificato rispetto a quello corrente e se non corrispondono è stato aggiornato da qualcun altro e restituito un errore.

    • pro: più utenti possono modificare diverse parti della pagina. La pagina di errore può portare a una pagina diff dove il secondo utente può unire le sue modifiche nella nuova pagina.

    • con: talvolta si sprecano grandi quantità di sforzi durante grandi modifiche simultanee.

  2. Quando un utente inizia a modificare la pagina bloccandola per un periodo di tempo ragionevole, quando un altro utente tenta di modificare riceve una pagina di errore e deve attendere fino alla scadenza del blocco o al primo tentativo dell'utente.

    • pro: gli sforzi di modifica non vengono sprecati.

    • con: un utente senza scrupoli può bloccare una pagina indefinitamente. Una pagina con un blocco scaduto potrebbe ancora essere in grado di eseguire il commit a meno che non sia stato affrontato diversamente (utilizzando la tecnica 1)

risposta data 25.11.2014 - 11:39
fonte
7

Utilizza Controllo di concorrenza ottimistico .

Aggiungi una colonna versionNumber o versionTimestamp alla tabella in questione (il numero intero è più sicuro).

L'utente 1 legge il record:

{id:1, status:unimportant, version:5}

L'utente 2 legge il record:

{id:1, status:unimportant, version:5}

L'utente 1 salva il record, questo incrementa la versione:

save {id:1, status:important, version:5}
new value {id:1, status:important, version:6}

L'utente 2 prova a salvare il record che hanno letto:

save {id:1, status:unimportant, version:5}
ERROR

Hibernate / JPA può farlo automaticamente con l'annotazione @Version

È necessario mantenere lo stato del record letto da qualche parte, generalmente in sessione (questo è più sicuro che in una variabile modulo nascosta).

    
risposta data 27.11.2014 - 00:37
fonte
1

Alcuni sistemi ORM (Object Relational Mapping) rileveranno quali campi di un oggetto sono stati modificati dopo essere stati caricati dal database e costruiranno l'istruzione di aggiornamento SQL per impostare solo tali valori. ActiveRecord per Ruby on Rails è uno di questi ORM.

L'effetto netto è che i campi che l'utente non ha modificato non sono inclusi nel comando UPDATE inviato al database. Le persone che aggiornano diversi campi allo stesso tempo non si sovrascrivono a vicenda.

A seconda del linguaggio di programmazione che stai utilizzando, cerca quali ORM sono disponibili e verifica se qualcuno di loro aggiorna solo le colonne nel database contrassegnate come "sporche" nella tua applicazione.

    
risposta data 25.11.2014 - 22:48
fonte

Leggi altre domande sui tag