Ir para o conteúdo
DedicatedPHP Contato

Retentativas não bastam: projetar reconciliação de trabalhos assíncronos em PHP

Aprenda a verificar resultados de negócio, detectar inconsistências e reparar trabalhos assíncronos em PHP sem confiar apenas em retentativas.

Diagrama de reconciliação de trabalhos assíncronos em uma aplicação PHP

Os trabalhos assíncronos permitem desacoplar importações, sincronizações, notificações, geração de documentos e integrações. No entanto, o fato de um consumidor ter processado uma mensagem não demonstra necessariamente que o resultado de negócio esteja correto. Um sucesso pode ter sido registrado antes de confirmar um efeito externo, pode ter ocorrido uma falha entre duas etapas ou a mesma operação pode ter sido executada mais de uma vez.

A reconciliação de trabalhos assíncronos em PHP cobre essa diferença: compara o que o sistema esperava alcançar com a evidência do que ocorreu, detecta ausências ou discrepâncias e aciona uma correção controlada. Ela não substitui a fila, as retentativas nem a idempotência; ela as complementa com uma verificação independente.

Uma execução técnica não equivale a um resultado de negócio

Uma execução técnica não equivale a um resultado de negócio — guía visual de DedicatedPHP

Uma tarefa pode terminar sem uma exceção e, ainda assim, deixar um processo incompleto. Por exemplo, uma aplicação cria uma solicitação de sincronização, o consumidor chama uma API externa e recebe uma resposta inconclusiva devido a uma interrupção de rede. Se ele tentar novamente sem uma chave idempotente, poderá criar uma duplicata. Se assumir o sucesso, poderá deixar o registro sem sincronizar.

Também não se deve presumir uma semântica de entrega específica da infraestrutura de mensageria. A possibilidade de redeliveries e execuções duplicadas depende do broker, de sua configuração de persistência, dos reconhecimentos, do comportamento do consumidor e das falhas que ocorrerem. O projeto deve verificar essas propriedades na tecnologia escolhida e, quando duplicatas ou reordenações puderem ocorrer, tolerá-las explicitamente.

A pergunta operacional não é apenas «a mensagem foi consumida?», mas «posso demonstrar que o efeito esperado existe, uma única vez quando aplicável, e com os dados corretos?». Essa demonstração requer uma fonte de evidência: uma resposta consultável do sistema externo, um identificador remoto persistido, um documento armazenado ou uma alteração de estado confirmada.

Retentativas, idempotência e reconciliação: responsabilidades distintas

As retentativas tratam erros transitórios: indisponibilidade temporária, limites de uso, bloqueios breves ou problemas de rede. Convém definir limite de tentativas, atraso progressivo, classificação de erros e destino para mensagens que precisam de atenção. Tentar novamente indefinidamente pode ocultar um erro de dados ou agravar um incidente externo.

A idempotência torna seguro repetir uma operação. Ela pode ser obtida com um identificador estável da operação enviado a um provedor externo, uma restrição única no banco de dados ou uma verificação transacional anterior ao efeito. Isso não significa que o efeito tenha ocorrido: significa que uma repetição não deve multiplicá-lo.

A reconciliação busca operações pendentes, incompletas ou contraditórias e decide o que fazer com cada uma. Ela é especialmente necessária quando há efeitos externos, processos em lote, atualizações de vários sistemas ou comunicações cujo recebimento não pode ser comprovado apenas a partir da aplicação emissora.

  • Use retentativas para tentar novamente falhas classificadas como transitórias.
  • Use idempotência para impedir que retentativas ou redeliveries dupliquem efeitos.
  • Use reconciliação para verificar o estado final e reparar diferenças detectadas.

Modelar a operação e preservar evidência verificável

Um projeto sustentável separa três conceitos. O trabalho solicitado representa a intenção, por exemplo, «sincronizar o pedido 452». O efeito esperado define o resultado observável: «o sistema externo contém o pedido com a versão 7». A confirmação armazena a evidência de que esse resultado existe: identificador remoto, versão, marca temporal, resposta validada ou resultado de uma consulta posterior.

Antes de publicar uma mensagem, crie um registro de execução em um banco de dados durável. Se a aplicação modificar dados próprios e publicar uma mensagem, considere o padrão outbox: salve a alteração de negócio e o evento pendente na mesma transação, e delegue a publicação a um processo posterior. Assim, reduz-se o risco de confirmar a alteração local e perder a mensagem, ou publicar uma mensagem para uma alteração que foi revertida.

O registro deve incluir, no mínimo:

  • operation_id imutável e único, usado para correlacionar mensagens, logs e chamadas externas.
  • Tipo de operação, entidade afetada e versão ou hash do conteúdo esperado.
  • Estado atual, número de tentativas, próxima tentativa permitida e marcas temporais.
  • Chave idempotente e, se existir, identificador do recurso remoto.
  • Evidência resumida e referências seguras a respostas ou erros, sem registrar segredos nem dados pessoais desnecessários.
  • Motivo de encerramento, compensação, descarte ou escalonamento para revisão humana.

Defina transições explícitas, por exemplo: pending, processing, awaiting_confirmation, confirmed, retry_scheduled, manual_review, compensated e not_applicable. Cada transição deve ter um responsável e uma condição verificável. Uma atualização condicional, como passar para processing apenas se o estado anterior for pending, reduz condições de corrida entre consumidores.

Construir o processo de reconciliação

