<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tsofnat M</title>
    <description>The latest articles on DEV Community by Tsofnat M (@tsofnat_m).</description>
    <link>https://dev.to/tsofnat_m</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3655536%2Fe9298d03-d44d-4b09-819e-ef7536a03f57.png</url>
      <title>DEV Community: Tsofnat M</title>
      <link>https://dev.to/tsofnat_m</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tsofnat_m"/>
    <language>en</language>
    <item>
      <title>How to Guarantee True Ordering in Complex Kafka Replays: Solving the Determinism Nightmare</title>
      <dc:creator>Tsofnat M</dc:creator>
      <pubDate>Wed, 10 Dec 2025 13:58:32 +0000</pubDate>
      <link>https://dev.to/tsofnat_m/how-to-guarantee-true-ordering-in-complex-kafka-replays-solving-the-determinism-nightmare-5d03</link>
      <guid>https://dev.to/tsofnat_m/how-to-guarantee-true-ordering-in-complex-kafka-replays-solving-the-determinism-nightmare-5d03</guid>
      <description>&lt;p&gt;בס"ד&lt;br&gt;
In modern event-driven architectures, &lt;strong&gt;data replay&lt;/strong&gt; is a cornerstone mechanism for incident analysis, data correction, or disaster recovery. But behind the seemingly simple task of "fetch events and send to Kafka," lies a complex world of ordering and consistency challenges.&lt;/p&gt;

&lt;p&gt;The challenge isn't the replay itself, but &lt;strong&gt;guaranteeing true chronological order&lt;/strong&gt; in an asynchronous environment. A single mistake in sequencing can lead to corrupted data, race conditions, and a failed recovery.&lt;/p&gt;

&lt;p&gt;This post dives deep into the significant determinism issues we faced and the architectural solutions we implemented to ensure a reliable replay.&lt;/p&gt;


&lt;h2&gt;
  
  
  The System Architecture
&lt;/h2&gt;

&lt;p&gt;To provide context, our replay system consists of the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Frontend:&lt;/strong&gt; For range selection and monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Service:&lt;/strong&gt; Generates replay commands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worker Service:&lt;/strong&gt; Queries backups and publishes to Kafka.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target Data Processor:&lt;/strong&gt; An asynchronous service that processes events using a thread-per-action model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket:&lt;/strong&gt; For real-time progress tracking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmpyge3gp983i3qj1jib.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmpyge3gp983i3qj1jib.png" alt="Replay flow frontend" width="692" height="330"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfp8yhkxss1s7tvd4edr.png" alt="Replay flow backend" width="692" height="346"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Challenge #1: The Async Order Problem
&lt;/h2&gt;

&lt;p&gt;The first step in the Replay process seems simple: fetching data from backup based on a date range and sending it to Kafka. In practice, &lt;strong&gt;the order in which events are sent is critical&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ⚠️ Race Conditions due to Asynchronous Processing
&lt;/h3&gt;

&lt;p&gt;Our target service (Data Processor) operates &lt;strong&gt;asynchronously&lt;/strong&gt;, opening a separate thread for each operation (CREATE/UPDATE/DELETE) and lacking a built-in internal ordering mechanism.&lt;/p&gt;

&lt;p&gt;If relevant events are not sent in the correct sequence (e.g., an &lt;code&gt;UPDATE&lt;/code&gt; before a &lt;code&gt;CREATE&lt;/code&gt; for the same entity), this can lead to &lt;strong&gt;Race Conditions&lt;/strong&gt; and improper data processing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd97v38k14c83n593u65.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd97v38k14c83n593u65.jpg" alt="Problem flow - race conditions diagram" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also considered an alternative proposal: processing all &lt;code&gt;CREATE&lt;/code&gt; operations first, then all &lt;code&gt;UPDATE&lt;/code&gt;s, and finally the &lt;code&gt;DELETE&lt;/code&gt;s. Even with this approach, improper event processing can still occur. For example, consider a unique &lt;code&gt;NAME&lt;/code&gt; column. What happens when an entity is created, deleted, and then created again with the exact same name?&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2f9ah21s8vt55lnu2kn.jpg" alt="Problem flow - unique constraint flaw diagram" width="800" height="380"&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Solution: Synchronous Replay Pattern
&lt;/h3&gt;

&lt;p&gt;The only way to guarantee full determinism at the publishing level is to enforce &lt;strong&gt;Synchronous Behavior&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Send a message to Kafka.&lt;/li&gt;
&lt;li&gt; Wait for an acknowledgment from the target service that processing is complete.&lt;/li&gt;
&lt;li&gt; Only then move to the next message.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Trade-off Note:&lt;/strong&gt; This guarantees order but imposes a high cost on Latency and significantly reduces Throughput.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl25xogi80o16dgbkjpci.jpg" alt="Synchronous replay flow diagram" width="800" height="266"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Challenge #2: The Failure of Timestamps (Global Ordering)
&lt;/h2&gt;

&lt;p&gt;Once we adopted a synchronous model, we needed a definitive order to query our backup database. Relying on Kafka's default Timestamp proved insufficient for several reasons. The immediate idea is to sort by the Timestamp provided by Kafka, but this is insufficient.&lt;/p&gt;

&lt;p&gt;Two main problems undermine the reliability of using timestamps as the sole sorting tool:&lt;/p&gt;
&lt;h3&gt;
  
  
  Timestamp Ties
