Rilevamento e risoluzione dei conflitti in AWS AppSync - AWS AppSync

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Rilevamento e risoluzione dei conflitti in AWS AppSync

Quando vengono eseguite scritture simultanee con AWS AppSync, è possibile configurare le strategie di rilevamento e risoluzione dei conflitti per gestire gli aggiornamenti in modo appropriato. Il rilevamento dei conflitti determina se la mutazione è in conflitto con l'elemento scritto effettivo nell'origine dati. Il rilevamento dei conflitti viene abilitato impostando il SyncConfig valore nel conflictDetection campo su. VERSION

La risoluzione dei conflitti è l'azione che viene eseguita nel caso in cui venga rilevato un conflitto. Ciò viene determinato impostando il campo Conflict Handler nel SyncConfig. Esistono tre strategie di risoluzione dei conflitti:

  • OPTIMISTIC_CONCURRENCY

  • AUTOMERGE

  • LAMBDA

Le versioni vengono incrementate automaticamente AWS AppSync durante le operazioni di scrittura e non devono essere modificate dai client o dall'esterno di un resolver configurato con un'origine dati abilitata alla versione. Ciò cambierà il comportamento di coerenza del sistema e potrebbe comportare la perdita di dati.

Concorrenza ottimistica

Optimistic Concurrency è una strategia di risoluzione dei conflitti che AWS AppSync prevede fonti di dati con versioni diverse. Quando il risolutore dei conflitti è impostato su Optimistic Concurrency, se viene rilevata una mutazione in ingresso per avere una versione diversa dalla versione effettiva dell'oggetto, il gestore dei conflitti rifiuterà semplicemente la richiesta in ingresso. All'interno della risposta GraphQL, verrà fornito l'elemento esistente sul server con la versione più recente. Il client dovrebbe quindi gestire questo conflitto localmente e riprovare la mutazione con la versione aggiornata dell'elemento.

Si fonde automaticamente

Automerge offre agli sviluppatori un modo semplice per configurare una strategia di risoluzione dei conflitti senza scrivere logica lato client per unire manualmente i conflitti che non erano in grado di essere gestiti da altre strategie. Automerge aderisce a un set di regole rigoroso quando si uniscono i dati per risolvere i conflitti. I principi di Automerge ruotano attorno al tipo di dati sottostante del campo GraphQL. Essi sono i seguenti:

  • Conflitto su un campo scalare: scalare GraphQL o qualsiasi campo che non sia una raccolta (ad esempio List, Set, Map). Rifiuto del valore in ingresso per il campo scalare e selezione del valore esistente nel server.

  • Conflitto su un elenco: il tipo GraphQL e il tipo di database sono elenchi. Concatenamento dell'elenco in entrata con l'elenco esistente nel server. I valori di elenco nella mutazione in arrivo verranno aggiunti alla fine dell'elenco nel server. I valori duplicati verranno mantenuti.

  • Conflitto su un set: il tipo GraphQL è un elenco e il tipo di database è un set. Applicazione di un insieme unione utilizzando il set in ingresso e il set esistente nel server. Questo aderisce alle proprietà di un set, vale a dire nessuna voce duplicata.

  • Quando una mutazione in arrivo aggiunge un nuovo campo all'elemento o viene creata su un campo con il valore dinull, uniscilo all'elemento esistente.

  • Conflitto su una mappa: quando il tipo di dati sottostante nel database è una mappa (ad esempio documento chiave-valore), applicare le regole di cui sopra mentre analizza ed elabora ciascuna proprietà della mappa.

Automerge è progettato per rilevare, unire e riprovare automaticamente le richieste con una versione aggiornata, assolvendo il client dalla necessità di unire manualmente i dati in conflitto.

Per mostrare un esempio di come Automerge gestisce un conflitto su un tipo scalare. Useremo il seguente record come punto di partenza.

{ "id" : 1, "name" : "Nadia", "jersey" : 5, "_version" : 4 }

Ora una mutazione in arrivo potrebbe tentare di aggiornare l'elemento ma con una versione precedente poiché il client non è ancora sincronizzato con il server. Dovrebbe essere simile a questo:

{ "id" : 1, "name" : "Nadia", "jersey" : 55, "_version" : 2 }

Si noti la versione obsoleta di 2 nella richiesta in entrata. Durante questo flusso, Automerge unirà i dati rifiutando l'aggiornamento del campo "jersey" a "55" e manterrà il valore a "5" con conseguente salvataggio della seguente immagine dell'elemento nel server.

