Modelos de coherencia de DAX y DynamoDB - Amazon DynamoDB

Modelos de coherencia de DAX y DynamoDB

Amazon DynamoDB Accelerator (DAX) es un servicio de almacenamiento en caché de escritura indirecta (write-through), diseñado para simplificar el proceso de agregar una caché a las tablas de DynamoDB. Dado que DAX funciona por separado de DynamoDB, es importante comprender los modelos de consistencia de DAX y de DynamoDB para asegurarse de que sus aplicaciones se comporten como se espera de ellas.

En muchos casos de uso, la forma de usar DAX en la aplicación afecta a la consistencia de datos en el clúster de DAX, así como a la consistencia de datos entre DAX y DynamoDB.

Coherencia entre los nodos de los clústeres de DAX

Para dotar de alta disponibilidad a la aplicación, recomendamos aprovisionar el clúster de DAX con al menos tres nodos. A continuación, coloque los nodos en varias zonas de disponibilidad de una región.

Mientras el clúster de DAX se está ejecutando, replica los datos entre todos los nodos del clúster (siempre y cuando haya aprovisionado más de un nodo). Tomemos una aplicación que realiza correctamente una operación UpdateItem mediante DAX. Esta acción hace que la caché de elementos del nodo principal se modifique con el nuevo valor. A continuación, este valor se replica en todos los demás nodos del clúster. Esta replicación presenta consistencia final y suele tardar menos de un segundo en llevarse a cabo.

En esta situación, es posible que dos clientes lean la misma clave del mismo clúster de DAX pero reciban valores distintos, según a qué nodo haya obtenido acceso cada cliente. Todos los nodos serán consistentes cuando la actualización haya terminado de replicarse en todos los nodos del clúster. (Este comportamiento se asemeja a la naturaleza eventualmente consistente de DynamoDB).

Si va a crear una aplicación que utiliza DAX, dicha aplicación debe diseñarse de forma que tolere los datos de consistencia final.

Comportamiento de la caché de elementos de DAX

Cada clúster de DAX presenta dos memorias caché diferentes, una caché de elemento y una caché de consulta. Para obtener más información, consulte DAX: cómo funciona.

En esta sección se abordan las implicaciones para la consistencia de la lectura y la escritura en la caché de elemento de DAX.

Coherencia de lectura

De forma predeterminada, con DynamoDB la operación GetItem lleva a cabo una lectura eventualmente consistente. Suponga que utiliza UpdateItem con el cliente de DynamoDB. Si inmediatamente después intenta leer el mismo elemento, es posible que los datos aparezcan igual que antes de la actualización. Esto se debe al retraso de propagación entre todas las ubicaciones de almacenamiento de DynamoDB. Normalmente, se logra la coherencia en cuestión de segundos. Si reintenta la lectura, probablemente verá el elemento actualizado.

Cuando utiliza GetItem con el cliente de DAX, la operación (en este caso, una lectura eventualmente consistente) se lleva a cabo como se muestra a continuación.

Diagrama de flujo de trabajo que muestra los pasos con números para actualizar un elemento.
  1. El cliente de DAX emite una solicitud GetItem. DAX intenta leer el elemento solicitado en la caché de elemento. Si el elemento está presente en la caché (acierto de caché), DAX se lo devuelve a la aplicación.

  2. Si el elemento no está disponible (error de caché), DAX realiza una operación GetItem de consistencia final en DynamoDB.

  3. DynamoDB devuelve el elemento solicitado y DAX lo almacena en la caché de elemento.

  4. DAX devuelve el elemento a la aplicación.

  5. (No se muestra) Si el clúster de DAX contiene más de un nodo, el elemento se replica en todos los demás nodos del clúster.

El elemento permanecerá en la caché de elemento de DAX, sujeto a la configuración del período de vida (TTL) y el algoritmo de elementos menos usados recientemente (LRU) de la caché. Para obtener más información, consulte DAX: cómo funciona.

