Intermediário5 min de leitura

Contadores Atômicos no DynamoDB

Um contador atômico é um atributo numérico que você incrementa no lugar com uma única chamada UpdateItem — sem leitura antes, sem corrida read-modify-write. O DynamoDB aplica cada incremento na ordem de chegada e nunca deixa dois escritores atropelarem a contagem um do outro.

O que é um contador atômico no DynamoDB?

Um contador atômico no DynamoDB é um atributo numérico que você incrementa no lugar com uma única chamada UpdateItem, usando uma expressão de atualização ADD (ou SET x = x + :n). O DynamoDB lê, soma e escreve o valor no lado do servidor, então escritores concorrentes se serializam sem atualizações perdidas — mas ele não é idempotente, então uma chamada repetida incrementa duas vezes.

  • Use ADD (ou SET x = x + :n) para incrementar em uma chamada. O DynamoDB lê, soma e escreve no lado do servidor — chamadores concorrentes se serializam, sem atualizações perdidas.
  • Sem leitura antes. Vindo do SQL você faria SELECT e depois UPDATE; aqui você pula a leitura inteiramente e a operação ainda é segura sob concorrência.
  • Contadores atômicos não são idempotentes. Um UpdateItem repetido incrementa de novo. Se você não tolera contar a mais ou a menos, use uma atualização condicional.
  • ADD em um atributo ausente começa em 0, então o primeiro incremento já funciona — sem escrita de semente necessária.

O problema com read-modify-write

Digamos que você acompanhe visualizações em um vídeo. O instinto ingênuo, direto do SQL, é: GetItem, somar um no seu app, PutItem o novo total de volta.

Dois espectadores apertam play ao mesmo tempo. Ambos leem views = 41. Ambos escrevem 42. Você contou uma visualização, não duas. Essa é uma atualização perdida — a cilada clássica de concorrência, e ela não aparece até você ter tráfego.

No SQL você a desviaria com UPDATE videos SET views = views + 1, empurrando a aritmética para dentro do banco de dados. O DynamoDB tem o mesmo movimento, e é o objetivo todo de um contador atômico.

Incremente em uma chamada

Modele um item de estatísticas por vídeo. Chave de partição VID#<id>, chave de classificação STATS#TOTAL, com um play_count numérico:

PKSKplay_count
"VID#9f3a""STATS#TOTAL"41

Para registrar um play, envie um UpdateItem com uma cláusula ADD:

# UpdateItem
Key               PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression  ADD play_count :one
Values            :one = 1

O DynamoDB lê play_count, soma 1 e escreve o resultado dentro de uma única operação no lado do servidor. Não há janela para outro escritor se infiltrar. Dez plays concorrentes produzem +10, toda vez — é isso que "atômico" te compra.

Você pode montar e copiar essa expressão exata — nomes, valores e todos os quatro tipos de cláusula — com o DynamoDB Expression Builder.

ADD funciona mesmo quando play_count ainda não existe: o DynamoDB trata um atributo numérico ausente como 0, então o primeiro play o cria em 1. Sem escrita de semente separada. (AWS: usando expressões de atualização)

ADD vs SET +: escolha um

Duas expressões fazem a mesma aritmética. A AWS recomenda SET para uso geral porque ele compõe com outras ações SET e lê de forma mais explícita. (AWS: usando expressões de atualização)

ADD play_count :oneSET play_count = play_count + :one
Atributo ausenteCria-o, começando em 0Erro — precisa de if_not_exists
Tipos de dadosSó números e conjuntosNúmeros (e mais) via SET
Combinar c/ SETCláusula separadaUma cláusula SET, separada por vírgula
Orientação AWSOk para contadoresPadrão recomendado

Se o atributo puder não existir e você quiser SET, proteja-o: SET play_count = if_not_exists(play_count, :zero) + :one. Com ADD você pula isso — ele semeia a partir de 0 de graça.

Faça isso no DynoTable

Abra o item, edite play_count, e você pode ver um incremento atômico acontecer sem escrever JSON à mão — o painel de atualização emite a expressão ADD para você e mostra o novo valor no momento em que ele é commitado.

A cilada: contadores não são idempotentes

Eis a parte que morde os times em produção. Um contador atômico incrementa toda vez que o UpdateItem roda. (AWS: trabalhando com itens)

Imagine uma falha de rede: você envia o incremento, a conexão cai antes de a resposta voltar, e você não sabe se ele aconteceu. Você repete. Se a primeira chamada de fato teve sucesso, você agora contou aquele play duas vezes.

Para visualizações de vídeo isso está ok — algumas contagens duplas em um milhão de plays não vão machucar ninguém, e a AWS chama esse exato caso de "rastrear visitantes" de uso canônico dos contadores atômicos. (AWS: trabalhando com itens)

Não está ok para qualquer coisa que precise ser exata: estoque que você pode vender em excesso, créditos que você pode gastar em dobro, um saldo que você pode corromper. Aí, recorra a uma atualização condicional.

Quando você precisa de exatidão: atualizações condicionais

Uma atualização condicional é idempotente se você condiciona no mesmo atributo que está mudando. Incremente play_count para 42, mas só se ele estiver atualmente em 41:

# UpdateItem
Key                  PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression     SET play_count = :next
ConditionExpression  play_count = :current
Values               :next = 42, :current = 41

Agora um retry é seguro: se a primeira escrita já moveu play_count para 42, a condição play_count = 41 falha na segunda vez e nada muda. (AWS: trabalhando com itens)

O custo é a concorrência. Dois escritores correndo na mesma condição significa que um ganha e um recebe um ConditionalCheckFailedException para repetir — você trocou o throughput do contador incondicional por correção. Para contadores exatos e disputados essa é a troca certa. Para contagens de visualização é exagero.

Ciladas

  • Um item quente. Uma única linha de contador é uma chave de partição. Um vídeo viral martelando VID#9f3a / STATS#TOTAL pode atingir um teto de escrita por partição. Faça sharding: espalhe as escritas por STATS#TOTAL#0..N e some na leitura.
  • Sem incremento em lote. BatchWriteItem é só put/delete — ele não pode rodar expressões de atualização. Contadores passam por UpdateItem, um item por chamada.
  • ADD é só números e conjuntos. Ele não toca strings ou booleanos; isso é um SET. Veja tipos de dados do DynamoDB para o modelo de atributo completo.

Próximos passos

Contadores atômicos são um padrão de escrita; como você as agregações de volta é uma questão de modelagem — veja single-table design para manter itens de estatísticas ao lado do seu pai, e Query vs Scan para que somar um contador com sharding continue sendo um Query.

Rascunhe e copie o incremento no DynamoDB Expression Builder, depois experimente o DynoTable para rodar atualizações atômicas contra suas próprias tabelas e ver as contagens se moverem.

Atualizado