Konflikterkennung und Konfliktlösung in AWS AppSync - AWS AppSync

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Konflikterkennung und Konfliktlösung in AWS AppSync

Bei gleichzeitigen Schreibvorgängen mit AWS AppSync können Sie Strategien zur Konflikterkennung und Konfliktlösung konfigurieren, um Aktualisierungen entsprechend zu handhaben. Die Konflikterkennung bestimmt, ob die Mutation mit dem tatsächlich geschriebenen Element in der Datenquelle in Konflikt steht. Die Konflikterkennung wird aktiviert, indem der Wert im conflictDetection Feld SyncConfig für auf VERSION gesetzt wird.

Die Konfliktlösung ist die Aktion, die für den Fall ausgeführt wird, dass ein Konflikt erkannt wird. Dies wird bestimmt, indem das Feld Conflict Handler im festgelegt wird SyncConfig. Es gibt drei Strategien zur Konfliktlösung:

  • OPTIMISTIC_CONCURRENCY

  • AUTOMERGE

  • LAMBDA

Versionen werden AWS AppSync bei Schreibvorgängen automatisch inkrementiert und sollten nicht von Clients oder außerhalb eines Resolvers geändert werden, der mit einer versionsfähigen Datenquelle konfiguriert ist. Dadurch ändert sich das Konsistenzverhalten des Systems, was zu Datenverlusten führen kann.

Optimistische Parallelität

Optimistische Parallelität ist eine Konfliktlösungsstrategie, die versionierte Datenquellen AWS AppSync vorsieht. Wenn der Konflikt-Resolver auf „Optimistische Parallelität“ gesetzt ist und eine eingehende Mutation eine Version hat, die sich von der tatsächlichen Version des Objekts unterscheidet, weist der Conflict Handler die eingehende Anforderung einfach zurück. Innerhalb der GraphQL-Antwort wird das vorhandene Element auf dem Server mit der neuesten Version bereitgestellt. Vom Client wird dann erwartet, dass er diesen Konflikt lokal behandelt und die Mutation mit der aktualisierten Version des Elements erneut versucht.

Automatische Zusammenführungen

Automerge bietet Entwicklern eine einfache Möglichkeit, eine Konfliktlösungsstrategie zu konfigurieren, ohne clientseitige Logik zu schreiben, um Konflikte manuell zusammenzuführen, die nicht von anderen Strategien behandelt werden konnten. Automerge hält sich an einen strengen Regelsatz, wenn Daten zusammengeführt werden, um Konflikte zu lösen. Die Grundsätze des Automerge-Verfahrens drehen sich um den zugrunde liegenden Datentyp des GraphQL-Feldes. Diese sind:

  • Konflikt in einem Skalarfeld: GraphQL-Skalar oder jedes Feld, das keine Sammlung ist (d. h. List, Set, Map). Zurückweisen des eingehenden Werts für das skalare Feld und Auswahl des auf dem Server vorhandenen Werts.

  • Konflikt in einer Liste: GraphQL-Typ und Datenbanktyp sind Listen. Verketten der eingehenden Liste mit der vorhandenen Liste auf dem Server. Die Listenwerte in der eingehenden Mutation werden an das Ende der Liste auf dem Server angehängt. Doppelte Werte werden beibehalten.

  • Konflikt auf einem Set: Der GraphQL-Typ ist eine Liste, und der Datenbanktyp ist ein Set. Anwenden einer Set-Vereinigung mit dem eingehenden und dem vorhandenen Set auf dem Server. Dies entspricht den Eigenschaften eines Sets, d. h. es gibt keine doppelten Einträge.

  • Wenn eine eingehende Mutation dem Element ein neues Feld hinzufügt oder auf ein Feld mit dem Wert von angewendet wirdnull, fügen Sie dieses Feld mit dem vorhandenen Element zusammen.

  • Konflikt auf einer Map: Wenn der zugrunde liegende Datentyp in der Datenbank eine Map ist (d.h ein Schlüssel-Wert-Dokument), werden die die oben genannten Regeln angewendet, wenn jede Eigenschaft der Map analysiert und verarbeitet wird.

Automerge wurde entwickelt, um Anforderungen automatisch mit einer aktualisierten Version zu erkennen, zusammenzuführen und erneut zu versuchen, so dass der Client keine Konflikte manuell zusammenführen muss.

Um ein Beispiel dafür zu zeigen, wie Automerge einen Konflikt auf einem Scalar-Typ verarbeitet, verwenden wir den folgenden Datensatz als Ausgangspunkt.

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

