<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>article Deep Dive - Streaming Deduplication in Technical Blog</title>
    <link>https://community.databricks.com/t5/technical-blog/deep-dive-streaming-deduplication/ba-p/105062</link>
    <description>&lt;P&gt;&lt;SPAN&gt;In this article we will cover in depth about &lt;/SPAN&gt;&lt;A href="https://spark.apache.org/docs/3.5.3/structured-streaming-programming-guide.html#streaming-deduplication" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;streaming deduplication&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; using &lt;/SPAN&gt;&lt;A href="https://spark.apache.org/docs/3.5.3/structured-streaming-programming-guide.html#handling-late-data-and-watermarking" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;watermarking&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; with &lt;/SPAN&gt;&lt;A href="https://jaceklaskowski.gitbooks.io/spark-structured-streaming/content/spark-sql-streaming-Dataset-dropDuplicates.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; and &lt;/SPAN&gt;&lt;A href="https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.DataFrame.dropDuplicatesWithinWatermark.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;dropDuplicatesWithinWatermark&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;, how they are different. This blog expects you to have a good understanding on how &lt;/SPAN&gt;&lt;A href="https://www.databricks.com/blog/feature-deep-dive-watermarking-apache-spark-structured-streaming" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;watermarking&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; works in Spark Structured streaming. Lets dive right in.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;When &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; is used in conjunction with &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;withWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; operator, it maintains a global state, the stream is unbounded and will keep all the keys across triggers, i.e., global deduplication. Whatever watermark is specified using &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;withWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; makes the stream to filter out the late arriving records, i.e., records with &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt; &lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; get discarded at the beginning of the micro-batch if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. This approach is generally not recommended; an unbounded state means that the state store grows continuously, potentially causing out-of-memory issues. Let's look at an example.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Create a source table to stream from:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;drop table if exists mt_asqs.demo.dd_source;
