Rilevamento e sincronizzazione dei conflitti - 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 sincronizzazione dei conflitti

Origini dati con versione

AWS AppSync attualmente supporta il controllo delle versioni su sorgenti dati DynamoDB. Le operazioni di rilevamento dei conflitti, risoluzione dei conflitti e sincronizzazione richiedono un'origine dati Versioned. Quando si abilitano le versioni multiple su un'origine dati, AWS AppSync automaticamente consentirà di:

  • Migliorare gli elementi con i metadati del controllo delle versioni degli oggetti.

  • Registrare le modifiche apportate agli elementi con mutazioni AWS AppSync a una tabella Delta.

  • Gestire gli elementi eliminati nella tabella Base con una "rimozione definitiva" per un periodo di tempo configurabile.

Configurazione origine dati con versione

Quando si attivano le versioni multiple in un'origine dati DynamoDB, specificare i seguenti campi:

BaseTableTTL

Il numero di minuti per mantenere gli elementi eliminati nella tabella Base con una "rimozione definitiva", un campo di metadati che indica che l'elemento è stato eliminato. È possibile impostare questo valore su 0 se si desidera che gli elementi vengano rimossi immediatamente quando vengono eliminati. Questo campo è obbligatorio.

DeltaSyncTableName

Il nome della tabella in cui vengono memorizzate le modifiche apportate agli elementi con mutazioni AWS AppSync . Questo campo è obbligatorio.

DeltaSyncTableTTL

Numero di minuti per conservare gli elementi nella tabella Delta. Questo campo è obbligatorio.

Tabella di sincronizzazione Delta

AWS AppSync attualmente supporta Delta Sync Logging per le mutazioni utilizzandoPutItemUpdateItem, e le operazioniDeleteItem DynamoDB.

Quando una mutazione AWS AppSync modifica un elemento in un'origine dati con versione, un record di tale modifica verrà memorizzato in una tabella Delta ottimizzata per gli aggiornamenti incrementali. Puoi scegliere di utilizzare diverse tabelle Delta (ad esempio una per tipo, una per area di dominio) per altre fonti di dati con versioni o una singola tabella Delta per la tua API. AWS AppSync consiglia di non utilizzare una singola tabella Delta per più API per evitare la collisione delle chiavi primarie.

Lo schema richiesto per questa tabella è il seguente:

ds_pk

Valore stringa utilizzato come chiave di partizione. È costruito concatenando il nome della fonte dati di base e il formato ISO 8601 della data in cui è avvenuta la modifica (ad es.Comments:2019-01-01).

Quando ilcustomPartitionKey flag del modello di mappatura VTL è impostato come nome della colonna della chiave di partizione (vedi Resolver Mapping Template Reference per DynamoDB nella Guida per gliAWS AppSync sviluppatori), il formato delleds_pk modifiche e la stringa vengono costruiti aggiungendogli il valore della chiave di partizione nel nuovo record nella tabella Base. Ad esempio, se il record nella tabella Base ha un valore della chiave di partizione1a e un valore della chiave di ordinamento pari a2b, il nuovo valore della stringa sarà:Comments:2019-01-01:1a.

ds_sk

Un valore stringa utilizzato come chiave di ordinamento. Viene costruito concatenando il formato ISO 8601 dell'ora in cui è avvenuta la modifica, la chiave primaria dell'elemento e la versione dell'elemento. La combinazione di questi campi garantisce l'unicità di ogni voce nella tabella Delta (ad esempio per un orario, un ID1a e una versione di2, questo sarebbe09:30:00:1a:2).09:30:00

Quando ilcustomPartitionKey flag del modello di mappatura VTL è impostato sul nome della colonna della chiave di partizione (vedi Resolver Mapping Template Reference per DynamoDB nella Guida per gliAWS AppSync sviluppatori), il formato delleds_sk modifiche e la stringa vengono costruiti sostituendo il valore della chiave combinata con il valore della chiave di ordinamento nella tabella Base. Utilizzando l'esempio precedente, se il record nella tabella Base ha un valore della chiave di partizione1a e un valore della chiave di ordinamento pari a2b, il nuovo valore della stringa sarà:09:30:00:2b:3.

_ttl

Un valore numerico che memorizza il timestamp, in secondi dall'epoca, quando un elemento deve essere rimosso dalla tabella Delta. Questo valore viene determinato aggiungendo il valore DeltaSyncTableTTL configurato nell'origine dati nel momento in cui si è verificata la modifica. Questo campo deve essere configurato come Attributo TTL DynamoDB.

Il ruolo IAM configurato per l'utilizzo con la tabella Base deve contenere anche le autorizzazioni per operare nella tabella Delta. In questo esempio, viene visualizzato il criterio delle autorizzazioni per una tabella Base denominata Comments e una tabella Delta denominata ChangeLog:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:DeleteItem", "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:UpdateItem" ], "Resource": [ "arn:aws:dynamodb:us-east-1:000000000000:table/Comments", "arn:aws:dynamodb:us-east-1:000000000000:table/Comments/*", "arn:aws:dynamodb:us-east-1:000000000000:table/ChangeLog", "arn:aws:dynamodb:us-east-1:000000000000:table/ChangeLog/*" ] } ] }

Metadati origine dati con versioni

AWS AppSync gestisce i campi di metadati sulle fonti diVersioned dati per tuo conto. La modifica di questi campi può causare errori nell'applicazione o perdita di dati. Questi campi includono:

_version

