Logica per creare / aggiornare valori mantenendo la loro unicità (in un ambiente multi-thread)

-1

Supponiamo di avere un deposito stupido che memorizza numeri (per ragioni di questo esempio). È stupido perché può solo creare un nuovo record, aggiornare il record specificato ed elencare tutti i record esistenti - nessun'altra logica inclusa.

Ma voglio implementare una logica di business che mi consenta di memorizzare nuovi record in modo sicuro, sapendo che non memorizzerò un record duplicato. Inoltre voglio aggiornare i record esistenti sapendo che non porterà a duplicati.

Iniziamo con la creazione di nuovi record. L'approccio ingenuo mi dice che ho bisogno di due passaggi per aggiungere un record:

  • (A) legge i record esistenti dal repository
  • (B) controlla se i record esistenti contengono già lo stesso record di cui ho bisogno per memorizzare
  • (C) se i record esistenti sono privi di duplicati - store record.

Funziona bene finché tutto viene eseguito in un singolo thread. Che ne dici di multi-threading?

Un altro approccio ingenuo mi dice che posso introdurre il blocco attorno ai passi A, B, C. Sono di nuovo al sicuro (ignoriamo tutte le considerazioni sulle prestazioni).

Ora consideriamo l'aggiornamento dei record. I passaggi sono simili alla creazione:

  • (A) leggi tutti i record
  • (B) controlla se si verificano duplicati dopo l'aggiornamento
  • (C) se il controllo è a nostro favore - aggiorna il record.

Problemi con multi-threading di nuovo? Usa nuovamente il blocco.

Ma per quanto riguarda la situazione in cui un thread crea un record 200 mentre un altro thread aggiorna il record 100 a un valore di 200. Entrambi passano i loro controlli sui passaggi (B), entrambi eseguono i passaggi (C) ... e terminiamo con un duplicato.

Ci porta a concludere che l'intero design è sbagliato.

Ma qual è il design giusto?

    
posta Dima 14.12.2018 - 22:49
fonte

2 risposte

0

Se comprendo correttamente la tua domanda, stai chiedendo come gestire il problema con multithreading con: 1. Il thread A verifica il valore X 2. La discussione B verifica il valore X 3. Il thread A inserisce / aggiorna il valore X 4. Thread B inserisce / aggiorna il valore X

Se questo è in realtà ciò di cui sei preoccupato, allora generalmente non lo farei, poiché non è molto probabile che accada per la maggior parte delle app .

Ora, se in effetti stai lavorando su un'app che garantisce questo tipo di controllo, dovrebbe avvenire a livello di archiviazione (ad esempio il database ). Questo può essere fatto in molti modi, ma generalmente una chiave unica farà il trucco.

Come hai detto tu, i tuoi repository sono (e dovrebbero essere) stupidi. Il loro compito è di interfacciare un livello aziendale (es. Servizi) con il sistema di archiviazione dei dati. Il suo lavoro è non per proteggere dai problemi di multithreading quando si aggiungono valori univoci.

Finché l'unicità è un requisito aziendale, suggerirei di adottare la logica che controlla l'univocità nel proprio livello aziendale, il che consentirà errori più eloquenti quando viene violato. Ma la protezione reale dal problema dovrebbe trovarsi all'origine della memorizzazione dei dati.

    
risposta data 15.12.2018 - 00:29
fonte
0

La soluzione che ho trovato che funziona è quella di utilizzare il DBMS come un sistema di gestione dei dati completo piuttosto che come un livello di persistenza. Come hanno dimostrato gli studi - come Controllo della concorrenza ferale concentrandosi su Ruby on Rails - implementando l'integrità dei dati nell'applicazione / ORM significa che sono soggetti a condizioni di gara, come hai trovato.

Dovrebbe essere invece qualsiasi regola aziendale che può essere implementata come un vincolo di integrità dei dati all'interno del database. Idealmente, questi dovrebbero essere implementati in modo dichiarativo usando i vincoli di verifica, esclusivi e di chiavi estranei disponibili nella maggior parte dei DBMS SQL. L'implementazione di questi nel motore DBMS dovrebbe includere qualsiasi meccanismo di controllo della concorrenza richiesto.

Sfortunatamente molti, se non tutti, i DBMS SQL non supportano la dichiarazione di asserzione SQL generica che consentirebbe la dichiarazione di qualsiasi vincolo di integrità dei dati di complessità arbitraria. Pertanto, li ho implementati in modo programmatico utilizzando i trigger e incluso il mio controllo della concorrenza utilizzando blocchi di avviso in PostgreSQL o DBMS_LOCK in Oracle .

Utilizzando anche tecniche di blocco ottimistiche o pessimistiche, sono sicuro di aver implementato correttamente un meccanismo di controllo della concorrenza che garantisce l'integrità dei dati senza condizioni di competizione.

    
risposta data 15.12.2018 - 17:39
fonte