create table mt_asqs.demo.dd_source (
 event_id string,
 event_time timestamp
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Streaming code:&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Note that we are using &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; as the watermarking column and not including the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column as deduplication key in &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; function.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;volume_path = '/Volumes/mt_asqs/demo/checkpoint_paths'
stream_df = (
 spark.readStream.format("delta")
   .table("mt_asqs.demo.dd_source")
   .withWatermark("event_time", "5 minutes")
   .dropDuplicates(["event_id"])
)
(
 stream_df.writeStream
   .format("delta")
   .option("checkpointLocation", f"{volume_path}/dd_test3")
   .table("mt_asqs.demo.dd_output")
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Inserting records into streaming source table&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source values ('A', to_timestamp('2024-01-01 10:00:00'));
insert into dd_source values ('B', to_timestamp('2024-01-01 10:00:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Since the watermark delay is set to 5 minutes, above records set the watermark to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;9:55.&amp;nbsp; &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;The state store would have 2 keys A &amp;amp; B.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="markup"&gt;insert into dd_source values ('C', to_timestamp('2024-01-01 10:07:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Watermarking advances to 10:02, C gets inserted into the state store. Ideally since the watermarking advanced to 10:02, Keys A &amp;amp; B should get evicted from the state store but that doesn’t happen with &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;, this makes global deduplication possible. Viewing the contents of the state store.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;display(spark.read.format("statestore").load("/Volumes/mt_asqs/demo/checkpoint_paths/dd_test3"))&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_0-1736447301690.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14014i7983ED4927C0D445/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_0-1736447301690.png" alt="MuraliTalluri_0-1736447301690.png" /&gt;&lt;/span&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_1-1736447301693.png" style="width: 303px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14012i7989C263907C8405/image-dimensions/303x168?v=v2" width="303" height="168" role="button" title="MuraliTalluri_1-1736447301693.png" alt="MuraliTalluri_1-1736447301693.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source values ('A', to_timestamp('2024-01-01 10:01:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Since watermarking is set to 10:02, the above record A gets discarded at the beginning of the micro-batch. At this point record A is not even compared to state store for deduplication since it's discarded prior to that.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source values ('D', to_timestamp('2024-01-01 10:10:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Advances the watermark to 10:05, D gets appended to the state store.&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source values ('A', to_timestamp('2024-01-01 10:12:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;The above record gets checked against the watermark, since it's not a late arriving record it won’t be discarded. This record advances watermarking time to 10:07, but since the key(A) already exists in the state store it gets discarded in the deduplication process.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source values ('E', to_timestamp('2024-01-01 10:07:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;E gets discarded due to the watermark(10:07). Contents of the streaming output table shows that there are no duplicate &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_ids&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_2-1736447301693.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14013i2CA2C80C2877983B/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_2-1736447301693.png" alt="MuraliTalluri_2-1736447301693.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;dropDuplicatesWithinWatermark&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicatesWithinWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; does exactly what you would expect it to do, this is what you should use for deduplication when you know the time window within which you expect duplicate records in your stream. When &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicatesWithinWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; is used in conjunction with &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;withWatermark &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;delay &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;D&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;, a record with event_time &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; gets removed from the state store only when the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;D&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. But the record will be discarded(late arriving) at the beginning of the micro-batch if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. Let's look at an example.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Create a source table to stream from:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;drop table if exists mt_asqs.demo.ddww_source;
create table mt_asqs.demo.ddww_source (
 event_id string,
 event_time timestamp
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Streaming code:&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;It’s basically the same as above but using the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicatesWithinWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; instead of &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;stream_df = (
 spark.readStream.format("delta")
   .table("mt_asqs.demo.ddww_source")
   .withWatermark("event_time", "5 minutes")
   .dropDuplicatesWithinWatermark(["event_id"])
)
volume_path = '/Volumes/mt_asqs/demo/checkpoint_paths'
(
 stream_df.writeStream
   .format("delta")
   .option("checkpointLocation", f"{volume_path}/ddww_test")
   .table("mt_asqs.demo.ddww_output")
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Inserting records into streaming source table&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into ddww_source values('A',to_timestamp('2024-01-01 10:00:00'));
insert into ddww_source values('B',to_timestamp('2024-01-01 10:00:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Since the watermarking delay is set to 5 minutes, the watermark advances to 9:55. The state store would have 2 keys A &amp;amp; B.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into ddww_source values('C',to_timestamp('2024-01-01 10:07:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Watermark advances to 10:02, C gets inserted into the state store. At the end of every micro-batch, existing records in the state store are checked if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + watermark delay (5 minutes). So both A &amp;amp; B prevail.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Viewing the contents of the state store, you can see that every key now has expiration time.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;display(spark.read.format("statestore").load("/Volumes/mt_asqs/demo/checkpoint_paths/ddww_test"))&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_3-1736447301693.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14015iCFDF4454BBFAB10D/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_3-1736447301693.png" alt="MuraliTalluri_3-1736447301693.png" /&gt;&lt;/span&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_4-1736447301693.png" style="width: 328px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14017iA8434F295107DD23/image-dimensions/328x188?v=v2" width="328" height="188" role="button" title="MuraliTalluri_4-1736447301693.png" alt="MuraliTalluri_4-1736447301693.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into ddww_source values('A',to_timestamp('2024-01-01 10:01:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Since watermarking is set to 10:02, the above record A gets discarded at the beginning of the micro-batch. At this point record A is not even compared to the state store for deduplication since it's discarded prior to that.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into ddww_source values('D',to_timestamp('2024-01-01 10:09:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Watermark advances to 10:04, D gets inserted into the state store. Existing records in the state store are checked if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + watermark delay (5 minutes). So all the records prevail.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_5-1736447301691.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14016i09B05DC263C1D751/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_5-1736447301691.png" alt="MuraliTalluri_5-1736447301691.png" /&gt;&lt;/span&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_6-1736447301694.png" style="width: 359px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14018i15791C60446AC55E/image-dimensions/359x216?v=v2" width="359" height="216" role="button" title="MuraliTalluri_6-1736447301694.png" alt="MuraliTalluri_6-1736447301694.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into ddww_source values('A',to_timestamp('2024-01-01 10:12:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Watermark advances to 10:07. Since A already exists in the state store, the new record is considered as duplicate and gets discarded. Existing records in the state store are checked if the watermark(10:07) is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + watermark delay (5 minutes). So C &amp;amp; D prevails, A &amp;amp; B gets evicted from the state store.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_7-1736447301693.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14020i36C8A3E8FF2D1117/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_7-1736447301693.png" alt="MuraliTalluri_7-1736447301693.png" /&gt;&lt;/span&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_8-1736447301692.png" style="width: 331px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14019i98330AA333F3774B/image-dimensions/331x196?v=v2" width="331" height="196" role="button" title="MuraliTalluri_8-1736447301692.png" alt="MuraliTalluri_8-1736447301692.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into ddww_source values('E',to_timestamp('2024-01-01 10:17:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Watermark advances to 10:12, E gets inserted into the state store. Existing records in the state store are checked if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + watermark delay (5 minutes). So D prevails and C gets evicted from the state store.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_9-1736447301690.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14021iFA120A71C41A5738/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_9-1736447301690.png" alt="MuraliTalluri_9-1736447301690.png" /&gt;&lt;/span&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_10-1736447301692.png" style="width: 352px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14022iBFBAC43F5712AAE1/image-dimensions/352x183?v=v2" width="352" height="183" role="button" title="MuraliTalluri_10-1736447301692.png" alt="MuraliTalluri_10-1736447301692.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into ddww_source values('C',to_timestamp('2024-01-01 10:13:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;The watermark still remains at 10:12. Since C doesn’t exist in the state store, it won’t be considered a duplicate record. So C gets inserted into the state store. Existing records in the state store are checked if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + watermark delay (5 minutes). So D &amp;amp; E prevails.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_11-1736447301691.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14023i54861B73830A0EF9/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_11-1736447301691.png" alt="MuraliTalluri_11-1736447301691.png" /&gt;&lt;/span&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_12-1736447301691.png" style="width: 332px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14024i96BF31732D61D45B/image-dimensions/332x170?v=v2" width="332" height="170" role="button" title="MuraliTalluri_12-1736447301691.png" alt="MuraliTalluri_12-1736447301691.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Contents of the streaming output table shows that there is one duplicate entry for &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_id&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; C. This would have been avoided if you had a larger watermark window, like 10 minutes.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_13-1736447301691.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14026iFF48D36A7687F792/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_13-1736447301691.png" alt="MuraliTalluri_13-1736447301691.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;dropDuplicates - &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;as deduplication key&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;We covered above where you specify &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;as the watermarking column but don’t include it as a deduplication key(parameter) in the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates. &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;But when event_time is specified as one of the deduplication keys, &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;won’t maintain an unbounded state, i.e., keys get evicted from the state store. If you set &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;withWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; delay &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;D,&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; a record with event_time &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; gets removed from the state store when the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. Records will be checked at the beginning of the micro-batch execution and discarded(late arriving) if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. Let's look at an example.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Create a source table to stream from:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;drop table if exists mt_asqs.demo.dd_source2;
create table mt_asqs.demo.dd_source2 (
 event_id string,
 event_time timestamp
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Streaming code:&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Note that we are using &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; as the watermarking column and it's also used as the deduplication key in the dropDuplicates function.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;stream_df = (
 spark.readStream.format("delta")
   .table("mt_asqs.demo.dd_source2")
   .withWatermark("event_time", "5 minutes")
   .dropDuplicates(["event_id", "event_time"])
)
volume_path = '/Volumes/mt_asqs/demo/checkpoint_paths'
(
 stream_df.writeStream
   .format("delta")
   .option("checkpointLocation", f"{volume_path}/dd_test2")
   .table("mt_asqs.demo.dd_output2")
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Inserting records into streaming source table&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source2 values ('A',to_timestamp('2024-01-01 10:00:00'));
insert into dd_source2 values ('B',to_timestamp('2024-01-01 10:00:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Since the watermarking delay is set to 5 minutes, above records set the watermark to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;9:55.&amp;nbsp; &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;The state store would have 2 records, A &amp;amp; B.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source2 values ('C',to_timestamp('2024-01-01 10:07:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Watermark advances to 10:02, C gets inserted into the state store. At the end of every micro-batch, existing records in the state store are checked if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. So both A &amp;amp; B get evicted from the state store.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Viewing the contents of the state store.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;display(spark.read.format("statestore").load("/Volumes/mt_asqs/demo/checkpoint_paths/dd_test2"))&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_14-1736447301693.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14025i606D42C56C7DD1D8/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_14-1736447301693.png" alt="MuraliTalluri_14-1736447301693.png" /&gt;&lt;/span&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_15-1736447301691.png" style="width: 334px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14027i51EB48FAC18A2F97/image-dimensions/334x203?v=v2" width="334" height="203" role="button" title="MuraliTalluri_15-1736447301691.png" alt="MuraliTalluri_15-1736447301691.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source2 values ('A',to_timestamp('2024-01-01 10:01:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Since watermarking is set to 10:02, the above record A gets discarded at the beginning of the micro-batch. At this point record A is not even compared to state store for deduplication since it's discarded prior to that.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source2 values ('C',to_timestamp('2024-01-01 10:09:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Advances the watermark to 10:04. C gets appended to the state store since &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; is also a deduplication key. The state store will have multiple rows for C.&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_16-1736447301692.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14028i3FFCD2F5A7EBD547/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_16-1736447301692.png" alt="MuraliTalluri_16-1736447301692.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source2 values ('D',to_timestamp('2024-01-01 10:10:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Advances the watermark to 10:05, D gets appended to the state store. Existing records in the state store are checked if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. Both the entries for C prevails.&amp;nbsp; The state store will have 3 entries(2 for C and 1 for D).&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;insert into dd_source2 values ('A',to_timestamp('2024-01-01 10:12:00'));&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Watermark advances to 10:07, A gets inserted into the state store. Existing records in the state store are checked if the watermark is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;. One of the entries for C (event_time 10:07) gets evicted while the other prevails.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Viewing the contents of the state store.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_17-1736447301692.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14029i0B93E2B978457D52/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_17-1736447301692.png" alt="MuraliTalluri_17-1736447301692.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Contents of the streaming output table.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MuraliTalluri_18-1736447301693.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14030iF43F2BACC0CC1E95/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MuraliTalluri_18-1736447301693.png" alt="MuraliTalluri_18-1736447301693.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;Conclusion&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;Each of these approaches solve different problems.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Approach&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Scenario&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;State store behavior&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;withWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Global deduplication and don’t expect the duplicates with the same &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;- Keep all the keys in the state store.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;- The state store grows continuously, potentially causing out-of-memory issues.&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicatesWithinWatermark &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;+ &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;withWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;- When you know the interval of time within which you might have duplicate records.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;- Deduplication within the specified time interval.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;- State store grows/shrinks based on the watermark delay.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;- For a watermark delay &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;D&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;, a record with event_time &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; gets removed from the state store only when the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;watermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;D&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;I&gt;&lt;SPAN&gt;dropDuplicates&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; + &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;withWatermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;, &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; is one of the deduplication keys.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;- When you expect the stream to have duplicates with the same &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;event_time&lt;/SPAN&gt;&lt;/I&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;- State store grows/shrinks based on the watermark delay.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;- For a watermark delay &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;D&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;, a record with event_time &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; gets removed from the state store only when the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;watermark&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; is greater than or equal to &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;T&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Sat, 11 Jan 2025 03:43:11 GMT</pubDate>
    <dc:creator>MuraliTalluri</dc:creator>
    <dc:date>2025-01-11T03:43:11Z</dc:date>
    <item>
      <title>Deep Dive - Streaming Deduplication</title>
      <link>https://community.databricks.com/t5/technical-blog/deep-dive-streaming-deduplication/ba-p/105062</link>
      <description>&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="streaming_deduplication_watermarking.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14011i06436D4C43D28AC5/image-size/large?v=v2&amp;amp;px=999" role="button" title="streaming_deduplication_watermarking.png" alt="streaming_deduplication_watermarking.png" /&gt;&lt;/span&gt;&lt;/P&gt;</description>
      <pubDate>Sat, 11 Jan 2025 03:43:11 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/deep-dive-streaming-deduplication/ba-p/105062</guid>
      <dc:creator>MuraliTalluri</dc:creator>
      <dc:date>2025-01-11T03:43:11Z</dc:date>
    </item>
    <item>
      <title>Re: Deep Dive - Streaming Deduplication</title>
      <link>https://community.databricks.com/t5/technical-blog/deep-dive-streaming-deduplication/bc-p/109569#M464</link>
      <description>&lt;P&gt;Hi &lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/48887"&gt;@MuraliTalluri&lt;/a&gt;,&lt;/P&gt;&lt;P&gt;Thank you for such a detailed article.&lt;/P&gt;&lt;P&gt;I am following &lt;EM&gt;dropDuplicatesWithinWatermark&lt;/EM&gt; with the same steps as yours. The only difference is that I am using autoloader and reading CSV files as the source and writing data to a Delta table. My stream is using &lt;EM&gt;trigger(once=True)&lt;/EM&gt;. I upload the source file and observe the behavior after triggering the code.&lt;/P&gt;&lt;DIV&gt;&lt;DIV&gt;&amp;nbsp;&lt;/DIV&gt;&lt;DIV&gt;While inserting the following record, I noticed that it got dropped due to deduplication, not because of the watermark, even though its event time is less than the watermark threshold (i.e., 10:01 &amp;lt; 10:02).&lt;/DIV&gt;&lt;DIV&gt;&amp;nbsp;&lt;/DIV&gt;&lt;/DIV&gt;&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="p2.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/14729i0DAFC3A8A39FABA1/image-size/large?v=v2&amp;amp;px=999" role="button" title="p2.png" alt="p2.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I inserted another record mimicking the same scenario as the above record, and this time, it was dropped due to the watermark as expected.&lt;/P&gt;&lt;P&gt;Could you please guide me on why there is this inconsistency in behavior?&lt;/P&gt;&lt;P&gt;Thanks&lt;/P&gt;</description>
      <pubDate>Mon, 10 Feb 2025 06:13:42 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/deep-dive-streaming-deduplication/bc-p/109569#M464</guid>
      <dc:creator>Muhammad_Umer</dc:creator>
      <dc:date>2025-02-10T06:13:42Z</dc:date>
    </item>
    <item>
      <title>Re: Deep Dive - Streaming Deduplication</title>
      <link>https://community.databricks.com/t5/technical-blog/deep-dive-streaming-deduplication/bc-p/122443#M654</link>
      <description>&lt;P&gt;The semantics of watermarking any records that are older than watermark threshold may or may not get processed. In your case, the first attempt may have processed the record even thought it is older than watermark threshold. Reference -&amp;nbsp;&lt;A href="https://spark.apache.org/docs/latest/streaming/apis-on-dataframes-and-datasets.html#semantic-guarantees-of-stream-stream-inner-joins-with-watermarking" target="_blank"&gt;https://spark.apache.org/docs/latest/streaming/apis-on-dataframes-and-datasets.html#semantic-guarantees-of-stream-stream-inner-joins-with-watermarking&lt;/A&gt;&lt;/P&gt;</description>
      <pubDate>Sat, 21 Jun 2025 20:10:31 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/deep-dive-streaming-deduplication/bc-p/122443#M654</guid>
      <dc:creator>Sai_Nandam</dc:creator>
      <dc:date>2025-06-21T20:10:31Z</dc:date>
    </item>
  </channel>
</rss>

