3

Como evitamos publicações duplicadas na API do TabNews

Uma falha de rede depois de um POST não prova que a publicação falhou. Para evitar duplicatas, o cliente precisa guardar a intenção antes do envio, registrar imediatamente qualquer identidade devolvida pelo provedor e bloquear novas tentativas quando o resultado for desconhecido. Sem uma chave de idempotência, reconciliar o estado vale mais que repetir a chamada.

Última atualização: 28 de agosto de 2026.

Durante a integração do TabNews ao GrowSocialelion, tratamos cinco partes do mesmo problema:

  1. modelar o resultado desconhecido;
  2. guardar a identidade devolvida pelo TabNews;
  3. bloquear retries automáticos inseguros;
  4. reconciliar o estado por leitura;
  5. separar o sucesso remoto da gravação local.

1. Como modelar um resultado desconhecido?

O caso perigoso acontece quando o cliente envia POST /api/v1/contents, mas não consegue ler uma resposta confiável. Um timeout, uma conexão interrompida ou um JSON ilegível deixam pelo menos duas possibilidades: o TabNews pode ter recusado a solicitação, ou pode ter criado o conteúdo e perdido a resposta no caminho de volta.

A RFC 9110, seção 9.2.2, recomenda que clientes não repitam automaticamente métodos não idempotentes sem uma forma de provar que a primeira solicitação não foi aplicada:

"A client SHOULD NOT automatically retry a request with a non-idempotent method"

Source: RFC 9110, "9.2.2 Idempotent Methods"

A rota oficial do TabNews usa POST /api/v1/contents para criar publicações e comentários.

Nosso resultado interno precisa de mais estados que "sucesso" e "erro":

EstadoO que sabemosPróxima ação segura
planneda pessoa aprovou um payload exatoreservar a operação
sendinga chamada saiu, mas ainda não terminouaguardar
acceptedo provedor devolveu uma identidade exataguardar a referência; reler se for público
unknowna chamada pode ter produzido efeitobloquear novo POST
rejectedo provedor recusou antes de criar conteúdocorrigir a causa antes de tentar de novo

O estado unknown parece incômodo, mas é informação. Transformá-lo em rejected para simplificar a interface é justamente o que abre espaço para a duplicata.

2. Como guardar a identidade devolvida pelo TabNews?

Uma resposta aceita pode trazer id, owner_username, slug e status. O par owner_username/slug vira a referência pública usada nas leituras e edições seguintes.

Assim que esses campos chegam, a integração deriva a referência remota e a mantém no resultado, mesmo que uma gravação local posterior falhe. A ordem é importante:

  1. validar a identidade devolvida;
  2. derivar a referência e o estado (draft, published, firewall ou deleted);
  3. para conteúdo público, ler a referência e comparar o payload;
  4. tentar atualizar uso e o histórico local;
  5. devolver a identidade exata para o chamador persistir o ciclo de vida.

Essa regra também protege rascunhos. Um rascunho já aceito deve mudar para published com PATCH na referência existente. Um segundo POST criaria outra publicação.

3. Quando bloquear retries automáticos?

Bloqueamos a repetição quando há erro de transporte, resposta de sucesso que não pode ser interpretada ou falha local depois de uma aceitação comprovada. Nesses casos, o operador precisa revisar o provedor antes de autorizar uma nova criação.

Backoff ajuda a reduzir carga, mas não responde se a primeira chamada criou algo. Esperar 2, 10 ou 60 segundos e repetir o mesmo POST mantém a ambiguidade.

O TabNews também aplica uma proteção por IP a criações de conteúdo raiz. No código oficial consultado em 28 de agosto de 2026, a função de firewall recusa a operação quando já há duas criações nos cinco segundos anteriores.

Na implementação local verificada em 28 de agosto de 2026, reservamos o início das gravações com 2,6 segundos de intervalo para processos que compartilham o mesmo diretório de dados. Esse valor é uma decisão do GrowSocialelion, não um limite publicado pelo TabNews. Ele mantém o terceiro início além da janela oficial de cinco segundos. A proteção local não cobre réplicas com discos separados; uma implantação distribuída precisa mover a reserva para uma transação de banco de dados.

