Amazon DynamoDB: Usando AWS Lambda e SQS para executar atualizações em massa, distribuídas e assíncronas

UPDATE X WHERE Y; é uma tarefa não trivial a ser executada no cenário NoSQL, em oposição a outros bancos de dados baseados em SQL. Em SQL, é apenas uma operação, enquanto no NoSQL você tem que executar a operação PUT em cada registro.

Aparentemente, essa não é uma tarefa fácil. Não é apenas demorado, porque temos que executar N operações de escrita, mas também exige muita taxa de transferência e é muito frágil — o throttling pode impedir que algumas atualizações ocorram, e você pode acabar com inconsistências nos dados.

Teoricamente, existem apenas três requisitos que precisam ser atendidos. Vamos ver alguns exemplos de como isso pode ser feito.

Uma grande atualização síncrona e por que isso é ruim

Embora essa solução possa ser perfeitamente adequada para pequenas operações. No entanto, depois de atingir um número maior de linhas, a execução poderá expirar devido à duração máxima da AWS Lambda. Isso é inaceitável. Portanto, podemos apenas dividir o processo em duas partes e desacoplar a consulta das atualizações.

Separar e paralelizar atualizações

Melhor. Porém, o problema anterior ainda não pode ser eliminado e, além disso, podemos atingir o limite da capacidade de escrita de nossa tabela, rejeitando efetivamente algumas de nossas chamadas, corrompendo os dados. Isso também é inaceitável.

Use SQS para armazenar em buffer

Este design tem uma grande vantagem. Ele permite colocar os trabalhos rejeitados de volta na fila e tentar novamente até que cada solicitação de atualização seja finalmente processada. Nosso primeiro requisito é atendido.

Observe que esse design requer alguns ajustes para obter o melhor desempenho para descobrir qual o melhor tamanho de lotes para trabalhar com SQS. Converter cada operação de atualização que está na fila em uma tarefa separada, não teria um bom custo-benefício, devido à quantidade de invocações da AWS Lambda e itens no SQS, por outro lado, dividir os itens na fila em grandes lotes pode bloquear o sistema e torná-lo inflexível.

Minha sugestão é criar lotes de 25 itens para tarefas de atualização usando BatchWriteItem e tentar definir os itens no SQS para conter um múltiplo de 25.

Porém, não é o ideal. Com uma simples mudança, podemos deixar isso melhor.

Fazer atualizações durante a transmissão de resultados da tabela

Devido à natureza da API do DynamoDB, obtemos resultados em páginas. Podemos usar essa característica para transmitir resultados página por página diretamente para uma fila, ao invés de esperar para buscar todos os dados e dividi-los em X lotes.

Portanto, apenas uma pequena alteração em nosso diagrama é necessária:

Mas você pode perguntar: “…e se a tabela de origem for grande demais para ser processada de uma só vez pela função “Get”?”

Minha solução é chamar a função context.getRemainingTimeInMillis() após o término de cada página para verificar quanto tempo nos resta. Se houver menos de um segundo, essa chamada específica da AWS Lambda deve finalizar seu processamento e, em em seguida, re-invocar outra instância passando seu argumento LastProcessedKey como ExclusiveStartKey para a próxima instância.

Verificando o progresso

Primeiro número que podemos obter assim que nossa função get chegar ao ponto em que não há mais itens em LastEvaluatedKeyiguais (e.g. igual a null).

Obter o segundo número é um pouco mais complicado. Como o modelo do DynamoDB é eventualmente consistente, a atualização da mesma linha por várias funções “atualizadoras” pode resultar em um número incorreto no final. Ao invés disso, devemos deixar que cada chamada de função coloque um novo registro separado e agregue uma soma de todos eles. Para evitar SCANs, nossa tabela deve ter uma chave composta pela chave de partição, que no nosso caso será sempre a mesma, e chave de intervalo que será um UUID exclusivo.

Assim que um novo registro é inserido, o DynamoDB transmitia a atualização para a função Lambda “orquestradora”, que consulta todas as operações realizadas, soma a contagem e compara com o “primeiro número” — número de linhas que precisam ser atualizadas.

Isso tudo é realmente uma boa ideia?

No entanto, esse padrão, com pequenas modificações, pode ser usado para migrar dados do SQL para o DynamoDB ou executar inserções de dados em uma escala massiva de modo confiável.

Se você precisar de dicas e experiência sobre DynamoDB ou Serverless, pode entrar em contato comigo.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store