Detección y resolución de conflictos en AWS AppSync - AWS AppSync

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Detección y resolución de conflictos en AWS AppSync

Cuando se producen escrituras simultáneas AWS AppSync, puede configurar estrategias de detección y resolución de conflictos para gestionar las actualizaciones de forma adecuada. La detección de conflictos determina si la mutación está en conflicto con el elemento real escrito en el origen de datos. La detección de conflictos se activa al establecer el valor SyncConfig del conflictDetection campo en. VERSION

La resolución de conflictos es la acción que se realiza en caso de que se detecte un conflicto. Esto se determina configurando el campo Controlador de conflictos en. SyncConfig Existen tres estrategias de resolución de conflictos:

  • OPTIMISTIC_CONCURRENCY

  • AUTOMERGE

  • LAMBDA

Las versiones se incrementan automáticamente AWS AppSync durante las operaciones de escritura y no deben modificarlas los clientes ni fuera de un solucionador configurado con una fuente de datos con versiones habilitadas. Si se hace, se modificará el comportamiento de consistencia del sistema y podría dar lugar a la pérdida de datos.

Simultaneidad optimista

La simultaneidad optimista es una estrategia de resolución de conflictos que AWS AppSync proporciona fuentes de datos versionadas. Si el solucionador de conflictos se ha establecido en Optimistic Concurrency (Simultaneidad optimista) y se detecta que una mutación entrante tiene una versión distinta de la versión real del objeto, el controlador de conflictos simplemente rechazará la solicitud entrante. Dentro de la respuesta de GraphQL, se proporcionará el elemento existente en el servidor que tiene la última versión. A continuación, se espera que el cliente gestione este conflicto localmente y vuelva a intentar la mutación con la versión actualizada del elemento.

Automerges

Automerge (Combinar automáticamente) proporciona a los desarrolladores una manera fácil de configurar una estrategia de resolución de conflictos sin tener que escribir la lógica del lado del cliente con el fin de combinar manualmente los conflictos que no se han podido gestionar mediante otras estrategias. Automerge (Combinar automáticamente) respeta un estricto conjunto de reglas al combinar los datos para resolver conflictos. Los principios de Automerge (Combinar automáticamente) se refieren al tipo de datos subyacente del campo de GraphQL. Se definen de la siguiente manera:

  • Conflicto en un campo escalar: escalar de GraphQL o cualquier campo que no sea una colección (es decir, List, Set, Map). Rechazar el valor entrante para el campo escalar y seleccionar el valor existente en el servidor.

  • Conflicto en una lista: el tipo de GraphQL y el tipo de la base de datos son listas. Concatenar la lista entrante con la lista existente en el servidor. Los valores de lista de la mutación entrante se anexarán al final de la lista en el servidor. Se conservarán los valores duplicados.

  • Conflicto en un conjunto: el tipo de GraphQL es una lista y el tipo de la base de datos es un conjunto. Aplicar una unión de conjuntos utilizando el conjunto entrante y el conjunto existente en el servidor. Se respetan las propiedades de un conjunto, lo que significa que no hay entradas duplicadas.

  • Cuando una mutación entrante agrega un nuevo campo al elemento o se realiza en un campo con el valor null, se combinan con el elemento existente.

  • Conflicto en un mapa: cuando el tipo de datos subyacente en la base de datos es un mapa (es decir, un documento de clave-valor), se aplican las reglas anteriores mientras se analiza y procesa cada propiedad del mapa.

Automerge (Combinar automáticamente) se ha diseñado para detectar, combinar y reintentar automáticamente las solicitudes con una versión actualizada, lo que evita al cliente la necesidad de combinar manualmente los datos en conflicto.

Para mostrar un ejemplo de cómo Automerge (Combinar automáticamente) gestiona un conflicto en un tipo escalar. Usaremos el siguiente registro como punto de partida.

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

