Modelli per mantenere la coerenza in un sistema distribuito, di origine evento?


12

Ho letto di recente sull'approvvigionamento di eventi e mi piacciono molto le idee alla base, ma sono bloccato con il seguente problema.

Supponiamo che tu abbia N processi simultanei che ricevono comandi (ad es. Server Web), generano eventi di conseguenza e li memorizzano in un archivio centralizzato. Supponiamo anche che tutto lo stato transitorio dell'applicazione venga mantenuto nella memoria dei singoli processi applicando sequenzialmente eventi dall'archivio.

Ora, supponiamo di avere la seguente regola aziendale: ogni utente distinto deve avere un nome utente univoco.

Se due processi ricevono un comando di registrazione utente per lo stesso nome utente X, entrambi verificano che X non sia nel loro elenco di nomi utente, la regola viene convalidata per entrambi i processi ed entrambi memorizzano un evento "nuovo utente con nome utente X" nel negozio .

Ora siamo entrati in uno stato globale incoerente perché la regola aziendale è stata violata (ci sono due utenti distinti con lo stesso nome utente).

In un sistema di tipo RDBMS tradizionale N server <-> 1, il database viene utilizzato come punto centrale di sincronizzazione che aiuta a prevenire tali incoerenze.

La mia domanda è: in che modo i sistemi basati su eventi in genere affrontano questo problema? Elaborano semplicemente tutti i comandi in sequenza (ad es. Limitano la quantità di processo che può scrivere nel negozio a 1)?


1
Tale restrizione è controllata dal codice o è un vincolo db? N eventi possono o non possono essere spediti elaborati in sequenza ... N eventi possono passare attraverso convalide contemporaneamente senza scartarsi. Se l'ordine conta, dovrai sincronizzare la validazione. O usare la coda per accodare gli eventi e inviarli in sequenza
Laiv

@Laiv giusto. Per semplicità ho pensato che non esistesse un database, tutto lo stato tenuto in memoria. L'elaborazione di tipi specifici di comandi in sequenza attraverso una coda sarebbe un'opzione, ma sembra che potrebbe essere complessa decidere quali comandi possono influenzare causalmente gli altri e probabilmente finirei per mettere tutti i comandi nella stessa coda, il che equivale ad avere un singolo processo di elaborazione dei comandi : / Ad esempio, se ho un utente che aggiunge un commento su un post di blog, "elimina utente", "sospendi utente", "elimina post di blog", "disabilita commenti di post di blog", ecc. Dovrebbero andare tutti nella stessa coda.
Olivier Lalonde,

1
Sono d'accordo con te, lavorare con code o semafori non è semplice. Né per lavorare con modelli di concorrenza o di origine evento. Ma fondamentalmente tutte le soluzioni finiscono con un sistema che orchestra il traffico degli eventi. Tuttavia è un paradigma interessante. Esistono anche cache esterne orientate verso tuple come Redis che potrebbero aiutare a gestire questo traffico tra nodi, come memorizzare nella cache l'ultimo stato di un'entità o se tale entità è in fase di elaborazione in questo momento. Le cache condivise sono abbastanza comuni in questo tipo di sviluppi. Può sembrare complesso ma non mollare ;-) è abbastanza interessante
Laiv

Risposte:


6

In un sistema di tipo RDBMS tradizionale N server <-> 1, il database viene utilizzato come punto centrale di sincronizzazione che aiuta a prevenire tali incoerenze.

Nei sistemi di origine evento, il "negozio eventi" svolge lo stesso ruolo. Per un oggetto proveniente da un evento, la tua scrittura è un'appendice dei tuoi nuovi eventi a una particolare versione del flusso di eventi. Quindi, proprio come con la programmazione concorrente, è possibile acquisire un blocco su quella cronologia durante l'elaborazione del comando. È più comune che i sistemi di provenienza degli eventi adottino un approccio più ottimistico: carica la cronologia precedente, calcola la nuova cronologia, quindi confronta e scambia. Se qualche altro comando ha anche scritto su quello stream, allora il tuo confronto e scambio falliscono. Da lì, o riesegui il tuo comando, o abbandoni il tuo comando, o magari unisci i risultati nella storia.

