Come modellare un registro di stato che non consente gli stati duplicati in sequenza

1

Ho bisogno di modellare lo stato di un oggetto nel tempo. Sto cercando il modo migliore per modellarlo in modo che le connessioni simultanee al database possano guardare lo stato corrente e aggiornare lo stato se lo stato più recente è diverso.

Supponiamo che in minima parte un record contenga object_id, status_id, start_date . Il mio primo approccio era quello di richiedere l'ultima, e quindi creare un nuovo record se gli stati erano diversi.

Il problema che ho riscontrato è che ogni volta che chiedo l'ultima e decido di creare un nuovo record, può essere creato un nuovo record (con qualche processo concorrente) che invalida la mia decisione di creare.

Un esempio è sul primo stato creato. Più processi possono leggere contemporaneamente nessuno stato precedente e ciascuno crea lo stato iniziale.

Sto cercando un consiglio perché non sono sicuro che ci sia semplicemente un modo più semplice per modellare i miei dati in modo da garantire che non ci siano duplicati (errori di integrità di cattura), o se ho bisogno di guardare più al blocco del database. Sto usando l'ORM Django con Postgres.

    
posta cdosborn 11.05.2018 - 22:47
fonte

2 risposte

0

Mi vengono in mente un paio di soluzioni.

  1. Procedure memorizzate che restituiscono codici di errore quando viene rilevato un duplicato

  2. Trigger sulle tabelle di stato per rifiutare le righe che creerebbero stati sequenziali (ad esempio due stati "pubblicati" in una riga).

  3. Vivi con i duplicati

  4. Implementa una sorta di meccanismo di blocco ottimistico

L'esecuzione di una qualsiasi attività fuori dal database ti apre alle condizioni di gara.

Avendo già implementato diverse di queste tabelle, penso che farei delle stored procedure che restituiscono un codice di errore in caso di errore, quindi l'applicazione può gestire meglio i duplicati. Normalmente non mi piacciono le stored procedure, ma hanno il loro posto. Questo potrebbe essere uno di questi.

La maggior parte degli ORM consente di chiamare le stored procedure direttamente per le operazioni INSERT, UPDATE o DELETE sul modello a oggetti.

D'altra parte, se ci vivi, e hai due persone che segnano un record come "trash", allora sai che la gente davvero, davvero non ne ha avuto bisogno (o "davvero, davvero, davvero" se 3 le persone lo hanno fatto allo stesso tempo.

    
risposta data 12.05.2018 - 00:44
fonte
0

Suppongo che desideri ignorare il secondo aggiornamento. Esistono altri scenari (ad esempio, anziché inserire una nuova riga, aggiornare determinati campi della riga esistente), ma deduco che stai cercando qualcosa chiamato AddOrIgnore anziché AddOrUpdate .

Qui ci sono alcune strade possibili, alcune migliori di altre.

Blocco a livello di applicazione .

Questo sembra il più semplice da implementare, ma non è la soluzione migliore. Ci sarà una quantità minima di tempo tra controllare per l'esistenza e salvare l'oggetto. In questo intervallo di tempo, è possibile inserire una riga e si otterrebbero comunque dati duplicati.

Le condizioni di gara sono notoriamente difficili da debugare, quindi suggerisco di evitarle a tutti i costi.

Blocco a livello di database .

Il blocco del database ti dà la possibilità di prevenire le condizioni della gara.

Tuttavia, se ti aspetti un sacco di accesso simultaneo a quella tabella (per oggetti diversi, i cui stati non colliderebbero mai), allora questo potrebbe diventare un collo di bottiglia per le prestazioni.

Non sono sicuro che il blocco possa essere garantito per tutti gli aggiornamenti della tabella. Perché se non può, allora corri il rischio che altre parti si dimentichino di implementare il comportamento di blocco.

Ripulitura dopo il fatto .

Questo è più facile da implementare ma potrebbe finire per causare un po 'più di lavoro. Questo si applica solo nei casi in cui l'aggiornamento dello stato non restituisce un valore e non ti interessa avvisare l'utente / l'applicazione che è stato trovato un duplicato (ma che invece continua a funzionare normalmente).

Il vantaggio è che non è necessario bloccare il comportamento e puoi semplicemente pulire i dati in seguito (rimuovendo eventuali duplicati). A seconda di evitare che i duplicati siano critici per l'azienda; potresti programmarlo come un lavoro. Oppure puoi creare un'attività per controllarla (preferibilmente in un thread separato che ignora e dimentica).

Lo svantaggio è che potresti finire per dover annullare il lavoro che hai appena fatto, che è un lavoro extra. Se il tuo server verrà premuto per le prestazioni, più lavoro sarà sempre una brutta cosa. La domanda qui è la frequenza con cui ti aspetti di incappare in collisioni e quanto verrai premuto per le prestazioni sul server.

Un secondo vantaggio è che la richiesta originale non viene rallentata eseguendo un controllo aggiuntivo e delegando invece il comportamento di controllo a un thread secondario / attività / processo pianificato.

Questo dipende molto dal tuo ambiente.

Se stai creando un servizio molto utilizzato in cui le voci duplicate sono rare e nient'altro che un difetto estetico, il blocco creerebbe un collo di bottiglia per tutti. L'aggiunta e la pulizia, tuttavia, bloccherà solo quelle richieste che accadono per essere eseguite in una collisione rara.

Se stai creando un servizio in cui le voci duplicate possono causare problemi importanti, dovresti concentrarti sul blocco anziché sulla pulizia dopo il fatto.

Se stai creando un servizio in cui le voci duplicate sono un'occorrenza comune, il comportamento di pulizia causa troppo lavoro extra troppo spesso. Se i duplicati non causano problemi, puoi pulirlo in un lavoro pianificato. Se i duplicati causano problemi, devi concentrarti sul comportamento di blocco.

    
risposta data 11.07.2018 - 09:26
fonte

Leggi altre domande sui tag