A reconciliação pode ser executada por meio de um comando PHP agendado, um worker dedicado ou um fluxo operacional. Ela deve trabalhar com janelas temporais: não examine operações criadas há segundos se a integração externa normalmente leva vários minutos. Defina a janela com dados reais de latência e revise-a ao alterar limites ou provedores.

Para cada operação elegível, compare fontes de verdade previamente definidas. A base local pode ser a autoridade sobre a intenção e a versão do dado; o sistema externo, sobre se recebeu ou criou o recurso. Quando não houver uma consulta confiável ao destino, a evidência pode ser uma confirmação assinada, um identificador do provedor ou uma verificação posterior por arquivo de resultado.

  1. Selecione operações não confirmadas que ultrapassem seu prazo esperado.
  2. Verifique se o efeito existe usando operation_id, chave idempotente ou uma chave de negócio inequívoca.
  3. Compare campos relevantes e versões, não apenas a existência do recurso.
  4. Classifique o caso como ausente, correto, divergente, ambíguo ou não aplicável.
  5. Execute a ação autorizada e registre a decisão com sua evidência.

Um resultado ambíguo não deve ser convertido automaticamente em reenfileiramento. Se uma chamada pode ter criado um recurso, mas não há como consultá-lo de forma confiável, tentar novamente poderá duplicar uma cobrança, uma notificação ou um documento. Nesses casos, bloqueie a ação automática e envie o caso para um painel de exceções com contexto suficiente para decidir.

Corrigir sem introduzir novos danos

A ação depende da discrepância e do custo de errar. Reenfileirar é adequado quando o efeito está ausente e a operação é idempotente. Compensar pode reverter um efeito incorreto por meio de uma operação de negócio explícita, não por uma exclusão técnica indiscriminada. Marcar para revisão é preferível diante de ambiguidade, conflito de versões ou consequências financeiras. Encerrar como não aplicável serve quando a entidade foi cancelada ou substituída conforme regras documentadas.

Os reparos manuais também devem deixar rastros: quem tomou a decisão, qual evidência consultou, qual ação aplicou e qual foi o resultado. Limite permissões e evite botões que executem uma operação sem mostrar a entidade, a versão, o destino e o risco de duplicação.

Observabilidade e testes que validam o projeto

Logs correlacionados por operation_id facilitam acompanhar uma operação entre web, workers e serviços externos. As métricas úteis não se limitam a exceções: meça a antiguidade das operações pendentes, a quantidade em revisão manual, a taxa de divergências, as retentativas por causa e o tempo até a confirmação. Os alertas devem ser disparados por acúmulo, antiguidade ou descumprimento de prazo, não por cada erro isolado.

Teste falhas representativas: falha após o efeito externo e antes de persistir a confirmação; execução duplicada; mensagem fora de ordem; reinicialização do worker; timeout com resultado remoto incerto; indisponibilidade prolongada; e alterações de versão enquanto uma operação continua pendente. O teste deve verificar tanto o estado final quanto a ausência de duplicatas e a qualidade da evidência armazenada.

Exemplo: sincronizar um registro com um sistema externo

Suponha que uma aplicação PHP sincronize um registro de cliente. Ao modificá-lo, ela cria a operação sync_customer com um identificador estável e a versão local esperada. O worker envia esses valores ao destino como chave idempotente. Se receber uma confirmação válida, persiste o identificador remoto e altera o estado para confirmed.

Se o timeout ocorrer após enviar a solicitação, o worker deixa a operação em awaiting_confirmation. O reconciliador consulta o destino pela chave idempotente. Se encontrar a mesma versão, confirma. Se não a encontrar, agenda um novo envio. Se encontrar uma versão diferente, marca o caso para revisão em vez de sobrescrever dados que poderiam ter sido modificados legitimamente no outro sistema.

Checklist para incorporar reconciliação sem reescrever o processo

Checklist para incorporar reconciliação sem reescrever o processo — guía visual de DedicatedPHP
  • Faça o inventário dos trabalhos existentes e priorize os que produzem efeitos externos, afetam dinheiro, dados regulatórios ou processos difíceis de repetir.
  • Para cada tipo, documente o efeito esperado, a fonte de verdade e a evidência que permitirá confirmá-lo.
  • Verifique no broker e nos consumidores específicos o que ocorre diante de falhas, reconhecimentos tardios, persistência, redelivery e ordem das mensagens.
  • Adicione um operation_id estável e propague-o na mensagem, nos logs, nas chamadas externas e nos registros de estado.
  • Introduza uma tabela de operações com estados, tentativas, prazos, chave idempotente e evidência; comece em modo de observação, se necessário.
  • Defina transições condicionais e uma política escrita para tentar novamente, confirmar, compensar, escalar ou encerrar como não aplicável.
  • Implemente um reconciliador limitado a uma janela temporal e a um tipo de operação piloto.
  • Valide com casos de duplicata, timeout ambíguo, falha entre etapas, reordenação e reinicialização antes de automatizar correções.
  • Crie um painel ou consulta de exceções com antiguidade, entidade, evidência e ação recomendada.
  • Revise periodicamente métricas, operações paralisadas e decisões manuais para ajustar prazos, regras e controles.

A adoção gradual permite melhorar a confiabilidade sem substituir toda a arquitetura: primeiro torne visíveis as operações incertas, depois confirme os resultados e, por fim, automatize apenas as correções cuja segurança possa demonstrar.

Deseja aplicar essas ideias ao seu projeto?Vamos discutir sua plataforma PHP.
Veja os serviços relacionados