Sin embargo, durante este periodo, DAX no vuelve a leer el elemento en DynamoDB. Si otra persona actualiza el elemento mediante un cliente de DynamoDB eludiendo por completo a DAX, entonces una solicitud GetItem realizada con el cliente de DAX dará un resultado distinto que si esa misma solicitud GetItem se lleva a cabo mediante el cliente de DynamoDB. Si esto sucede, DAX y DynamoDB contendrán valores inconsistentes para la misma clave hasta que venza el TTL del elemento de DAX.

Si una aplicación modifica los datos de una tabla de DynamoDB subyacente eludiendo DAX, la aplicación tendrá que prever y tolerar las inconsistencias de datos que surjan.

nota

Además de GetItem, el cliente de DAX también admite solicitudes BatchGetItem. En esencia, BatchGetItem es un encapsulador que contiene una o varias solicitudes GetItem, de modo que DAX trata cada una de ellas como una operación GetItem individual.

Coherencia de escritura

DAX es una caché de escritura indirecta (write-through). Esto simplifica el proceso de mantener la consistencia entre la caché de elemento de DAX y las tablas de DynamoDB subyacentes.

El cliente de DAX admite las mismas operaciones de API de escritura que DynamoDB (PutItem, UpdateItem, DeleteItem, BatchWriteItem y TransactWriteItems). Cuando se utilizan estas operaciones con el cliente de DAX, los elementos se modifican tanto en DAX como en DynamoDB. DAX actualizará los elementos en la caché de elemento, independientemente del valor de TTL que tengan.

Por ejemplo, supongamos que emite una solicitud GetItem del cliente de DAX para leer un elemento de la tabla ProductCatalog. (La clave de partición es Id y no hay clave de ordenación). Recupera el elemento cuyo Id es 101. El valor QuantityOnHand para ese elemento es 42. DAX almacena el elemento en su caché de elemento con un TTL concreto. En este ejemplo, vamos a suponer que el TTL es de diez minutos. A continuación, 3 minutos más tarde, otra aplicación utiliza el cliente de DAX para actualizar el mismo elemento, de forma que su valor de QuantityOnHand pasa a ser 41. Suponiendo que el elemento no se vuelva a actualizar, todas las lecturas posteriores del mismo elemento que tengan lugar en los próximos diez minutos devolverán el valor de QuantityOnHand almacenado en caché (41).

Cómo procesa DAX las escrituras

DAX está pensado para aplicaciones que requieren lecturas de alto rendimiento. Como caché de escritura indirecta, DAX pasa las escrituras a DynamoDB de forma sincrónica y, a continuación, replica de forma automática y asíncrona las actualizaciones resultantes a la caché de elemento en todos los nodos del clúster. No es necesario administrar la lógica de invalidación de caché, porque DAX lo hace automáticamente.

DAX admite las siguientes operaciones de escritura: PutItem, UpdateItem, DeleteItem, BatchWriteItem y TransactWriteItems.

Cuando se envía una solicitud PutItem, UpdateItem, DeleteItem o BatchWriteItem a DAX, ocurre lo siguiente:

  • DAX envía la solicitud a DynamoDB.

  • DynamoDB responde a DAX para confirmar que la escritura se ha llevado a cabo correctamente.

  • DAX escribe el elemento en su caché de elemento.

  • DAX indica al solicitante que la escritura se ha realizado correctamente.

Cuando se envía una solicitud TransactWriteItems a DAX, ocurre lo siguiente:

  • DAX envía la solicitud a DynamoDB.

  • DynamoDB responde a DAX para confirmar que la transacción se ha completado.

  • DAX indica al solicitante que la escritura se ha realizado correctamente.

  • En segundo plano, DAX realiza una solicitud TransactGetItems para que cada elemento en la solicitud TransactWriteItems almacene el elemento en la caché de elemento. TransactGetItems se utiliza para garantizar un aislamiento serializable.

