(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisbon

How to do deduplication in Real-Time Analytics: step by step

João Barros 05 de August de 2026 4 min read

This tutorial shows how to implement event deduplication in Real-Time Analytics to avoid duplicate counts when multiple identical events arrive within a short time. Deduplication is useful for correct metrics, billing and data integrity in streaming pipelines.

Prerequisites

  • Account and access to a streaming platform that supports window/state operations (e.g.: Azure Stream Analytics, Apache Flink, Kafka Streams).
  • Event stream with a unique identifier per entity (e.g.: event_id, user_id) and timestamp.
  • Editor/terminal to test queries/code and view logs.

Step 1: understand the problem and the strategy

Why should we deduplicate? In Real-Time Analytics, duplicate events arise from retries, network failures or at-least-once delivery. A common strategy is to use a time window + state storage that records the event_id already processed during that window. We will implement a "time-window" deduplication that accepts the first event per event_id within X minutes.

Step 2: define the minimal event format

Start by ensuring each event has: event_id (string), timestamp (ISO) and payload. JSON example for local testing:

{
  "event_id": "abc-123",
  "timestamp": "2026-08-05T12:34:56Z",
  "user_id": "u42",
  "action": "click"
}

Step 3: implement simple deduplication with streaming SQL

If you are using an engine that supports SQL over streaming (Azure Stream Analytics, Flink SQL), use a window clause and an aggregation function to choose the first timestamp per event_id. Conceptual example (adjust for your platform):

-- Tumbling window of 5 minutes, keep only the first event per event_id
SELECT
  event_id,
  System.Timestamp() AS window_end,
  MIN(timestamp) as first_timestamp,
  ANY_VALUE(action) as action
FROM InputStream
GROUP BY
  TumblingWindow(minute, 5),
  event_id

Explanation: groups by event_id within 5-minute windows; MIN(timestamp) determines the first event. ANY_VALUE(action) is a placeholder to return the associated payload.

Step 4: deduplication with state (pseudo-code example in Flink style)

For scenarios where you want to guarantee more flexible (stateful) deduplication, use a state store with TTL. Pseudo-code example that illustrates the logic:

// For each received event
onEvent(event) {
  key = event.event_id
  now = event.timestamp
  if (!state.exists(key) || state.get(key) < now - dedupTTL) {
    // not seen within TTL → process and record mark
    process(event)
    state.put(key, now)
  } else {
    // duplicate event within TTL → ignore
    drop(event)
  }
}

// state has TTL configured to dedupTTL (e.g.: 5 minutes)

Explanation: the state stores the last seen timestamp per event_id; if the event is newer than the TTL, it is accepted and updates the state, otherwise it is ignored.

Step 5: optimize for memory and scale

Common mistakes: storing all event_id without expiring; using a TTL that is too long; not partitioning by key. Best practices: choose an appropriate TTL (e.g.: 5–15 minutes), use key compression/hashing when there is high cardinality, and partition the stream by event_id to distribute state.

Step 6: handle idempotency downstream

Even with deduplication, consider idempotency in the target system (e.g.: upserts by event_id). If the target supports native deduplication (e.g.: unique indexes), combine both approaches for greater robustness.

Verify the result

Test with a small batch of events where some share the same event_id within the TTL window: expect to receive only the first. Processing logs should show 'process' for the first and 'drop' for the duplicates. Check metrics: number of ingested events vs processed events. If you use a SQL engine, run queries over the output and confirm that each event_id appears at most once per window.

Conclusion

You implemented deduplication in Real-Time Analytics using windows and/or state with TTL — this reduces false counts and improves data integrity. Next steps: integrate with your messaging system (Kafka/Event Hubs), test under load and tune the TTL. Tip: what is the trade-off between a short TTL and losing legitimate events — which TTL makes sense for your case?