Jetzt versucht eine eingehende Mutation möglicherweise, das Element zu aktualisieren, jedoch mit einer älteren Version, da der Client noch nicht mit dem Server synchronisiert wurde. Dies sollte wie folgt aussehen:

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

Beachten Sie die veraltete Version von 2 in der eingehenden Anforderung. Während dieses Vorgangs führt Automerge die Daten zusammen, indem die Aktualisierung des Feldes „jersey“ auf „55“ abgelehnt wird und der Wert bei „5“ bleibt, was dazu führt, dass das folgende Abbild des Elements auf dem Server gespeichert wird.

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

Angesichts des Status des oben gezeigten Elements mit Version 5, nehmen wir jetzt an, es geht eine Mutation ein, die versucht, das Element mit dem folgenden Abbild zu mutieren:

{ "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 }

Es gibt drei Punkte zu dieser eingehenden Mutation drei wichtige Punkte. Der Name, ein Skalar, wurde geändert, aber zwei neue Felder „interests“, ein Satz und „points“, eine Liste, wurden hinzugefügt. In diesem Szenario wird ein Konflikt aufgrund fehlender Versionsübereinstimmungen erkannt. Automerge hält sich an seine Eigenschaften und weist die Namensänderung zurück, da es sich um einen Skalar und eine Hinzufügung zu den nicht in Konflikt stehenden Feldern handelt. Dies führt dazu, dass das Element, das auf dem Server gespeichert wird, wie folgt aussieht.

{ "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 }

Bei dem aktualisierten Abbild des Elements mit Version 6 nehmen wir nur an, dass eine eingehende Mutation (mit einer anderen fehlenden Versionsübereinstimmung) versucht, das Element in Folgendes umzuwandeln:

{ "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 }

Hier beobachten wir, dass das eingehende Feld für „interests“ einen doppelten Wert hat, der auf dem Server vorhanden ist, sowie zwei neue Werte. Da der zugrunde liegende Datentyp ein Set ist, kombiniert Automerge in diesem Fall die Werte, die auf dem Server vorhanden sind, mit den Werten in der eingehenden Anforderung und entfernt alle Duplikate. Ähnlich besteht ein Konflikt im Feld „points“, in dem ein doppelter Wert und ein neuer Wert vorhanden ist. Da der zugrunde liegende Datentyp hier jedoch eine Liste ist, hängt Automerge einfach alle Werte in der eingehenden Anforderung an das Ende der bereits auf dem Server vorhandenen Werte an. Das resultierende zusammengeführte Abbild, das auf dem Server gespeichert wird, sieht wie folgt aus:

{ "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 }

Nehmen wir an, dass das Element, das auf dem Server gespeichert ist, in Version 8 wie folgt aussieht.

{ "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 }

Eine eingehende Anforderung versucht jedoch, das Element mit dem folgenden Abbild zu aktualisieren, erneut mit einer fehlenden Versionsübereinstimmung:

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

In diesem Szenario können wir sehen, dass die Felder, die bereits auf dem Server vorhanden sind, fehlen (interests, points, jersey). Zusätzlich wird der Wert für „ppg“ innerhalb der Map „stats“ bearbeitet, ein neuer Wert „rpg“ wird hinzugefügt, und „apg“ wird weggelassen. Automerge behält die Felder, die weggelassen wurden (Hinweis: Wenn Felder entfernt werden sollen, muss die Anforderung erneut mit der übereinstimmenden Version versucht werden), damit sie nicht verloren gehen. Es wendet auch die gleichen Regeln auf Felder in Maps an, weshalb die Änderung zu „ppg“ abgelehnt wird, während „apg“ erhalten bleibt und „rpg“, ein neues Feld, hinzugefügt wird. Das resultierende Element, das auf dem Server gespeichert wird, sieht jetzt wie folgt aus:

{ "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 }

Lambdas

Es stehen mehrere Strategien zur Lambda-Auflösung zur Auswahl:

  • RESOLVE: Ersetzt den vorhandenen Artikel durch einen neuen Artikel, der als Antwort-Payload geliefert wird. Sie können denselben Vorgang nur für ein einzelnes Element gleichzeitig wiederholen. Derzeit unterstützt für DynamoDB PutItem und UpdateItem.

  • REJECT: Lehnt die Mutation ab und gibt einen Fehler mit dem vorhandenen Element in der GraphQL-Antwort zurück. Derzeit unterstützt für DynamoDB PutItem, UpdateItem und DeleteItem.

  • REMOVE: Entfernt das vorhandene Element. Derzeit unterstützt für DynamoDB DeleteItem.

Die Lambda-Aufrufanforderung

Der AWS AppSync DynamoDB-Resolver ruft die Lambda-Funktion auf, die in der angegeben ist. LambdaConflictHandlerArn Er verwendet die gleiche service-role-arn-Konfiguration für die Datenquelle. Die Nutzlast des Aufrufs hat die folgende Struktur:

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

Die Felder sind wie folgt definiert:

newItem

Das Vorschauelement, wenn die Mutation erfolgreich war.

existingItem

Das Element, das sich derzeit in der DynamoDB-Tabelle befindet.

arguments

Die Argumente aus der GraphQL-Mutation.

resolver

Informationen über den Resolver. AWS AppSync

identity

Informationen über den Aufrufer. Dieses Feld ist auf Null gesetzt, wenn der Zugriff mit einem API Schlüssel erfolgt.

Beispiel-Nutzlast:

{ "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" } }

Die Lambda-Aufrufantwort

Für PutItem- und UpdateItem-Konfliktlösung

Die Mutation RESOLVE. Die Antwort muss das folgende Format haben.

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

Das item-Feld stellt ein Objekt dar, das verwendet wird, um das vorhandene Element in der zugrunde liegenden Datenquelle zu ersetzen. Der Primärschlüssel und die Synchronisierungsmetadaten werden ignoriert, wenn sie in item enthalten sind.

Die Mutation REJECT. Die Antwort muss das folgende Format haben.

{ "action": "REJECT" }

Für DeleteItem-Konfliktlösung

Das Element REMOVE Die Antwort muss das folgende Format haben.

{ "action": "REMOVE" }

Die Mutation REJECT. Die Antwort muss das folgende Format haben.

{ "action": "REJECT" }

Die folgende Lambda-Beispielfunktion überprüft, wer den Aufruf tätigt, sowie den Namen des Resolvers. Wenn es von REMOVE dem Objekt für den DeletePost Resolver oder RESOLVE dem Konflikt mit einem neuen Element für Update/Put-Resolver erstellt wurdejeffTheAdmin. Wenn nicht, ist die Mutation 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; }

Fehler

Im Folgenden finden Sie eine Liste möglicher Fehler, die während eines Konfliktlösungsprozesses auftreten können:

ConflictUnhandled

Die Konflikterkennung findet eine fehlende Versionsübereinstimmung, und der Conflict Handler lehnt die Mutation ab.

Beispiel: Konfliktlösung mit einem Conflict Handler für optimistische Parallelität. Oder Lambda-Conflict Handler, zurückgegeben mit REJECT.

ConflictError

Beim Versuch, einen Konflikt zu lösen, tritt ein interner Fehler auf.

Beispiel: Lambda-Conflict Handler gibt eine nicht wohlgeformte Antwort zurück. Oder: Lambda-Conflict Handler kann nicht aufgerufen werden, da die bereitgestellte Ressource LambdaConflictHandlerArn nicht gefunden wurde.

MaxConflicts

Für die Konfliktlösung wurde die maximale Zahl der Wiederholungsversuche erreicht.

Beispiel: Zu viele gleichzeitige Anfragen für dasselbe Objekt. Bevor der Konflikt gelöst wird, wird das Objekt von einem anderen Client auf eine neue Version aktualisiert.

BadRequest

Client versucht, Metadatenfelder (_version, _ttl, _lastChangedAt, _deleted) zu aktualisieren.

Beispiel: Der Client versucht, ein Objekt mit _version einer Aktualisierungsmutation zu aktualisieren.

DeltaSyncWriteError

Fehler beim Schreiben des Delta-Synchronisierungsdatensatzes.

Beispiel: Mutation erfolgreich, aber ein interner Fehler trat auf, als versucht wurde, in die Delta-Synchronisationstabelle zu schreiben.

InternalFailure

Es ist ein interner Fehler aufgetreten.

UnsupportedOperation

Der Vorgang 'wird nicht unterstütztX'. Die Versionierung von Datenquellen unterstützt nur die folgenden Operationen (TransactGetItems,,, Scan PutItemBatchGetItem, Query,,, GetItem DeleteItemUpdateItem, Sync).

Beispiel: Verwendung bestimmter Transaktions- und Batch-Operationen mit aktivierter Konflikterkennung/-lösung. Diese Operationen werden derzeit nicht unterstützt.

CloudWatch Logs

Wenn CloudWatch Logs aktiviert und die Protokollierungseinstellungen auf Protokolle auf Feldebene enabled und Protokollebene für die Protokolle auf Feldebene auf gesetzt sindALL, AWS AppSync werden Informationen zur Konflikterkennung und -lösung an die Protokollgruppe ausgegeben. AWS AppSync API Informationen zum Format der Protokollmeldungen finden Sie in der Dokumentation zu Konflikterkennung und Synchronisierungsprotokollierung.