GH GambleHub

Deduplicação de eventos

1) Por que precisa de dedução

As duplicações aparecem devido a retais, temporizações de rede, restaurações de feeds e replicações de dados históricos. Se não os controlar:
  • invariantes são violados (débitos duplos, e-mail repetido/SMS, pedido «duplamente criado»);
  • aumentam os custos (redescobrimento/processamento);
  • distorce-se o analista.

O objetivo da deduplicação é fornecer um efeito observável uma vez nas repetições de transporte permitidas, muitas vezes juntamente com a idimpotência.

2) Onde colocar a deduplicação (níveis)

1. Edge/API-passarela - Cortando as duplicações explícitas por 'Idempotency-Keu '/corpo + assinatura.
2. Corretor/estrim - Deduplicação lógica por chave/sequência, coalescing por falha (menos por causa do custo).
3. O receptor de eventos (consumer) é o local principal: Inbox/tabela de chaves/dinheiro.
4. Sink (BD/kesh) - chaves exclusivas/UPSERT/versões/compactação.
5. ETL/análise - Deadup por janela de tempo e chave em estojos invertebrados.

Regra: O mais cedo possível, mas tendo em conta o custo das falsas operações e a necessidade de replicar.

3) Chaves de dedução

3. 1 Natural (preferencialmente)

`payment_id`, `order_id`, `saga_id#step`, `aggregate_id#seq`.
Garantem estabilidade e sentido.

3. 2 Compostos

`(tenant_id, type, external_id, version)` или `(user_id, event_ts_truncated, payload_hash)`.

3. 3 Impressão digital (fingerprint)

Hash um subconjunto de campo determinado (normalizar ordem/registros), opcionalmente 'HMAC (segredo, payload)'.

3. 4 Sequências/versões

Monótonos 'seq' per aggregate (bloqueio/versionagem otimista).

Anti-Pattern: «UUID randômica» sem ligação com a entidade empresarial - o dedão é impossível.

4) Janelas de tempo e ordem

A janela de dedução é o período em que o evento pode voltar a ocorrer (normalmente 24-72h; para as finanças - mais tempo).
Out-of-order: vamos atrasar (lateness). Em quadros de streaming - event time + watermarks.
Sliding/Fix-window deadup: "A chave foi vista nos últimos minutos N? ».
Sequence-aware: Se 'seq' ≤ o último processado for capturado/repetido.

5) Estruturas de dados e implementação

5. 1 Contabilidade exata (exact)

REDIS SET/STRING + TTL: 'SETNX key 1 EX 86400' → 'é a primeira vez que processamos, senão SKIP'.
LRU/LFU cash (in-proc): rápido, mas volátil → melhor apenas como a primeira barreira.
SQL Índice + UPSERT único: «Cole ou atualize» (Efeito Idumpotente).

5. 2 Estruturas próximas (protabilistic)

Bloom/Cutkoo filter: memória barata, pode ser um falso acionamento (falso positivo). Adequado para drop «barulhento» explícito (por exemplo, telemetria), não para finanças/pedidos.
Count-Min Sketch: avaliação de frequências para proteção contra suplentes quentes.

5. 3 Estados de streaming

Kafka Streams/Flink: keyed state store com TTL, a chave da janela; checkpoint/restore.
Watermark + allowed lateness: controla a janela de eventos atrasados.

6) Pattern transaccionais

6. 1 Inbox (tabela de entrada)

Salvamos 'mensagem _ id '/chave e resultado para efeitos colaterais:
pseudo
BEGIN;
ins = INSERT INTO inbox(id, received_at) ON CONFLICT DO NOTHING;
IF ins_not_inserted THEN RETURN cached_result;
result = handle(event);
UPSERT sink with result; -- idempotent sync
UPDATE inbox SET status='done', result_hash=... WHERE id=...;
COMMIT;

A repetição verá a gravação e não repete o efeito.

6. 2 Outbox

A gravação de negócios e o evento de uma transação é dado por um corretor. Não elimina a tomada do consumidor, mas exclui «buracos».

6. 3 Índices exclusivos/UPSERT

