Exactly-once semântica
O que é exactly-once realmente
Por «exactly-once» muitas vezes se percebe duas coisas diferentes:- Uma mensagem será entregue ao consumidor exatamente uma vez.
- Processamento: Efeito colateral final (gravação no banco de dados, reequilíbrio, emissão de outro evento) ocorrerá exatamente uma vez, mesmo que as entregas ou tentativas tenham sido maiores.
Em sistemas distribuídos, é mais confiável falar de semântica de processamento. A entrega é uma única vez difícil de fornecer (duplicados e repetições são inevitáveis), mas você pode fazer com que o estado final seja equivalente a um único processamento.
Quando o EOS é necessário e quando não
É necessário um EOS se:- Transações em dinheiro e balanços: Duplo cancelamento é inadmissível.
- Registro de licenças/quotas, contadores de billing.
- Chamadas externas irreversíveis (por exemplo, ativação exclusiva de chave).
- Os efeitos são reversíveis ou compensáveis (sagas, devoluções).
- É permitido duplicar temporariamente em vitrines/logs.
- É mais barato fornecer um sink idumpotente do que arrastar transações através de todo o caminho.
Modelo: end-to-end vs. hop-by-hop
Hop-by-hop EOS: Cada área (origem → processador → receptor) garante que a sua ação será aplicada exatamente uma vez.
End-to-end EOS: Toda a cadeia garante que o resultado é equivalente a um único processamento.
Na prática, end-to-end é alcançado por uma combinação de transações e/ou idempotação em cada hop.
Blocos básicos de construção
1. Operações Idumpotentes
A repetição da mesma solicitação sobre a chave da operação tem o mesmo resultado.
Ключи: `idempotency_key`/`event_id`/`operation_id`.
Implementação: tabela de operações «vistas» com TTL ≥ retenção do login de entrada.
2. Transações «ler-processar-escrever» (read-processo-write)
Uma unidade de trabalho atômica registra o efeito secundário e o progresso da leitura (ofset/posição). Isso elimina «fantasmas» ao cair entre os passos.
3. Versioning/SEQUENCE
O dispositivo armazena a versão/contagem; as alterações só são aplicadas se 'expected _ version' corresponder. As repetições do mesmo evento não aumentam a versão → o efeito uma vez.
4. Deduplicação
Índice por '(consumer _ id, event _ id)' ou pela operação natural 'business _ id'.
Pattern de implementação
1) Logs de transação + sink de transação com fixação de ofset
Perfeito para processamento de estrim.
Lê a partir do logem (apenas registros confirmados).
Estamos a fazer o processamento.
- a) Gravamos o efeito no sink (BD/tabela),
- b) registramos «lido antes do ofset N» (no mesmo BD).
- Uma comitiva. Com o restarte, tudo está forjado (e o ofset é movido) ou nada.
Propriedades: não prejudica a execução; «exatamente uma vez», mesmo que a mensagem tenha sido lida duas vezes.
2) Outbox + Consoador Idumpotente
Para os serviços de transação.
Em uma transação BD, altere a gravação de domínio e escreva o evento em outbox.
O replicador entrega o evento para o pneu com o mesmo 'event _ id'.
Os conceituadores aplicam os eventos de forma idempotiva (deadup por 'event _ id').
Propriedades: O produtor garante que o fato não se perde; os consoantes garantem exatamente um efeito.
3) EOS em Kafka/Flink-sistemas similares (conceitualmente)
Produtor Idumpotent: protege contra dublagens em retais de envio.
Transações do produtor: grupo de gravações em topics + alteração do Consumer Comutação Atomário; os leitores usam o isolamento 'read _ committed'.
O lado do processamento armazena o status (state store) e o configura junto com a transação.
Propriedades: reativar o estore/touca não resulta em duplo efeito; Duplicar «não é visível» downstream.
4) Idempotentes «sikes» (sinks) através de upsert/merge
O Sink aceita 'operation _ id '/' event _ id' e executa 'UPSERT... WHERE NOT EXISTS`.
O efeito colateral (por exemplo, a cobrança) é executado de forma atômica, verificando se já foi aplicado.
Propriedades: método EOS barato na fronteira com o armazenamento, sem transações distribuídas.
Peças-chave de implementação
Identificadores de operação
Devem ser detectados para repetições (não gerem um novo UUID na retraia).
Ter uma área de visibilidade sustentável (por consumer/por unidade/sistema).
Tabela de Deduplicação
Колонки: `consumer_id`, `operation_id`, `applied_at`, `ttl_expires_at`.
Índices por '(consumer _ id, operation _ id)'.
O TTL ≥ a janela máxima de repetição (retenção do logs + potenciais atrasos).
Competição otimista
No modelo write, guarde a versão do aparelho.
Ao aplicar o evento/comando, use 'WHERE versão =: exposed'; a duplicação não aumenta a versão.
Encomenda/ordem
O EOS não é da mesma ordem. Assegure a consistência através da chave de partição (todos os eventos do aparelho → uma única partição) e/ou comparação de 'sequence'.
Chamadas externas idimpotentes
Para métodos inseguros (como webhooks HTTP em um serviço de terceiros), adicione «Idempotency-Key» e exija que o parceiro o apoie.
Armadilhas frequentes
O EOS está apenas em um lugar: se o sink é idumpotental, mas você emite eventos secundários sem idempotação, você receberá «exatamente muitas vezes» downstream.
Duas empresas, primeiro na BD, e depois na corretora, a queda entre elas cria duplicados efeitos.
CDC crus para fora: A alteração da base de dados rompe a idempotação dos consumidores.
As chaves instáveis de 'operation _ id' dependem do tempo/rândomo e mudam ao retraí.
Custos e compromissos
Latitude: transações/leitura isolada → crescimento p95/p99.
Overhead de armazenamento: tabelas de dedução, state stores, logs de transações.
Dificuldade de operação: temporizações de transações, revalidação de fluxos, sessões «vazadas».
Diagnóstico: Mais estados («em camita», «visto como read _ committed», «reversão»).
Selecione o EOS pontualmente para agregados e efeitos críticos; cubra o resto com idoneidade e compensações.
Teste exactly-once
1. Fault-inhation: a queda do processo entre os passos «gravou o efeito» e «registrou o ofset».
2. Duplicado: Balanceie a mesma mensagem 2-5 vezes, verifique um efeito.
3. Restarte e rebalance: paragem/reinício de workers, verificação de falta de duplo processamento.
4. Flappies de rede, temporizações no meio da transação, repetição da empresa.
5. Testes de carga: aumento de filas → se não há degradação para «para sempre na transação».
Mini-modelos (pseudo)
Sink Idumpotente com fixação de ofset
pseudo begin tx if not exists(select 1 from dedup where consumer_id=:c and op_id=:id)
then apply_effect(...) -- upsert / merge / add_one_time_action insert into dedup(c, id, applied_at) values(:c,:id, now)
end if update offsets set pos=:pos where consumer_id=:c commit
Comando com versão do aparelho
pseudo begin tx update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
if row_count=0 then error CONCURRENT_MODIFICATION commit
Segurança e Complacência
PII/PCI nas tabelas de dedução: guarde o mínimo, use os tocantes em vez dos dados «crus».
Auditoria: logue 'operation _ id', 'trace _ id', resultado (APPLIED/ALREADY _ APPLIED).
Política de armazenamento: TTL em tabelas de dados, arquivamento de arquivos/logs.
Anti-pattern
«Presente exactly-once entrega»: tentativa de excluir as duplicações ao nível do protocolo de transporte sem idempotidade de efeito.
Transações globais distribuídas para tudo: XA/2PC através de todos os serviços - frágil e lento.
Mistura de subprodutos não-ideais (por exemplo, e-mail enviado para Comit Offset).
Falta de chaves de operação: confiança em «exclusividade» da carga útil.
Folha de cheque de produção
- Cada efeito crítico tem uma chave idimpotente.
- Ofset/posição de leitura é registrado em uma transação de efeito.
- As tabelas de dedução são indexadas; TTL ≥ retenção do logs.
- As unidades incluem concorrência otimista (versão/sequence).
- Os fluxos/topics são lidos no modo «apenas forjados» (se disponíveis).
- Os testes de duplicação e queda estão presentes em CI/CD.
- Dashboards: proporção de repetições, transações falhadas, tempo de bloqueio, lagoas.
- Documentação para integradores por 'Idempotency-Keu '/repetições/temporizações.
FAQ
O EOS pode ser fornecido sem transações?
Muitas vezes sim - através da idempotidade do sink 'ov (upsert/merge) e da versionização das unidades. As transações facilitam as garantias, mas aumentam o custo.
Todos precisam de «exactly-once»?
Não. Ele é querido. Use o ponto onde a compensação é impossível/estrada.
Como associar e-mails/webhooks a EOS?
Selecione a notificação até a comitiva e envie-a depois de fixar o efeito; Guarde 'notification _ id' e faça o envio idimpotente.
O que é mais importante: entrega ou processamento?
Processamento. As entregas podem ser repetidas; O estado final deve ser correto e o único.
Resultado
Exactly-once é sobre o efeito correto, não sobre a falta de dublagem no fio. Ela é alcançada por uma combinação de idempotidade, fixação atômica do efeito e progresso da leitura, partilha inteligente e disciplina de versionização. Use o EOS onde o custo do erro é inaceitável e verifique sua realidade com testes de queda e duplicação - não acredita no transporte.