Si una escritura no se puede realizar en DynamoDB por cualquier motivo, incluida la limitación controlada, el elemento no se almacenará en caché en DAX. Se devuelve al solicitante la excepción correspondiente al error. De este modo se garantiza que los datos no se escriban en la caché de DAX a no ser que antes se hayan escrito correctamente en DynamoDB.

nota

Cada escritura en DAX altera el estado de la caché de elemento. Sin embargo, las escrituras en ella no afectan a la caché de consultas. (La caché de elemento y la caché de consulta de DAX se utilizan con fines diferentes y operan de manera independiente una de la otra).

Comportamiento de la caché de consulta de DAX

DAX almacena en su caché de consulta los resultados de las solicitudes Query y Scan. Sin embargo, estos resultados no afectan en absoluto a la caché de elementos. Cuando la aplicación emite una solicitud Query o Scan con DAX, el conjunto de resultados se guarda en la caché de consulta, no en la de elemento. No se puede "calentar" la caché de elementos antes de llevar a cabo una operación Scan, ya que la caché de elementos y la caché de consultas son entidades independientes.

Coherencia de consulta-actualización-consulta

Las actualizaciones de la caché de elementos o de la tabla de DynamoDB subyacente no invalidan ni modifican los resultados almacenados en la caché de consultas.

A modo de ejemplo, considere la siguiente situación. Una aplicación está trabajando con la tabla DocumentRevisions, que tiene una clave de partición DocId y una clave de ordenación RevisionNumber.

  1. Un cliente emite una Query para DocId 101, para todos los elementos con RevisionNumber mayor o igual que 5. DAX almacena el conjunto de resultados en la caché de consulta y se lo devuelve al usuario.

  2. El cliente emite una solicitud PutItem para DocId 101 con un valor de RevisionNumber de 20.

  3. El cliente emite la misma operación Query que se describe en el paso 1 (DocId 101 y RevisionNumber >= 5).

En este caso, el conjunto de resultados almacenado en caché correspondiente a la operación Query emitida en el paso 3 es idéntico al que se almacenó en caché en el paso 1. El motivo es que DAX no invalida los conjuntos de resultados de Query o Scan cuando se actualizan elementos individuales. La operación PutItem del paso 2 solo se refleja en la caché de consulta de DAX cuando vence el TTL de la Query.

La aplicación debe tener en cuenta el valor de TTL de la caché de consultas y el tiempo durante el cual la aplicación puede tolerar resultados inconsistentes entre las cachés de consultas y de elementos.

Lecturas altamente coherentes y transaccionales

Para realizar una solicitud de lectura fuertemente consistente GetItem, BatchGetItem, Query o Scan, debe establecer el parámetro ConsistentRead en true (verdadero). DAX pasa las solicitudes de lectura fuertemente consistentes a DynamoDB. Cuando recibe una respuesta de DynamoDB, DAX devuelve los resultados al cliente, pero no almacena en caché los resultados. DAX no puede atender por sí solo a las lecturas fuertemente consistentes, porque no está estrechamente acoplado a DynamoDB. Debido a esto, todas las lecturas posteriores de DAX tendrían que ser lecturas eventualmente consistentes. Y las lecturas fuertemente consistentes posteriores tendrían que pasarse a través de DynamoDB.

DAX administra las solicitudes TransactGetItems de la misma manera que administra las lecturas fuertemente consistentes. DAX pasa todas las solicitudes TransactGetItems a DynamoDB. Cuando recibe una respuesta de DynamoDB, DAX devuelve los resultados al cliente, pero no almacena en caché los resultados.

Almacenamiento en caché negativo

DAX admite las entradas de caché negativas, tanto en la caché de elemento como en la caché de consulta. Una entrada de caché negativa se produce cuando DAX no encuentra los elementos solicitados en una tabla de DynamoDB subyacente. En lugar de generar un error, DAX almacena en caché un resultado vacío y se lo devuelve al usuario.