Ahora, una mutación entrante podría estar intentando actualizar el elemento, pero con una versión anterior, ya que el cliente aún no se ha sincronizado con el servidor. Su aspecto es el siguiente:

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

Observe la versión obsoleta de 2 en la solicitud entrante. Durante este flujo, Automerge (Combinar automáticamente) combinará los datos rechazando la actualización de campo 'jersey' a '55' y mantendrá el valor en '5', lo que dará lugar a la siguiente imagen del elemento que se guarda en el servidor.

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

Dado el estado del elemento mostrado anteriormente en la versión 5, ahora suponga que una mutación entrante intenta mutar el elemento con la siguiente imagen:

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

Hay tres puntos de interés en la mutación entrante. El nombre, un escalar, se ha cambiado, pero se han agregado dos nuevos campos “interests” (intereses), que es un conjunto, y “points” (puntos), que es una Lista. En este escenario, se detectará un conflicto debido a la discrepancia de versión. Automerge (Combinar automáticamente) respeta sus propiedades, rechaza el cambio de nombre debido a que es un escalar y agrega los campos que no presentan conflictos. Esto hace que el elemento que se guarda en el servidor aparezca de la siguiente manera.

{ "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 la imagen actualizada del elemento con la versión 6, ahora suponga que una mutación entrante (con otra discrepancia de versión) intenta transformar el elemento a lo siguiente:

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

Aquí observamos que el campo entrante para “interests” (intereses) tiene un valor duplicado existente en el servidor y dos nuevos valores. En este caso, dado que el tipo de datos subyacente es un conjunto, Automerge (Combinar automáticamente) combinará los valores existentes en el servidor con los de la solicitud entrante y eliminará los duplicados. Del mismo modo, hay un conflicto en el campo “points” (puntos), donde hay un valor duplicado y un nuevo valor. Sin embargo, dado que el tipo de datos subyacente aquí es una lista, Automerge (Combinar automáticamente) simplemente agregará todos los valores de la solicitud entrante al final de los valores que ya existen en el servidor. La imagen combinada resultante que se almacenaría en el servidor tendría el siguiente aspecto:

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

Ahora, supongamos que el elemento almacenado en el servidor aparece de la siguiente manera en la versión 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 }

Sin embargo, una solicitud entrante intenta actualizar el elemento con la siguiente imagen, una vez más con una discrepancia de versión:

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

En este escenario, observamos que faltan los campos que ya existen en el servidor (interests, points, jersey). Además, se está editando el valor de “ppg” dentro del mapa “stats”, se añade un nuevo valor “rpg” y se omite “apg”. Automerge (Combinar automáticamente) conserva los campos que se han omitido (nota: si los campos están destinados a ser eliminados, entonces la solicitud se debe volver a intentar con la versión coincidente), por lo que no se perderán. También aplicará las mismas reglas a los campos dentro de los mapas y, por lo tanto, el cambio a “ppg” se rechazará, mientras que se conservará “apg” y se añadirá “rpg”, que es un nuevo campo. Ahora, el elemento resultante almacenado en el servidor tendrá este aspecto:

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

Hay varias estrategias de resolución de Lambda entre las que elegir:

  • RESOLVE: Sustituye el artículo existente por uno nuevo suministrado como carga útil de respuesta. Solo puede volver a intentar la misma operación con un único elemento a la vez. Actualmente, es compatible con PutItem y UpdateItem de DynamoDB.

  • REJECT: Rechaza la mutación y devuelve un error con el elemento existente en la respuesta de GraphQL. Actualmente es compatible con PutItem, UpdateItem y DeleteItem de DynamoDB.

  • REMOVE: Elimina el elemento existente. Actualmente es compatible con DeleteItem de DynamoDB.

La solicitud de invocación Lambda

El AWS AppSync solucionador de DynamoDB invoca la función Lambda especificada en. LambdaConflictHandlerArn Se utiliza el mismo service-role-arn configurado en el origen de datos. La carga de la invocación tiene la siguiente estructura:

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

