Intermediário6 min de leitura

DynamoDB Expressões de condição: o guia completo (com exemplos)

Uma expressão de condição é um predicado DynamoDB avaliado no item existente antes de confirmar sua gravação. Se o predicado for falso, a gravação é rejeitado e nada muda. É a coisa mais próxima que DynamoDB tem de um Cláusula WHERE em uma gravação - e a única maneira segura de impor uma invariante.

Como funcionam as expressões de condição DynamoDB?

Uma expressão de condição é um predicado DynamoDB que avalia o lado do servidor em relação ao item atual antes de confirmar uma gravação. Se for verdade, a gravação prossegue; se for falso, a gravação é rejeitada com ConditionalCheckFailedException e nada muda. Ele reúne a verificação e a mutação em uma operação atômica, para que os chamadores simultâneos não possam competir com uma leitura obsoleta.

  • É um protetor, não um filtro. ConditionExpression roda no lado do servidor item atual; um resultado falso falha na gravação com ConditionalCheckFailedException.
  • Ele substitui leitura e gravação. Sem SELECT e depois UPDATE de ida e volta - o check e a mutação são uma operação atômica, portanto, dois chamadores não podem competir.
  • É gratuito para rejeitar, não é gratuito para executar. Uma gravação condicional com falha ainda consome capacidade de gravação. Uma nota de emissão rejeitada WCUs para o tamanho do item existente com o qual foi verificado (mínimo 1) — uma falha na criação se estiver ausente custa 1 WCU.

Vindo de SQL, você leria a linha, verificaria no código do aplicativo e atualizaria. Em DynamoDB essa lacuna entre leitura e gravação é um bug de corrupção de dados esperando por um chamador simultâneo. A expressão de condição fecha a lacuna.

Onde eles se aplicam

Você anexa um ConditionExpression a PutItem, UpdateItem, DeleteItem e cada ação dentro de TransactWriteItems. não faz parte de Query ou Scan

  • esses usam FilterExpression, que é uma coisa diferente no caminho de leitura.

Essa distinção confunde as pessoas, então seja preciso:

ConditionExpressionFilterExpression
CaminhoGrava (Put/Update/Delete)Lê (Query/Scan)
Efeito na falhaRejeita toda a gravaçãoElimina o item dos resultados
O item atual, pré-gravaçãoCada item candidato, pós-leitura
CustoFalha ao escrever contas aindaOs itens filtrados ainda são cobrados pela leitura

Ambos são executados no lado do servidor. A diferença é o que “false” faz: uma condição aborta uma mutação; um filtro apenas oculta uma linha que você já pagou para ler. (AWS: Expressões de condição)

As funções que você realmente usará

A linguagem da condição é pequena. Os burros de carga:

  • attribute_exists(path) / attribute_not_exists(path) — faz isso

existe no item? A expressão clássica para "criar somente se estiver ausente" / "atualizar somente se presente".

  • Comparadores — =, <>, <, <=, >, >= — contra um valor ou outro atributo.
  • attribute_type, begins_with, contains, size — verificações de tipo e string/conjunto.
  • ENTRE… E …, IN (...) — alcance e associação.
  • AND, OR, NOT, parênteses — para combinar o acima.

attribute_not_exists noé a maneira canônica de fazer PutItem se comporta como uma inserção que não atrapalha um item existente — DynamoDB não tem operação de "inserção" separada, portanto a condição é a semântica de inserção. (AWS: Operador de comparação e referência de função)

Um exemplo prático: proteger um livro-razão contra cheque especial

Pegue um livro-razão bancário. Cada conta é um item:

PK = "ACCT#a7f3"
SK = "BALANCE"
clearedCents = 50000
holdCents    = 0

Um débito nunca deve empurrar o saldo disponível abaixo de zero, e você nunca deve debitar uma conta que não existe. Duas regras, ambas aplicáveis em a escrita em si.

O caminho errado (a arma)

GetItem ACCT#a7f3 / BALANCE     → clearedCents = 50000
if (50000 >= 30000) ...         ← app-side check
UpdateItem  SET clearedCents = 20000

Entre GetItem e UpdateItem, um segundo débito pode ler o mesmo 50000, passe seu próprio cheque e escreva também. Ambos têm sucesso; a conta vai negativo. Esta é uma corrida de leitura-modificação-gravação e nenhuma validação do lado do aplicativo corrige isso - a verificação e a gravação são operações separadas.

O jeito certo

Dobre o cheque para escrever. Débito 30.000 centavos, condicionado à conta existente e mantendo o suficiente:

