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:
- modelar o resultado desconhecido;
- guardar a identidade devolvida pelo TabNews;
- bloquear retries automáticos inseguros;
- reconciliar o estado por leitura;
- 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":
| Estado | O que sabemos | Próxima ação segura |
|---|---|---|
planned | a pessoa aprovou um payload exato | reservar a operação |
sending | a chamada saiu, mas ainda não terminou | aguardar |
accepted | o provedor devolveu uma identidade exata | guardar a referência; reler se for público |
unknown | a chamada pode ter produzido efeito | bloquear novo POST |
rejected | o provedor recusou antes de criar conteúdo | corrigir 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:
- validar a identidade devolvida;
- derivar a referência e o estado (
draft,published,firewalloudeleted); - para conteúdo público, ler a referência e comparar o payload;
- tentar atualizar uso e o histórico local;
- 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.