&lt;/h3&gt;

&lt;p&gt;When traffic is high, multiple events can receive the &lt;strong&gt;exact same timestamp&lt;/strong&gt; (typically measured in milliseconds/microseconds). The sorting creates a "tie," and the system is forced to break it using an arbitrary rule (such as database query order), which can result in an incorrect and non-deterministic sequence.&lt;/p&gt;
&lt;h3&gt;
  
  
  Clock Skew
&lt;/h3&gt;

&lt;p&gt;Every server in the network has its own clock, and even with the use of NTP, small deviations exist. The practical implication is that an event that logically occurred &lt;strong&gt;later&lt;/strong&gt; (in real-time) might receive an earlier timestamp because the clock of the machine that generated it is slower.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Solution: Global Offset via Postgres Sequence
&lt;/h3&gt;

&lt;p&gt;Since Kafka lacks a built-in continuous global index across all topics, we created one.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Every message published through our API requests a new &lt;strong&gt;Sequence Number&lt;/strong&gt; from PostgreSQL.&lt;/li&gt;
&lt;li&gt; This atomic integer is embedded in the Kafka Header as a &lt;code&gt;global_offset&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This provides a stable, source-of-truth order independent of machine clocks.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a3coyo0szkuoa58kjys.jpg" alt="Global offset flow diagram" width="800" height="676"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Challenge #3: Consistency and Order in Optimization Sub-Replays (Overlapping Replay Ranges)
&lt;/h2&gt;

&lt;p&gt;Another ordering problem can occur when a user selects a date range for a replay, and within that range, previous sub-replays have already been executed. To maintain a complete history, we would want to include and replay these sub-ranges as well.&lt;/p&gt;

&lt;p&gt;The problem arises when we arrange all relevant ranges on a timeline and discover &lt;strong&gt;overlapping ranges&lt;/strong&gt;. While we want to optimize and avoid processing the same ranges more than once, we must preserve the true order of events. This means the overlapping data must be processed according to the &lt;strong&gt;latest source&lt;/strong&gt; that modified it within the overlapping window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxq07mccsolkf7nnnopy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxq07mccsolkf7nnnopy.jpg" alt="Overlapping problem description diagram" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Last-Wins Resolution
&lt;/h3&gt;

&lt;p&gt;To prevent duplication (Optimization) while ensuring correct processing order, we use the &lt;strong&gt;'Last-Wins' logic&lt;/strong&gt; as a Pre-Processing step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The algorithm resolves overlapping ranges using 'Last-Wins' logic instead of processing redundant data.&lt;/li&gt;
&lt;li&gt;This ensures that every timestamp is processed only once.&lt;/li&gt;
&lt;li&gt;Processing is carried out only by the &lt;strong&gt;latest source&lt;/strong&gt; that created the change in the overlapping time range.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Algorithm Steps (High Level)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; All ranges are sorted by their &lt;strong&gt;creation time&lt;/strong&gt; (Crucial for Last-Wins logic).&lt;/li&gt;
&lt;li&gt; Iterate through this sorted list.&lt;/li&gt;
&lt;li&gt; For each new range, search for overlaps with existing (preceding) ranges.&lt;/li&gt;
&lt;li&gt; Trim and merge existing ranges, and insert the new range into a new list with higher precedence.&lt;/li&gt;
&lt;li&gt; Finally, the result is sorted by precedence for the final execution order.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9ba7u92qt12nhtr7cfv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9ba7u92qt12nhtr7cfv.jpg" alt="last-wins resolution flow diagram" width="800" height="245"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SplitRange&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SplitOverlappingRanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;rootRunId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;allRuns&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetOverlappingRunsRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rootRunId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Sort by Creation Time (Crucial for "Last-Wins" logic)&lt;/span&gt;
    &lt;span class="n"&gt;allRuns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatedAt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatedAt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;splitRanges&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TreeDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SplitRange&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;currentRangeIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;allRuns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Handle Predecessor (Trim the tail of the previous range)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;splitRanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToDate&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToDate&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;AddSplitSegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;splitRanges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTicks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTicks&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Trim existing&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Handle Successors (Remove or trim future overlapping ranges)&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;splitRanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetSuccessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromDate&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToDate&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;AddSplitSegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;splitRanges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTicks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;splitRanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Remove completely overlapped part&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Insert the current "Winning" Range&lt;/span&gt;
        &lt;span class="n"&gt;splitRanges&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SplitRange&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;RunId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;FromDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ToDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;RangeIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentRangeIndex&lt;/span&gt;&lt;span class="p"&gt;++&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;splitRanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RangeIndex&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ThenBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a truly deterministic data replay system requires moving beyond the basic assumptions of Kafka and timestamps. By enforcing &lt;strong&gt;Synchronous Processing&lt;/strong&gt;, implementing a &lt;strong&gt;Global Offset Sequence&lt;/strong&gt;, and using a &lt;strong&gt;Last-Wins Pre-Processing Algorithm&lt;/strong&gt;, we were able to guarantee absolute chronological order.&lt;/p&gt;

&lt;p&gt;Thank you for reading! What are your strategies for dealing with data replay order in large-scale event systems? If you have any questions about the implementation of the Range Splitter or managing Global Offsets in Postgres, I'd be happy to discuss them in the comments.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>infrastructure</category>
      <category>systemdesign</category>
      <category>eventdriven</category>
    </item>
  </channel>
</rss>
