Come evitare di "aggiornare" la data invariata utilizzando Data Mappers?

2

In alcuni libri l'implementazione di Data Mappers semplicemente aggiorna l'intera riga di una tabella usando i dati all'interno di un oggetto, ma in un sistema è possibile che due diverse operazioni aggiornino campi diversi da una stessa riga in un database, il che può rendere un aggiornamento semplicemente sovrascrive un altro se entrambi recuperano la vecchia versione della riga e provano ad aggiornarla. Come posso risolvere questo problema? Ho pensato di utilizzare una mappa di identità all'interno del mio mappatore e di memorizzare lo stato dell'oggetto quando arrivano all'applicazione dal database e aggiornare solo i campi che sono stati modificati. Questa è una buona soluzione o sto trascurando qualcosa?

    
posta Lucas Piske 22.07.2016 - 06:39
fonte

2 risposte

1

Normalizzazione

Se il sistema dovrebbe essere in grado di aggiornare diversi dati in una riga della tabella, lo schema potrebbe non essere in un livello di normalizzazione adeguato. Ciò che dici con questo è: i dati che stai manipolando non sono nello stesso dominio di conflitto. La domanda è: perché i dati che non risiedono nello stesso dominio di conflitto sono raggruppati in una tabella?

Domini dei conflitti

Inoltre, mi sembra che il sistema su cui si lavora con domini di conflitto definiti correttamente non sia stato preso in considerazione.

Devi riflettere attentamente su quali dati possono essere manipolati indipendentemente dagli altri, in modo da preservare la coerenza dei dati.

Meccanismi di blocco

Se sei sicuro di quale tipo di cosiddetti monitor devi introdurre devi decidere quale tipo di meccanismo di bloccaggio vuoi utilizzare. Ci sono due possibilità: blocco pessimistico e ottimistico.

Blocco pessimistico: impedisce ad altri processi di entrare nel monitor finché il processo corrente non lo abbandona.

Per ottenere il blocco pessimistico devi definire un mutex da acquisire prima che un processo voglia accedere al monitor.

Blocco ottimistico: consente ad altri processi di accedere al monitor con una vista isolata sullo stato del sistema. Ma previ che altri processi pubblichino i loro dati prima di lasciare il monitor se sono state identificate modifiche in conflitto.

Per ottenere un blocco ottimistico, devi definire un identificatore di versione che viene selezionato prima che un processo entri nel monitor e confrontato prima che il processo possa pubblicare i suoi dati.

Composito o Separazione

Poiché non conosco i requisiti aziendali, ci sono due possibilità:

Se c'è solo UN dominio di conflitto, devi definire UNO mutex per entrambi i campi di dati o UN identificatore di versione per entrambi i campi di dati.

Se ci sono DUE domini di conflitto, allora devi definire UN mutex per OGNI campo di dati o UN identificatore di versione per OGNI campo di dati.

Implementazione tecnica

Nel contesto dei database il tuo monitor è la transazione. Il problema è che le cose che devi considerare durante l'implementazione dipendono dalle possibilità concrete E dalla configurazione corrente del tuo database.

Il comportamento delle transazioni e l'isolamento delle viste potrebbero essere diversi. In MySQL ci sono per esempio diversi tipi di tabelle che danno differenti asserzioni sul comportamento della transazione.

Poiché è impossibile per me aprire una discussione per QUALSIASI tecnologia e QUALSIASI configurazione del sistema, proverò con la più comune.

Supponendo di avere DIVERSI domini di conflitto (la modifica contemporanea di entrambi i dati in una riga non causerà uno stato incoerente) è possibile introdurre due identificatori di versione che è necessario selezionare prima e utilizzare alla fine della transazione per confrontare e incrementare la versione . Pertanto devi avere DUE mapper dei dati.

UPDATE tableX SET version=v + 1 WHERE version = v;

O vuoi avere un blocco pessimistico con UNO o DUE mutex, a seconda dei meccanismi supportati dal database. Se il database supporta solo il blocco delle righe, è necessario un solo mutex. Questo è spesso fatto seguendo:

SELECT id_column FROM tableX WHERE id_column=id_to_lock FOR UPDATE

Al centro: problema di aggiornamento perso

Il tuo problema potrebbe essere risolto se utilizzi due mapper di dati che aggiorneranno diverse parti di una riga.

Inoltre puoi normalizzare lo schema a un livello di normalizzazione più elevato.

Se vuoi mantenere il tuo mapper dei dati (supponi implicitamente UN UNICO dominio di conflitto) E vuoi impedire gli UPDATE PERSO hai solo la possibilità di introdurre un identificatore di versione. Quindi usi implicitamente il blocco ottimistico.

Il punto è che il blocco ottimisitico utilizza ALTRE informazioni per identificare modifiche in conflitto all'interno di un dominio di conflitto. Mentre il blocco pessimistico dice "Non entrare finché non sono pronto!" blocco ottimistico dice "È possibile inserire, MA non è possibile pubblicare i dati se la versione dei dati proceduto.". Quindi non puoi impedire gli UPDATE PERSO con il blocco pessimistico da solo.

    
risposta data 19.11.2016 - 14:25
fonte
0

La solita procedura per sincronizzare le righe in un database sarebbe iniziare una sessione di transazione. Prima di aggiornare la riga, leggi una seconda istanza della stessa riga dal database (durante la sessione della transazione per evitare modifiche nel frattempo) e confronta i dati di qualsiasi colonna per le modifiche. Come confrontare qualsiasi colonna può essere un compito intensivo in termini di prestazioni, specialmente quando una tabella è composta da molte colonne, l'uso di timestamp è consigliato (in primo luogo recuperare solo il timestamp!) Per sapere in anticipo se è necessario recuperare nuovamente l'intera riga.  La riga con la data / ora più recente sovrascrive le modifiche alla (stessa!) Locale, in memoria, ma è consapevole che possono esserci degli scenari (es .: conflitti di dati concomitanti) in cui è necessario chiedere all'utente se ciò è realmente previsto!

Per una riga tre (!) istanze con una colonna timestamp è necessario sapere quale è la più recente. Ricorda di ricavare i tuoi timestamp da un fuso orario unico (ad esempio: UTC!)

  • Il 1st timestamp è preso dalla riga stessa, potrebbe essere aggiornato quando modificato in memoria e lo stato della riga cambia da UNCHANGED a MODIFIED.
  • Il secondo timestamp è un duplicato del primo (nel momento in cui la riga è stata caricata in memoria ed è ancora UNCHANGED)
  • Il 3 timestamp è preso dalla seconda istanza della riga caricata durante la sessione della transazione.

Tabella delle conclusioni:

1st != 2nd: your local, in-memory row has changed (an update is necessary!)
2nd != 3rd: in the meanwhile the row has changed in database
 1st > 3rd: your local row is more recent than the one from the database
 3rd > 2nd: the row from the database is more recent
 3rd > 1st: the row from the database is even more recent then your modified in-memory row (!)
2nd == 3rd: comparing data not necessary, simply update your local row to your database (the case you were looking for?!)

In questo modo è possibile determinare la riga di destinazione in cui copiare i dati della colonna o almeno richiedere la convalida da parte di un essere umano. Una volta che tutte le colonne sono state elaborate e la tua riga è stata aggiornata, puoi finalizzare la sincronizzazione inserendo la transazione nel database.

    
risposta data 22.07.2016 - 11:39
fonte