sql
INSERT INTO payments(id, status, amount)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO NOTHING; -- "create once"
ou atualização controlada da versão:
sql
UPDATE orders
SET status = $new, version = version + 1
WHERE id=$id AND version = $expected; -- optimistic blocking

6. 4 Versionização de unidades

O evento é válido se 'event. version = aggregate. version + 1`. Senão, tiragem/repetição/conflito.

7) Deadup e corretores/striptease

7. 1 Kafka

O Idempotent Producer reduz as duplicações na entrada.
As transmissões permitem que os registos de saída sejam transmitidos de forma atômica.
Competition: armazena o último valor per key - pós-faturamento/coligação (não para pagamentos).
Consumer-side: state store/Redis/DB para a janela de chaves.

7. 2 NATS / JetStream

Ack/raro → at-least-once. Deadup no consumidor (Inbox/Redis).
JetStream sequence/durabote do consumidor facilitam a identificação de repetições.

7. 3 Filas (Rabbit/SQS)

Visibility timeout + reaproveitamento → precisa de chave + deadup stor.
O SQS FIFO com 'MessageGroupId '/' DeduplicationId' ajuda, mas as janelas TTL são restritas ao provedor - armazene as chaves por mais tempo se o negócio exigir.

8) Armazéns e análises

8. 1 ClickHouse/BigQuery

"ORDER BY key, ts 'e' argMax '/' anyLast 'com uma condição.

ClickHouse:
sql
SELECT key,
anyLast(value) AS v
FROM t
WHERE ts >= now() - INTERVAL 1 DAY
GROUP BY key;

Ou uma camada materializada de eventos exclusivos (merj por chave/versão).

8. 2 Logi/Telemetria

Digamos approximate-deadup (Bloom) em ingest → economizar rede/disco.

9) Reaproveitamento, replica e backphill

As chaves de dedução devem sobreviver a réplicas (TTL ≥ janela de réplica).
Use o espaço das chaves com versão ('key # fonte = batch2025') ou «ameixas» individuais para não interferir com a janela online.
Armazene os artefatos de resultado (hash/versão) - isso acelera «fast-skip» nas repetições.

10) Métricas e observabilidade

'dedup _ hit _ total '/' dedup _ hit _ rate' é a proporção de dublagens capturadas.
'dedup _ fp _ rate' para filtros prováveis.
'window _ size _ secunds' é real (por telemetry late arrivals).
`inbox_conflict_total`, `upsert_conflict_total`.
`replayed_events_total`, `skipped_by_inbox_total`.
Perfis por tenant/key/tipo: onde há mais dublês e porquê.

Логи: `message_id`, `idempotency_key`, `seq`, `window_id`, `action=process|skip`.

11) Segurança e privacidade

Não coloque PII na chave; use hashi/pseudônimos.
Para assinar a impressão digital - HMAC (segredo, canonical _ payload) para evitar conflitos/falsificação.
Mantenha as chaves em sintonia com a compilação (GDPR Retenshn).

12) Desempenho e custo

In-pro LRU ≪ Redis ≪ SQL de latência/custo de operação.
Redis: barato e rápido, mas leve em conta o volume de chaves e TTL; curte por 'tenant/hash'.
SQL: Caro p99, mas oferece fortes garantias e auditoria.
Filtros Propabilistic: muito barato, mas possível FP - aplique onde o'SKIP extra "não é crítico.

13) Anti-pattern

«Nós temos Kafka exactly-once - a chave não é necessária». Preciso de um sink/camada de negócios.
Um TTL curto demais para as chaves de réplica/atraso levará a tomada.
Global deadup store solitário, hotspot e SPOF; não está chardeado por tenant/chave.
A única memória é a perda do processo = onda de duplicação.
Bloom para dinheiro/encomendas - falso positivo vai tirar a operação legítima.
Canonização de payload discordante - hashs diferentes para mensagens idênticas.
Ignorar out-of-order - Eventos recentes são marcados de forma errada.

