Modelos de consistência do DAX e do DynamoDB
O Amazon DynamoDB Accelerator (DAX) é um serviço de armazenamento em cache por gravação simultânea (write-through) projetado para simplificar o processo de adição de um cache a tabelas do DynamoDB. Como o DAX opera separadamente do DynamoDB, é importante compreender os modelos de consistência do DAX e do DynamoDB para garantir que as aplicações se comportem conforme esperado.
Em muitos casos de uso, a maneira como a aplicação usa o DAX afeta a consistência dos dados dentro do cluster do DAX e a consistência dos dados entre o DAX e o DynamoDB.
Tópicos
Consistência entre nós de cluster do DAX
Para obter alta disponibilidade para sua aplicação, recomendamos provisionar o cluster do DAX com pelo menos três nós. Além disso, coloque esses nós em várias zonas de disponibilidade dentro de uma região.
Quando o cluster do DAX estiver em execução, ele replicará os dados entre todos os nós do cluster (supondo que você tenha provisionado mais de um nó). Considere uma aplicação que realiza uma operação UpdateItem
bem-sucedida usando o DAX. Essa ação faz com que o cache de itens no nó primário seja modificado com o novo valor. Esse valor é, então, replicado em todos os outros nós no cluster. Essa replicação é eventualmente consistente e costuma demorar menos de um segundo para ser concluída.
Nesse cenário, é possível que dois clientes leiam a mesma chave do mesmo cluster do DAX, mas recebam valores diferentes, dependendo do nó que cada cliente acessou. Os nós estarão todos consistentes quando a atualização tiver sido totalmente replicada por todos os nós do cluster. (Esse comportamento é semelhante à natureza final consistente do DynamoDB.)
Se você estiver criando uma aplicação que usa o DAX, essa aplicação deverá ser projetada para tolerância a dados finais consistentes.
Comportamento do cache de itens do DAX
Cada cluster do DAX tem dois caches distintos: um cache de itens e um cache de consultas. Para ter mais informações, consulte DAX: como ele funciona.
Esta seção aborda as implicações de consistência da leitura e da gravação no cache de itens do DAX.
Consistência de leituras
Com o DynamoDB, por padrão, a operação GetItem
executa uma leitura final consistente. Suponha que você use UpdateItem
com o cliente do DynamoDB. Se você tentar ler o mesmo item imediatamente, poderá ver os dados no estado em que estavam antes da atualização. Isso acontece devido ao atraso da propagação em todos os locais de armazenamento do DynamoDB. A consistência normalmente é atingida em segundos. Portanto, se você repetir a leitura, provavelmente verá o item atualizado.
Ao usar GetItem
com o cliente do DAX, a operação (neste caso, uma leitura final consistente) ocorre conforme mostrado abaixo.
-
O cliente do DAX emite uma solicitação
GetItem
. O DAX tenta ler o item solicitado do cache de itens. Se o item estiver no cache (acerto no cache), o DAX o retornará à aplicação. -
Se o item não estiver disponível (erro de cache), o DAX realizará uma operação final consistente
GetItem
no DynamoDB. -
O DynamoDB retornará o item solicitado, e o DAX o armazenará no cache de itens.
-
O DAX retornará o item para a aplicação.
-
(Não mostrado) Se o cluster do DAX contiver mais de um nó, o item será replicado em todos os outros nós do cluster.
O item permanece no cache de itens do DAX, sujeito à configuração de vida útil (TTL) e ao algoritmo de Least Recently Used (LRU – Menos usado recentemente) do cache. Para ter mais informações, consulte DAX: como ele funciona.
No entanto, durante esse período, o DAX não lê novamente o item no DynamoDB. Se outra pessoa atualizar o item usando um cliente do DynamoDB, ignorando totalmente o DAX, uma solicitação GetItem
usando o cliente do DAX produzirá resultados diferentes da mesma solicitação GetItem
usando o cliente do DynamoDB. Nesse cenário, o DAX e o DynamoDB manterão valores inconsistentes para a mesma chave até que o TTL do item do DAX expire.
Se uma aplicação modificar dados em uma tabela subjacente do DynamoDB, ignorando o DAX, a aplicação precisará antecipar e tolerar as inconsistências de dados que possam surgir.
nota
Além do GetItem
, o cliente do DAX também oferece suporte a solicitações BatchGetItem
. BatchGetItem
é essencialmente um wrapper ao redor de uma ou mais solicitações GetItem
e, dessa forma, o DAX trata cada uma delas como uma operação GetItem
individual.
Consistência de gravações
O DAX é um cache write-through, o que simplifica o processo de manter o cache de itens do DAX consistente com as tabelas do DynamoDB subjacentes.
O cliente do DAX oferece suporte às mesmas operações da API de gravação que o DynamoDB (PutItem
, UpdateItem
, DeleteItem
, BatchWriteItem
e TransactWriteItems
). Quando você usa essas operações com o cliente do DAX, os itens são modificados tanto no DAX quanto no DynamoDB. O DAX atualizará os itens em seu cache de itens, independentemente do valor de TTL desses itens.
Por exemplo, suponha que você emita uma solicitação GetItem
do cliente do DAX para ler um item da tabela ProductCatalog
. (A chave de partição é Id
e não há uma chave de classificação). Você recupera o item cujo Id
é 101
. O valorQuantityOnHand
para esse item é 42
. O DAX armazena o item em seu cache de itens com um TTL específico. Para este exemplo, suponha que o TTL é de 10 minutos. Três minutos depois, outra aplicação usa o cliente do DAX para atualizar o mesmo item de forma que o valor de QuantityOnHand
agora é 41
. Supondo que o item não seja atualizado novamente, qualquer leitura subsequente do mesmo item durante os próximos dez minutos retornará o valor armazenado em cache a QuantityOnHand
(41
).
Como o DAX processa gravações
O DAX foi projetado para aplicações que exigem leituras de alta performance. Por ser um cache write-through, o DAX passa suas gravações para o DynamoDB de forma síncrona e, em seguida, replica assíncrona e automaticamente as atualizações resultantes para o cache de itens em todos os nós do cluster. Você não precisa gerenciar a lógica de invalidação do cache, pois o DAX lida com ela automaticamente.
O DAX oferece suporte às seguintes operações de gravação: PutItem
, UpdateItem
, DeleteItem
, BatchWriteItem
e TransactWriteItems
.
Quando você envia uma solicitação PutItem
, UpdateItem
, DeleteItem
ou BatchWriteItem
ao DAX:
-
O DAX envia a solicitação para o DynamoDB.
-
O DynamoDB responde ao DAX, confirmando que a gravação foi bem-sucedida.
-
O DAX grava o item em seu cache de itens.
-
O DAX retorna uma resposta de êxito ao solicitante.
Quando você envia uma solicitação TransactWriteItems
ao DAX:
-
O DAX envia a solicitação para o DynamoDB.
-
O DynamoDB responde ao DAX, confirmando que a transação foi concluída.
-
O DAX retorna uma resposta de êxito ao solicitante.
-
Em segundo plano, o DAX faz uma solicitação
TransactGetItems
para cada item na solicitaçãoTransactWriteItems
para armazenar o item no cache de itens.TransactGetItems
é usada para garantir o isolamento serializável.
Se houver falha em uma gravação no DynamoDB por qualquer motivo, inclusive controle de utilização, o item não será armazenado em cache no DAX. A exceção da falha é retornada ao solicitante. Isso garante que os dados sejam gravados no cache do DAX somente se forem gravados primeiro com êxito no DynamoDB.
nota
Toda gravação no DAX altera o estado do cache de itens. No entanto, as gravações no cache de itens não afeta o cache de consultas. (O cache de itens e o cache de consultas do DAX têm finalidades diferentes e operam de forma independente um do outro.)
Comportamento do cache de consultas DAX
O DAX armazena em cache os resultados das solicitações Query
e Scan
em seu cache de consultas. No entanto, esses resultados não afetam de forma alguma o cache de itens. Quando sua aplicação emite uma solicitação Query
ou Scan
com o DAX, o conjunto de resultados é salvo no cache de consultas, e não no cache de itens. Você não pode "aquecer" o cache de itens executando uma operação Scan
porque o cache de itens e o cache de consultas são entidades separadas.
Consistência de consulta-atualização-consulta
As atualizações no cache de itens, ou na tabela subjacente do DynamoDB, não invalidam nem modificam os resultados armazenados no cache de consultas.
Para ilustrar, considere o seguinte cenário. Um aplicativo está trabalhando com a tabela DocumentRevisions
, que tem DocId
como chave de partição e RevisionNumber
como chave de classificação.
-
Um cliente emite uma solicitação
Query
paraDocId
101
para todos os itens cujoRevisionNumber
é maior ou igual a5
. O DAX armazena o conjunto de resultados no cache de consultas e retorna esse conjunto ao usuário. -
O cliente emite uma solicitação de
PutItem
deDocId
101
com um valor deRevisionNumber
igual a20
. -
O cliente emite a mesma
Query
descrita na etapa 1 (DocId
101
eRevisionNumber
>=5
).
Neste cenário, o conjunto de resultados armazenado em cache para a Query
emitida na etapa 3 será idêntico ao conjunto de resultados que foi armazenado em cache na etapa 1. Isso acontece porque o DAX não invalida os conjuntos de resultados de Query
ou Scan
com base em atualizações em itens individuais. A operação PutItem
da etapa 2 é refletida no cache de consultas do DAX apenas quando o TTL de Query
expira.
Seu aplicativo deve considerar o valor do TTL do cache de consultas e por quanto tempo pode tolerar resultados inconsistentes entre o cache de consultas e o cache de itens.
Leituras altamente consistentes e transacionais
Para realizar uma solicitação de GetItem
, BatchGetItem
, Query
ou Scan
fortemente consistente, defina o parâmetro ConsistentRead
como true. O DAX transfere solicitações de leitura fortemente consistente ao DynamoDB. Ao receber uma resposta do DynamoDB, o DAX retorna os resultados ao cliente, mas não armazena os resultados em cache. O DAX não pode servir leituras fortemente consistentes por conta própria, pois ele não está firmemente acoplado ao DynamoDB. Por esse motivo, todas as leituras subsequentes do DAX precisariam ser leituras finais consistentes. E todas as leituras fortemente consistentes subsequentes precisariam ser passadas para o DynamoDB.
O DAX trata solicitações TransactGetItems
da mesma forma como trata leituras fortemente consistentes. O DAX passa todas as solicitações TransactGetItems
para o DynamoDB. Ao receber uma resposta do DynamoDB, o DAX retorna os resultados ao cliente, mas não armazena os resultados em cache.
Armazenamento em cache negativo
O DAX oferece suporte a entradas de cache negativas, tanto no cache de itens quanto no cache de consultas. Uma entrada de cache negativa ocorre quando o DAX não consegue localizar os itens solicitados em uma tabela subjacente do DynamoDB. Em vez de gerar um erro, o DAX armazena em cache um resultado vazio e retorna esse resultado ao usuário.
Por exemplo, suponha que uma aplicação envie uma solicitação GetItem
a um cluster do DAX e que não haja itens correspondentes no cache de itens do DAX. Isso faz com que o DAX leia o item correspondente na tabela subjacente do DynamoDB. Se o item não existir no DynamoDB, o DAX armazenará um item vazio no cache de itens e retornará esse item vazio para a aplicação. Agora, suponha que a aplicação envie outra solicitação GetItem
para o mesmo item. O DAX encontrará o item vazio no cache de itens e o retornará imediatamente para a aplicação. Ele não consulta o DynamoDB.
Uma entrada de cache negativa permanece no cache de itens do DAX até que o TTL do item expire, a LRU seja invocada ou o item seja modificado via PutItem
, UpdateItem
ou DeleteItem
.
O cache de consulta do DAX manipula resultados de cache negativos de maneira semelhante. Se uma aplicação executar uma operação Query
ou Scan
e o cache de consultas do DAX não contiver um resultado armazenado em cache, o DAX enviará a solicitação para o DynamoDB. Se não houver itens correspondentes no conjunto de resultados, o DAX armazenará um conjunto de resultados vazio no cache de consultas e retornará esse conjunto vazio à aplicação. Solicitações Query
ou Scan
subsequentes produzem o mesmo conjunto de resultados (vazio), até que o TTL desse conjunto de resultados tenha expirado.
Estratégias para gravações
O comportamento de gravação simultânea do DAX é apropriado para muitos padrões de aplicações. No entanto, existem alguns padrões de aplicativo em que um modelo de gravação simultânea pode não ser apropriado.
Para aplicações sensíveis à latência, a gravação por meio do DAX incorre em um salto de rede extra. Portanto, uma gravação no DAX é um pouco mais lenta que uma gravação diretamente no DynamoDB. Se a sua aplicação for sensível à latência de gravação, você poderá reduzir essa latência gravando diretamente no DynamoDB. Para ter mais informações, consulte Write-around (gravação direta).
Para aplicações que fazem uso intensivo de gravações (como aqueles que executam carregamento de dados em massa), talvez não seja desejável gravar todos os dados via DAX porque apenas uma porcentagem pequena deles é lida pela aplicação. Quando você grava grandes quantidades de dados via DAX, ele deve chamar seu algoritmo LRU de forma a liberar espaço no cache para que os novos itens sejam lidos. Isso diminui a eficácia do DAX como um cache de leitura.
Quando você grava um item no DAX, o estado do cache de itens é alterado para acomodar o novo item. (Por exemplo, o DAX talvez precise remover dados antigos do cache de itens para liberar espaço para o novo item.) O novo item permanece no cache de itens, sujeito ao algoritmo LRU do cache e à configuração de TTL do cache. Enquanto o item persistir no cache de itens, o DAX não lerá o item novamente no DynamoDB.
Gravação simultânea
O cache de itens do DAX implementa uma política de gravação simultânea (write-through). Para ter mais informações, consulte Como o DAX processa gravações.
Quando você grava um item, o DAX garante que o item em cache seja sincronizado com o item existente no DynamoDB. Isto é útil para aplicativos que precisam repetir a leitura de um item logo depois de gravá-lo. No entanto, se outras aplicações gravarem diretamente em uma tabela do DynamoDB, o cache de itens do DAX não estará mais em sincronia com o DynamoDB.
Para ilustrar isso, considere dois usuários (Alice e Bob) que estão trabalhando com a tabela ProductCatalog
. Alice acessa a tabela usando o DAX, mas Bob ignora o DAX e acessa a tabela diretamente no DynamoDB.
-
Alice atualiza um item na tabela
ProductCatalog
. O DAX encaminha a solicitação ao DynamoDB, e a atualização é bem-sucedida. Em seguida, o DAX grava o item em seu cache de itens e retorna uma resposta bem-sucedida a Alice. Desse ponto em diante, até que o item seja finalmente removido do cache, qualquer usuário que ler o item no DAX o verá com a atualização de Alice. -
Pouco depois, Bob atualiza o mesmo item do
ProductCatalog
gravado por Alice. No entanto, Bob atualiza o item diretamente no DynamoDB. O DAX não atualiza automaticamente o cache de itens em resposta às atualizações feitas no DynamoDB. Portanto, os usuários do DAX não veem a atualização de Bob. -
Alice lê novamente o item do DAX. O item está no cache de itens e, portanto, o DAX o retorna para Alice sem acessar a tabela do DynamoDB.
Nesse cenário, Alice e Bob veem representações diferentes do mesmo item do ProductCatalog
. Esse será o caso até que o DAX remova o item do cache de itens ou até que outro usuário atualize o mesmo item novamente usando o DAX.
Write-around (gravação direta)
Se a sua aplicação precisa gravar grandes quantidades de dados (como um carregamento em massa de dados), talvez faça sentido ignorar o DAX e gravar os dados diretamente no DynamoDB. Essa estratégia de gravação direta (write-around) reduz a latência de gravação. No entanto, o cache de itens não permanece em sincronia com os dados no DynamoDB.
Se você optar por usar uma estratégia de gravação direta (write-around), lembre-se de que o DAX preenche o cache de itens sempre que as aplicações usam o cliente do DAX para ler os dados. Isso pode ser vantajoso em alguns casos porque garante que apenas os dados lidos com mais frequência sejam armazenados em cache (não os dados gravados com mais frequência).
Por exemplo, considere um usuário (Charlie) que deseja trabalhar com uma tabela diferente, a tabela GameScores
usando o DAX. A chave de partição de GameScores
é UserId
e, portanto, todas as pontuações de Charlie teriam o mesmo UserId
.
-
Charlie deseja recuperar todas as suas pontuações e, para isso, envia uma
Query
para o DAX. Supondo que essa consulta não foi emitida antes, o DAX a encaminha ao DynamoDB para processamento. Ele armazena os resultados no cache de consultas do DAX e retorna os resultados a Charlie. O conjunto de resultados permanece disponível no cache de consultas até ser removido. -
Agora, suponha que Charlie jogue uma partida de Meteor Blasters e obtenha uma pontuação elevada. Charlie envia uma solicitação de
UpdateItem
ao DynamoDB, modificando um item na tabelaGameScores
. -
Por fim, Charlie decide executar novamente sua
Query
anterior para recuperar todos os dados deGameScores
. Charlie não vê sua alta pontuação do jogo Meteor Blasters nos resultados. Isso ocorre porque os resultados da consulta vêm do cache de consultas, e não do cache de itens. Os dois caches são independentes um do outro e, portanto, uma alteração em um cache não afeta o outro.
O DAX não atualiza conjuntos de resultados no cache de consultas com os dados mais atuais do DynamoDB. Cada conjunto de resultados no cache de consultas é atual no momento em que a operação Query
ou Scan
foi executada. Portanto, os resultados da Query
de Charlie não refletem sua operação PutItem
. Esse será o caso até que o DAX remova o conjunto de resultados do cache de consultas.