Los campos se definen de la siguiente manera:

newItem

El elemento de vista previa, si la mutación se ha realizado correctamente.

existingItem

El elemento residía actualmente en la tabla de DynamoDB.

arguments

Los argumentos de la mutación de GraphQL.

resolver

Información sobre el solucionador. AWS AppSync

identity

Información sobre el intermediario. Este campo se establece en nulo si se accede con una API clave.

Ejemplo de carga:

{ "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 respuesta de invocación Lambda

Para la resolución de conflictos de PutItem y UpdateItem

RESOLVE para la mutación. La respuesta debe tener el siguiente formato.

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

El campo item representa un objeto que se utilizará para sustituir el elemento existente en el origen de datos subyacente. La clave principal y los metadatos de sincronización se pasarán por alto si se han incluido en item.

REJECT para la mutación. La respuesta debe tener el siguiente formato.

{ "action": "REJECT" }

Para resolver los conflictos de DeleteItem

Se ejecuta REMOVE para eliminar el elemento. La respuesta debe tener el siguiente formato.

{ "action": "REMOVE" }

REJECT para la mutación. La respuesta debe tener el siguiente formato.

{ "action": "REJECT" }

El ejemplo de función de Lambda siguiente comprueba quién realiza la llamada y el nombre del solucionador. Si está creado por REMOVE el objeto que se jeffTheAdmin va a DeletePost resolver o el conflicto con un elemento nuevo es para RESOLVE los solucionadores de Update/Put. Si no, la mutación es 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; }

Errores

A continuación se muestra una lista de los posibles errores que pueden producirse durante un proceso de resolución de conflictos:

ConflictUnhandled

La detección de conflictos encuentra una disconformidad de versión y el controlador de conflictos rechaza la mutación.

Ejemplo: Resolución de conflictos con un controlador de conflictos Optimistic Concurrency (Simultaneidad optimista). O bien, se devuelve el controlador de conflictos de Lambda con REJECT.

ConflictError

Se produce un error interno al intentar resolver un conflicto.

Ejemplo: El controlador de conflictos de Lambda devolvió una respuesta mal formada. O bien, no se puede invocar el controlador de conflictos de Lambda porque el recurso suministrado LambdaConflictHandlerArn no se encuentra.

MaxConflicts

Se alcanzó el número máximo de reintentos para la resolución de conflictos.

Ejemplo: Demasiadas solicitudes simultáneas en el mismo objeto. Antes de resolver el conflicto, otro cliente actualiza el objeto a una nueva versión.

BadRequest

El cliente intenta actualizar los campos de metadatos (_version, _ttl, _lastChangedAt, _deleted).

Ejemplo: el cliente intenta actualizar _version un objeto con una mutación de actualización.

DeltaSyncWriteError

Error al escribir el registro de Delta Sync.

Ejemplo: La mutación se ha realizado correctamente, pero se ha producido un error interno al intentar escribir en la tabla de Delta Sync.

InternalFailure

Se ha producido un error interno.

UnsupportedOperation

Operación no admitida 'X'. El control de versiones de fuentes de datos solo admite las siguientes operaciones (TransactGetItems,, Escanear PutItemBatchGetItem, Consultar,,, GetItem DeleteItemUpdateItem, Sincronizar).

Ejemplo: usar determinadas operaciones de transacción y lote con la detección y resolución de conflictos habilitadas. Estas operaciones no se admiten actualmente.

CloudWatch Registros

Si AWS AppSync API ha activado CloudWatch los registros con la configuración de registro establecida en Registros a nivel de campo enabled y a nivel de registro para los registros a nivel de campo establecida enALL, AWS AppSync emitirá información de detección y resolución de conflictos al grupo de registros. Para obtener información sobre el formato de los mensajes de registro, consulte la documentación de detección de conflictos y registro de sincronización.