UpdateItem  ACCT#a7f3 / BALANCE
  SET clearedCents = clearedCents - :amt
  ConditionExpression:
    attribute_exists(PK) AND clearedCents >= :amt

com :amt = 30000. Se o saldo for muito baixo ou o item nunca tiver sido criado, DynamoDB rejeita a gravação com ConditionalCheckFailedException e o equilíbrio está intocado. O débito simultâneo vê o saldo original e é verificado em relação a ele ou vê o atualizado - nunca uma leitura obsoleta que agiu.

Você pode construir e copiar a expressão exata – nomes, valores e tudo – com o DynamoDB construtor de expressão em vez de montando manualmente o mapa ExpressionAttributeValues.

Experimente aqui - este construtor está predefinido para um PutItem protegido (attribute_not_exists) para que você possa ler o ConditionExpression gerado:

Monte sua requisição
Código gerado
new PutItemCommand({
  "TableName": "AuditLog",
  "Item": {
    "pk": {
      "S": "TENANT#acme"
    },
    "sk": {
      "S": "EVENT#2026-06-24T10:00:00Z"
    },
    "action": {
      "S": "login"
    }
  },
  "ConditionExpression": "attribute_not_exists(#cond0)",
  "ExpressionAttributeNames": {
    "#cond0": "pk"
  }
})

Inspecionando o guarda em DynoTable

Quando uma gravação condicional falha, você deseja ver o estado real do item, e não adivinhar nisso. Puxe o item da conta para cima e leia clearedCents diretamente.

A coleção do razão em DynoTable — o item BALANCE mostra clearedCents acima dos itens de transação da conta.
A coleção do razão em DynoTable — o item BALANCE mostra clearedCents acima dos itens de transação da conta.

Leia a rejeição, não tente novamente cegamente

ConditionalCheckFailedException não é um erro transitório - tentar novamente o mesmo escrever não muda nada. Significa que uma regra de negócios foi acionada: fundos insuficientes, criação duplicada, versão obsoleta. Mostre isso como um resultado de domínio, não um infra pontinho.

Duas coisas tornam as falhas depuráveis:

  • ReturnValuesOnConditionCheckFailure: ALL_OLD — DynamoDB retorna o item atual ao lado da falha, para que você possa mostrar "o saldo era 20.000, você pediu 30.000" sem uma segunda leitura. (AWS: Trabalhando com itens)
  • Distinguindo os dois motivos de falha. attribute_exists(PK) AND clearedCents >= :amt reúne "sem conta" e "sem fundos" em um exceção. Se os chamadores precisarem diferenciá-los, divida-os em duas gravações ou inspecione o item devolvido.

O bloqueio otimista é o mesmo truque

O padrão de número de versão é apenas uma expressão de condição com um formato diferente chapéu. Armazene um atributo versão; cada gravação afirma a versão que você lê e bate:

UpdateItem  ACCT#a7f3 / BALANCE
  SET clearedCents = :new, version = :next
  ConditionExpression: version = :seen

Se outro escritor se moveu primeiro, version = :seen é falso, a gravação é rejeitada, e você relê e tenta novamente. É assim que DynamoDB faz o controle de simultaneidade sem fechaduras – afirme o que você viu, falhe se ele se mover. (AWS: Bloqueio Otimista com Número da versão) A área de teste do DynoTable executa esse padrão para você - um a edição simultânea surge como um conflito a ser resolvido, não como uma gravação perdida.

Armadilhas e próximos passos

  • Nomes que colidem com palavras reservadas. status, size, name e ~570 outros são reservados. Alias eles com ExpressionAttributeNames (#s = status) ou a solicitação é rejeitada com uma ValidationException ('Nome do atributo é um palavra-chave reservada'). O verificador de palavras reservadas pega seu nomes de atributos e devolve o mapa de alias pronto para colar.
  • Uma condição não pode fazer referência a outro item. Ela apenas vê o item sendo escrito. Invariantes entre itens precisam de TransactWriteItems com uma ação por ação ConditionExpression, ou um ConditionCheck contra um item sentinela.
  • Escritas com falha ainda custam WCUs. Um guarda que rejeita 90% das vezes ainda contas para essas rejeições. Seguro barato, mas não gratuito.

Para modelar as chaves contra as quais esses guardas são executados, consulte design de tabela única e Query vs Scan. Quando você estiver pronto para emitir condições escreve em dados reais, download DynoTable e executa-os em suas próprias mesas.

Atualizado