Un contatore monotonicamente crescente che viene aggiornato ogni volta che si apporta una modifica a un elemento.

_lastChangedAt

Un valore numerico che memorizza il timestamp, in millisecondi dall'epoca, al momento dell'ultima modifica di un elemento.

_deleted

Un valore booleano "contrassegnato per la rimozione definitiva" che indica che un elemento è stato eliminato. Questo può essere utilizzato dalle applicazioni per rimuovere gli elementi eliminati dagli archivi dati locali.

_ttl

Un valore numerico che memorizza il timestamp, in secondi dall'epoca, quando un elemento deve essere rimosso dall'origine dati sottostante.

ds_pk

Valore stringa utilizzato come chiave di partizione per le tabelle Delta.

ds_sk

Valore stringa utilizzato come chiave di ordinamento per le tabelle Delta.

gsi_ds_pk

Un attributo di valore di stringa generato per supportare un indice secondario globale come chiave di partizione. Sarà incluso solo se sia i flag checustomPartitionKeypopulateIndexFields i flag sono abilitati nel modello di mappatura VTL (vedi Resolver Mapping Template Reference per DynamoDB nella Guida per gliAWS AppSync sviluppatori). Se abilitato, il valore verrà costruito concatenando il nome dell'origine dati di base e il formato ISO 8601 della data in cui è avvenuta la modifica (ad esempio, se la tabella Base è denominata Commenti, questo record verrà impostato comeComments:2019-01-01).

gsi_ds_sk

Un attributo di valore di stringa generato per supportare un indice secondario globale come chiave di ordinamento. Sarà incluso solo se sia i flag checustomPartitionKeypopulateIndexFields i flag sono abilitati nel modello di mappatura VTL (vedi Resolver Mapping Template Reference per DynamoDB nella Guida per gliAWS AppSync sviluppatori). Se abilitato, il valore verrà costruito concatenando il formato ISO 8601 dell'ora in cui è avvenuta la modifica, la chiave di partizione dell'elemento nella tabella Base, la chiave di ordinamento dell'elemento nella tabella Base e la versione dell'elemento (ad esempio, per un periodo di09:30:00, un valore della chiave di partizione di1a, un valore della chiave di2b ordinamento e la versione di3, questo sarebbe09:30:00:1a#2b:3).

Questi campi di metadati influiranno sulle dimensioni complessive degli elementi nell'origine dati sottostante. AWS AppSync consiglia di riservare la dimensione massima della chiave primaria di 500 byte per i metadati delle fonti di dati con versioni durante la progettazione dell'applicazione. Per utilizzare questi metadati nelle applicazioni client, includere i campi _version, _lastChangedAt e _deleted nei tipi GraphQL e nel set di selezione per le mutazioni.

Rilevamento e risoluzione dei conflitti

Quando si verificano scritture simultanee con AWS AppSync, è possibile configurare 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 è abilitato impostando il valore nelconflictDetection campo SyncConfig perVERSION.

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 in SyncConfig. Esistono tre strategie di risoluzione dei conflitti:

  • OPTIMISTIC_CONCURRENCY

  • AUTOMERGE

  • LAMBDA

Ognuna di queste strategie di risoluzione dei conflitti è spiegata nei dettagli di seguito.

Le versioni vengono incrementate automaticamente 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.

Optimistic Concurrency

Optimistic Concurrency è una strategia di risoluzione dei conflitti che AWS AppSync fornisce per le origini dati con versioni. 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.

Automerge

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 entrata 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

Opzioni di risoluzione dei conflitti:

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

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

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

La richiesta di invocazione Lambda

Il resolverAWS AppSync DynamoDB richiama la funzione Lambda specificata inLambdaConflictHandlerArn. 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 l'accesso avviene 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 è stato creato dajeffTheAdmin,REMOVE l'oggetto per il DeletePost risolutore oRESOLVE è in conflitto con un nuovo elemento per i risolutori 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

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 di aggiornare _version di 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.

CloudWatch Registri

Se un'AWS AppSync API ha abilitato CloudWatch i log con le impostazioni di registrazione impostate su Registri a livello di campoenabled 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.

1.000.000 di operazioni di sincronizzazione

Le fonti di dati con versioni supportanoSync operazioni che consentono di recuperare tutti i risultati da una tabella DynamoDB e quindi ricevere solo i dati modificati dall'ultima query (gli aggiornamenti delta). Quando AWS AppSync riceve una richiesta per un'operazione Sync, utilizza i campi specificati nella richiesta per stabilire se è necessario accedere alla tabella Base o alla tabella Delta .

  • Se il lastSync campo non è specificato, viene eseguita una Scan nella tabella Base.

  • Se il campo lastSync è specificato, ma il valore è prima del current moment - DeltaSyncTTL, viene eseguita una Scan sulla tabella di base.

  • Se il campo lastSync è specificato e il valore è attivo o dopo current moment - DeltaSyncTTL, viene eseguita una Query sulla tabella Delta.

AWS AppSync restituisce ilstartedAt campo al modello di mappatura delle risposte per tutte leSync operazioni. Il campo startedAt è il momento, in millisecondi dall'epoca, in cui è iniziata l'operazione Sync che è possibile memorizzare localmente e utilizzare in un'altra richiesta. Se un token di paginazione è stato incluso nella richiesta, questo valore sarà lo stesso di quello restituito dalla richiesta per la prima pagina di risultati.

Per informazioni sul formato per i modelli di mappatura Sync, consulta il riferimento del modello di mappatura.