La contesa diventa un grosso problema se tutti gli N server con i loro comandi M stanno provando a scrivere in un singolo flusso. La solita risposta qui è allocare una cronologia per ogni entità di origine evento nel tuo modello. Quindi l'utente (Bob) avrebbe una cronologia distinta dall'utente (Alice) e le scritture su una non bloccheranno le scritture sull'altra.

La mia domanda è: in che modo i sistemi basati su eventi in genere affrontano questo problema? Elaborano semplicemente ogni comando in sequenza?

Greg Young su Set Validation

Esiste un modo elegante per verificare le contraddizioni uniche sugli attributi degli oggetti di dominio senza spostare la logica aziendale nel livello di servizio?

Una risposta breve, in molti casi, indagando più a fondo su tale requisito rivela che (a) è un proxy mal compreso per qualche altro requisito, oppure (b) che le violazioni della "regola" sono accettabili se possono essere rilevate (rapporto di eccezione) , mitigato in un intervallo di tempo o a bassa frequenza (ad esempio: i client possono verificare se un nome è disponibile prima di inviare un comando per usarlo).

In alcuni casi, in cui l'archivio eventi è valido per la convalida impostata (ovvero: un database relazionale), è possibile implementare il requisito scrivendo in una tabella "nomi univoci" nella stessa transazione che persiste gli eventi.

In alcuni casi, è possibile applicare il requisito solo facendo pubblicare tutti i nomi utente nello stesso flusso (che consente di valutare l'insieme di nomi in memoria, come parte del modello di dominio). - In questo caso, due processi aggiorneranno il tentativo di aggiornare "la" cronologia del flusso, ma una delle operazioni di confronto e scambio non riuscirà e il nuovo tentativo di quel comando sarà in grado di rilevare il conflitto.


1) Grazie per i suggerimenti e i riferimenti. Quando dici "confronta-e-scambia" intendi che il processo, al momento della memorizzazione di un evento, rileverà che sono arrivati ​​nuovi eventi da quando ha iniziato a elaborare il comando? Immagino che ciò richiederebbe un negozio di eventi che supporti la semantica "confronta e scambia", giusto? (es. "scrivi questo evento solo e solo se l'ultimo evento ha l'ID X")?
Olivier Lalonde,

2) Mi piace anche l'idea di accettare incoerenze temporanee e ripararle alla fine, ma non sono sicuro di come codificherei in modo affidabile ... forse ho un processo dedicato che convalida gli eventi in sequenza e crea eventi di rollback quando rileva qualcosa è andato storto? Grazie!
Olivier Lalonde,

(1) Direi "nuova versione della storia" piuttosto che "nuovi eventi", ma hai l'idea; sostituire la cronologia solo se è quella che ci aspettiamo.
VoiceOfUnreason,

(2) Sì. È un po 'di logica che legge gli eventi dal negozio in batch e alla fine del batch trasmette un rapporto di eccezione ("abbiamo troppi utenti di nome Bob") o invia comandi per compensare il problema (supponendo che la risposta giusta sia calcolabile senza intervento umano).
VoiceOfUnreason,

2

Sembra che tu possa implementare un processo aziendale ( saganel contesto di Domain Driven Design) per la registrazione dell'utente in cui l'utente è trattato come un CRDT.

risorse

  1. https://doc.akka.io/docs/akka/current/distributed-data.html http://archive.is/t0QIx

  2. "CRDT con dati distribuiti Akka" https://www.slideshare.net/markusjura/crdts-with-akka-distributed-data per conoscere i dati

    • CmRDTs - CRDT basati sull'operazione
    • CvRDTs - CRTD basati sullo stato
  3. Esempi di codice in Scala https://github.com/akka/akka-samples/tree/master/akka-sample-distributed-data-scala . Forse "carrello della spesa" è più adatto.

  4. Tour of Akka Cluster - Akka Distributed Data https://manuel.bernhardt.io/2018/01/03/tour-akka-cluster-akka-distributed-data/
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.