Por ejemplo, supongamos que una aplicación envía una solicitud GetItem a un clúster de DAX, pero que no hay ningún elemento coincidente en la caché de elemento de DAX. Esto hace que DAX lea el elemento correspondiente en la tabla de DynamoDB subyacente. Si el elemento tampoco está presente en DynamoDB, entonces DAX almacena un elemento vacío en su caché de elemento y, a continuación, devuelve este elemento vacío a la aplicación. Ahora, supongamos que la aplicación envía otra solicitud GetItem para obtener el mismo elemento. DAX encontrará el elemento vacío en la caché de elemento y se lo devolverá de inmediato a la aplicación. No consultará DynamoDB en absoluto.

Una entrada de caché negativa se conserva en la caché de elemento de DAX hasta que vence el TTL del elemento, se invoca su LRU o el elemento se modifica mediante PutItem, UpdateItem o DeleteItem.

La caché de consulta de DAX administra los resultados de caché negativos de forma similar. Si una aplicación realiza una operación Query o Scan y la caché de consulta de DAX no contiene un resultado en caché, DAX envía la solicitud a DynamoDB. Si no hay elementos coincidentes en el conjunto de resultados, DAX almacena un conjunto de resultados vacío en la caché de consulta y devuelve el conjunto de resultados vacío a la aplicación. Las solicitudes Query o Scan producen el mismo conjunto de resultados (vacío), hasta que vence su TTL.

Estrategias de escritura

El comportamiento de escritura indirecta (write-through) de DAX es adecuado para muchos patrones de aplicaciones. No obstante, existen algunos patrones de aplicación en los que este modelo de escritura indirecta podría no ser el adecuado.

En las aplicaciones que son sensibles a la latencia, la escritura a través de DAX genera un salto de red más. Por eso, la escritura en DAX es algo más lenta que si se realiza directamente en DynamoDB. Si la aplicación es sensible a la latencia de escritura, puede reducir esa latencia escribiendo directamente en DynamoDB. Para obtener más información, consulte Escritura directa.

Para las aplicaciones con gran intensidad de escritura (como las que llevan a cabo la carga de datos masivos), no es deseable escribir todos los datos a través de DAX, puesto que la aplicación solo leerá un pequeño porcentaje de ellos. Cuando se escriben grandes cantidades de datos a través de DAX, debe invocar el algoritmo LRU con el fin de dejar espacio en la caché para los nuevos elementos que hay que leer. Esto reduce la eficacia de DAX como caché de lectura.

Al escribir un elemento en DAX, el estado de la caché de elemento se modifica para dar cabida al nuevo elemento. (Por ejemplo, DAX podría tener que expulsar datos antiguos de la caché de elemento con el fin de dejar espacio para el nuevo elemento). El nuevo elemento permanecerá en la caché de elementos, de conformidad con el algoritmo LRU y el ajuste de TTL de esta última. Mientras el elemento persista en la caché de elemento, DAX no volverá a leerlo en DynamoDB.

Escritura indirecta

La caché de elemento de DAX implementa una política de escritura indirecta. Para obtener más información, consulte Cómo procesa DAX las escrituras.

Cuando se escribe un elemento, DAX se asegura de que el elemento almacenado en caché esté sincronizado con el elemento presente en DynamoDB. Esto resulta útil para las aplicaciones que tienen que volver a leer un elemento inmediatamente después de escribirlo. Sin embargo, si otras aplicaciones escriben directamente en una tabla de DynamoDB, el elemento contenido en la caché de elemento de DAX dejará de estar sincronizada con DynamoDB.

Por ejemplo, tomemos dos usuarios (Alice y Bob) que están utilizando la tabla ProductCatalog. Alice obtiene acceso a la tabla mediante DAX, pero Bob elude DAX y obtiene acceso a la tabla directamente en DynamoDB.