14) Folha de cheque de implementação

  • Identifique a chave natural (ou a impressão digital composta).
  • Instale a janela Deadup e a política 'lateness'.
  • Selecione o nível (eu): edge, consumer, sink; preveja o charding.
  • Implemente o Inbox/UPSERT; para os fluxos - keyed state + TTL.
  • Se você precisar de uma barreira approximate - Bloom/Cutkoo (somente para domínios não-ríticos).
  • Configure a réplica de compatibilidade (TTL ≥ janela de replay/backphill).
  • Métricas 'dedup _ hit _ rate', conflitos e aberturas de janela; dashboards per-tenant.
  • Game Day: timeouts/retrações, réplicas, out-of-order, queda de cachê.
  • Documente a canonização payload e a versionização das chaves.
  • Faça testes de carga em «chaves quentes» e janelas longas.

15) Exemplos de configuração/código

15. 1 Redis SETNX + TTL (barreira)

lua
-- KEYS[1] = "dedup:{tenant}:{key}"
-- ARGV[1] = ttl_seconds local ok = redis. call("SET", KEYS[1], "1", "NX", "EX", ARGV[1])
if ok then return "PROCESS"
else return "SKIP"
end

15. 2 PostgreSQL Inbox

sql
CREATE TABLE inbox (
id text PRIMARY KEY,
received_at timestamptz default now(),
status text default 'received',
result_hash text
);
-- In the handler: INSERT... ON CONFLICT DO NOTHING -> check, then UPSERT in blue.

15. 3 Kafka Streams (deadup na janela)

java var deduped = input
.selectKey((k,v) -> v.idempotencyKey())
.groupByKey()
.windowedBy(TimeWindows. ofSizeWithNoGrace(Duration. ofHours(24)))
.reduce((oldV,newV) -> oldV)   // first wins
.toStream()
.map((wKey,val) -> KeyValue. pair(wKey. key(), val));

15. 4 Flink (keyed state + TTL, pseudo)

java
ValueState<Boolean> seen;
env. enableCheckpointing(10000);
onEvent(e):
if (!seen.value()) { process(e); seen. update(true); }

15. 5 porta de entrada NGINX/API (Idempotency-Key em edge)

nginx map $http_idempotency_key $idkey { default ""; }
Proxy the key to the backend; backend solves deadup (Inbox/Redis).

16) FAQ

O que escolher, o Dedup ou a pura Idempotidade?
A: Normalmente, o deadup é um «filtro» rápido (economizar), a idempotidade é a garantia do efeito correto.

Q: Que tipo de TTL colocar?
A: ≥ o tempo máximo de uma possível nova entrega + reserva. Típicamente 24-72h; para finanças e tarefas adiadas - dias/semanas.

Q: Como processar eventos recentes?
A: Configure 'allowed lateness' e o alarme 'late _ event'; tardias - através de um ramo separado (recompute/skip).

Podemos deduzir todo o fluxo de telemetria?
A: Sim, os filtros approximate (Bloom) em edge, mas leve em conta FP e não aplique a efeitos empresariais críticos.

O Deadup está a atrapalhar o backfill?
A: Divida o espaço das chaves ('key # batch2025') ou desliga a barreira durante o backphill; As chaves TTL devem cobrir apenas janelas online.

17) Resultados

A dedução é uma composição: chave correta, janela e estrutura de estado + pattern transaccionais (Inbox/Outbox/UPSERT) e trabalho consciente com eventos atrasados. Coloque barreiras onde for mais barato, garanta idempotidade em sinos, mede 'dedup _ hit _ rate' e teste réplicas/feels - de modo que você consiga «efetivamente exactly-once» sem mais caudas de latência e custo.

Contact

Entrar em contacto

Contacte-nos para qualquer questão ou necessidade de apoio.Estamos sempre prontos para ajudar!

Iniciar integração

O Email é obrigatório. Telegram ou WhatsApp — opcionais.

O seu nome opcional
Email opcional
Assunto opcional
Mensagem opcional
Telegram opcional
@
Se indicar Telegram — responderemos também por lá.
WhatsApp opcional
Formato: +indicativo e número (ex.: +351XXXXXXXXX).

Ao clicar, concorda com o tratamento dos seus dados.