4. Como reconciliar o estado por leitura?

Depois de criar ou editar conteúdo público com uma referência exata, lemos GET /contents/{username}/{slug} e comparamos o resultado com o payload aprovado. Título, corpo, autor, slug e estado precisam corresponder ao que a pessoa autorizou. Se a leitura falhar ou divergir, preservamos a identidade aceita e bloqueamos o retry.

Rascunhos são diferentes: as rotas públicas não permitem relê-los. Nesse caso, guardamos a resposta aceita e o payload aprovado para publicar o mesmo rascunho com PATCH, sem criar outro item.

Quando a criação ficou unknown sem uma referência, a API não oferece uma consulta por chave de operação. Nesse cenário, uma busca manual na conta e a revisão da linha do tempo são mais seguras que outro envio. Conteúdo semelhante não é prova suficiente para adoção automática, pois duas publicações legítimas podem ter texto próximo.

Edições exigem outra precaução. Antes do PATCH, buscamos a versão atual e guardamos seu updated_at. A atualização só segue se o formulário ainda representa essa versão. Isso evita que uma tela antiga apague uma correção feita diretamente no TabNews.

5. Como separar o sucesso remoto da gravação local?

Uma integração cruza dois sistemas que não participam da mesma transação. O TabNews pode aceitar a publicação e, na instrução seguinte, o banco local pode ficar indisponível. Não existe rollback automático entre os dois lados.

Tratamos a resposta remota como a fonte da identidade e o armazenamento local como um trabalho reparável. Se o segundo falhar, preservamos o ID remoto na resposta, encerramos a aprovação e impedimos novo envio. Uma fila de reconciliação pode completar métricas e auditoria depois.

O mesmo princípio vale para exclusão. O TabNews informa na FAQ sobre TabCoins que apagar conteúdo pode reduzir o saldo recebido por ele. Por isso, uma publicação útil de prova deve permanecer publicada. Exclusão serve para um conteúdo realmente descartável e exige uma decisão humana separada.

Perguntas frequentes

Um hash do texto resolve a duplicação?

O hash liga a aprovação a um payload exato, mas não prova se o provedor criou o item. Em APIs genéricas, duas operações diferentes também podem compartilhar o mesmo payload. No TabNews, repetir o mesmo conteúdo é proibido; a identidade da operação e a referência devolvida pelo provedor continuam necessárias para impedir essa repetição.

Por que manter esta publicação depois da prova?

O conteúdo documenta uma decisão de engenharia reproduzível e segue a orientação do FAQ do TabNews para publicações sobre projetos: explicar decisões, dificuldades e detalhes técnicos em vez de publicar apenas material comercial.

Carregando publicação patrocinada...
1

Muito bom o artigo! Essa modelagem explícita do estado "unknown" ataca de frente um dos erros mais comuns em integrações: tratar chamada de rede como algo puramente binário (sucesso ou erro), esquecendo que o servidor pode ter processado o POST e apenas a resposta ter se perdido na volta.
O impulso padrão de muita gente quando uma API não tem Idempotency-Key nativo é colocar um retry cego no catch, o que é a receita clássica para duplicar posts ou requisições. Salvar a intenção antes do disparo e reconciliar por leitura (fazendo um GET para checar a existência antes de tentar de novo) é o padrão correto.
Eu estava justamente fuçando no frontend do TabNews esses dias para resolver aquele bug do botão de compartilhar que sumia da publicação após enviar um comentário. Consegui arrumar o estado no React sem precisar de refresh, mas confesso que no final fiquei com vergonha de abrir a PR lá no repositório oficial kkkk.
Ler o seu post dissecando como o cliente e a API devem conversar com essa máquina de estados deu uma aula de como tratar incerteza de rede com rigor. Parabéns pelo post!

1

Complemento prático: um estado unknown precisa guardar o hash da aprovação, o endpoint, o horário e a causa da incerteza.

Sem esses campos, a revisão humana sabe que deve parar, mas não tem dados suficientes para reconciliar a operação com segurança.