Diagrama de flujo de trabajo que muestra los pasos con números de cómo los usuarios Alice y Bob acceden a la tabla con DAX y DynamoDB.
  1. Alice actualiza un elemento de la tabla ProductCatalog. DAX reenvía la solicitud a DynamoDB y la actualización se lleva a cabo correctamente. Después, DAX escribe el elemento en su caché de elemento y devuelve una respuesta correcta a Alice. A partir de ese momento, hasta que el elemento se expulse definitivamente de la caché, cualquier usuario que lo lea en DAX ve el elemento con la actualización de Alice.

  2. Poco después, Bob actualiza el mismo elemento ProductCatalog en el que Alice ha escrito. Sin embargo, Bob actualiza el elemento directamente en DynamoDB. DAX no actualiza automáticamente su caché de elemento en respuesta a las actualizaciones a través de DynamoDB. Por tanto, los usuarios de DAX no verán la actualización de Bob.

  3. Alice vuelve a leer el elemento en DAX. Este elemento se encuentra en la caché del elemento, por lo que DAX se lo devuelve a Alice sin obtener acceso a la tabla de DynamoDB.

En este caso, Alice y Bob obtienen representaciones distintas del mismo elemento ProductCatalog. Esto continuará siendo así hasta que DAX expulse el elemento de la caché de elemento o hasta que otro usuario lo vuelva a actualizar mediante DAX.

Escritura directa

Si la aplicación tiene que escribir grandes cantidades de datos (por ejemplo, en una carga de datos masivos), puede ser más lógico eludir DAX y escribir los datos directamente en DynamoDB. Esta estrategia de escritura directa (write-around) reduce la latencia. Sin embargo, la caché de elemento no se mantendrá sincronizada con los datos de DynamoDB.

Si decide utilizar una estrategia de escritura directa, recuerde que DAX rellena su caché de elemento cada vez que las aplicaciones utilizan el cliente de DAX para leer los datos. Esto puede ser beneficioso en algunos casos, ya que garantiza que solo se almacenen en caché los datos que se leen con mayor frecuencia (en lugar de los datos que se escriben con mayor frecuencia).

Suponga, por ejemplo, que un usuario (Charlie) desea trabajar con una tabla diferente, la tabla GameScores, mediante DAX. La clave de partición de GameScores es UserId, por lo que todas las puntuaciones de Charlie tendrían el mismo UserId.

Diagrama de flujo de trabajo que muestra los pasos con números de cómo Charlie trabaja con una tabla de DynamoDB mediante DAX.
  1. Charlie desea recuperar todas sus puntuaciones, por lo que envía una Query a DAX. Suponiendo que esta consulta no se haya emitido antes, DAX se la reenvía a DynamoDB para procesarla. Almacena los resultados en la caché de consulta de DAX y, por último, devuelve los resultados a Charlie. El conjunto de resultados seguirá disponible en la caché de consultas hasta que se expulse.

  2. Ahora, supongamos que Charlie juega a Blasters Meteor y logra una puntuación máxima. Charlie envía una solicitud UpdateItem a DynamoDB para modificar un elemento en la tabla GameScores.

  3. Por último, Charlie decide volver a ejecutar la operación Query anterior para recuperar todos sus datos de GameScores. Charlie no ve su puntuación máxima en Meteor Blasters en los resultados. Esto se debe a que los resultados de la consulta proceden de la caché de consultas, no de la caché de elementos. Las dos cachés son independientes entre sí, de modo que un cambio en una de ellas no afecta a la otra.

DAX no actualiza los conjuntos de resultados de la caché de consulta con los datos más recientes de DynamoDB. Cada conjunto de resultados de la caché de consultas estaba actualizado en el momento de ejecutar la operación Query o Scan. Por lo tanto, los resultados que Charlie obtiene con la operación Query no reflejan su operación PutItem. Esto seguirá siendo así hasta que DAX expulse el conjunto de resultados de la caché de consulta.