{ "id" : 1, "name" : "Nadia", "jersey" : 5, "_version" : 5 # version is incremented every time automerge performs a merge that is stored on the server. }

Dato lo stato dell'elemento mostrato sopra alla versione 5, ora supponiamo che una mutazione in entrata tenti di cambiare l'elemento con la seguente immagine:

{ "id" : 1, "name" : "Shaggy", "jersey" : 5, "interests" : ["breakfast", "lunch", "dinner"] # underlying data type is a Set "points": [24, 30, 27] # underlying data type is a List "_version" : 3 }

La mutazione in entrata ha tre punti di interesse. Il nome, uno scalare, è stato modificato ma sono stati aggiunti due nuovi campi "interessi", un Set, "punti" e un Elenco. In questo scenario, verrà rilevato un conflitto a causa della mancata corrispondenza della versione. Automerge aderisce alle sue proprietà e rifiuta la modifica del nome data la sua natura scalare e l'add-on ai campi non in conflitto. In questo modo, l'elemento che viene salvato nel server viene visualizzato come segue.

{ "id" : 1, "name" : "Nadia", "jersey" : 5, "interests" : ["breakfast", "lunch", "dinner"] # underlying data type is a Set "points": [24, 30, 27] # underlying data type is a List "_version" : 6 }

Con l'immagine aggiornata dell'elemento con la versione 6, ora supponiamo che una mutazione in entrata (con un'altra mancata corrispondenza della versione) cerchi di trasformare l'elemento nel seguente modo:

{ "id" : 1, "name" : "Nadia", "jersey" : 5, "interests" : ["breakfast", "lunch", "brunch"] # underlying data type is a Set "points": [30, 35] # underlying data type is a List "_version" : 5 }

Qui osserviamo che il campo in entrata per "interessi" ha un valore duplicato che esiste nel server e due nuovi valori. In questo caso, poiché il tipo di dati sottostante è un Set, Automerge combinerà i valori esistenti nel server con quelli nella richiesta in arrivo e rimuoverà eventuali duplicati. Allo stesso modo c'è un conflitto nel campo "punti" in cui c'è un valore duplicato e un nuovo valore. Ma poiché il tipo di dati sottostante qui è un elenco, Automerge aggiungerà semplicemente tutti i valori nella richiesta in entrata alla fine dei valori già esistenti nel server. L'immagine risultante unita memorizzata sul server apparirà come segue:

{ "id" : 1, "name" : "Nadia", "jersey" : 5, "interests" : ["breakfast", "lunch", "dinner", "brunch"] # underlying data type is a Set "points": [24, 30, 27, 30, 35] # underlying data type is a List "_version" : 7 }

Ora supponiamo che l'elemento memorizzato nel server appaia come segue, nella versione 8.

{ "id" : 1, "name" : "Nadia", "jersey" : 5, "interests" : ["breakfast", "lunch", "dinner", "brunch"] # underlying data type is a Set "points": [24, 30, 27, 30, 35] # underlying data type is a List "stats": { "ppg": "35.4", "apg": "6.3" } "_version" : 8 }

Ma supponiamo anche che una richiesta in entrata tenti di aggiornare l'elemento con la seguente immagine, ancora una volta con una mancata corrispondenza della versione:

{ "id" : 1, "name" : "Nadia", "stats": { "ppg": "25.7", "rpg": "6.9" } "_version" : 3 }

Ora, in questo scenario, possiamo vedere che i campi che già esistono nel server sono mancanti (interessi, punti, jersey). Inoltre, il valore per "ppg" all'interno della mappa "statistiche" viene modificato, viene aggiunto un nuovo valore "rpg" e "apg" viene omesso. Automerge conserva i campi che sono stati omessi (nota: se i campi sono destinati a essere rimossi, allora la richiesta deve essere riprovata con la versione corrispondente), e quindi non andranno persi. Applicherà anche le stesse regole ai campi all'interno delle mappe e quindi la modifica a "ppg" verrà rifiutata mentre "apg" è conservato e "rpg", un nuovo campo, viene aggiunto. L'elemento risultante memorizzato nel server verrà ora visualizzato come:

{ "id" : 1, "name" : "Nadia", "jersey" : 5, "interests" : ["breakfast", "lunch", "dinner", "brunch"] # underlying data type is a Set "points": [24, 30, 27, 30, 35] # underlying data type is a List "stats": { "ppg": "35.4", "apg": "6.3", "rpg": "6.9" } "_version" : 9 }

Lambda

Esistono diverse strategie di risoluzione Lambda tra cui scegliere:

  • RESOLVE: sostituisce l'articolo esistente con un nuovo articolo fornito nel payload di risposta. È possibile riprovare la stessa operazione solo su un singolo elemento alla volta. Attualmente supportato per DynamoDB PutItem e UpdateItem.

  • REJECT: rifiuta la mutazione e restituisce un errore con l'elemento esistente nella risposta GraphQL. Attualmente supportato per DynamoDB PutItem, UpdateItem e DeleteItem.

  • REMOVE: rimuove l'elemento esistente. Attualmente supportato per DynamoDB DeleteItem.

La richiesta di invocazione Lambda

Il AWS AppSync resolver DynamoDB richiama la funzione Lambda specificata in. LambdaConflictHandlerArn Utilizza lo stesso service-role-arn configurato per l'origine dati. Il payload dell'invocazione ha la seguente struttura:

{ "newItem": { ... }, "existingItem": {... }, "arguments": { ... }, "resolver": { ... }, "identity": { ... } }

I campi sono definiti come segue:

newItem

L'elemento di anteprima, se la mutazione è riuscita.

existingItem

L'elemento attualmente risiede nella tabella DynamoDB.

arguments

Gli argomenti della mutazione GraphQL.

resolver

Informazioni sul resolver AWS AppSync .

identity

Informazioni sul chiamante. Questo campo è impostato su null, se si accede con chiave. API

Esempio di payload:

{ "newItem": { "id": "1", "author": "Jeff", "title": "Foo Bar", "rating": 5, "comments": ["hello world"], }, "existingItem": { "id": "1", "author": "Foo", "rating": 5, "comments": ["old comment"] }, "arguments": { "id": "1", "author": "Jeff", "title": "Foo Bar", "comments": ["hello world"] }, "resolver": { "tableName": "post-table", "awsRegion": "us-west-2", "parentType": "Mutation", "field": "updatePost" }, "identity": { "accountId": "123456789012", "sourceIp": "x.x.x.x", "username": "AIDAAAAAAAAAAAAAAAAAA", "userArn": "arn:aws:iam::123456789012:user/appsync" } }

La risposta all'invocazione Lambda

Per PutItem e risoluzione UpdateItem dei conflitti

Rifiuto della mutazione (RESOLVE). La risposta deve essere nel seguente formato.

{ "action": "RESOLVE", "item": { ... } }

Il campo item rappresenta un oggetto che verrà utilizzato per sostituire l'elemento esistente nell'origine dati sottostante. La chiave primaria e i metadati di sincronizzazione verranno ignorati se inclusi in item.

Rifiuto della mutazione (REJECT). La risposta deve essere nel seguente formato.

{ "action": "REJECT" }

Per la risoluzione dei conflitti DeleteItem

REMOVE l'articolo. La risposta deve essere nel seguente formato.

{ "action": "REMOVE" }

Rifiuto della mutazione (REJECT). La risposta deve essere nel seguente formato.

{ "action": "REJECT" }

La funzione Lambda di esempio qui sotto controlla chi effettua la chiamata e il nome del resolver. Se è creato dajeffTheAdmin, REMOVE l'oggetto per il DeletePost resolver o il conflitto con un nuovo elemento per RESOLVE i resolver Update/Put. In caso contrario, la mutazione è REJECT.

exports.handler = async (event, context, callback) => { console.log("Event: "+ JSON.stringify(event)); // Business logic goes here. var response; if ( event.identity.user == "jeffTheAdmin" ) { let resolver = event.resolver.field; switch(resolver) { case "deletePost": response = { "action" : "REMOVE" } break; case "updatePost": case "createPost": response = { "action" : "RESOLVE", "item": event.newItem } break; default: response = { "action" : "REJECT" }; } } else { response = { "action" : "REJECT" }; } console.log("Response: "+ JSON.stringify(response)); return response; }

Errori

Di seguito è riportato un elenco di possibili errori che possono verificarsi durante un processo di risoluzione dei conflitti:

ConflictUnhandled

Rilevamento dei conflitti individua una mancata corrispondenza della versione e il gestore dei conflitti rifiuta la mutazione.

Esempio: risoluzione dei conflitti con un gestore di conflitti di Optimistic Concurrency. Oppure, il gestore di conflitti Lambda è stato restituito con REJECT.

ConflictError

Si verifica un errore interno quando si tenta di risolvere un conflitto.

Esempio: il gestore di conflitti Lambda ha restituito una risposta non valida. In alternativa, non è possibile richiamare il gestore di conflitti Lambda perché la risorsa fornita LambdaConflictHandlerArn non viene trovata.

MaxConflicts

Sono stati raggiunti tentativi massimi per la risoluzione dei conflitti.

Esempio: troppe richieste simultanee sullo stesso oggetto. Prima che il conflitto venga risolto, l'oggetto viene aggiornato a una nuova versione da un altro client.

BadRequest

Il client tenta di aggiornare i campi dei metadati (_version, _ttl, _lastChangedAt, _deleted).

Esempio: il client tenta _version di aggiornare un oggetto con una mutazione di aggiornamento.

DeltaSyncWriteError

Impossibile scrivere il record di sincronizzazione delta.

Esempio: la mutazione è riuscita, ma si è verificato un errore interno durante il tentativo di scrivere nella tabella di sincronizzazione delta.

InternalFailure

Si è verificato un errore interno.

UnsupportedOperation

Operazione non supportata 'X'. Datasource Versioning supporta solo le seguenti operazioni (TransactGetItems,,, Scan PutItemBatchGetItem, Query,,, GetItem DeleteItemUpdateItem, Sync).

Esempio: utilizzo di determinate operazioni di transazione e batch con il rilevamento/risoluzione dei conflitti abilitati. Queste operazioni non sono attualmente supportate.

CloudWatch Registri

Se un AWS AppSync API ha abilitato CloudWatch i registri con le impostazioni di registrazione impostate su Registri a livello di campo enabled e a livello di registro per i registri a livello di campo impostate suALL, AWS AppSync invierà le informazioni di rilevamento e risoluzione dei conflitti al gruppo di log. Per informazioni sul formato dei messaggi di registro, consulta la documentazione relativa al rilevamento conflitti e alla registrazione di sincronizzazione.