Intermediário6 min de leitura

DynamoDB Contagens de referência

Uma contagem de referência é um número que você armazena em um item pai que rastreia quantos itens secundários apontam para ele – curtidas em uma postagem, membros em um espaço de trabalho, respostas em um comente. Você o mantém porque contar os filhos em cada leitura é muito caro.

Como você mantém uma contagem em DynamoDB?

Armazene o total acumulado como um número no item pai e atualize-o na mesma gravação que cria o filho. UMfaz com que ambos caiam ou nenhum, e uma condição para a gravação do filho interrompe novas tentativas de contagem dupla - portanto, um único GetItem retorna uma contagem precisa.

  • Não conte crianças na hora da leitura. Um Query para contar curtidas paga por cada como item que ele verifica. Armazene o total na postagem e leia um item.
  • Mantenha a contagem onde está escrito o filho, não depois. Bata no mesmo operação que cria a criança para que os dois nunca se desviem.
  • Use umquando a escrita e o impacto tocam itens diferentes. Um like é um item, a contagem reside em outro - TransactWriteItems faz ambos pousarem ou nenhum.
  • A arma está contando duas vezes. Uma tentativa repetida ou duplicada como essa executa novamente o incremento infla o número. Guarde a criança para escrever com uma condição.

Por que contar?

Vindo de SQL, você nunca armazenaria uma contagem de curtidas - você SELECT COUNT(*) FROM likes WHERE post_id = ? e deixe um índice torná-lo barato. DynamoDB não tem COUNT(*) que pula itens de leitura.

Um Query sobre as curtidas de uma postagem lê - e cobra - cada item curtido naquele partição, mesmo se você quiser apenas o número. Em uma postagem viral com milhares de RCUs para responder "quantas curtidas?" Essa é a contagem de referência de armas de fogo que existe para matar.

Então você : armazene o total acumulado na própria postagem. Lendo a contagem torna-se um único GetItem. O custo é que agora você possui a manutenção da precisão.

Modele os itens

Dois tipos de itens compartilham uma partição para que a postagem e suas curtidas fiquem em uma coleção de itens. Chaves inventadas:

Post item
PKSKattributes
POST#a91fMETAlikeTally (Number), body, authorId, createdAt
Like item
PKSKattributes
POST#a91fLIKE#USER#7c20likedAt

O atributo likeTally no item META é a contagem de referência. Cada LIKE# item é uma criança. Colocar ambos em PK = "POST#a91f" significa que um único Query pode busque a postagem e seus curtidores juntos quando quiser a lista.

Aumente a contagem atomicamente

DynamoDB incrementa um número com um ADD (ou SET x = x + :n)- este é um contador atômico: DynamoDB aplica o delta do lado do servidor sem você lendo o valor atual primeiro, para que os incrementos simultâneos não se sobreponham. (AWS: contadores atômicos)

Curtir uma postagem equivale a duas gravações em dois itens — crie o LIKE# item e adicione 1 a likeTally em META. Se o semelhante pousar, mas o impacto falhar, o a contagem está errada para sempre. Você precisa de ambos ou nenhum.

Isso é o que TransactWriteItems garante - tudo ou nada em vários itens, e cancela toda a transação se algum item for modificado simultaneamente (AWS: bloqueio pessimista com transações):

{
  "TransactItems": [
    {
      "Put": {
        "TableName": "Social",
        "Item": {
          "PK": {"S": "POST#a91f"},
          "SK": {"S": "LIKE#USER#7c20"},
          "likedAt": {"N": "1750636800"}
        },
        "ConditionExpression": "attribute_not_exists(SK)"
      }
    },
    {
      "Update": {
        "TableName": "Social",
        "Key": {
          "PK": {"S": "POST#a91f"},
          "SK": {"S": "META"}
        },
        "UpdateExpression": "ADD likeTally :one",
        "ExpressionAttributeValues": {":one": {"N": "1"}}
      }
    }
  ]
}

O Put e o Update são confirmados juntos. Se um deles falhar, DynamoDB rola ambos de volta e retorna uma TransactionCanceledException.

Proteja-se contra contagem dupla

O verdadeiro bug não é algo escrito pela metade - a transação evita isso. É o mesmo usuário curtindo duas vezes ou um cliente tenta reproduzir a solicitação novamente. Cada repetição adiciona outro 1 e likeTally flutua silenciosamente acima da contagem verdadeira.

O ConditionExpression: attribute_not_exists(SK) no Put é o guarda. Se o item LIKE# desse usuário já existe, a condição Put falha, todo o a transação é cancelada e - criticamente - o ADD nunca é executado. Um like por usuário, imposta pela chave.

Crie e copie essas expressões de atualização e condição — com o direito Guarda ExpressionAttributeValues e attribute_not_exists — no DynamoDB Expression Builder em vez de montando manualmente o JSON.

Ao contrário, e o custo

Remover um like é a imagem espelhada: Delete o item LIKE# com ConditionExpression: attribute_exists(SK) e ADD likeTally :minusOne no mesma transação. A condição impede que um duplo ao contrário torne a contagem negativa.

Conheça o preço. Uma gravação transacional custa 2 WCUs por item para itens de até 1 KB — um para preparar, um para confirmar - versus 1 WCU para uma escrita simples. Um like são dois itens, então cada curtida equivale a aproximadamente quatro WCUs. Barato por ação, mas vale a pena conhecer antes de uma postagem de celebridade sofre uma tempestade.

Veja no DynoTable

Quando você suspeita que uma contagem mudou, você deseja comparar o likeTally armazenado em relação ao número real de filhos LIKE# — sem executar uma consulta de contagem no prod.

O item post META ao lado de seus LIKE# filhos em uma coleção de itens, para que você possa observar a contagem armazenada em relação à contagem real de filhos.
O item post META ao lado de seus LIKE# filhos em uma coleção de itens, para que você possa observar a contagem armazenada em relação à contagem real de filhos.

Para uma verdadeira reconciliação em um conjunto limitado de postagens - "cujas contagens não correspondem seu filho conta?" — DynoTable's SQL Workbench executa o GROUP BY e a junção lado do cliente sobre as linhas que você carregou, que o PartiQL simples não pode expressar.

Armadilhas e próximos passos

  • Não mantenha a contagem fora da banda (um Lambda que reconta todas as noites). É um band-aid sobre um caminho de gravação que deveria ter sido transacional desde o início.
  • Assistir. Uma única postagem extremamente popular concentra todos os gostos - e cada registro — em uma chave de partição. A contagem está correta; a partição ainda pode acelerar.
  • Reconciliar raramente, reparar cirurgicamente. O desvio deve ser próximo de zero se cada mutação está condicionado. Trate uma incompatibilidade como um bug a ser encontrado, não como um número a ser substituído.

Leitura relacionada: design de tabela única para saber por que a postagem e curtidas compartilham uma partição e Query vs Scan para saber por quê contar as crianças na hora da leitura é o padrão que você está evitando.

Então download DynoTable para inspecionar essas coleções de itens e verificar seu corresponde às suas próprias tabelas.

Atualizado