<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Oxyprogrammer's blog]]></title><description><![CDATA[Shaping robust architectures for innovative solutions that empower businesses to thrive.]]></description><link>https://oxyprogrammer.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 13:31:25 GMT</lastBuildDate><atom:link href="https://oxyprogrammer.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Practical Approach to learn memory optimization using goroutines]]></title><description><![CDATA[Introduction
After writing Tenets of Multithreading in Go: Detailed Tutorial, I felt compelled to share a problem I had to tackle during my work as a software engineer. Real-life software engineering often involves solving unique challenges, each wit...]]></description><link>https://oxyprogrammer.com/practical-approach-to-learn-memory-optimization-using-goroutines</link><guid isPermaLink="true">https://oxyprogrammer.com/practical-approach-to-learn-memory-optimization-using-goroutines</guid><category><![CDATA[green threading]]></category><category><![CDATA[thread optimization]]></category><category><![CDATA[golang]]></category><category><![CDATA[multithreading]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Fri, 16 May 2025 11:09:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747393747973/2d1f5c13-0f0a-48cc-afc7-53850d67f86f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>After writing <a target="_blank" href="https://oxyprogrammer.com/tenets-of-multithreading-in-go-detailed-tutorial">Tenets of Multithreading in Go: Detailed Tutorial</a>, I felt compelled to share a problem I had to tackle during my work as a software engineer. Real-life software engineering often involves solving unique challenges, each with its own narrative.</p>
<p>In this article, we will analyze four different approaches to a specific problem step-by-step. We will evaluate each solution, weighing the pros and cons, to demonstrate that there is no “one-size-fits-all” approach, as the best solution depends on the circumstances at hand.</p>
<p>By the end, we will compare all four approaches and make an informed choice based on our case.</p>
<h1 id="heading-the-problem">The problem</h1>
<p>The problem itself is not overly complicated. We had an NFS file location where numerous CSV files were being generated by an upstream process over which we had no control.</p>
<p>The task was to set up a cron job that would execute at regular intervals to check for newly created files. If new files were found, the job would upload their contents into a database and subsequently move the files to an archive location. A new instance of the cron job would be launched for each file detected. The requirement was to create a Go program to perform the cron job tasks.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXf847UwqnbGbMyW-iTw4bEqCPll04POyUttSbqBNksIP-isezJKpXN-ZzfS-sLvAGuHiTCc3fsFdw6W-xgw5Thn2E_U7AXxC-S0f6OnfUU3_elo4IVgL-TVvFpdzAAM5y6TSS8DKg?key=JbTeKPAQpA2r0dujoXXrO6oU" alt class="image--center mx-auto" /></p>
<h1 id="heading-code-walkthrough">Code Walkthrough</h1>
<p>To test the various approaches, I created an API application in Go. <a target="_blank" href="https://github.com/OxyProgrammer/go-file-uploader">Here is the repository</a>. When you run the application for the first time, it creates a sample CSV file with 10 million records (approximately 0.5 GB).</p>
<p>The API provides four endpoints:</p>
<ul>
<li><p><code>GET /solution-one</code></p>
</li>
<li><p><code>GET /solution-two</code></p>
</li>
<li><p><code>GET /solution-three</code></p>
</li>
<li><p><code>GET /solution-four</code></p>
</li>
</ul>
<p>All of these endpoints perform the same tasks:</p>
<ol>
<li><p>Read all lines from the file.</p>
</li>
<li><p>Convert to read entities.</p>
</li>
<li><p>Transform into write entities.</p>
</li>
<li><p>Insert the write entities into memory.</p>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeVrQ9UE57OY5KlOyig3WTpNRMJLeILxfZNGKQoL6bbvBLmnIBnA-3K7TtFGE_d8Bb3Pkobylt_ccWNBEO7wG1BV7Za7JXUZwNJMNrIMPdMD-bACIpQMo0KitEcm0WLCZ6x69gu?key=JbTeKPAQpA2r0dujoXXrO6oU" alt class="image--center mx-auto" /></p>
<p>The application uses GORM as an ORM to facilitate easy bulk insert functionalities, configured to insert in batches of 1000. Each endpoint returns a response that includes:</p>
<ul>
<li><p>Time taken for the solution.</p>
</li>
<li><p>Memory consumed for the operation.</p>
</li>
</ul>
<p>The database used in the application is SQLite, which allows only one connection at a time, creating a bottleneck during write operations. For databases like PostgreSQL, the readings gathered from the runs of different solutions may yield different results, especially for the multithreaded approaches.</p>
<h1 id="heading-solution-1-brute-force">Solution 1: Brute force</h1>
<p>The brute force method involves the following steps:</p>
<ol>
<li><p>Read all lines from the file into memory.</p>
</li>
<li><p>Convert read entities and hold them in memory.</p>
</li>
<li><p>Transform all read entities into write entities.</p>
</li>
<li><p>Insert all write entities into the database.</p>
</li>
</ol>
<p>Here’s the straightforward code:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">LoadAllAndInsert</span><span class="hljs-params">(database *db.DB)</span> <span class="hljs-title">error</span></span> {
    landReadModels, err := utils.ReadCSVAll(<span class="hljs-string">"data/land_feed.csv"</span>)
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(err) &gt; <span class="hljs-number">0</span> {
        log.Fatal(err)
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"Some error happened. Check logs."</span>)
    }


    <span class="hljs-keyword">var</span> dbLandModels []*models.Land


    <span class="hljs-keyword">for</span> _, landReadModel := <span class="hljs-keyword">range</span> landReadModels {
        dbLandModels = <span class="hljs-built_in">append</span>(dbLandModels, models.FromReadModel(landReadModel))
    }


    eror := database.CreateLands(dbLandModels)
    <span class="hljs-keyword">return</span> eror
}
</code></pre>
<p>The response received is as follows:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"error"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Successfully completed request for Load All And Insert In Batches."</span>,
    <span class="hljs-attr">"memoryUsage"</span>: <span class="hljs-number">3296896352</span>,
    <span class="hljs-attr">"elapsed"</span>: <span class="hljs-number">18968041000</span>
}
</code></pre>
<h1 id="heading-solution-2-buffered-inserts-along-with-reading">Solution 2: Buffered inserts along with reading</h1>
<p>This method utilizes buffered inserts while reading:</p>
<ol>
<li><p>Read a line from the file and convert it to a read entity.</p>
</li>
<li><p>Convert the read entity into a write entity.</p>
</li>
<li><p>Push the write entity into a buffer.</p>
</li>
<li><p>If the buffer size is full, perform a bulk insert into the database; if not, continue populating the buffer.</p>
</li>
</ol>
<p>Here’s the code:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ReadLineAndAndInsertInBatches</span><span class="hljs-params">(database *db.DB)</span> <span class="hljs-title">error</span></span> {
    file, err := os.Open(<span class="hljs-string">"data/land_feed.csv"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }
    <span class="hljs-keyword">defer</span> file.Close()
    <span class="hljs-comment">// Create a CSV reader</span>
    reader := csv.NewReader(file)

    <span class="hljs-comment">//Read the headers</span>
    _, _ = reader.Read()
    lineNumber := <span class="hljs-number">1</span>
    <span class="hljs-keyword">var</span> buffer []*models.Land

    <span class="hljs-keyword">for</span> {
        record, err := reader.Read()
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">if</span> err == io.EOF {
                <span class="hljs-keyword">break</span>
            }
            <span class="hljs-keyword">return</span> err
        }
        readEntity, err := utils.CreateEntityFromRecord(record, lineNumber)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> err
        }
        dbEntity := models.FromReadModel(*readEntity)
        buffer = <span class="hljs-built_in">append</span>(buffer, dbEntity)
        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(buffer) == <span class="hljs-number">10000</span> {
            database.CreateLands(buffer)
            buffer = buffer[:<span class="hljs-number">0</span>]
        }
        lineNumber++
    }
    <span class="hljs-comment">// Flush any remaining entities in the buffer</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(buffer) &gt; <span class="hljs-number">0</span> {
        database.CreateLands(buffer)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>The response received is as follows:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"error"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Successfully completed request for Read Line And Insert In Batches."</span>,
    <span class="hljs-attr">"memoryUsage"</span>: <span class="hljs-number">3851704</span>,
    <span class="hljs-attr">"elapsed"</span>: <span class="hljs-number">26142007900</span>
}
</code></pre>
<h1 id="heading-solution-3-using-goroutines-to-optimize-worker-pool">Solution 3: Using goroutines to optimize: Worker Pool</h1>
<p>To further optimize the process, I implemented goroutines with a worker pool approach. This method employs two channels: <code>readChannel</code> for read entities and <code>doneChannel</code> for signaling completion. The process consists of the following:</p>
<ol>
<li><p>A Producer goroutine reads each line from the CSV file, converts it into a read entity, and sends it to the <code>readChannel</code>.</p>
</li>
<li><p>Five Consumer goroutines listen to the <code>readChannel</code>, convert each read entity into a write entity, and maintain a buffer of 10,000 write items to be written to the database. When the buffer is full, it is written to the database, and the buffer is cleared.</p>
</li>
<li><p>Once all worker consumers signal completion, the <code>doneChannel</code> is triggered, allowing the parent method to exit.</p>
</li>
</ol>
<p>Here’s the implementation:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">MultiprocessingForReadingAndWriting</span><span class="hljs-params">(database *db.DB)</span> <span class="hljs-title">error</span></span> {

    readCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> *models.LandRead, <span class="hljs-number">1000</span>)
    doneCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    errCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> error)
    <span class="hljs-keyword">const</span> numWriters = <span class="hljs-number">5</span> <span class="hljs-comment">// Number of writing goroutines</span>
    <span class="hljs-keyword">go</span> readAndProduceModelAsync(readCh, errCh)

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">var</span> mutex sync.Mutex

    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; numWriters; i++ {
        wg.Add(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">go</span> writeAndConsumeAsync(database, readCh, errCh, &amp;wg, &amp;mutex)
    }

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        wg.Wait()
        <span class="hljs-built_in">close</span>(doneCh)
    }()

    <span class="hljs-keyword">for</span> {
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> err := &lt;-errCh:
            <span class="hljs-keyword">return</span> err
        <span class="hljs-keyword">case</span> &lt;-doneCh:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">readAndProduceModelAsync</span><span class="hljs-params">(readCh <span class="hljs-keyword">chan</span>&lt;- *models.LandRead, errCh <span class="hljs-keyword">chan</span>&lt;- error)</span></span> {

    <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(readCh)

    file, err := os.Open(<span class="hljs-string">"data/land_feed.csv"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> file.Close()
    <span class="hljs-comment">// Create a CSV reader</span>
    reader := csv.NewReader(file)
    <span class="hljs-comment">//Read the headers</span>
    _, _ = reader.Read()

    lineNumber := <span class="hljs-number">1</span>

    <span class="hljs-keyword">for</span> {
        record, err := reader.Read()
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">if</span> err == io.EOF {
                <span class="hljs-keyword">return</span>
            }
            errCh &lt;- err
            <span class="hljs-keyword">return</span>
        }

        readEntity, err := utils.CreateEntityFromRecord(record, lineNumber)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            errCh &lt;- err
            <span class="hljs-keyword">return</span>
        }

        readCh &lt;- readEntity
        lineNumber++
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeAndConsumeAsync</span><span class="hljs-params">(database *db.DB, readCh &lt;-<span class="hljs-keyword">chan</span> *models.LandRead, errCh <span class="hljs-keyword">chan</span>&lt;- error, wg *sync.WaitGroup, mutex *sync.Mutex)</span></span> {
    <span class="hljs-keyword">defer</span> wg.Done()

    <span class="hljs-keyword">var</span> buffer []*models.Land
    batchSize := <span class="hljs-number">10000</span>

    <span class="hljs-keyword">for</span> readEntity := <span class="hljs-keyword">range</span> readCh {
        dbEntity := models.FromReadModel(*readEntity)
        buffer = <span class="hljs-built_in">append</span>(buffer, dbEntity)


        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(buffer) == batchSize {
            mutex.Lock()
            err := database.CreateLands(buffer)
            mutex.Unlock()
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                errCh &lt;- err
                <span class="hljs-keyword">return</span>
            }
            buffer = buffer[:<span class="hljs-number">0</span>]
        }
    }
    <span class="hljs-comment">// Flush any remaining entities in the buffer</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(buffer) &gt; <span class="hljs-number">0</span> {
        mutex.Lock()
        err := database.CreateLands(buffer)
        mutex.Unlock()
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            errCh &lt;- err
            <span class="hljs-keyword">return</span>
        }
    }
}
</code></pre>
<p>The response received is as follows:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"error"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Successfully completed request for Multiprocessing For Reading And Writing."</span>,
    <span class="hljs-attr">"memoryUsage"</span>: <span class="hljs-number">5289472</span>,
    <span class="hljs-attr">"elapsed"</span>: <span class="hljs-number">24760674200</span>
}
</code></pre>
<h1 id="heading-solution-4-further-squeezing-with-worker-pool-pipeline">Solution 4: Further squeezing with worker pool pipeline</h1>
<p>In this final solution, we enhance the previous design by introducing an additional type of routine to streamline the processing pipeline. The parent method establishes three channels:</p>
<ul>
<li><p><code>recordChannel</code> for raw string records from the file.</p>
</li>
<li><p><code>transformChannel</code> for converted write entities.</p>
</li>
<li><p><code>doneChannel</code> for signaling the completion of the operation.</p>
</li>
</ul>
<p>The approach consists of:</p>
<ol>
<li><p>A Producer routine that reads lines from the CSV file and sends the split strings to the <code>recordChannel</code>.</p>
</li>
<li><p>Five Transforming routines that listen to <code>recordChannel</code>, convert raw records into read entities, and publish the transformed write entities to the <code>transformChannel</code>.</p>
</li>
<li><p>Five Writing routines that listen to the <code>transformChannel</code>, maintain a buffer of 10,000 write entities, and write them to the database when the buffer is filled.</p>
</li>
<li><p>The <code>doneChannel</code> is signaled once all writing routines are completed, allowing the parent method to exit.</p>
</li>
</ol>
<p>Here’s the implementation:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Record <span class="hljs-keyword">struct</span> {
    LineNo <span class="hljs-keyword">int</span>
    Data   []<span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">MultiProcessingForReadingTransformAndWriting</span><span class="hljs-params">(database *db.DB)</span> <span class="hljs-title">error</span></span> {
    recordCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> *Record, <span class="hljs-number">1000</span>)
    transformCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> *models.Land, <span class="hljs-number">1000</span>)
    doneCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    errCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> error)
    <span class="hljs-keyword">const</span> numTransformers = <span class="hljs-number">5</span> <span class="hljs-comment">// Number of transformation goroutines</span>
    <span class="hljs-keyword">const</span> numWriters = <span class="hljs-number">5</span>      <span class="hljs-comment">// Number of writing goroutines</span>

    <span class="hljs-keyword">go</span> readAndProduceRecords(recordCh, errCh)

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; numTransformers; i++ {
        wg.Add(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">go</span> transformAndProduceDbModel(recordCh, transformCh, errCh, &amp;wg)
    }

    <span class="hljs-keyword">var</span> writeWg sync.WaitGroup
    <span class="hljs-keyword">var</span> mutex sync.Mutex
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; numWriters; i++ {
        writeWg.Add(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">go</span> writeAndConsumeDbModel(database, transformCh, errCh, &amp;writeWg, &amp;mutex)
    }

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        wg.Wait()
        <span class="hljs-built_in">close</span>(transformCh) <span class="hljs-comment">// Close transformCh once all transform goroutines finish</span>
    }()

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        writeWg.Wait()
        <span class="hljs-built_in">close</span>(doneCh)
    }()

    <span class="hljs-keyword">for</span> {
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> err := &lt;-errCh:
            <span class="hljs-keyword">return</span> err
        <span class="hljs-keyword">case</span> &lt;-doneCh:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">readAndProduceRecords</span><span class="hljs-params">(recordCh <span class="hljs-keyword">chan</span>&lt;- *Record, errCh <span class="hljs-keyword">chan</span>&lt;- error)</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(recordCh)
    file, err := os.Open(<span class="hljs-string">"data/land_feed.csv"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> file.Close()
    reader := csv.NewReader(file)
    _, _ = reader.Read() <span class="hljs-comment">// Read the headers</span>
    lineNumber := <span class="hljs-number">1</span>
    <span class="hljs-keyword">for</span> {
        record, err := reader.Read()
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">if</span> err == io.EOF {
                <span class="hljs-keyword">return</span>
            }
            errCh &lt;- err
            <span class="hljs-keyword">return</span>
        }
        recordCh &lt;- &amp;Record{
            LineNo: lineNumber,
            Data:   record,
        }
        lineNumber++
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">transformAndProduceDbModel</span><span class="hljs-params">(recordCh &lt;-<span class="hljs-keyword">chan</span> *Record, transformCh <span class="hljs-keyword">chan</span>&lt;- *models.Land, errCh <span class="hljs-keyword">chan</span>&lt;- error, wg *sync.WaitGroup)</span></span> {
    <span class="hljs-keyword">defer</span> wg.Done()
    <span class="hljs-keyword">for</span> record := <span class="hljs-keyword">range</span> recordCh {
        readEntity, err := utils.CreateEntityFromRecord(record.Data, record.LineNo)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            errCh &lt;- err
            <span class="hljs-keyword">return</span>
        }
        dbEntity := models.FromReadModel(*readEntity)
        transformCh &lt;- dbEntity
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeAndConsumeDbModel</span><span class="hljs-params">(database *db.DB, transformCh &lt;-<span class="hljs-keyword">chan</span> *models.Land, errCh <span class="hljs-keyword">chan</span>&lt;- error, wg *sync.WaitGroup, mutex *sync.Mutex)</span></span> {
    <span class="hljs-keyword">defer</span> wg.Done()
    <span class="hljs-keyword">var</span> buffer []*models.Land
    batchSize := <span class="hljs-number">10000</span>
    <span class="hljs-keyword">for</span> dbEntity := <span class="hljs-keyword">range</span> transformCh {
        buffer = <span class="hljs-built_in">append</span>(buffer, dbEntity)
        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(buffer) == batchSize {
            mutex.Lock()
            err := database.CreateLands(buffer)
            mutex.Unlock()
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                errCh &lt;- err
                <span class="hljs-keyword">return</span>
            }
            buffer = buffer[:<span class="hljs-number">0</span>]
        }
    }
    <span class="hljs-comment">// Flush any remaining entities in the buffer</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(buffer) &gt; <span class="hljs-number">0</span> {
        mutex.Lock()
        err := database.CreateLands(buffer)
        mutex.Unlock()
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            errCh &lt;- err
            <span class="hljs-keyword">return</span>
        }
    }
}
</code></pre>
<p>The response received was as follows:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"error"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Successfully completed request for Multiprocessing For Reading, Transform And Writing."</span>,
    <span class="hljs-attr">"memoryUsage"</span>: <span class="hljs-number">4515240</span>,
    <span class="hljs-attr">"elapsed"</span>: <span class="hljs-number">23637820200</span>
}
</code></pre>
<h1 id="heading-conclusion">Conclusion</h1>
<p>The following table provides a comparative view of all four approaches:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Endpoint</strong></td><td><strong>Description</strong></td><td><strong>Memory Usage (bytes)</strong></td><td><strong>Elapsed Time (ns)</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>GET /solution-one</code></td><td>Load all data and insert in batches.</td><td>3,296,896,352</td><td>18,968,041,000</td></tr>
<tr>
<td><code>GET /solution-two</code></td><td>Read line by line and insert in batches of 10,000.</td><td>3,851,704</td><td>26,142,007,900</td></tr>
<tr>
<td><code>GET /solution-three</code></td><td>Use multiprocessing with a worker pool for reading and writing.</td><td>5,289,472</td><td>24,760,674,200</td></tr>
<tr>
<td><code>GET /solution-four</code></td><td>Pipeline approach on worker pool for reading, transforming, and writing.</td><td>4,515,240</td><td>23,637,820,200</td></tr>
</tbody>
</table>
</div><p>From the results, we can observe the following insights:</p>
<ul>
<li><p>Solution One is the fastest but consumes a disproportionate amount of memory as it loads everything into memory.</p>
</li>
<li><p>Solution Two uses the least memory but is the slowest.</p>
</li>
<li><p>Solution Three slightly improves time over Solution Two while drastically reducing memory usage compared to Solution One.</p>
</li>
<li><p>Solution Four further improves memory usage (second best overall) and execution time (also second best).</p>
</li>
</ul>
<p>In many cloud environments, prolonged computational costs can lead to overshooting budgets. Thus, Solution Four stands out as the optimal choice for our scenario. While it may not be the absolute best for speed or memory usage, it strikes a balance, ranking second best in both metrics. This reflects the utility of multithreading: while it doesn’t always deliver the best results outright, it often yields the best overall performance across multiple criteria.</p>
<p>In this article, we examined a real-life problem and applied our knowledge of multiprocessing to arrive at an optimal solution.</p>
]]></content:encoded></item><item><title><![CDATA[Architecture of a NextJS app on AWS: an interview story]]></title><description><![CDATA[Introduction
In a recent job interview for an Architect role, I was asked to create a deployment plan for a Next.js application. While the straightforward approach of deploying the app on Vercel directly from GitHub may seem like a viable option, it ...]]></description><link>https://oxyprogrammer.com/architecture-of-a-nextjs-app-on-aws-an-interview-story</link><guid isPermaLink="true">https://oxyprogrammer.com/architecture-of-a-nextjs-app-on-aws-an-interview-story</guid><category><![CDATA[AWS]]></category><category><![CDATA[AWS VPC]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[next js]]></category><category><![CDATA[architecture-decisions]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Fri, 14 Mar 2025 06:01:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735040709584/1e911bba-9adc-4789-ad3c-22692e6693cd.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In a recent job interview for an Architect role, I was asked to create a deployment plan for a Next.js application. While the straightforward approach of deploying the app on Vercel directly from GitHub may seem like a viable option, it may not be the best choice for enterprise-level applications. Vercel can be costly for high-traffic applications, and there is less control over security and scalability aspects. Additionally, if the application has multiple components like a database, cache, and file system, the costs can quickly escalate.</p>
<p>In this article, I will share the thought process I used to address the interviewer's concerns and propose a more cost-effective and scalable deployment solution using AWS services. The aim is not only to showcase the various AWS services that can be leveraged, but also to provide an example of how real-world interviews can progress and the thought process that should guide the response.</p>
<p>This article follows an unconventional format, experimenting with an interview-style narrative. I, as the candidate, will present my proposed solutions, and the interviewer will provide feedback and additional constraints to challenge the solution. Through this interactive format, I hope to demonstrate the thought process and adaptability required in a technical interview setting.</p>
<p>The target audience for this article includes DevOps professionals, full-stack developers, and AWS enthusiasts who are interested in exploring deployment strategies for Next.js applications.</p>
<h1 id="heading-ice-breaker">Ice Breaker</h1>
<p><strong>Interviewer</strong>: What are your preferred technologies for full-stack application development?</p>
<p><strong>Me</strong>: It depends on the application size and scope. For enterprise-level applications, I prefer C# and Angular/React. For distributed applications with smaller services, I tend to use Golang. And for small to medium-sized projects that require less compute-intensive operations, I often recommend Next.js.</p>
<p><strong>Interviewer</strong>: That's interesting. Let's assume we have a medium-sized application, and since you prefer Next.js, how would you suggest deploying the application?</p>
<p><strong>Me</strong>: Well, Vercel and similar cloud providers offer great integration with GitHub, providing out-of-the-box environment support and custom domain management. This can be a straightforward solution for a basic Next.js application.</p>
<p><strong>Interviewer</strong>: Our application, however, makes use of a database, file system, and a cache. Do you think Vercel is the best option from a cost perspective?</p>
<p>Me: You're right. Considering the complexity of the application with the additional components, using Vercel may not be the most cost-effective solution. In this case, I would suggest leveraging AWS services directly.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">With his second question, it became clear that the interviewer was more focused on the DevOps plan than on the technology side or principles of distributed systems. I aimed to provide the simplest possible answer, starting with a brute force approach, even though I recognized it might not be the best solution.</div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">As he continued with subsequent questions, he introduced additional components to the application, making it apparent that choosing Vercel was no longer a viable option.</div>
</div>

<h1 id="heading-first-solution">First Solution</h1>
<p><strong>Me</strong>: May I assume the database to be an RDS?</p>
<p><strong>Interviewer</strong>: Be my guest!</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcDy_mwOnQCDkQdurkisso8kzKbXBu4gAIT0B5iBpqMpKeVacO0Es-Cj3fBuWTE-nJVAAMucLz6xEfbfZu0c5nrT3TIBsYKK2h-6cIOfdI2jKz4ehzFlPgsR9TQLhiBqKwzb-6U1Q?key=tCg7g6G0mFGpEvR7eDnSui3C" alt /></p>
<p><strong>Me</strong>: I propose the following as the initial solution:</p>
<ul>
<li><p>An EC2 instance for hosting the Next.js application.</p>
</li>
<li><p>Aurora DB, SQS, and EFS (Elastic File System) as managed AWS services. Being managed services, Aurora, EFS, and SQS provide high availability.</p>
</li>
<li><p>Route 53 for DNS management.</p>
</li>
<li><p>A GitHub workflow to push the latest build to the EC2 instance.</p>
</li>
</ul>
<p><strong>Interviewer</strong>: This solution has quite a few problems. Firstly, while the managed services are highly available, the EC2 instance isn’t. My traffic follows a pattern where there are only a few hours in the day when the application experiences very high traffic—almost 10 times the usual volume. How should I proceed with a cost-effective solution?</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">The interviewer clearly wanted me to provide a cloud provider solution, as he introduced conditions to advance the discussion. His initial feedback on my solution was complemented by a second, where he acknowledged the high availability of the managed services but pointed out that a single EC2 instance lacks that same availability. This made it evident that the application itself was not highly available.</div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Additionally, he noted the traffic surge that occurs for only a few hours a day, which likely suggests he had a trading application in mind that operates solely during market hours. Simply adding more EC2 instances to improve availability isn’t a viable option; provisioning EC2 capacity to handle 10 times the usual traffic during peak hours would not be cost-effective.</div>
</div>

<h1 id="heading-challenge-1-scalability-vis-a-vis-cost">Challenge 1: Scalability vis-a-vis cost</h1>
<p>Me: Since we want to enhance the scalability of our application in an on-demand manner, I propose using an ECS Fargate cluster, as illustrated in the diagram below:</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXftFeC5xyizNW_8ErQiqwfsRrd9ixJCI82LDriY3YCgqOuBhuvpSJp7BKSh2B_3WmbDSqsdUNHn9Xu4dpsi2dTJ4XEEQgDl2hMMJeOE1PXpoyA3bVavjryrrFtH4SKhm3r5itsZlg?key=tCg7g6G0mFGpEvR7eDnSui3C" alt /></p>
<p><strong>Explanation</strong>:</p>
<ul>
<li><p>The Aurora DB, EFS, and SQS will continue to be managed AWS services, ensuring their availability.</p>
</li>
<li><p>ECS (Elastic Container Service) allows you to run containers on demand. During periods of high traffic, ECS services will automatically spin up tasks (containers), and as the load decreases, the extra containers will be terminated. Billing is based on usage, and the underlying hardware is fully managed by AWS, alleviating concerns about infrastructure management.</p>
</li>
<li><p>ECS can also be run on EC2 instances; however, the cluster requires designated EC2 instances to operate. Given the disproportionate traffic spikes, Fargate is the chosen solution for this scenario.</p>
</li>
<li><p>ECS can connect directly to ECR (Elastic Container Registry), where the built image can be stored and accessed through the GitHub workflow.</p>
</li>
</ul>
<p><strong>Interviewer</strong>: While the scalability aspect is addressed, I still don’t see how the application is highly available. Additionally, do you believe the security aspects are adequately handled?</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>The interviewer was correct in noting that availability has not yet been addressed; the ECS cluster would still operate within the same Availability Zone (AZ), posing a risk of downtime. I overlooked this important aspect.</em></div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>He also hinted at the necessity of considering the security aspects of the deployed application in the public cloud.</em></div>
</div>

<h1 id="heading-challenge-2-security-and-availability">Challenge 2: Security and Availability</h1>
<p><strong>Me</strong>: My apologies! I overlooked the availability aspect, and I understand that security must also be addressed. I propose the following diagram:</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeNEsvepwwpjE1YYmkdhPgXLTm0-Ty8fo7nyDSkbhFjOkNW1qlvvv1dbqxlI4Sk74mSc7CDYDliGi8BK-llvZszGc5fbUqCv_JnymJDWwJ9glzMcnoTvnYNW4Yv4H7fwXRayVZRJw?key=tCg7g6G0mFGpEvR7eDnSui3C" alt /></p>
<p><strong>Explanation</strong>:</p>
<ol>
<li><p>A VPC belongs to a region, and we can have subnets (public/private) within a single Availability Zone (AZ). By deploying two ECS clusters in different private subnets, we can enhance the application's availability.</p>
</li>
<li><p>An Application Load Balancer (ALB) is a highly available managed AWS service that can operate across multiple public subnets (and thus multiple AZs). The load balancer will distribute traffic in a round-robin manner across the two clusters.</p>
</li>
<li><p>Since the ECS clusters are now in private subnets, their access to the ECR repository and managed services like Aurora DB, SQS, and EFS is restricted. Therefore, the VPC must include the following endpoints:</p>
<ol>
<li><p>A Gateway Endpoint for accessing the managed Aurora DB service.</p>
</li>
<li><p>An Interface Endpoint for accessing the managed SQS service over a private link.</p>
</li>
<li><p>An EFS mount target for connecting to the EFS.</p>
</li>
<li><p>An ECR Endpoint for accessing the private ECR repository.</p>
</li>
</ol>
</li>
<li><p>The VPC will be connected to an Internet Gateway (IG) to allow external traffic to reach the ALB in the public subnets. As the first point of contact, the ALB will handle SSL offloading, and Route 53 will resolve to the ALB's Elastic IP address.</p>
</li>
</ol>
<p><strong>Interviewer</strong>: I see that most of the issues are resolved. Is there a way to fetch a report for download using our app that can only be created on our on-premises servers due to confidentiality reasons?</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">The interviewer is introducing an additional condition related to connecting with the on-premises data center.</div>
</div>

<h1 id="heading-challenge-3-integration-with-on-prem-data-center">Challenge 3: Integration with on Prem Data center</h1>
<p><strong>Me</strong>: To accommodate the added condition of connectivity with the on-premises data center, we can utilize AWS Direct Connect. The following diagram elaborates on this idea:</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdwZkrUGDZ0nbOeKgmI42ngfoKxEvTtv53CNjuZOKcJ7dffY4_M1q8uFMxhDmuM-QGnLJ74pI-SsE87d1k50bvyyzPzvktxYwaOfBSqugdHEA9rvd2ZN7ZUsRaDdEimMx-_6NWo4A?key=tCg7g6G0mFGpEvR7eDnSui3C" alt /></p>
<p><strong>Explanation</strong>: The VPC connects to the on-premises data center using AWS Direct Connect.</p>
<h1 id="heading-further">Further</h1>
<p>The article introduces numerous AWS services and concepts; however, the architecture has been overly simplified for brevity, and many minor details have been omitted. For example:</p>
<ol>
<li><p>SSL on the ALB may be managed automatically by AWS Certificate Manager (ACM), which handles renewals upon expiry. Additionally, the ALB will require an Elastic IP address.</p>
</li>
<li><p>Many flows have been simplified for clarity, and what is presented in the diagrams offers a high-level view of the overall architecture. For instance, the connection from GitHub to ECR would involve additional intricacies. If an Infrastructure as a Service (IaaS) tool like Terraform is required, more components and services would be necessary in the DevOps flow.</p>
</li>
<li><p>Moreover, the subnets within the VPC will need their own route tables and security groups, which have been omitted in the diagrams for the sake of brevity.</p>
</li>
<li><p>There is no one way of doing things and the same problem could have been solved using EKS (Elastic Kubernetes Service) however using Kubernetes for a single next JS application seems overkill at first sight. ECS fits the bill perfectly.</p>
</li>
</ol>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we explored how enterprise-level deployments are structured for a Next.js application. We discussed various AWS services, including ECS, services, tasks, VPC, subnets, and the different endpoints such as gateway and interface endpoints. Additionally, we examined AWS Direct Connect, which facilitates the connection between an AWS VPC and an on-premises data center.</p>
<p>Throughout the interview process, we realized that providing minimal input allows the interviewer to guide the discussion effectively. A skilled interviewer is typically adept at prompting the interviewee to elicit the answers they are seeking.</p>
<p>I hope this article has offered you valuable insights into the enterprise deployment strategies employed by large organizations. Even a small application built with Next.js requires robust infrastructure to operate in a scalable, available, and secure manner.</p>
]]></content:encoded></item><item><title><![CDATA[Tenets of Multithreading in GO: Detailed Tutorial]]></title><description><![CDATA[Introduction
Multiprocessing or multithreading is a critical aspect of many compiled languages, and go (often referred to as Golang) is no exception. Go began development around 2007-08, a time when chip manufacturers recognized the benefits of using...]]></description><link>https://oxyprogrammer.com/tenets-of-multithreading-in-go-detailed-tutorial</link><guid isPermaLink="true">https://oxyprogrammer.com/tenets-of-multithreading-in-go-detailed-tutorial</guid><category><![CDATA[multithreading in golang]]></category><category><![CDATA[threading patterns in golang]]></category><category><![CDATA[golang]]></category><category><![CDATA[multithreading]]></category><category><![CDATA[parallel processing]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Fri, 14 Mar 2025 05:57:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044299208/bde427b8-f578-4265-ad47-9ff84d56f8ac.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Multiprocessing or multithreading is a critical aspect of many compiled languages, and go (often referred to as Golang) is no exception. Go began development around 2007-08, a time when chip manufacturers recognized the benefits of using multiple processors to increase computational power, rather than merely boosting the clock speed of single processors. While multithreading existed before this period, its real advantages became apparent with the advent of multiprocessor computers. In this article, we will review the various tools that Go provides for writing robust multiprocessing code. We will also explore simple code examples to help us grasp the concepts these tenets offer.</p>
<h1 id="heading-background">Background</h1>
<p>Multiprocessing and Multitasking are two distinct concepts, and most languages supporting multithreading conceal the details within their concurrency frameworks. As a programmer, you might not be aware if a new OS thread is being created under the hood. Additionally, system threads differ from managed or green threads that are created by the code.</p>
<p>You may be a seasoned developer who is well-versed in the concepts of multitasking and multiprocessing. However, I strongly recommend reading through <a target="_blank" href="https://oxyprogrammer.com/the-basics-of-async-and-parallel-programming">this article</a> to clarify any grey areas you may have. This will also ensure that we (you and I) are on the same page as we proceed. Another request is to have access to the <a target="_blank" href="https://go.dev/play/">Go playground</a> if you wish to try out the code provided in the article. I have endeavored to keep the code extremely simple so it can be tried on the fly without needing to set up a Go development environment.</p>
<p>I am also assuming that you are here specifically for multithreading in <code>go</code> and you are otherwise ok with <code>go</code> syntax.</p>
<h1 id="heading-goroutines">Goroutines</h1>
<p>A Goroutine is a green thread built into the Go language. Green threads (also known as managed threads or user threads) allow thousands of them to run simultaneously in a Go program. The complexities of memory management are handled seamlessly by the Go runtime.</p>
<p>Let’s start with a simple program that does not use a Goroutine.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"fmt"</span>
  <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{
  start := time.Now()
  doFakeApiCall()
  fmt.Println(<span class="hljs-string">"\nFinished"</span>)
  elapsed := time.Since(start)
  fmt.Printf(<span class="hljs-string">"\nProcesses took %s"</span>, elapsed)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doFakeApiCall</span><span class="hljs-params">()</span></span>{
  time.Sleep(time.Second*<span class="hljs-number">2</span>);
  fmt.Println(<span class="hljs-string">"\nAPI call took two seconds!"</span>)
}
</code></pre>
<p>If you run this code in the Go playground, you will see the following output, which is clear and easy to understand:</p>
<pre><code class="lang-plaintext">API call took two seconds!
Finished
Processes took 2s
Program exited.
</code></pre>
<p>However, if we place a go keyword in front of the <code>doFakeApiCall()</code> function, the program will exit before the <code>doFakeApiCall()</code> function completes. As a result, you will get:</p>
<pre><code class="lang-plaintext">Finished
Processes took 0s
Program exited.
</code></pre>
<p>This occurs because the <code>doFakeApiCall()</code> function call is now happening asynchronously. The main function does not wait for it to return and proceeds to the next instructions, causing the main function to exit before <code>doFakeApiCall()</code> has completed. Since we cannot predict the duration of asynchronous processing, we need to implement a mechanism in the main function that ensures it waits until the Goroutine returns. We will explore this in the next section.</p>
<h1 id="heading-wait-group">Wait Group</h1>
<p>We concluded the last section with a problem. Below is a Go program similar to the previous one, but now it includes an additional fake database call function.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"sync"</span>
   <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{
   start := time.Now()
   <span class="hljs-keyword">go</span> doFakeApiCall()
   <span class="hljs-keyword">go</span> doFakeDbCall()
   fmt.Println(<span class="hljs-string">"\nFinished"</span>)
   elapsed := time.Since(start)
   fmt.Printf(<span class="hljs-string">"\nProcesses took %s"</span>, elapsed)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doFakeApiCall</span><span class="hljs-params">()</span></span>{
   time.Sleep(time.Second*<span class="hljs-number">2</span>);
   fmt.Println(<span class="hljs-string">"\nAPI call took two seconds!"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doFakeDbCall</span><span class="hljs-params">()</span></span>{
   time.Sleep(time.Second*<span class="hljs-number">1</span>);
   fmt.Println(<span class="hljs-string">"\nDB call took one second!"</span>)
}
</code></pre>
<p>As you may have guessed, the program will exit before the <code>doFakeApiCall()</code> and <code>doFakeDbCall()</code> functions complete. Here, <code>WaitGroup</code> from the sync package comes to the rescue. <code>WaitGroup</code> provides several methods that help synchronize the completion of concurrent tasks:</p>
<ul>
<li><p><code>Add(&lt;int&gt;)</code>: Sets the number of concurrent tasks to handle.</p>
</li>
<li><p><code>Done()</code>: Decrements the count of concurrent tasks.</p>
</li>
<li><p><code>Wait()</code>: Blocks until the counter reaches zero, allowing the application flow to proceed.</p>
</li>
</ul>
<p>Let’s amend our previous code to make use of <code>WaitGroup</code>.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main


<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"sync"</span>
   <span class="hljs-string">"time"</span>
)

<span class="hljs-keyword">var</span> wg = sync.WaitGroup{}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
   wg.Add(<span class="hljs-number">2</span>) <span class="hljs-comment">//Add two tasks to the waitgroup</span>
   start := time.Now()
   <span class="hljs-keyword">go</span> doFakeApiCall()
   <span class="hljs-keyword">go</span> doFakeDbCall()
   wg.Wait() <span class="hljs-comment">//Wait for all tasks to finish.</span>
   fmt.Println(<span class="hljs-string">"\nFinished"</span>)
   elapsed := time.Since(start)
   fmt.Printf(<span class="hljs-string">"\nProcesses took %s"</span>, elapsed)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doFakeApiCall</span><span class="hljs-params">()</span></span> {
   time.Sleep(time.Second * <span class="hljs-number">2</span>)
   fmt.Println(<span class="hljs-string">"\nAPI call took two seconds!"</span>)
   wg.Done() <span class="hljs-comment">//reduce waitgroup counter by 1</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doFakeDbCall</span><span class="hljs-params">()</span></span> {
   time.Sleep(time.Second * <span class="hljs-number">1</span>)
   fmt.Println(<span class="hljs-string">"\nDB call took one second!"</span>)
   wg.Done() <span class="hljs-comment">//reduce waitgroup counter by 1</span>
}
</code></pre>
<p>The output for this program will be as follows:</p>
<pre><code class="lang-plaintext">DB call took one second!
API call took two seconds!
Finished
Processes took 2s
Program exited.
</code></pre>
<p>You may have noticed that the DB call finished first, despite being called after the API call. The total time taken for both the <code>doFakeApiCall()</code> and <code>doFakeDbCall()</code> calls is 2 seconds. If these were called synchronously, they would have taken 3 seconds.</p>
<p>While Wait Groups are effective for running independent goroutines, they do not facilitate communication between them. In Golang, channels are constructs used for sharing memory between goroutines. Below are some syntax details for channels:</p>
<ul>
<li><p><code>ch := make(chan &lt;Type&gt;)</code>: Creates a channel.</p>
</li>
<li><p><code>ch &lt;- someData</code>: Publishes data to the channel. This call is blocking, meaning control won't proceed until the data is read from the channel.</p>
</li>
<li><p><code>someVar := &lt;-ch</code>: Reads from the channel into a variable. This is also a blocking call, waiting for data to become available.</p>
</li>
<li><p><code>close(ch)</code>: Closes a channel.</p>
</li>
</ul>
<p>Building on the example from the last section, let's consider an additional goroutine, <code>doCreateFakeReport</code>, which creates a report from the outputs of <code>doFakeDbCall</code> and <code>doFakeApiCall</code>.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main


<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"time"</span>
   <span class="hljs-string">"sync"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
   start := time.Now()
   apiCh:=<span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
   dbCh:=<span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
   <span class="hljs-keyword">var</span> wg sync.WaitGroup
   wg.Add(<span class="hljs-number">1</span>)

   <span class="hljs-keyword">go</span> doFakeApiCall(apiCh)
   <span class="hljs-keyword">go</span> doFakeDbCall(dbCh)
   <span class="hljs-keyword">go</span> doCreateFakeReport(apiCh,dbCh,&amp;wg)

   wg.Wait()
   fmt.Println(<span class="hljs-string">"Finished"</span>)
   elapsed := time.Since(start)
   fmt.Printf(<span class="hljs-string">"Processes took %s"</span>, elapsed)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doFakeApiCall</span><span class="hljs-params">(apiChan <span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)</span></span> {
   time.Sleep(time.Second * <span class="hljs-number">2</span>)
   fmt.Println(<span class="hljs-string">"API call took two seconds!"</span>)
   apiChan&lt;-<span class="hljs-string">"API 123"</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doFakeDbCall</span><span class="hljs-params">(dbChan <span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)</span></span> {
   time.Sleep(time.Second * <span class="hljs-number">1</span>)
   fmt.Println(<span class="hljs-string">"DB call took one second!"</span>)
   dbChan&lt;-<span class="hljs-string">"DB 123"</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doCreateFakeReport</span><span class="hljs-params">(apiChan <span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>,dbChan <span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>, wg *sync.WaitGroup)</span></span>{
   fmt.Println(<span class="hljs-string">"Final Report: Api result is"</span>, &lt;-apiChan , <span class="hljs-string">"Db Result is:"</span>, &lt;-dbChan)
   wg.Done()
}
</code></pre>
<p>In this code, we are:</p>
<ul>
<li><p>Creating channels for <code>doFakeDbCall</code> and <code>doFakeApiCall</code> to publish their results.</p>
</li>
<li><p>Passing these channels to the <code>doCreateFakeReport</code> function to read from them.</p>
</li>
<li><p>Using a Wait Group to wait until <code>doCreateFakeReport</code> finishes.</p>
</li>
</ul>
<p>The output of this code is as follows:</p>
<pre><code class="lang-plaintext">DB call took one second!
API call took two seconds!
Final Report: Api result is API 123 Db Result is: DB 123
Finished
Processes took 2s
Program exited.
</code></pre>
<h2 id="heading-buffered-channels">Buffered Channels</h2>
<p>Buffered channels allow you to publish a predetermined number of items to the channel before it blocks for reading. Here is an example of a buffered channel:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main


<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{

   ch:=<span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">3</span>)

   <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++{
           <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{
               time.Sleep(time.Second*<span class="hljs-number">1</span>)
               ch&lt;- i
           }()
       }

   fmt.Println(<span class="hljs-string">"Channel published: "</span>, &lt;-ch)
   fmt.Println(<span class="hljs-string">"Channel published: "</span>, &lt;-ch)
   fmt.Println(<span class="hljs-string">"Channel published: "</span>, &lt;-ch)

   fmt.Printf(<span class="hljs-string">"Exiting"</span>)
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Channel published:  0
Channel published:  1
Channel published:  2
Exiting
Program exited.
</code></pre>
<p>Using buffered channels is preferred when you know the number of goroutines that will be launched.</p>
<h3 id="heading-select-pattern-for-channels"><strong>Select Pattern for Channels</strong></h3>
<p>A common pattern for reading from multiple channels is using a select statement within an infinite loop. Here's an example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main


<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ch1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
    ch4 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
    endCh := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)


    <span class="hljs-keyword">go</span> doPublish1(ch1)
    <span class="hljs-keyword">go</span> doPublish4(ch4)
    <span class="hljs-keyword">go</span> doEndOn10(endCh)


    <span class="hljs-keyword">for</span> {
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> msg := &lt;-ch1:
            fmt.Println(msg)
        <span class="hljs-keyword">case</span> msg := &lt;-ch4:
            fmt.Println(msg)
        <span class="hljs-keyword">case</span> msg := &lt;-endCh:
            fmt.Println(msg)
            os.Exit(<span class="hljs-number">0</span>)
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doPublish1</span><span class="hljs-params">(ch <span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)</span></span> {
    <span class="hljs-keyword">for</span> {
        time.Sleep(time.Second * <span class="hljs-number">1</span>)
        ch &lt;- <span class="hljs-string">"Publishing every 1 second"</span>
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doPublish4</span><span class="hljs-params">(ch <span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)</span></span> {
    <span class="hljs-keyword">for</span> {
        time.Sleep(time.Second * <span class="hljs-number">4</span>)
        ch &lt;- <span class="hljs-string">"Publishing every 4 seconds"</span>
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doEndOn10</span><span class="hljs-params">(ch <span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-keyword">for</span> {
        time.Sleep(time.Second * <span class="hljs-number">10</span>)
        fmt.Println(<span class="hljs-string">"Sending end signal"</span>)
        ch &lt;- <span class="hljs-number">0</span>
    }
}
</code></pre>
<ol>
<li><p>The first goroutine, <code>doPublish1</code>, publishes to its channel every 1 second.</p>
</li>
<li><p>The second goroutine, <code>doPublish4</code>, publishes to its channel every 4 seconds.</p>
</li>
<li><p>The last goroutine waits for 10 seconds before publishing to its channel, and once it does, the program exits.</p>
</li>
</ol>
<p>The output in the Go playground is as follows:</p>
<pre><code class="lang-plaintext">Publishing every 1 second
Publishing every 1 second
Publishing every 1 second
Publishing every 1 second
Publishing every 4 seconds
Publishing every 1 second
Publishing every 1 second
Publishing every 1 second
Publishing every 1 second
Publishing every 4 seconds
Publishing every 1 second
Publishing every 1 second
Sending end signal
0
</code></pre>
<h1 id="heading-synchronization">Synchronization</h1>
<p>There may be situations where shared code is accessed by multiple goroutines, which can lead to race conditions. Golang offers several constructs to help with the synchronization of shared code.</p>
<p>For example, the following code demonstrates a clear case of a race condition:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main


<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"fmt"</span>
  <span class="hljs-string">"sync"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{
   counter:=<span class="hljs-number">0</span>
   <span class="hljs-keyword">var</span> wg sync.WaitGroup
   wg.Add(<span class="hljs-number">2</span>)

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Increments counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
               counter++
       }
       wg.Done()
   }()

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Decrements counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
               counter--
       }
       wg.Done()
   }()

   wg.Wait()
   fmt.Println(<span class="hljs-string">"Final counter value: "</span>,counter)
}
</code></pre>
<p>This code simply increments and decrements a counter using two separate goroutines. The program waits for the goroutines to finish executing and then prints the counter's value. Because the counter is a shared variable between the goroutines, this creates a race condition. Every time you run this code in the Go playground, you may notice different values for the counter, including 0.</p>
<p>In the following sections, we will explore the constructs provided by Go to resolve race conditions arising from shared resources.</p>
<h2 id="heading-mutexes">Mutexes</h2>
<p>A mutex, short for mutual exclusion, helps with code synchronization by ensuring that only one goroutine can access a portion of the code that can lead to a race condition. The previous example can be corrected as shown below:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main


<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"fmt"</span>
  <span class="hljs-string">"sync"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{
   counter:=<span class="hljs-number">0</span>
   <span class="hljs-keyword">var</span> wg sync.WaitGroup
   <span class="hljs-keyword">var</span> mut sync.Mutex
   wg.Add(<span class="hljs-number">2</span>)

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Increments counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
               mut.Lock()
               counter++
               mut.Unlock()
       }
       wg.Done()
   }()

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Decrements counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
               mut.Lock()
               counter--
               mut.Unlock()
       }
       wg.Done()
   }()

   wg.Wait()
   fmt.Println(<span class="hljs-string">"Final counter value: "</span>,counter)
}
</code></pre>
<p>Running the above code will consistently yield 0. Note that the code between the lock and unlock statements of the mutex is referred to as the critical section.</p>
<h2 id="heading-atomic-variables">Atomic Variables</h2>
<p>While mutexes help achieve synchronization, they introduce some boilerplate code. Atomic operations can enhance brevity. The previous example can be rewritten using atomic operations as follows:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"fmt"</span>
  <span class="hljs-string">"sync"</span>
  <span class="hljs-string">"sync/atomic"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{
   <span class="hljs-keyword">var</span> counter <span class="hljs-keyword">int32</span> = <span class="hljs-number">0</span>
   <span class="hljs-keyword">var</span> wg sync.WaitGroup
   wg.Add(<span class="hljs-number">2</span>)

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Increments counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
              atomic.AddInt32(&amp;counter,<span class="hljs-number">1</span>)
       }
       wg.Done()
   }()

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Decrements counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
              atomic.AddInt32(&amp;counter,<span class="hljs-number">-1</span>)
       }
       wg.Done()
   }()

   wg.Wait()
   fmt.Println(<span class="hljs-string">"Final counter value: "</span>,counter)
}
</code></pre>
<p>In this example, the following code:</p>
<pre><code class="lang-go">mut.Lock()
counter--
mut.Unlock()
</code></pre>
<p>has been replaced with:</p>
<pre><code class="lang-go">atomic.AddInt32(&amp;counter,<span class="hljs-number">1</span>)
</code></pre>
<h2 id="heading-condition-variables">Condition Variables</h2>
<p>In the previous code examples, while synchronization ensures the correct final result, we also need to impose a condition during the intermediate stages. Specifically, in the example from the last section, we want to ensure that the counter never goes below zero at any point during program execution.</p>
<p>Below is the amended code that modifies the mutex example to ensure that the value of the counter never falls below 0:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"fmt"</span>
  <span class="hljs-string">"sync"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{
   counter:=<span class="hljs-number">0</span>
   <span class="hljs-keyword">var</span> wg sync.WaitGroup
   <span class="hljs-keyword">var</span> mut sync.Mutex
   <span class="hljs-keyword">var</span> counterChecker = sync.NewCond(&amp;mut)
   wg.Add(<span class="hljs-number">2</span>)

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Increments counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
           mut.Lock()
           counter++
           fmt.Println(counter)
           counterChecker.Signal()
           mut.Unlock()
       }
       wg.Done()
   }()

   <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>{ <span class="hljs-comment">//Decrements counter by 30000</span>
       <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">30000</span>; i++{
           mut.Lock()
           <span class="hljs-keyword">if</span> counter<span class="hljs-number">-1</span> &lt; <span class="hljs-number">0</span>{
               counterChecker.Wait()
           }
           counter--
           fmt.Println(counter)
           mut.Unlock()
       }
       wg.Done()
   }()

   wg.Wait()
   fmt.Println(<span class="hljs-string">"Final counter value: "</span>,counter)
}
</code></pre>
<p>In this code, the decrement function waits on the conditional variable whenever it is about to decrease the counter into the negative range. Conversely, the increment function sends a signal each time it increments the counter.</p>
<p>This mechanism ensures that the counter's value is maintained above zero throughout the program's execution.</p>
<h1 id="heading-worker-pool">Worker Pool</h1>
<p>The worker pool is a fundamental Go concurrency pattern that facilitates the creation of efficient pipelines. In Go, a pipeline is constructed using channels where one set of goroutines feeds data into a channel, while another set processes and offloads it.</p>
<p>The worker pool pattern represents the most basic form of a pipeline. It helps manage computational resources on a machine while leveraging the benefits of multiprocessing. This pattern is particularly useful for controlling the rate of resource consumption and maintaining system stability under heavy loads.</p>
<p>Implementation:</p>
<ol>
<li><p>A fixed number of worker goroutines are initialized.</p>
</li>
<li><p>These workers continuously dequeue items from a feed channel.</p>
</li>
<li><p>After processing, workers send results to a results channel.</p>
</li>
<li><p>The results channel can serve as a feed channel for subsequent stages, creating a multi-stage pipeline.</p>
</li>
</ol>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main


<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"time"</span>
   <span class="hljs-string">"strconv"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
   numWorkers := <span class="hljs-number">3</span>
   numJobs    := <span class="hljs-number">10</span>

   jobs := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, numJobs)
   results:=<span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>, numJobs)

   <span class="hljs-comment">// Start worker goroutines</span>
   <span class="hljs-keyword">for</span> w := <span class="hljs-number">1</span>; w &lt;= numWorkers; w++ {
      <span class="hljs-keyword">go</span> worker(w, jobs, results)
   }

   <span class="hljs-comment">// Send jobs to the jobs channel</span>
   <span class="hljs-keyword">for</span> j := <span class="hljs-number">1</span>; j &lt;= numJobs; j++ {
      jobs &lt;- j
      fmt.Println(<span class="hljs-string">"Produced job"</span>, j)
   }

   <span class="hljs-built_in">close</span>(jobs) <span class="hljs-comment">// Close the channel to indicate no more jobs will be sent</span>

   <span class="hljs-keyword">for</span> r := <span class="hljs-number">1</span>; r&lt;= numJobs; r++{
       fmt.Println(&lt;-results)
   }
   fmt.Println(<span class="hljs-string">"All jobs have been processed."</span>)
}

<span class="hljs-comment">// Worker function processes jobs from the jobs channel</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">worker</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>, jobs &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, results <span class="hljs-keyword">chan</span> &lt;- <span class="hljs-keyword">string</span>)</span></span> {
   fmt.Println(<span class="hljs-string">"Waiting in worker"</span>)
   <span class="hljs-keyword">for</span> job := <span class="hljs-keyword">range</span> jobs {
      <span class="hljs-comment">// Simulate doing some work</span>
      fmt.Println(<span class="hljs-string">"Worker"</span>, id, <span class="hljs-string">"started job"</span>, job)
      time.Sleep(time.Millisecond * <span class="hljs-number">1500</span>) <span class="hljs-comment">// Simulate work duration</span>
      results&lt;- <span class="hljs-string">"Worker "</span> + strconv.Itoa(id) + <span class="hljs-string">" finished job "</span> + strconv.Itoa(job)
   }
}
</code></pre>
<p>Code Breakdown:</p>
<ol>
<li><p>Channel Initialization:</p>
<ul>
<li><p>Two buffered channels are created: <code>jobs</code> for incoming tasks and <code>results</code> for processed outputs.</p>
</li>
<li><p>Buffered channels prevent blocking, allowing for smoother operation.</p>
</li>
</ul>
</li>
<li><p>Worker Goroutine Deployment:</p>
<ul>
<li><p>A specified number of worker goroutines are launched.</p>
</li>
<li><p>Each worker function takes the <code>jobs</code> channel as input and the <code>results</code> channel as output.</p>
</li>
</ul>
</li>
<li><p>Job Distribution:</p>
<ul>
<li><p>The main function populates the <code>jobs</code> channel with tasks.</p>
</li>
<li><p>After all jobs are queued, the <code>jobs</code> channel is closed to signal completion.</p>
</li>
</ul>
</li>
<li><p>Job Processing:</p>
<ul>
<li><p>Workers continuously pull jobs from the <code>jobs</code> channel using a <code>range</code> loop.</p>
</li>
<li><p>Each job is processed (simulated with a time delay in this example).</p>
</li>
<li><p>Results are sent to the <code>results</code> channel.</p>
</li>
</ul>
</li>
<li><p>Result Collection:</p>
<ul>
<li><p>The main function retrieves and prints results from the <code>results</code> channel.</p>
</li>
<li><p>The program exits after processing all results.</p>
</li>
</ul>
</li>
</ol>
<p>This pattern demonstrates effective concurrent processing, load balancing across multiple workers, and controlled resource utilization. It serves as a foundation for more complex concurrent systems and pipelines in Go.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we explored how goroutines in Go enable concurrent execution and help optimize the use of computational resources. We covered the various concurrency primitives provided by the Go programming language, including wait groups, channels, buffered channels, and mutexes. We also delved into the use of conditional variables and atomic operations.</p>
<p>A key tenet of Go's concurrency philosophy is "communication by sharing memory, not sharing memory by communication." We discussed how this principle guides the design of Go's concurrency features and promotes effective coordination between concurrent entities.</p>
<p>Additionally, we examined the select pattern, which allows for handling multiple channels simultaneously, and the worker pool model, a fundamental concurrent design pattern. The worker pool pattern demonstrates efficient load distribution and resource management, making it a valuable tool in building scalable and high-performance concurrent systems.</p>
<p>Through these discussions, I believe this article has significantly expanded your knowledge of Go's concurrency capabilities and equipped you with a stronger set of tools to leverage the power of concurrent programming in your Go projects.</p>
]]></content:encoded></item><item><title><![CDATA[The basics of Async and Parallel Programming]]></title><description><![CDATA[Introduction
Asynchronous programming is an essential part of modern software development, regardless of the programming language you use. It's crucial for optimizing hardware usage and improving application performance. However, in my day-to-day wor...]]></description><link>https://oxyprogrammer.com/the-basics-of-async-and-parallel-programming</link><guid isPermaLink="true">https://oxyprogrammer.com/the-basics-of-async-and-parallel-programming</guid><category><![CDATA[Amdahl’s Law]]></category><category><![CDATA[Gustafson’s Law]]></category><category><![CDATA[green thread]]></category><category><![CDATA[multithreading]]></category><category><![CDATA[multitasking]]></category><category><![CDATA[Threads]]></category><category><![CDATA[Threading]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Fri, 07 Mar 2025 05:45:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735040476529/6d194dfb-8567-46bc-89c6-a7397826dfac.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Asynchronous programming is an essential part of modern software development, regardless of the programming language you use. It's crucial for optimizing hardware usage and improving application performance. However, in my day-to-day work, I've noticed that terms like <code>async</code>, <code>parallel</code>, and <code>concurrency</code> are often used interchangeably, leading to confusion. This article aims to address this issue for three main reasons:</p>
<ol>
<li><p>To explore the history and establish clear distinctions between these various terms.</p>
</li>
<li><p>To incorporate relevant academic concepts from Computer Science, helping to connect the dots.</p>
</li>
<li><p>To create a foundational article that will serve as a basis for future discussions on multithreaded programming.</p>
</li>
</ol>
<p>In this article, we'll examine the buzzwords <code>async</code>, <code>parallel</code> and <code>concurrency</code> clarifying their meanings and how they should be used. We'll also compare how these concepts are implemented across different programming languages, providing a comprehensive overview of these critical programming paradigms.</p>
<h1 id="heading-moores-law-and-a-little-bit-of-history">Moore’s Law and a little bit of History</h1>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Moore%27s_law">Moore's Law</a>, an empirical observation rather than a physical law, states that the number of transistors in an integrated circuit (IC) doubles approximately every two years. This principle has largely held true since its inception in 1965.</p>
<p>For many years, this increase in transistor count directly translated to higher clock frequencies in single processors, as illustrated in the figure below.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeV-iYxqCvCPIcyiOQahMukbkAgDv6IvfqcO9g_HKFURHEK5bDsTkeJA-3W7-zqVbfRqauRMLVpV-UiNcTntIMiHKEJWCqUEWrXA1v5XaG2JEu5mb_MhJHWT1MQDZzu1QcFaFN9ow?key=mTrrW-u6YrxXONgafrQrHVb9" alt /></p>
<p>(Source: <a target="_blank" href="https://github.com/karlrupp/microprocessor-trend-data">https://github.com/karlrupp/microprocessor-trend-data</a>)</p>
<p>However, as highlighted in the figure, a significant shift occurred around 2008. Chip manufacturers stopped increasing clock speeds, despite the continued growth in transistor count. This change was driven by the realization that higher clock frequencies led to increased heat generation and power consumption, making it challenging to use these chips in smaller computers like laptops. The need for larger cooling systems and reduced battery life made it difficult to manufacture compact, portable devices.</p>
<p>To address these issues while still adhering to Moore's Law, chip makers began increasing the number of logical cores instead of clock speeds. This shift marked a turning point in processor design, focusing on multi-core architectures to boost computational power rather than relying solely on faster single-core processors.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Interestingly, this shift parallels the preference for horizontal scaling over vertical scaling in computing infrastructure, where adding more machines (cores) is often favored over increasing the power of a single machine (core).</div>
</div>

<p>This transition to multi-core processors has had profound implications for software development, necessitating new approaches to take full advantage of the available computational resources.</p>
<h1 id="heading-concurrency">Concurrency</h1>
<p>Concurrency is a broad term often used interchangeably with multitasking or delegation. Let's explore this concept using an everyday example.</p>
<p>Imagine a morning schedule with the following tasks:</p>
<ol>
<li><p>Wash dishes (30 minutes)</p>
</li>
<li><p>Do laundry (30 minutes)</p>
</li>
<li><p>Cook lunch (60 minutes)</p>
</li>
<li><p>Shower and prayer (30 minutes)</p>
</li>
</ol>
<p>Completing these tasks sequentially would take 30 + 30 + 60 + 30 = 150 minutes or 2 hours and 30 minutes.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXe6srCyTVHga4lOCPMhIt2NQEB2oPfa-xuGHd3DTnXs2rUpflSP20erk06RDLYkGLivkIyByKVT8A1YSDKCjQcHWsvzwEJRQ5MlHt6Zf0SoZgakq4iUAqheKhcuQ0MH48U_lpqA?key=mTrrW-u6YrxXONgafrQrHVb9" alt /></p>
<h2 id="heading-async-multitasking">Async / Multitasking</h2>
<p>In our example, we can observe opportunities for multitasking:</p>
<ul>
<li><p><strong>Dishwashing</strong>: 15 minutes to load/unload, 15 minutes of independent machine operation</p>
</li>
<li><p><strong>Laundry</strong>: 15 minutes to load/unload, 15 minutes of independent machine operation</p>
</li>
<li><p><strong>Cooking lunch</strong>: Requires full engagement</p>
</li>
<li><p><strong>Showering and prayer</strong>: Requires full engagement</p>
</li>
</ul>
<p>A more efficient approach would be:</p>
<ol>
<li><p>Load the dishwasher</p>
</li>
<li><p>While dishes are washing, load the laundry</p>
</li>
<li><p>Start cooking lunch</p>
</li>
<li><p>Respond to signals from the dishwasher and washing machine to unload when ready</p>
</li>
<li><p>Resume cooking</p>
</li>
<li><p>After cooking, shower and pray</p>
</li>
</ol>
<p>This multitasking approach saves about 30 minutes from the original schedule.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdNEFt2yJAVtHsCoXP5YWGPSlKS83LDfyHNYCeqe4SUj1f4f27qDrEXt3_62KRYVfeB_Pu9PdL4lfyKfZ0jXK-YlqtPJIpc3hdil0n6paKR4kD5BySDMWJpUrOUaA7kpZL-SOprsw?key=mTrrW-u6YrxXONgafrQrHVb9" alt /></p>
<h2 id="heading-multi-processing-parallelism">Multi Processing/ Parallelism</h2>
<p>To save even more time, we could outsource lunch preparation to a nearby eatery that delivers home-cooked meals. This parallel processing reduces the total time to 1 hour and 15 minutes—a 50% reduction from the original schedule.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc0eA6FLZbXvRBZpvALT8j9O8obooAQgUphgvU0FEqqTbw6z05YciI73qNYvJuMnP6AUng_MIoqDy3GVPmRn3KJMYQfolyJ-4FCqsRwD8wDz-4D4CFzeg_A7shRW0yPXpbzZ1-Msw?key=mTrrW-u6YrxXONgafrQrHVb9" alt /></p>
<p>Key Points:</p>
<ol>
<li><p>The 50% time reduction was achieved through both multitasking (async) and multi-processing (parallelism).</p>
</li>
<li><p>The entire optimized morning routine is an example of concurrent operations.</p>
</li>
<li><p>Both async and parallelism are subsets of concurrency.</p>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdtUf-HwQeXopso7pLjz2lIYy7WUsT1_hUf2Lm3Yftezg_xq6QGWwnl1DhXouhOWL10d-VPHOvskOmLcK3gmZ9yfF_L0mPftG9lhabfuxoWvufxgOputsZJvZV0HFcUy3CFzouVEw?key=mTrrW-u6YrxXONgafrQrHVb9" alt /></p>
<p>Correlating above example with programming</p>
<p><strong>Asynchronous Operations:</strong></p>
<ol>
<li>IO operations (e.g., reading from disk or socket) are analogous to doing dishes and laundry. When the processor initiates a disk read, it delegates to a disk controller and can perform other tasks while waiting. Once the operation completes, an interrupt (similar to a machine's signal) notifies the processor that the data is ready.</li>
</ol>
<p><strong>Parallel Processing:</strong></p>
<ol start="2">
<li>CPU-intensive calculations benefit from additional processors, much like outsourcing lunch preparation. For example, a desktop app might perform intensive calculations on a background thread using a separate processor, while the main thread keeps the app responsive on another processor.</li>
</ol>
<p>This real-world analogy helps illustrate how async operations, parallel processing, and concurrency work together in modern computing to optimize resource usage and improve overall efficiency.</p>
<h1 id="heading-limitations-of-concurrency">Limitations of Concurrency</h1>
<p>We've observed that concurrency encompasses both multitasking and parallelism, achieved through:</p>
<ol>
<li><p>Multitasking on a single processor</p>
</li>
<li><p>Parallel computation on multiple processors</p>
</li>
</ol>
<p>While this may seem straightforward at first glance, it's actually quite complex. Let's delve deeper into our morning chores example to illustrate some key challenges:</p>
<p>Synchronization Requirements:</p>
<p>Even in our simplified scenario, there are frequent occasions that require synchronization:</p>
<ul>
<li><p>The dishwasher and washing machine need attention when they signal completion.</p>
</li>
<li><p>Lunch delivery requires a response when the doorbell rings.</p>
</li>
</ul>
<p>These synchronization points highlight the need for careful coordination in concurrent systems.</p>
<p>Limits of Parallelization:</p>
<p>There's a limit to how much parallelization can optimize a process. For instance, if we added two more helpers to our morning routine:</p>
<p><strong>Helper 1</strong>: Load dishes, wait, unload dishes</p>
<p><strong>Helper 2</strong>: Load laundry, wait, unload laundry</p>
<p><strong>Eatery</strong>: Cook and deliver lunch</p>
<p><strong>Self</strong>: Shower and pray</p>
<p>In this scenario, we would only save an additional 30 minutes. The entire process can't be shortened beyond 1 hour because cooking and delivering lunch remains the longest task. Notably, Helper 1 and Helper 2 spend a significant amount of time waiting idly by their respective machines.</p>
<p>Adding more helpers beyond this point would be inefficient, as they would have no tasks to perform and would sit idle.</p>
<h2 id="heading-amdahls-law">Amdahl’s Law</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Amdahl%27s_law">Amdahl's Law</a> focuses on the potential speedup of a program when part of it is improved or parallelized.</p>
<p><strong>Speedup</strong>:</p>
<p>$$\frac{1}{(1 - P) + \frac{P}{N}}$$</p><p><strong>Where:</strong></p>
<p>P = Portion of the program that can be parallelized</p>
<p>N = Number of processors</p>
<p>(1 - P) = Portion that remains sequential</p>
<hr />
<p>This law shows that the overall speedup is limited by the sequential part of the program. As N increases, the speedup approaches a maximum limit of 1 / (1 - P).</p>
<h2 id="heading-gustafsons-law">Gustafson’s Law</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Gustafson%27s_law#:~:text=Gustafson's%20law%20argues%20that%20a,and%20functions%20of%20the%20system.">Gustafson's Law</a> considers that as we get more computing resources, we tend to take on larger problems rather than just solving the same problem faster.</p>
<p><strong>Scaled speedup</strong>:</p>
<p>$$N + (1 - N) \cdot s$$</p><p><strong>Where</strong>:</p>
<p>N = Number of processors</p>
<p>s = Serial fraction of the program</p>
<hr />
<p>This law suggests that the speedup can scale roughly linearly with the number of processors for many real-world problems.</p>
<p>Imagine a financial reporting system that needs to gather data from various financial data providers, process the data, and then combine the results into a comprehensive report.</p>
<p><strong>Amdahl's Law</strong> would apply to the part of the program that fetches the data from the independent sources (e.g., stock prices, exchange rates, economic indicators). By parallelizing this data fetch, the program can speed up this part of the process.</p>
<p>Gustafson's Law would come into play as the program is able to handle more data sources and generate more comprehensive reports as the computing resources (processors) are increased. The program can then take on larger and more complex financial reporting tasks.</p>
<p>By understanding both Amdahl's Law and Gustafson's Law, the developers of the financial reporting system can optimize the program's performance and scalability, ensuring that it can efficiently handle increasing amounts of data and reporting needs.</p>
<h1 id="heading-green-thread-managed-thread-user-level-thread">Green Thread, Managed Thread, User Level Thread</h1>
<blockquote>
<p>Q : Why are green threads called green threads?</p>
<p><em>A: Because they are green</em> 😛</p>
<p><em>Q: Isn’t the processor color blind?</em> 🤔</p>
</blockquote>
<p>A thread is the basic unit of execution that can run on a processor. Computers with multiple processors can run as many threads as there are CPUs or processors.</p>
<p>A processor takes a thread from the ready queue, processes it for a short duration, and then switches to the next thread. This rapid switching between threads from the ready queue creates the illusion of parallel execution, even on a single-processor computer.</p>
<p>There are several reasons that cause a processor to change context and pick a new thread. These include time slice expiration, preemption, and priority-based scheduling. However, one major reason is I/O operations. If the processor detects that a thread is waiting for an I/O operation, it will immediately release that thread and pick another one from the ready queue.</p>
<p>This approach works well for a finite number of threads, but as the number of threads grows very high, the time spent on context switching can increase drastically.</p>
<p>To address this issue, modern programming runtimes like Golang and C# have introduced the concept of <code>green threads</code> or <code>managed threads</code>. When you create a thread in these programming languages, it is not an actual OS-level thread (also known as a kernel thread) that is created. Instead, it is a green thread or managed thread, which is a user-level thread.</p>
<p>The runtime creates the actual OS-level threads and then assigns the green/managed threads to these OS threads. In this way, a single OS thread can run multiple green/managed threads within it.</p>
<p>When a green/managed thread encounters an I/O operation, the runtime creates a new OS thread and assigns the remaining green/managed threads to it. This optimization of running multiple green/managed threads within a single OS thread helps reduce the overhead of context switching, which becomes increasingly important as the number of threads grows.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXf7eo7lSB3KDgZ02y-XsPsnzGnRC7eNZNdP9oOpbSukwPYV_r96s--SPqFryEDNpaw6KeQVWA69g9f0TYO76AevRnXv4oYFmJweNY7sNKWXj43FGuQKK4p53FzCFLVmYMN5iCm_Fw?key=mTrrW-u6YrxXONgafrQrHVb9" alt /></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we explored the evolution of computing systems and the importance of concurrency to improve computational performance beyond the limitations of increasing clock frequencies on single processors.</p>
<p>We learned that multitasking and multiprocessing are distinct but related concepts that fall under the broader umbrella of concurrency. While JavaScript, a single-threaded language, can support multitasking through techniques like event-driven programming, modern languages like C# and Golang offer robust support for multiprocessing by leveraging parallel operations across multiple CPUs.</p>
<p>A key concept we discussed was the difference between OS-level threads (also known as kernel threads) and user-level threads, often referred to as <code>green threads</code> or <code>managed threads</code>. This distinction is important, as it allows programming runtimes to optimize thread management and reduce the overhead of context switching, particularly as the number of threads grows.</p>
<p>Whether you are a seasoned developer or new to the world of concurrency, I hope this article has provided clarity and added valuable knowledge to your understanding of these fundamental computer science concepts. Even if you were already familiar with the topics covered, I trust that this article has helped solidify your grasp of the subject matter and perhaps even challenged your previous assumptions.</p>
<p>Thank you for taking the time to read this article. I appreciate your engagement and hope that the insights shared here will prove useful in your future endeavors.</p>
]]></content:encoded></item><item><title><![CDATA[The internals of TCP: A deep dive]]></title><description><![CDATA[Introduction
TCP has been the foundational protocol for numerous higher-level protocols, such as HTTP and WebSockets, due to its guarantee of data integrity. Imagine querying a database table and missing a few rows of data—that would be catastrophic....]]></description><link>https://oxyprogrammer.com/the-internals-of-tcp-a-deep-dive</link><guid isPermaLink="true">https://oxyprogrammer.com/the-internals-of-tcp-a-deep-dive</guid><category><![CDATA[TCP]]></category><category><![CDATA[TCP Handshake]]></category><category><![CDATA[TCP/IP]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Fri, 07 Mar 2025 05:40:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747489830768/e0701e04-a65a-4f66-91e5-4286c4c9cb73.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>TCP has been the foundational protocol for numerous higher-level protocols, such as HTTP and WebSockets, due to its guarantee of data integrity. Imagine querying a database table and missing a few rows of data—that would be catastrophic. Yet, we make database calls without concern about missing data, thanks to TCP's integrity guarantees. In networking, balancing data integrity with latency is a challenge, especially given the unpredictable nature of the internet. We never know how many routers our network calls hop through, and despite many routers or switches potentially dropping packets, data loss is not an issue.</p>
<p>In the <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">first article</a> of this networking series, we briefly discussed TCP connections, examining how the <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp#heading-os-facilitating-tcp-connection-under-the-hood">OS handles them internally</a>. We concluded with several open-ended questions. I recommend reviewing <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">that article</a>, as it provides an essential piece of the puzzle we aim to complete.</p>
<p>This article, I believe will clarify your understanding of TCP and dispel misconceptions about this enduring protocol that has stood the test of time for over the past four decades</p>
<h1 id="heading-ip-packet-header">IP Packet Header</h1>
<p>In our <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">previous discussion</a>, we explored how IP packets exist at Layer 3 of the OSI model, while TCP segments operate at Layer 4. A TCP segment, along with its header, gets stuffed inside the IP Packet data and gets sent.</p>
<p>To fully understand the subsequent sections, it is important to familiarize ourselves with the IP packet header. Below is a diagram illustrating the IP packet header structure:</p>
<pre><code class="lang-plaintext">    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
</code></pre>
<p>Source: <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc791#page-11">RFC 791</a></p>
<ul>
<li><p><strong>Version</strong>: Specifies the IP protocol version, either IPv4 or IPv6.</p>
</li>
<li><p><strong>IHL (Internet Header Length)</strong>: Indicates the length of the header, including any options.</p>
</li>
<li><p><strong>Type of Service</strong>: Defines aspects like priority and quality of service.</p>
</li>
<li><p><strong>Total Length</strong>: Specifies the packet's total length, including the header and data.</p>
</li>
<li><p><strong>Identification, Flags, Fragment Offset</strong>: These fields are used for packet fragmentation and reassembly if needed.</p>
</li>
<li><p><strong>Time to Live (TTL)</strong>: Limits the lifespan of a packet by defining the maximum number of hops it can make before being discarded. Each router decrements this value by one.</p>
</li>
<li><p><strong>Protocol</strong>: Specifies the protocol used in the data portion (e.g., ICMP, TCP, UDP).</p>
</li>
<li><p><strong>Header Checksum</strong>: Used to verify the integrity of the header data in IPv4 packets.</p>
</li>
<li><p><strong>Source and Destination IP Address</strong>: Specifies the sender's and receiver's IP addresses. At Layer 3, only these IP addresses are involved in routing.</p>
</li>
</ul>
<p>Understanding these fields will aid in navigating the complexities of network packet handling in further discussions.</p>
<h1 id="heading-tcp-connection">TCP Connection</h1>
<p>The Transmission Control Protocol (TCP) focuses on controlling data transmission, unlike the more lenient UDP. TCP is methodical about initiating, maintaining, and terminating data transmission. A crucial aspect is the TCP connection.</p>
<p>A TCP connection is bidirectional, enabling protocols like HTTP and WebSocket to operate effectively. It is initiated by the client and accepted by the server. A client is typically an application used directly by the user to initiate operations, while a server is a continuously available application that handles client requests.</p>
<p>Therefore, it's more practical for clients to identify the server rather than vice versa, aligning with software architecture, not any inherent TCP directionality.</p>
<p>TCP, a Layer 4 protocol with port visibility (see the <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">first article</a>), requires an established connection for data transmission. A TCP connection resembles an agreement between a client and server, identified by:</p>
<ul>
<li><p>Client IP</p>
</li>
<li><p>Client Port</p>
</li>
<li><p>Destination IP</p>
</li>
<li><p>Destination Port</p>
</li>
</ul>
<p>Establishing a TCP connection involves a three-way handshake: SYN, SYN-ACK, and ACK. <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp#heading-traveling-through-the-layers-of-network">TCP segments</a> are sequenced and acknowledged. A delay in segment acknowledgment triggers a retransmission, which we will explore shortly.</p>
<p>Multiple connections can exist between the same client and server, with TCP segments multiplexed into a single stream. This stream is then demultiplexed and routed to the appropriate programs listening on relevant ports.</p>
<h2 id="heading-connection-establishing">Connection Establishing</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735036838686/a608f912-0c32-4fcd-96a6-65310e0399cf.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-tcp-connection-establishment-consists-of-three-way-handshake">TCP connection establishment consists of three way handshake:</h2>
<ol>
<li><p>The sender sends a SYN request.</p>
</li>
<li><p>The receiver responds with a SYN/ACK message.</p>
</li>
<li><p>The initiator sends back an ACK message, finalizing the connection. Both sender and receiver now have sockets and file descriptors, signifying an established connection.</p>
</li>
</ol>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>For information on File Descriptors and Sockets, </em><a target="_self" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp#heading-os-facilitating-tcp-connection-under-the-hood"><em>read this article</em></a><em>.</em></div>
</div>

<h2 id="heading-transmission-of-data">Transmission of Data</h2>
<p>With the connection established, the sender begins transmitting TCP segments. These segments are acknowledged by the receiver upon receipt.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfVhtArdMgZfcwYWYYgRizU2Dr9gkayhDSsyFkm2-UN2qGD-cP9u0noFdgrUbRV97AgP0l-qwr0twX6tjgvRkKPwAS3ybFp-hwlHoA5Fe0vQHZlTlJYMqdV4h6gW_r1F4IqwWx4rw?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>The receiver may acknowledge multiple segments with a single acknowledgment. For instance, in the given scenario (refer the diagram above), the sender might send three segments numbered based on the initial SYN connection request sequence. The receiver acknowledges the last segment, implicitly acknowledging prior segments as well.</p>
<h2 id="heading-re-transmission-of-data">Re transmission of Data</h2>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXepcpcuFr8YgJTy2q6E4zb7JAkuof0q10y33A3aNE55muNTlqTpyBPW64SxB83kfwnJWZq0iscIOXNyibwCBz3siGGU67xC7PXaGK6g2c3kQw6hBsb2BVQORymWgnA35WvV0bPn5g?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>For illustration:</p>
<ol>
<li><p>The segment with the third sequence number is dropped.</p>
</li>
<li><p>The sender waits for acknowledgement and receives acknowledgment only for sequence 2.</p>
</li>
<li><p>After a timeout, the sender retransmits the segment marked with sequence 3.</p>
</li>
<li><p>The receiver then acknowledges sequence 3.</p>
</li>
</ol>
<p>You might be thinking what would happen if sequence 2 got dropped while sequence 3 went through. Well that would result in <strong>Line of Head blocking</strong> and we will explore that in the last section of the article.</p>
<h2 id="heading-connection-closure-and-connection-states">Connection Closure and connection States</h2>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXd7t0R1GIUF7KlWEge3fb-RikXfUbVu1oJc0A5msLkldC-1mSoLiRHYyxUb7r1i11uG2U5yYhDiMVSvPJc7PiycF9dstKkWcXZlfBEITbG_xsmOZpYO2dcsC5NR_9RSiQmWGEegig?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>While a TCP connection is established via a three-way handshake, closing it involves a four-way handshake. The connection state transitions as follows:</p>
<ol>
<li><p>The sender initiates closure by sending a FIN request, entering the FIN WAIT state.</p>
</li>
<li><p>The receiver receives the FIN, sends back an ACK, and moves to the CLOSE WAIT state.</p>
</li>
<li><p>The sender receives the ACK, moving to the FIN WAIT 2 state.</p>
</li>
<li><p>The receiver enters the LAST ACK state, sending a FIN back.</p>
</li>
<li><p>The sender, on receiving the FIN, moves to the TIME WAIT state, sending a final ACK.</p>
</li>
<li><p>On receiving the last ACK, the receiver transitions the connection to the CLOSED state and waits (usually around 4 minutes) to ensure no more messages are incoming before finally moving to the CLOSED state.</p>
</li>
</ol>
<p>The responsibility for waiting and closing the connection falls on the initiator, hence the recommendation for the client to initiate the connection. Additionally, the removal of sockets and file descriptors continues even after the connection is closed, as the OS independently manages resource disposal.</p>
<h1 id="heading-anatomy-of-tcp-segment">Anatomy of TCP Segment</h1>
<p>The TCP header format is as follows:</p>
<p><em>Note: Each tick mark represents one bit position.</em></p>
<pre><code class="lang-plaintext">0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|            Window  Size       |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             Data                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
</code></pre>
<p>Source: <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc793#page-15">RFC 793</a></p>
<p>The header consists of 5 × 4 = 20 bytes. <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc793#page-15">Additional headers</a> may be included as options. The Source Port and Destination Port fields specify the ports for the source and destination (the IP addresses are contained in the IP packet header).</p>
<p>The Acknowledgment Number is only relevant if the ACK flag is set. <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc793#page-15">The Window</a> Size field indicates the amount of data the receiver can handle (more on this in the Sliding Window section).</p>
<p>Notable are the 9-bit flags:</p>
<ul>
<li><p><strong>FIN</strong>: Indicates a connection closure request.</p>
</li>
<li><p><strong>SYN</strong>: Initiates a sequence number for the initial handshake.</p>
</li>
<li><p><strong>RST</strong>: Resets the connection.</p>
</li>
<li><p><strong>ACK</strong>: Acknowledges received data.</p>
</li>
<li><p><strong>URG</strong>: Marks urgent data.</p>
</li>
<li><p><strong>ECE</strong>: Signals congestion notification.</p>
</li>
<li><p><strong>CWR</strong>: Indicates congestion window reduction.</p>
</li>
<li><p><strong>NA</strong>: Notification anomalies.</p>
</li>
</ul>
<p>The relevance of these flags will become clearer as we explore more aspects of TCP.</p>
<h1 id="heading-flow-control">Flow Control</h1>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc2tCgSIqzSO7zCkAGrLo4dWZd4FBPeZxpyc6OJo0zCRMe6meSTFizFQIcWijg_uPAqLt8SUWljPaw52N7YeDcXqI4EsXBMF-P2B1ycnOJXhJOVfbYcvp16z-3irYISxUFMJhg1PA?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>Consider a scenario where the sender wants to transmit Segments 1, 2, and 3 to the receiver. Receiving a single acknowledgment for multiple segments is more efficient. However, the sender must have a way to know how many segments to send before waiting for an acknowledgment. Sending too many segments could overwhelm the receiver's buffer, leading to dropped segments.</p>
<p>This is where the Window Size field (refer to TCP segment anatomy) comes into play. Each acknowledgment from the receiver includes the current Window Size, informing the sender of how many packets can be sent.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">While the Window Size is significant, there are other factors influencing flow control that we will discuss in upcoming sections.</div>
</div>

<h1 id="heading-receiver-sliding-window">Receiver (Sliding) Window</h1>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeVIDjPEXTIxwPQ0QyFJ2d5mep2VLdN5nR-1BvXFN-xZj85A_k5Io_U1EKzYdUBh0H04PpJ1CHJbOVtVdcH8J8oMOD7Q26z8IjniPNjWEERsCgg4KSEOFxQZ2i29dzqmq5SbHVDNg?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>The sliding window is a critical mechanism in TCP used by the sender. As illustrated, assume the sender wants to transmit six segments to the receiver.</p>
<ol>
<li><p>The sender transmits Segments 1, 2, and 3.</p>
</li>
<li><p>The receiver acknowledges Segment 2.</p>
</li>
<li><p>The sender realizes Segment 3 is either still in transit or lost.</p>
</li>
<li><p>The window slides to include Segments 4 and 5, keeping Segment 3 within the window.</p>
</li>
<li><p>Segments 1 and 2 are excluded from the sender's window as they have moved out of the window and may be dropped.</p>
</li>
<li><p>The sender transmits Segments 4 and 5.</p>
</li>
<li><p>Upon receiving acknowledgment for Segment 3, the window slides further to include Segment 6 and remove Segment 3.</p>
</li>
</ol>
<p>This sliding window mechanism ensures orderly data transmission and efficient use of network resources.</p>
<p><strong>But what should be the size of this window?</strong></p>
<p>Clearly, the number of segments that are to be sent to the receiver are to be included in the window. The Size of the window gets set with every acknowledgement that comes back (remember the Window Size field in the TCP header?).</p>
<h1 id="heading-congestion-control">Congestion Control</h1>
<p>The flow of data from sender to receiver in TCP is not solely managed by flow control. Although flow control ensures that the receiver is not overwhelmed, the data must traverse several intermediary devices such as routers and switches. These network elements might not support rapid data flow even if the receiver can handle it. Therefore, TCP also incorporates congestion control to manage data transmission effectively.</p>
<p>In addition to the Receiver Window (RWND), TCP utilizes a Congestion Window (CWND), which plays a crucial role in congestion control. It’s important to note that the CWND can never exceed the RWND.</p>
<p>There are two primary algorithms for determining the size of the CWND:</p>
<ol>
<li><p><strong>TCP Slow Start</strong>: This algorithm gradually increases the CWND size to identify the network's capacity without causing congestion.</p>
</li>
<li><p><strong>Congestion Avoidance</strong>: This algorithm aims to optimize data flow by adjusting the CWND size to avoid congestion once the initial network capacity has been identified.</p>
</li>
</ol>
<p>Let's explore each of these algorithms in detail.</p>
<h2 id="heading-tcp-slow-start">TCP Slow Start</h2>
<p>Ironically, despite its name, TCP Slow Start is actually the fastest among the congestion control algorithms.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXe-_OUVd-jkJbTKjQ5uBNy6vhd3BE6Na9_UcRZMBgE4I_vGNDVhmjSrX8vys2DB-kmo_-eqSNd_eGk_DypXxJPtpFSdlbcLXSc3C5r_dFY86HNFrXVp8QfyKHLaAOboRHCEcd7u?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>In the example illustrated above:</p>
<ol>
<li><p>The Congestion Window (CWND) begins with a capacity of 1 segment. Accordingly, only one segment is sent initially.</p>
</li>
<li><p>Upon receiving acknowledgment from the receiver, the CWND is increased by 1.</p>
</li>
<li><p>The sender then transmits two segments: Segments 2 and 3.</p>
</li>
<li><p>In response, the receiver sends back acknowledgments for both segments (2 and 3). As a result, the CWND increases by 2 (1 for each acknowledgment received).</p>
</li>
<li><p>With the CWND now allowing for larger transmission, the sender proceeds to send Segments 4, 5, 6, and 7.</p>
</li>
</ol>
<h2 id="heading-congestion-avoidance">Congestion Avoidance</h2>
<p>Congestion Avoidance algorithm also increases the CWND but at a slower rate than TCP Slow start.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc6IhmnSLsQeVlDxg7BWg7OpPv78JiW6Fqf0N8q2oKnKL3rvM4mkQ527m9xD-TpcDpD_Qd2KacK_FvSo3eMHi9X8aIk_Pz8-Wd9h_q7LM8L505TWJvCJFdNSvNLD935UMX3vdofiw?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>In the above diagram example:</p>
<ol>
<li><p>The Congestion Window (CWND) begins with the capacity for 1 segment, so only one segment is initially sent.</p>
</li>
<li><p>Upon receiving an acknowledgment from the receiver, the CWND is increased by 1.</p>
</li>
<li><p>The sender then transmits two segments: Segments 2 and 3.</p>
</li>
<li><p>The receiver sends back acknowledgments for both segments. The CWND is increased by 1 for this entire round trip, as it pertains to the single window of data sent.</p>
</li>
<li><p>With the updated CWND, the sender now sends Segments 4, 5, and 6.</p>
</li>
</ol>
<h1 id="heading-congestion-notification">Congestion Notification</h1>
<p>Routers operate at Layer 3 of the OSI model, providing them visibility into IP packets. IP packets include a field known as ECN (Explicit Congestion Notification). When routers detect their buffers are becoming full, they mark the ECN field in the IP packets and forward them to the receiver. Upon receiving these packets, the receiver notes the ECN marking in the IP headers and communicates this information back to the sender. The sender will  reduce the transmission rate to alleviate congestion.</p>
<h1 id="heading-nagles-algorithm">Nagle’s Algorithm</h1>
<p>Nagle’s algorithm specifies that if there are in-flight segments—meaning segments that have been sent but for which an acknowledgment (ACK) has not yet been received—an IP packet will only be transmitted if it is completely filled. Conversely, if there are no in-flight segments, a packet will be sent even if it is only partially filled.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfI9AzDg889hvsguUc6GZ4IUR61iuXovpIY72OK3626xxLKMk-zYR4p0ip1PE3UgzxwnDhN-M5GrImcteUb3pOuL04KxwC9JGc5U_oysNb-50D6upIJPyzGOrW1rQXUs9OnH4QrLw?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>In the diagram example provided:</p>
<ul>
<li><p>Three completely filled IP packets are sent, while a partially filled packet is held back.</p>
</li>
<li><p>Once the acknowledgments for the three packets are received, the partially filled packet is then transmitted.</p>
</li>
</ul>
<p>Nagle’s algorithm is often disabled in modern networking practices because it can introduce additional latency.</p>
<h1 id="heading-delayed-acknowledgement-algorithm">Delayed Acknowledgement Algorithm</h1>
<p>The Delayed Acknowledgment Algorithm is implemented on the receiver's side. This algorithm suggests that the receiver should wait to receive multiple packets before sending an acknowledgment (ACK). By doing so, the number of acknowledgments sent is reduced, which can help decrease overall latency in the communication process.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfiuNi68X7lZ0thL7iEl3e_-A1CmM9egQMJYG-yOLegRBxk_u-8buxG4DQLSnKTLvJMeXbUfCEah0GF2QvAybSFXC3LCfOyPKrBBA6e39xZ_Qxthvi25MXrEtYoTK5Po2L-palL?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>A problem arises when both Nagle’s algorithm and the Delayed Acknowledgment Algorithm are used simultaneously. Nagle’s algorithm, which operates on the sender's side, holds back a packet until an acknowledgment (ACK) for previously sent segments is received. Meanwhile, the Delayed Acknowledgment Algorithm, implemented on the receiver's side, waits to receive multiple segments before sending back an ACK.</p>
<p>This combination can create a deadlock-like situation, where the sender is waiting for an ACK that is not being sent because the receiver is holding off until it receives additional segments. As a result, this can lead to retransmissions and timeouts, negatively impacting network performance.</p>
<h1 id="heading-tcp-head-of-line-blocking">TCP Head of Line blocking</h1>
<p>TCP ensures that segments are delivered in the order they are sent. Line of Head Blocking occurs when a segment in the middle of a sequence gets dropped, which particularly impacts HTTP requests because they often use the same connection to send multiple requests.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXduJXUlbZFCSo1pqqh4UhPzzjo2LFZEEQMhDdec4bVeT3hxDY2fuQoK-vL8xi42lHnRTCsQSbTFxxrPCqMg_A48rS05ECE3gmgAdsKoMdCyHM1M3ckMhKksKendaRIeEkuYrI_soA?key=allS53MX6v5H-VzZb3VLXJ-m" alt /></p>
<p>Consider the diagram example illustrated above:</p>
<ul>
<li><p>Request 1 is divided into Segments 1 and 2.</p>
</li>
<li><p>Request 2 is divided into Segments 3 and 4.</p>
</li>
</ul>
<p>If Segments 2, 3, and 4 are successfully transmitted but Segment 1 is dropped, the server will not send acknowledgments (ACKs) for Segments 2, 3, and 4 until Segment 1 is retransmitted.</p>
<p>As a result, Request 2 suffers delays even though it was fully transmitted, because it is dependent on the status of Request 1.</p>
<p>This scenario exemplifies Line of Head Blocking, where the processing of one request is held up due to the loss of an earlier packet.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this comprehensive article, we built upon the foundation established in a <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">previous piece</a> and delved into the internals of TCP communication. We began by examining the anatomy of IP packets and then explored the mechanisms involved in TCP connection creation and closure.</p>
<p>We discussed various mechanisms, such as Flow Control and Congestion Control, that TCP employs to ensure smooth and reliable data transmission. The sliding window technique facilitates controlled data flow, allowing for efficient management of both flow and congestion.</p>
<p>Additionally, we reviewed Nagle’s algorithm and the Delayed Acknowledgment Algorithm, both designed to reduce transmissions for improved efficiency. However, we noted that their combined use can lead to counterproductive outcomes. Finally, we addressed the concept of Line of Head Blocking, illustrating its impact on data transmission.</p>
<p>I trust that this article has contributed to your understanding and provided valuable insights into the intricacies of TCP internals.</p>
]]></content:encoded></item><item><title><![CDATA[Apache Kafka: Architectural Overview and Performance Mechanisms]]></title><description><![CDATA[Introduction
Kafka is a distributed event store and stream processing platform originally developed by LinkedIn for real-time processing. In 2011, Kafka was transferred to the Apache Software Foundation, and since then, countless software development...]]></description><link>https://oxyprogrammer.com/apache-kafka-architectural-overview-and-performance-mechanisms</link><guid isPermaLink="true">https://oxyprogrammer.com/apache-kafka-architectural-overview-and-performance-mechanisms</guid><category><![CDATA[kafka]]></category><category><![CDATA[kafka topic]]></category><category><![CDATA[kafka producer]]></category><category><![CDATA[kafka consumers]]></category><category><![CDATA[kafka-partition]]></category><category><![CDATA[kafka broker]]></category><category><![CDATA[Kraft]]></category><category><![CDATA[distributed system]]></category><category><![CDATA[leader election]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Sun, 24 Nov 2024 04:30:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731582744475/f23cc5b3-0f3e-41f3-9b46-47f12a7a9bca.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Kafka is a distributed event store and stream processing platform originally developed by LinkedIn for real-time processing. In 2011, Kafka was transferred to the Apache Software Foundation, and since then, countless software development teams have adopted it for various requirements. In this article, we will examine the various components of Kafka, how they function, and what enables Kafka to be highly available while handling millions of messages per second.</p>
<h1 id="heading-components-of-kafka">Components of Kafka</h1>
<h2 id="heading-topics">Topics</h2>
<p>A Kafka topic can be likened to a database table; while it is not strictly a table, this analogy helps illustrate its function. Each topic is identified by a unique name and does not perform data validation like a database table. The sequence of messages within a topic is referred to as a data stream. Topics are immutable, meaning that once data is written, it cannot be deleted or modified. </p>
<p>Data in Kafka topics does not get removed after consumption, distinguishing Kafka from traditional message queues. Instead, the data is persisted in topics for a limited time, with a default expiry period of seven days that can be configured based on requirements. Topics are fed by producers, and the data within them is consumed by consumers, as we will explore shortly. </p>
<h2 id="heading-partitions-and-offsets">Partitions and Offsets</h2>
<p>One reason Kafka achieves high throughput is due to the distribution of topics across partitions. A single topic can consist of multiple partitions. When a message is published to a topic, a component of Kafka known as the producer (covered later) determines which partition will receive the message. Users can also instruct producers to route data to a specific partition by providing a partition key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731580749267/6bb6bfcb-5902-4aa6-ba71-fd9dddfe4e34.png" alt class="image--center mx-auto" /></p>
<p>Within each partition, messages are assigned an incremental identifier known as an offset. The offset is meaningful only within a specific partition; thus, offset 2 in partition 1 is unrelated to offset 2 in partition 2. This design ensures that order is guaranteed only within partitions, not across the entire topic. Additionally, offsets will not be reused, even after data has been deleted. </p>
<h2 id="heading-producers">Producers</h2>
<p>A producer is a client that writes data to Kafka topics, which are composed of partitions. Producers have the following key attributes: </p>
<ul>
<li><p>They perform load balancing across partitions of a topic until a specific partition key is provided.</p>
</li>
<li><p>If a broker hosting the intended partition goes down while the producer is pushing messages, the producer can recover by shifting to a replicated partition. We will delve deeper into replication in Kafka later in this article.</p>
</li>
<li><p>Producers know which broker (a physical machine where a topic partition resides) contains the necessary partition for writing data. A producer employs partitioner logic to determine which partition to use for a record. This logic uses a key in binary format and the Murmur2 algorithm to hash the value, which identifies the target partition. The calculation follows this formula:<br />  <code>targetParition = Math.abs(Utils.murmur2(keyBytes)) % (numPartitions-1)</code></p>
</li>
</ul>
<p>A Kafka message sent by the broker comprises the following components: </p>
<ul>
<li><p><strong>Key</strong>: In binary format and can be null.</p>
</li>
<li><p><strong>Value</strong>: The message itself, also in binary format and nullable.</p>
</li>
<li><p><strong>Compression Type</strong>: The supported compression types include <em>none</em>, <em>gzip</em>, <em>snappy</em>, <em>lz4</em>, and <em>zstd</em>.</p>
</li>
<li><p><strong>Headers</strong>: In key-value format, these are optional.</p>
</li>
<li><p><strong>Partition</strong>: Contains information about the partition where the message will be written.</p>
</li>
<li><p><strong>Timestamp</strong>: This can be either user-defined or system-generated.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731580800043/5e3bfb01-9275-4706-9567-042dca32f178.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-kafka-message-serializer">Kafka Message Serializer</h3>
<p>Kafka topic partitions exclusively accept bytes from producers and return bytes to consumers (which we will discuss shortly). The key and value in each message are serialized before being pushed into the topics.</p>
<h4 id="heading-common-serializers-provided-by-kafka">Common Serializers provided by Kafka:</h4>
<ul>
<li><p>String (including JSON)</p>
</li>
<li><p>Int, Float</p>
</li>
<li><p>Avro</p>
</li>
<li><p>Protobuf</p>
</li>
</ul>
<h1 id="heading-consumers">Consumers</h1>
<p>Consumers are clients that perform the opposite function of producers: they <strong>pull</strong> data from a topic. Data is read sequentially from low to high within each partition.</p>
<h2 id="heading-consumer-deserializer">Consumer Deserializer</h2>
<p>Just as producers have serializers, consumers utilize deserializers. Common deserializers include: </p>
<ul>
<li><p>String (including JSON)</p>
</li>
<li><p>Int, Float</p>
</li>
<li><p>Avro</p>
</li>
<li><p>Protobuf</p>
</li>
</ul>
<p>It is important to note that a Kafka topic cannot change its serialization or deserialization type throughout its lifecycle. The same serialization format must be used for both serialization and deserialization processes.</p>
<h2 id="heading-consumer-groups">Consumer Groups</h2>
<p>All consumers within an application read data as part of consumer groups. </p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Each consumer within a group can read from multiple partitions, but a single partition cannot be consumed by more than one consumer at a time. Refer the diagram below.</strong></div>
</div>

<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731580949407/6ee16286-f143-490a-83ba-81d386ced393.png" alt class="image--center mx-auto" /></p>
<p><strong>A natural question arises</strong>: What happens if the number of consumers in a consumer group exceeds the number of available partitions? </p>
<p>In this case, the additional consumers will exist but remain inactive, as illustrated in the diagram below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731580971547/16764ebc-b977-427f-9b97-e5dc4d8c3af7.png" alt class="image--center mx-auto" /></p>
<p>A topic can be connected to multiple consumer groups without any issue.</p>
<p>To create distinct consumer groups, the Kafka consumer framework provides a property called <code>group ID</code> For example, you might have a notification service and a dashboard service that both listen to a topic called <code>truck_gps_location</code> While the dashboard service updates a geographic map on the dashboard, the notification service is responsible for raising alerts for interested users via email or text. These two services belong to different consumer groups.</p>
<h2 id="heading-consumer-offsets">Consumer Offsets</h2>
<p>Kafka stores the read offsets for each consumer group in an internal topic called <code>__consumer_offsets</code>. This mechanism ensures that if a consumer in a group fails, Kafka allows it to resume data retrieval from the last position it accessed. </p>
<p>Consumers need to periodically commit the read offsets in Kafka, which can be done either automatically or manually. If committed manually, there are three delivery semantics to consider: </p>
<ul>
<li><p><strong>At Least Once (usually preferred)</strong>: Offsets are committed after the message is processed. If an error occurs during processing, the message will be read again, which might lead to duplicate processing. Therefore, consumers should handle messages idempotently.</p>
</li>
<li><p><strong>At Most Once</strong>: Offsets are committed immediately upon receiving the messages. If an error occurs during processing, some messages may be lost and will not be read again.</p>
</li>
<li><p><strong>Exactly Once</strong>: This approach is recommended when there is a need to read from a topic and then write back to it. The Transactional API of Kafka can be used for this purpose.</p>
</li>
</ul>
<h2 id="heading-kafka-brokers-and-topic-replication-factor">Kafka Brokers and Topic Replication Factor</h2>
<p>Kafka is a distributed software platform that spans multiple nodes or servers, referred to as brokers. The partitions for a topic are distributed across different brokers to facilitate horizontal scaling, and they are also replicated to ensure high availability. The <strong>replication factor</strong> of a topic determines how many replicas of each partition will be stored.</p>
<p>For example, consider a Kafka cluster with three brokers and a replication factor of 2.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731581026226/a0660232-d69e-48b9-9179-fca58744db93.png" alt class="image--center mx-auto" /></p>
<p>As illustrated in the above diagram, the partitions for both topics are distributed across the three brokers, with each partition's replication also stored across the brokers. This design allows Kafka topics to be both distributed and highly available.</p>
<p>When a client connects to any broker within the Kafka cluster, the broker acts as a bootstrap broker. It provides the client with information about all the other brokers, their addresses, and the partitions stored on them, allowing the client to know which broker to connect to for the specific partition it requires.</p>
<p>Regarding the replicas of a partition, only one copy—the leader replica—will accept writes from producers. Producers send messages only to the leader partition. Consumers, on the other hand, read from the closest replica of the partition.</p>
<h2 id="heading-zookeeper-kraft">ZooKeeper / KRaft</h2>
<p><code>ZooKeeper</code>, a distributed system in its own right, was initially used to coordinate the brokers in a Kafka cluster, enabling them to manage events such as new topic creation, leader elections across partitions, and broker failures (both the death of a broker and its recovery). However, <code>ZooKeeper</code> faced scaling issues after approximately 100,000 partitions. Consequently, starting with Kafka version 4.0, <code>ZooKeeper</code> has been deprecated and replaced with <code>KRaft</code>, which is a Raft implementation of Kafka itself, allowing a Kafka cluster to scale to millions of partitions.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we examined the various components of Apache Kafka and gained an understanding of how they function. At a high level, we explored the factors that make Kafka both scalable and highly available. We began with topics and delved into components such as partitions. We also discussed producers, consumers, and consumer groups, highlighting the anatomy of a Kafka message sent by the producer to the partitions and how consumer groups allow different services to read from the same topic for various business purposes.</p>
<p>Kafka is continually being enhanced for performance improvements and remains a highly sought-after technology for high-speed event processing. I hope this article has clarified many of your questions about Kafka and has instilled confidence in you to consider it for your next project.</p>
]]></content:encoded></item><item><title><![CDATA[Demystifying DNS: Understanding Domain Name Resolution]]></title><description><![CDATA[Introduction
The route to reach a server hosted on the public internet is through its public IP address. If you know the IP address of a server, you can access it directly. However, there are two main drawbacks to this approach:

Hard to Operate: Rem...]]></description><link>https://oxyprogrammer.com/demystifying-dns-understanding-domain-name-resolution</link><guid isPermaLink="true">https://oxyprogrammer.com/demystifying-dns-understanding-domain-name-resolution</guid><category><![CDATA[networking]]></category><category><![CDATA[dns]]></category><category><![CDATA[dns resolver]]></category><category><![CDATA[#TLD]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Sat, 23 Nov 2024 04:30:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731218576888/aa7c722f-06a6-4c96-8703-bf7bebb52fd6.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>The route to reach a server hosted on the public internet is through its public IP address. If you know the IP address of a server, you can access it directly. However, there are two main drawbacks to this approach:</p>
<ol>
<li><p><strong>Hard to Operate</strong>: Remembering an IP address can be challenging, and they are subject to change. Common users who work with IP addresses often find it difficult to remember them and cope with frequently changing addresses. It’s easier to remember google.com than <code>142.250.182.14</code>.</p>
</li>
<li><p><strong>Not Scalable</strong>: High-demand servers, like Google, need to have hundreds of servers located around the globe. The Google server that caters to a user in Australia does not serve a user in North America, meaning there is not just one IP address.</p>
</li>
</ol>
<p>To address these problems, the Domain Name System (DNS) is utilized. In this article, we will take a detailed look at how DNS resolution occurs under the hood.</p>
<h1 id="heading-dns-resolution-under-the-hood">DNS Resolution Under the hood</h1>
<p>DNS is a protocol built on top of UDP, a layer 4 protocol. You may refer to the <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">first article in this series</a> for a more in-depth understanding of network layers. The preferred port for DNS is 53 (similar to port 80 for HTTP).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731218610142/937dc599-e4cd-469d-b658-8639a220afd3.png" alt class="image--center mx-auto" /></p>
<p>The process begins when you type <a target="_blank" href="http://google.com">google.com</a> into your browser's address bar. The following steps will occur (refer diagram above):</p>
<ol>
<li><p><strong>Hit the Resolver</strong>: If the browser does not have the IP address of <code>google.com</code> in its local cache, it will send a request to the resolver server for the IP address of <code>google.com</code>. This is a sync request. Resolver server addresses are configured within your network. Once you connect to a network that has internet access, the resolver is recognized by your machine through your ISP.</p>
</li>
<li><p><strong>Resolver Hits Root</strong>: The resolver then queries a DNS root server. The address of the local root server is known to the resolver. There are over 13 root servers globally, ensuring high availability and reduced latency. The root server will provide the address of the top-level domain server (TLD), such as “<code>.com</code>.”</p>
</li>
<li><p><strong>Resolver Hits the TLD</strong>: Next, the resolver contacts the TLD server, which, in this case, will be the <code>.com</code> server. This TLD server has records for all .com domain name registries. Like the root servers, TLD servers are also replicated worldwide. The TLD server returns the address of an authoritative name server.</p>
</li>
<li><p><strong>Authoritative Name Server Response</strong>: The authoritative name servers return the IP address of the second-level domain that is closest to the user. This is also where load balancing occurs; for example, <code>google.com</code> will return different IP addresses of replicated servers in response to different requests to distribute the load effectively.</p>
</li>
</ol>
<h1 id="heading-dns-packet-header-anatomy">DNS packet header anatomy</h1>
<p>A question that arises is: how does the resolver distinguish between the different requests that are coming in?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731218650674/8ed4ee2b-33f2-46e7-a9ba-f2fbb95267ee.png" alt class="image--center mx-auto" /></p>
<p>Examining the DNS request headers can help answer this question. A DNS packet header contains a <mark>Transaction ID</mark>, which assists in tracking the request. Additionally, it is worth noting that the resolver maintains a local cache to avoid contacting the root server for each request.</p>
<h1 id="heading-further-reading">Further Reading</h1>
<ul>
<li><p><a target="_blank" href="https://www.usenix.org/system/files/sec20-zheng.pdf">https://www.usenix.org/system/files/sec20-zheng.pdf</a></p>
</li>
<li><p><a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc1035">https://datatracker.ietf.org/doc/html/rfc1035</a> </p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>It is fascinating to realize how many processes occur behind the scenes when we attempt to access a domain name through our browser. We explored the various steps that constitute the DNS resolution process. We also noted that the DNS request is synchronous and built on top of UDP, ensuring fast communication.</p>
]]></content:encoded></item><item><title><![CDATA[Streamlining Pagination in TypeScript: An Efficient Paginator Class]]></title><description><![CDATA[Introduction
Pagination is a crucial aspect of frontend engineering, and no frontend developer can claim to have avoided the need for it. There are standard methods for implementing pagination, and they are well understood. In this article, I present...]]></description><link>https://oxyprogrammer.com/streamlining-pagination-in-typescript-an-efficient-paginator-class</link><guid isPermaLink="true">https://oxyprogrammer.com/streamlining-pagination-in-typescript-an-efficient-paginator-class</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Pagination]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[caching]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Fri, 22 Nov 2024 04:30:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731136050989/98f86da7-6148-4c70-a9c2-f36755b70220.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Pagination is a crucial aspect of frontend engineering, and no frontend developer can claim to have avoided the need for it. There are standard methods for implementing pagination, and they are well understood. In this article, I present a paginator designed to abstract pagination into a separate layer, thereby keeping the viewing component code clean. Additionally, it utilizes caching to make fast navigation through pages.</p>
<h1 id="heading-about-the-paginator">About the paginator</h1>
<p>Here’s how the final page utilizing the paginator looks:</p>
<p>You can find the entire code at:</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text"><a target="_self" href="https://github.com/OxyProgrammer/efficient-paginator">https://github.com/OxyProgrammer/efficient-paginator</a></div>
</div>

<h1 id="heading-peeking-into-the-code">Peeking into the Code</h1>
<p>The paginator is a generic TypeScript class that provides on-demand iteration.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">class</span> EfficientPaginator&lt;T&gt; {
  <span class="hljs-keyword">private</span> currentPage: <span class="hljs-built_in">number</span>;
  <span class="hljs-keyword">private</span> pageSize: <span class="hljs-built_in">number</span>;
  <span class="hljs-keyword">private</span> hasMore: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-keyword">private</span> cache: <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">number</span>, FetchResponse&lt;T&gt;&gt;;
  <span class="hljs-keyword">private</span> fetchFunction: FetchFunction&lt;T&gt;;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">pageSize: <span class="hljs-built_in">number</span>, fetchFunction: FetchFunction&lt;T&gt;</span>) {
    <span class="hljs-built_in">this</span>.currentPage = <span class="hljs-number">0</span>;
    <span class="hljs-built_in">this</span>.pageSize = pageSize;
    <span class="hljs-built_in">this</span>.hasMore = <span class="hljs-literal">true</span>;
    <span class="hljs-built_in">this</span>.cache = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();
    <span class="hljs-built_in">this</span>.fetchFunction = fetchFunction; <span class="hljs-comment">// Assign the fetch function</span>
  }

  <span class="hljs-keyword">async</span> getItems(direction: Direction): <span class="hljs-built_in">Promise</span>&lt;T[]&gt; {
    <span class="hljs-keyword">if</span> (direction === Direction.Next) {
      <span class="hljs-built_in">this</span>.currentPage++;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (direction === Direction.Previous &amp;&amp; <span class="hljs-built_in">this</span>.currentPage &gt; <span class="hljs-number">1</span>) {
      <span class="hljs-built_in">this</span>.currentPage--;
    }


    <span class="hljs-keyword">let</span> cacheEntry = <span class="hljs-built_in">this</span>.cache.get(<span class="hljs-built_in">this</span>.currentPage);
    <span class="hljs-keyword">if</span> (cacheEntry) {
      <span class="hljs-built_in">this</span>.hasMore = cacheEntry.hasMore;
      <span class="hljs-keyword">return</span> cacheEntry.users;
    }

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.fetchFunction(
        <span class="hljs-built_in">this</span>.currentPage,
        <span class="hljs-built_in">this</span>.pageSize
      );
      <span class="hljs-built_in">this</span>.cache.set(<span class="hljs-built_in">this</span>.currentPage, response);
      <span class="hljs-built_in">this</span>.hasMore = response.hasMore;
      <span class="hljs-keyword">return</span> response.users;
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error fetching items:'</span>, error);
      <span class="hljs-keyword">return</span> [];
    }
  }

  hasPrevious(): <span class="hljs-built_in">boolean</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.currentPage &gt; <span class="hljs-number">1</span>;
  }

  hasNext(): <span class="hljs-built_in">boolean</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.hasMore;
  }

  getCurrentPage(): <span class="hljs-built_in">number</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.currentPage;
  }

  getPageSize(): <span class="hljs-built_in">number</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.pageSize;
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> FetchFunction&lt;T&gt; {
  (page: <span class="hljs-built_in">number</span>, pageSize: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">Promise</span>&lt;FetchResponse&lt;T&gt;&gt;;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> FetchResponse&lt;T&gt; {
  users: T[];
  hasMore: <span class="hljs-built_in">boolean</span>;
}
</code></pre>
<ul>
<li><p>An instance of <code>EfficientPaginator</code> is initialized with the page size and a <code>fetchFunction</code>. </p>
</li>
<li><p>The page size remains constant throughout the lifetime of this instance, as does the fetch function. </p>
</li>
<li><p>Caching is implemented using a map that takes the page number as a key and stores the returned items as an array of items returned by the fetch function. </p>
</li>
<li><p>The paginator exposes <code>hasPrevious</code> and <code>hasNext</code> as boolean values, enabling the calling function to be immediately aware of the previous and next page's state and availability.</p>
</li>
<li><p>The <code>currentPage</code> and <code>pageSize</code> are also exposed to assist the calling function in providing visual aids to the user. </p>
</li>
<li><p>The paginator enforces a specific signature for the <code>fetchFunction</code> (<code>FetchFunction&lt;T&gt;</code>) and the schema of the response (<code>FetchResponse&lt;T&gt;</code>) that it receives from the function.  </p>
</li>
</ul>
<h2 id="heading-caveats">Caveats</h2>
<p>There are a few caveats with this technique, but they can be easily managed by making client-side caching optional. </p>
<ul>
<li><p>The returned objects are cached on the UI side, and if the number becomes too large, the browser may experience slowdowns.</p>
</li>
<li><p>Additionally, if client-side caching is enabled, it may display stale data that could have changed on the server. Changing the page size using a new instance of <code>Paginator</code> will start afresh though, ensuring that the latest data is shown.</p>
</li>
</ul>
<h1 id="heading-improvements">Improvements</h1>
<p>The intention of the code and demo is to illustrate the concept of abstracting pagination logic rather than creating a full-fledged component for reuse. Consequently, the paginator presented in the demo is not perfect. There are opportunities for improvement, and I welcome anyone interested to submit a pull request addressing the following issues: </p>
<ul>
<li><p>Exposing the current page and total pages through the paginator. This would require the <code>FetchResponse</code> class to be modified to include the total pages returned by the server.</p>
</li>
<li><p>Making client-side caching optional to address the concerns mentioned in the caveats.</p>
</li>
<li><p>Currently, if there is an issue calling the fetch function, the paginator logs the error and returns an empty array. It could be improved by propagating a valid exception to the caller.</p>
</li>
</ul>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we explored a TypeScript paginator class that implements client-side caching and provides a low-latency experience for users. It also encapsulates the pagination.</p>
]]></content:encoded></item><item><title><![CDATA[Navigating the Dual Write Problem: Implementing the Outbox Pattern for Data Consistency in Distributed Systems]]></title><description><![CDATA[Introduction
Maintaining data consistency in distributed systems is a challenge due to the possibility of service or component failures at any time.
In a previous article, I provided a detailed explanation of CQRS with Event Sourcing, discussing thes...]]></description><link>https://oxyprogrammer.com/navigating-the-dual-write-problem-implementing-the-outbox-pattern-for-data-consistency-in-distributed-systems</link><guid isPermaLink="true">https://oxyprogrammer.com/navigating-the-dual-write-problem-implementing-the-outbox-pattern-for-data-consistency-in-distributed-systems</guid><category><![CDATA[Microservice Architecture]]></category><category><![CDATA[dualwrite]]></category><category><![CDATA[outbox pattern]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Thu, 21 Nov 2024 04:30:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731233142968/43d88527-b070-4f47-b271-06f607a93038.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Maintaining data consistency in distributed systems is a challenge due to the possibility of service or component failures at any time.</p>
<p>In a previous article, I provided a detailed explanation of CQRS with Event Sourcing, discussing these two patterns in principle and elaborating on their implementation using .NET. A caveat that was left unanswered in that article was the possibility of failure when publishing an event after writing to the event store.</p>
<p>In this article, we will explore the classic distributed system problem known as the Dual Write Problem and a possible solution to it.</p>
<h1 id="heading-dual-write-problem">Dual Write Problem</h1>
<p>Most of us are familiar with database transactions, where all Data Manipulation Language (DML) statements executed within the scope of a transaction are either fully committed or entirely reverted.</p>
<p>Now, imagine achieving a similar result across two entirely different systems. Consider the common scenario in which a microservice needs to update its database and then publish an event to an event bus (such as Kafka or an MQ). Refer to the diagram below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731233168524/04edbb41-c6e1-4067-89cb-e641fef96b51.png" alt class="image--center mx-auto" /></p>
<p>Considering what can go wrong:</p>
<ol>
<li><p>It’s a positive outcome if both transactions are successful.</p>
</li>
<li><p>However, if both transactions fail, it still presents a happy situation from a data consistency perspective.</p>
</li>
<li><p>If the database transaction succeeds but an issue arises before event publishing, leaving the event unpublished, this leads to an inconsistent state in our system.</p>
</li>
<li><p>Reversing the order of these operations alters the situation, but the underlying issue remains.</p>
</li>
</ol>
<p>If the event fails to be placed onto the event bus due to its unavailability or a network issue, the event will be lost, resulting in inconsistent data.</p>
<p>Traditional databases address transaction problems through transaction logs, checkpointing, etc. However, in this case, we are dealing with two distinct systems.</p>
<h1 id="heading-the-solution">The solution</h1>
<p>The solution is to execute these transactions in a series. The state of the data should progress from its original state to an intermediate state and finally to the desired distributed state.</p>
<p>Here is a diagram to help you visualize the various scenarios:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731233208667/10adab08-c5ac-43d5-99e3-1afa3f38b5db.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>The happy case occurs when the data flows to the database and then to the event bus.</p>
</li>
<li><p>In another happy scenario, data does not flow to the database (due to the database being down or some network issue), and the event bus also does not receive any events. The data state remains consistent.</p>
</li>
<li><p>The case where data writes are successful to the database but fail when attempting to publish to the event bus (due to network issues or an unavailable event bus) creates an intermediate state. Here, the intermediate data (I.D.) is recorded in the database. Whenever the event bus becomes available, we can replay the intermediate data, ensuring that the system reaches a consistent state.</p>
</li>
</ol>
<h1 id="heading-outbox-pattern">Outbox Pattern</h1>
<p>The Outbox Pattern (also known as the Transactional Outbox Pattern) is an implementation of the solution described above.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">It operates similarly to an email outbox. When an email is sent, it is first stored in the outbox, and once the email is successfully sent, the message is removed and placed in the sent items. Hence the name!</div>
</div>

<p>Refer to the diagram below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731233295485/e819d713-0c56-45d9-9f46-4ebd4d29f697.png" alt class="image--center mx-auto" /></p>
<p>The service writes data to the database and also records the event in an outbox table or collection. A worker process continuously monitors the outbox. As soon as an entry is made, it attempts to push it to the event bus and subsequently removes that entry from the outbox.</p>
<p>If the worker cannot place an entry onto the event bus due to issues like downtime or network problems, the event item will remain in the outbox and will be cleared once the worker successfully pushes it to the event bus.</p>
<p>A few points worth considering are:</p>
<ul>
<li><p>The worker process may go down after publishing to the event bus but before removing the event item from the outbox. Therefore, any consuming service (not shown in the diagram) must be <a target="_blank" href="https://en.wikipedia.org/wiki/Idempotence">idempotent</a>, as they may receive the same event multiple times.</p>
</li>
<li><p>In some implementations, the task of the worker process is handled by the service itself.</p>
</li>
</ul>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we examined the Dual Write Problem in distributed systems and explored a corresponding solution. We determined that the Transactional Outbox Pattern is a viable solution for the Dual Write Problem, guaranteeing at least once delivery to the event bus, necessitating that consumer services be idempotent. This pattern enhances the consistency of our system's data.</p>
]]></content:encoded></item><item><title><![CDATA[Efficient Data Handling: Understanding JavaScript Iterators and Generators]]></title><description><![CDATA[Introduction
Looping is one of the essential features of any programming language, and JavaScript is no exception. Coming from a C# background, I am familiar with the IEnumerable interface, which allows a class to be iterable. JavaScript provides a s...]]></description><link>https://oxyprogrammer.com/efficient-data-handling-understanding-javascript-iterators-and-generators</link><guid isPermaLink="true">https://oxyprogrammer.com/efficient-data-handling-understanding-javascript-iterators-and-generators</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[javascript framework]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Wed, 20 Nov 2024 04:30:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731136141131/01da352a-afbd-4967-878f-c9e49413e574.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Looping is one of the essential features of any programming language, and JavaScript is no exception. Coming from a C# background, I am familiar with the <code>IEnumerable</code> interface, which allows a class to be iterable. JavaScript provides a similar feature called Iterable, and when combined with generator function, it offers powerful iteration capabilities.</p>
<h1 id="heading-background">Background</h1>
<p>JavaScript supports various looping mechanisms, including <code>for...in</code> and <code>for...of</code>, in addition to the standard for loop present in almost every programming language.</p>
<p>The <code>for...in</code> loop enables us to iterate through the keys of an object:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> obj = { <span class="hljs-attr">a</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">b</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">c</span>: <span class="hljs-number">3</span> };
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> prop <span class="hljs-keyword">in</span> obj) {
    <span class="hljs-built_in">console</span>.log(prop + <span class="hljs-string">': '</span> + obj[prop]);
}
</code></pre>
<p>On the other hand, the <code>for...of</code> loop lets us iterate through a collection of objects:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> arr = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> value <span class="hljs-keyword">of</span> arr) {
    <span class="hljs-built_in">console</span>.log(value);
}
</code></pre>
<p>This led me to wonder what allows the array object to function within a <code>for...of</code> loop. The answer lies in the <code>Symbol.iterator</code>. When we combine it with lazy-loading generator functions, we create powerful encapsulations over complex executions. Let's explore this further in the following sections.</p>
<h1 id="heading-iterator">Iterator</h1>
<p><code>Symbol.iterator</code> is a special function that, when defined on a prototype, grants looping capabilities to that object. In other words, we can use a for...of loop to iterate over that symbol. Here’s a sample code snippet to help you visualize this:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FirstNNumbers</span> </span>{
    <span class="hljs-keyword">constructor</span>(N) {
        <span class="hljs-built_in">this</span>.N = N;
    }

    [<span class="hljs-built_in">Symbol</span>.iterator]() {
       <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
       <span class="hljs-keyword">const</span> limit = <span class="hljs-built_in">this</span>.N;

       <span class="hljs-keyword">return</span> {
           <span class="hljs-attr">next</span>: <span class="hljs-function">() =&gt;</span> {
               count++;
               <span class="hljs-keyword">if</span> (count &lt;= limit) {
                   <span class="hljs-keyword">return</span> { <span class="hljs-attr">value</span>: count, <span class="hljs-attr">done</span>: <span class="hljs-literal">false</span> };
               } <span class="hljs-keyword">else</span> {
                   <span class="hljs-keyword">return</span> { <span class="hljs-attr">done</span>: <span class="hljs-literal">true</span> };
               }
            }
        };
    }
}

<span class="hljs-comment">// Example Usage</span>
<span class="hljs-keyword">const</span> firstFiveNumbers = <span class="hljs-keyword">new</span> FirstNNumbers(<span class="hljs-number">5</span>);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> num <span class="hljs-keyword">of</span> firstFiveNumbers) {
    <span class="hljs-built_in">console</span>.log(num); <span class="hljs-comment">// Outputs: 1, 2, 3, 4, 5</span>
}
</code></pre>
<p>The example above demonstrates a JavaScript class (which I prefer). However, if you favor a functional programming style, you can achieve the same functionality with functional code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> FirstNNumbers = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">N</span>) </span>{
    <span class="hljs-built_in">this</span>.N = N; };


FirstNNumbers.prototype[<span class="hljs-built_in">Symbol</span>.iterator] = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">const</span> limit = <span class="hljs-built_in">this</span>.N;

    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">next</span>: <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">if</span> (count &lt; limit) {
                count++;
                <span class="hljs-keyword">return</span> { <span class="hljs-attr">value</span>: count, <span class="hljs-attr">done</span>: <span class="hljs-literal">false</span> }; r
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span> { <span class="hljs-attr">done</span>: <span class="hljs-literal">true</span> };
            }
        }
    };
};

<span class="hljs-comment">// Example Usage</span>
<span class="hljs-keyword">const</span> firstFiveNumbers = <span class="hljs-keyword">new</span> FirstNNumbers(<span class="hljs-number">5</span>);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> num <span class="hljs-keyword">of</span> firstFiveNumbers) {
    <span class="hljs-built_in">console</span>.log(num); <span class="hljs-comment">// Outputs: 1, 2, 3, 4, 5</span>
}
</code></pre>
<p>If you're a TypeScript enthusiast, you can certainly implement this in TypeScript as well, reaping the benefits of generics.</p>
<p>For more information, refer to the TypeScript documentation on iterators and generators: <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html">TypeScript Iterators and Generators</a>.</p>
<h1 id="heading-generator">Generator</h1>
<p>Now that we've explored iterators, let's look into generators. Generator functions in JavaScript allow us to return a sequence that is lazy-loaded or evaluated later. This feature utilizes the <code>yield</code> keyword, enabling us to create a stream that can be paused and resumed. The function definition must include an asterisk (<code>*</code>). Here’s how a generator function looks:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">numberGenerator</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">while</span> (count &lt;= <span class="hljs-number">3</span>) {
        <span class="hljs-keyword">yield</span> count; <span class="hljs-comment">// Pause here and return the current count</span>
        count++;
    }
}

<span class="hljs-comment">// Example Usage</span>
<span class="hljs-keyword">const</span> generator = numberGenerator();

<span class="hljs-built_in">console</span>.log(generator.next()); <span class="hljs-comment">// { value: 1; first yield will be called here. }</span>
<span class="hljs-built_in">console</span>.log(generator.next()); <span class="hljs-comment">// { value: 2, second yield will be called here. }</span>
<span class="hljs-built_in">console</span>.log(generator.next()); <span class="hljs-comment">// { value: 3, third yield will be called here. }</span>
<span class="hljs-built_in">console</span>.log(generator.next()); <span class="hljs-comment">// { done: true }</span>
</code></pre>
<h2 id="heading-async-please">Async Please?</h2>
<p>Generator functions can also handle asynchronous functionalities, making them extremely useful. This allows us to evaluate each sequence item for every yield call, leading to efficient memory usage and performance. Running the following example code using Node.js will demonstrate a slight delay between console prints:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">fetchDataGenerator</span>(<span class="hljs-params">urls</span>) </span>{
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> url <span class="hljs-keyword">of</span> urls) {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(url);
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();
    <span class="hljs-keyword">yield</span> data;
  }
}

<span class="hljs-keyword">const</span> apiUrls = [
  <span class="hljs-string">'https://jsonplaceholder.typicode.com/posts/1'</span>,
  <span class="hljs-string">'https://jsonplaceholder.typicode.com/posts/2'</span>,
  <span class="hljs-string">'https://jsonplaceholder.typicode.com/posts/3'</span>,
];

(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> dataGenerator = fetchDataGenerator(apiUrls);


  <span class="hljs-keyword">for</span> <span class="hljs-keyword">await</span> (<span class="hljs-keyword">const</span> data <span class="hljs-keyword">of</span> dataGenerator) {
    <span class="hljs-built_in">console</span>.log(data);
  }
})();
</code></pre>
<p>The delay occurs because the <code>yield</code> keyword returns a value as soon as it becomes available, allowing the calling function to print it. During the next iteration, the <code>fetchData</code> generator retains its state, knowing exactly where to resume. It then fetches the next result and returns it immediately.</p>
<h1 id="heading-mixing-both">Mixing Both</h1>
<p>The <code>Symbol.iterator</code> can be combined with asynchronous generator functions to create a powerful iteration abstraction within an object. Consider the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Dummy method to simulate some API call or time taking task.</span>
<span class="hljs-keyword">const</span> fetchItems = <span class="hljs-keyword">async</span> (baseValue) =&gt; {
  <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve</span>) =&gt;</span> <span class="hljs-built_in">setTimeout</span>(resolve, <span class="hljs-number">500</span>));
  <span class="hljs-keyword">const</span> dummyArray = [];
  <span class="hljs-keyword">const</span> startIndex = baseValue * <span class="hljs-number">10</span>;
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = baseValue; i &lt; baseValue + <span class="hljs-number">5</span>; i++) {
    dummyArray.push({
      <span class="hljs-attr">prop1</span>: <span class="hljs-string">`Prop1: <span class="hljs-subst">${startIndex}</span>`</span>,
      <span class="hljs-attr">prop2</span>: <span class="hljs-string">`Prop 2:<span class="hljs-subst">${startIndex * <span class="hljs-number">10</span>}</span>`</span>,
    });
  }
  <span class="hljs-keyword">return</span> dummyArray;
};

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ItemEnumerable</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.currentCounter = <span class="hljs-number">1</span>;
  }

  <span class="hljs-keyword">async</span> *[<span class="hljs-built_in">Symbol</span>.asyncIterator]() {
    <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
      <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> fetchItems(<span class="hljs-built_in">this</span>.currentCounter);
      <span class="hljs-keyword">if</span> (users.length === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">break</span>;
      }
      <span class="hljs-keyword">yield</span>* users;
      <span class="hljs-built_in">this</span>.currentCounter++;

      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.currentCounter &gt; <span class="hljs-number">3</span>) {
        <span class="hljs-keyword">break</span>;
      }
    }
  }
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadItems</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> enumerable = <span class="hljs-keyword">new</span> ItemEnumerable();
  <span class="hljs-keyword">for</span> <span class="hljs-keyword">await</span> (<span class="hljs-keyword">const</span> item <span class="hljs-keyword">of</span> enumerable) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Item: <span class="hljs-subst">${item.prop1}</span> &amp; <span class="hljs-subst">${item.prop2}</span>`</span>);
  }
}

loadItems();
</code></pre>
<h3 id="heading-explanation-of-the-code"><strong>Explanation of the Code:</strong></h3>
<p>In the code above:</p>
<ul>
<li><p>The <code>ItemEnumerable</code> class utilises the asynchronous generator in its Symbol.asyncIterator implementation.</p>
</li>
<li><p>The instance maintains an internal counter that is used to simulate API calls.</p>
</li>
<li><p>The <code>fetchItems</code> function simulates fetching five items for each call.</p>
</li>
<li><p>Once the API call returns, those five items are yielded immediately to the calling function (i.e., the for await...of loop) and printed.</p>
</li>
<li><p>The iterator's internal state allows it to perform three iterations, resulting in a total of 15 items returned: five items for each of the three iterations of the internal counter (<code>currentCounter</code>).</p>
</li>
<li><p>When executing this code, you'll notice a slight delay after every five items due to the simulated API call.</p>
</li>
</ul>
<h3 id="heading-output-example"><strong>Output Example:</strong></h3>
<pre><code class="lang-plaintext">Item: Prop1: 10 &amp; Prop 2:100
Item: Prop1: 10 &amp; Prop 2:100
Item: Prop1: 10 &amp; Prop 2:100
Item: Prop1: 10 &amp; Prop 2:100
Item: Prop1: 10 &amp; Prop 2:100
Item: Prop1: 20 &amp; Prop 2:200
Item: Prop1: 20 &amp; Prop 2:200
Item: Prop1: 20 &amp; Prop 2:200
Item: Prop1: 20 &amp; Prop 2:200
Item: Prop1: 20 &amp; Prop 2:200
Item: Prop1: 30 &amp; Prop 2:300
Item: Prop1: 30 &amp; Prop 2:300
Item: Prop1: 30 &amp; Prop 2:300
Item: Prop1: 30 &amp; Prop 2:300
Item: Prop1: 30 &amp; Prop 2:300
</code></pre>
<h3 id="heading-use-cases"><strong>Use Cases:</strong></h3>
<p>Combining the <code>Symbol.iterator</code> and async generator functions can be beneficial for various applications, such as:</p>
<ul>
<li><p>Streaming Data: Handling IoT sensor data, social media feeds, log files, etc.</p>
</li>
<li><p>Database Queries: Efficiently fetching and chunking data from databases.</p>
</li>
<li><p>Recursive Data Structures: Flattening complex structures using the combined functionality of iterators and async generators.</p>
</li>
</ul>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we explored the <code>Symbol.iterator</code> function and asynchronous generator functions. We demonstrated how they provide a fantastic abstraction for iterating over data, both synchronously and asynchronously. Additionally, we highlighted several practical use cases where this pattern can be advantageous.</p>
]]></content:encoded></item><item><title><![CDATA[Demystifying ARP and NAT: The Backbone of Internet Traffic]]></title><description><![CDATA[Introduction
In the first article of this series, Navigating Networking Layers: The Journey of Data Through TCP we explored how data flows through various layers of the network, transitioning from segments to packets to data frames.
In this article, ...]]></description><link>https://oxyprogrammer.com/demystifying-arp-and-nat-the-backbone-of-internet-traffic</link><guid isPermaLink="true">https://oxyprogrammer.com/demystifying-arp-and-nat-the-backbone-of-internet-traffic</guid><category><![CDATA[address routing protocol]]></category><category><![CDATA[address routing]]></category><category><![CDATA[networkingbasics]]></category><category><![CDATA[ARP]]></category><category><![CDATA[nat]]></category><category><![CDATA[networking]]></category><category><![CDATA[internet]]></category><category><![CDATA[Internet Gateway]]></category><category><![CDATA[network address translation]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Tue, 19 Nov 2024 04:30:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731162110610/4713d8a9-bf11-4e21-93f8-a49656bf916a.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In the first article of this series, <a target="_blank" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">Navigating Networking Layers: The Journey of Data Through TCP</a> we explored how data flows through various layers of the network, transitioning from segments to packets to data frames.</p>
<p>In this article, we will delve into two essential concepts: <strong>Address Resolution Protocol (ARP)</strong> and <strong>Network Address Translation (NAT)</strong>. These play crucial roles in facilitating communication between hosts within a private network and across the internet, respectively.</p>
<p>While this article stands on its own, readers who choose not to refer to the previous installment will still find valuable insights here. We aim to provide a clear understanding of how computers communicate with one another, whether they are situated within the same premises or on opposite sides of the globe.</p>
<h1 id="heading-arp-address-routing-protocol">ARP (Address Routing Protocol)</h1>
<p>To begin, let's start with something relatable—our home internet setup.</p>
<p>The following diagram illustrates this setup:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731162160367/3eb271a2-4bcf-4740-86aa-5bc77c923564.png" alt class="image--center mx-auto" /></p>
<p>Many of us may not have encountered a switch, as most Wi-Fi routers also function as switches. However, in the past, when internet connections were primarily provided via cables, all devices had to connect to a switch, which, in turn, connected to a router.</p>
<p>This setup constitutes a private network. As soon as you connect your device to the network, it is assigned an IP address. The CIDR ranges for private networks, as outlined in the RFC 1918 Address Allocation guideline, are as follows:</p>
<ul>
<li><p>192.168.0.0/16</p>
</li>
<li><p>172.16.0.0/12</p>
</li>
<li><p>10.0.0.0/8</p>
</li>
</ul>
<p>For most home users, the first CIDR range is the most common choice, as it provides a sufficient number of IP addresses for typical household usage. The IP address displayed on your device is not inherent to the machine itself; rather, it is an attribute of the <strong>Network Interface Card (NIC)</strong> connected to your device. The NIC also has a unique MAC address, assigned upon manufacture, which follows the format <code>aa:bb:cc:dd:ee:ff</code>. This means a MAC address is made up of six octets.</p>
<p>Both the MAC address of your NIC and the IP address assigned to your NIC serve as identifiers within a private network. It is essential to note that no two devices can share the same IP address or MAC address within the same private network (subnet).</p>
<p>Now, let’s visualize a scenario in which two hosts within a given subnet want to communicate.</p>
<p>Communication occurs using IP addresses; however, within a subnet, MAC addresses are used. This process is facilitated through an Address Resolution Protocol (ARP) table maintained by all hosts. The ARP table acts as a mapping device, linking IP addresses to MAC addresses.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731162189768/73507df3-385c-4cbd-9959-c8af53915f05.png" alt class="image--center mx-auto" /></p>
<p>Note that the IP and MAC addresses in the ARP tables have been abbreviated for brevity. For instance, '1' represents 192.168.0.1 and 'aa' denotes aa:aa:aa:aa:aa:aa.</p>
<p>There are two potential scenarios:</p>
<ol>
<li><p>A host wishes to connect to another host within the subnet.</p>
</li>
<li><p>A host aims to connect to a host outside the subnet, residing elsewhere on the internet.</p>
</li>
</ol>
<p>We will explore both scenarios in detail.</p>
<h2 id="heading-scenario-1-host-wants-to-connect-to-another-host-within-the-subnet">Scenario 1: Host Wants to Connect to Another Host within the Subnet</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731162221406/0c7356c2-670c-493a-a42e-e20aba24a14a.png" alt class="image--center mx-auto" /></p>
<p>Starting with a fresh slate, the ARP tables of all hosts will initially contain only their own IP-MAC mappings. Let’s assume Host 1 wants to connect to Host 4. All Host 1 knows is the IP address of Host 4, which is 192.168.0.4. The following steps occur:</p>
<ol>
<li><p><strong>Determine Subnet Membership</strong>: Host 1 checks whether the target IP address (192.168.0.4) belongs to the same subnet (the method for this determination will be discussed in a later section).</p>
</li>
<li><p><strong>ARP Table Lookup</strong>: Host 1 consults its ARP table but does not find an entry for Host 4.</p>
</li>
<li><p><strong>Broadcast ARP Request</strong>: To locate Host 4's MAC address, Host 1 broadcasts an ARP request to all hosts within the subnet connected via the router/switch. For simplicity, we assume that the router functions as a switch in this scenario.</p>
</li>
</ol>
<p>From the broadcast message, Host 4 learns the MAC address of Host 1. All hosts receive the broadcast, but only Host 4 will respond.</p>
<p>Host 1 then updates its ARP table and uses Host 4's MAC address to send messages.</p>
<h2 id="heading-how-does-a-host-determine-if-another-host-is-within-the-same-subnet">How does a host determine if another host is within the same subnet?</h2>
<p>A host determines if another host's IP address is within the same subnet by performing the following steps:</p>
<ol>
<li><p>Obtain the Subnet Mask: The host uses its configured subnet mask to identify the network portion and the host portion of its own IP address.</p>
</li>
<li><p>Perform a Bitwise AND Operation: The host executes a bitwise AND operation between its own IP address and its subnet mask, and then does the same for the target IP address.</p>
<ul>
<li><p>Example:</p>
<ul>
<li><p>Host's IP: <code>192.168.0.10</code></p>
</li>
<li><p>Subnet Mask: <code>255.255.0.0</code></p>
</li>
<li><p>Target IP: <code>192.168.0.5</code></p>
</li>
</ul>
</li>
<li><p>Convert to binary:</p>
<ul>
<li><p>Host IP: <code>11000000.10101000.00000000.00000001</code></p>
</li>
<li><p>Subnet Mask: <code>11111111.11111111.00000000.00000000</code></p>
</li>
<li><p>Target IP: <code>11000000.10101000.00000000.00000101</code></p>
</li>
</ul>
</li>
<li><p>Perform bitwise AND:</p>
<ul>
<li><p>Host: <code>11000000.10101000.00000000.00000000</code>  (result: <code>192.168.0.0</code>)</p>
</li>
<li><p>Target: <code>11000000.10101000.00000000.00000101</code> (result: <code>192.168.0.0</code>)</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>Compare Results: If the results of the AND operation for both IP addresses are identical, then both hosts are in the same subnet. In the above example, since both results are <code>192.168.0.0</code>, the host determines that <code>192.168.0.5</code> is in the same subnet.</p>
</li>
</ol>
<h2 id="heading-scenario-2-host-wants-to-connect-to-a-host-who-is-outside-the-subnet-and-is-located-somewhere-on-the-internet">Scenario 2: Host wants to connect to a host who is outside the subnet and is located somewhere on the internet</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731162363655/df11c78f-041d-475b-b3db-e3eec9e74bc0.png" alt class="image--center mx-auto" /></p>
<p>Now, let’s examine the scenario in which a host wants to connect to an IP address located somewhere on the internet.</p>
<p>Assuming Host 1 intends to connect to a server with the IP address <code>153.178.67.7</code>, the following steps occur:</p>
<ol>
<li><p><strong>Determine Subnet Membership</strong>: Host 1 checks whether the target IP address (<code>153.178.67.7</code>) is part of its subnet.</p>
</li>
<li><p><strong>Realize It’s Outside the Network</strong>: Upon realizing that the target IP is not within the same network, Host 1 prepares to send the message to the gateway.</p>
</li>
<li><p><strong>Identify the Gateway</strong>: The gateway serves as a special host acting as a bridge to an external network. Host 1 will now consult its ARP table to find the MAC address of the gateway.</p>
</li>
<li><p><strong>ARP Broadcast</strong>: In the previous diagram, the gateway is also represented by the router. Host 1 will issue an ARP broadcast to request the MAC address of the gateway (represented as <code>ee:ee:ee:ee:ee:ee</code>). It is important to note that this step is where <strong><mark>ARP poisoning</mark></strong> can potentially occur, a topic we will cover shortly.</p>
</li>
<li><p><strong>Receive the Gateway’s MAC Address</strong>: The router replies with its MAC address, allowing Host 1 to send the message to the gateway.</p>
</li>
</ol>
<p>At this juncture, you may be wondering why a request for an external IP address is sent to the gateway. We will explore this topic in detail in the upcoming NAT section.</p>
<h2 id="heading-arp-poisoning">ARP Poisoning</h2>
<p>When a connected host broadcasts a message to the gateway, it is possible for a malicious host to respond to the broadcast, impersonating the gateway. If the reply from the malicious host reaches the requesting host before the legitimate gateway's response, the requesting host may mistakenly identify the malicious host as the gateway. Consequently, the requesting host will direct all internet requests to this impersonating host. This process is known as <strong>ARP poisoning</strong>.</p>
<h1 id="heading-nat-network-address-translation">NAT (Network Address Translation)</h1>
<p>Internet gateways or routers that connect the Wide Area Network (WAN) and Local Area Network (LAN) possess two IP addresses: one internal IP and one public IP.</p>
<p>In the previous section, we observed that when a host needs to send a message to another host on the internet, it forwards the message to the internet gateway.</p>
<p>The Internet Gateway (IG) recognizes that the message must be routed to a remote server on the internet. It reads the From IP (<code>192.168.0.1</code>) and Port (<code>6666</code>) from the IP packet. Thus, internet gateways operate at Layer 3 of the networking model.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">This was discussed in <a target="_self" href="https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp">Navigating Networking Layers: The Journey of Data Through TCP</a>.</div>
</div>

<p>The IG makes an entry in its ARP table, which includes a public port that corresponds to the combination of the From IP and Port as well as the To IP and Port.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731162590533/0ac8859d-7f2c-4b25-b04f-7471bef72d42.png" alt class="image--center mx-auto" /></p>
<p>Referring to the diagram above, the ARP table of the router contains entries for two different hosts that communicate with the same application on the same server. The IG rewrites the headers of the IP packets, replacing the From IP and Port with its own public IP. As a result, the packets sent to the remote server now have a From IP and Port of <code>19.18.5.1:7777</code>.</p>
<p>When the response from the remote server returns to the internet gateway, it arrives at port 7777. The IG checks its NAT table and recognizes that traffic on port <code>7777</code> should be redirected to the local machine at <code>192.168.0.1</code>, port <code>6666</code>.</p>
<p>Interestingly, API gateways, which serve as entry points to a microservices cluster, also perform NATing. They route requests to the appropriate microservice based on the incoming request, presenting only one gateway to the caller rather than multiple services.</p>
<p><strong>Why Can’t All Machines Be Assigned a Public IP to Avoid Internet Gateways?</strong></p>
<p>This limitation is precisely what will be addressed with IPv6. Currently, IPv4 has restrictions concerning the number of connected devices. NAT provides a solution, allowing many devices to connect to the internet while utilizing a limited number of public IP addresses.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we have explored two fundamental protocols—Address Resolution Protocol (ARP) and Network Address Translation (NAT)—that play critical roles in networking, facilitating seamless communication within private networks and between those networks and the internet.</p>
<p>We began with an overview of how ARP enables hosts within a subnet to resolve IP addresses into MAC addresses, thus allowing devices to identify and communicate with one another effectively. By maintaining an ARP table, each host can efficiently map IP addresses to their corresponding MAC addresses, ensuring reliable connectivity within a private network.</p>
<p>We then delved into NAT, which serves as a vital mechanism employed by internet gateways to allow multiple devices on a private network to share a single public IP address. This process not only conserves the limited number of IPv4 addresses available but also enhances security by hiding internal IP addresses from external networks. NAT enables the correct routing of data packets between local machines and remote servers, ensuring that responses from the internet are directed to the appropriate device within the private network.</p>
<p>Together, ARP and NAT form a foundation that underpins modern network communication. Understanding these protocols is essential for appreciating how data flows seamlessly across various layers of the network, whether between devices within the same premises or on opposite sides of the globe. As we continue to move toward an increasingly interconnected world, the importance of these protocols will only grow, paving the way for advancements in networking technology.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding CQRS and Event Sourcing: A Path to More Robust Distributed Systems]]></title><description><![CDATA[Introduction
Distributed systems are all the rage these days! With system design gaining traction in the software world, everyone is eager to learn and implement distributed systems. They (Distributed Systems) undoubtedly have their merits and provid...]]></description><link>https://oxyprogrammer.com/understanding-cqrs-and-event-sourcing-a-path-to-more-robust-distributed-systems</link><guid isPermaLink="true">https://oxyprogrammer.com/understanding-cqrs-and-event-sourcing-a-path-to-more-robust-distributed-systems</guid><category><![CDATA[#CQRS]]></category><category><![CDATA[distributed system]]></category><category><![CDATA[C#]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[kafka]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Event Sourcing]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Mon, 18 Nov 2024 04:30:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731139050090/5ea53d9d-ea59-4226-ad40-b168b9299de4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Distributed systems are all the rage these days! With system design gaining traction in the software world, everyone is eager to learn and implement distributed systems. They (Distributed Systems) undoubtedly have their merits and provide viable solutions to many of the challenges modern software faces. However, they come with a few caveats.</p>
<p>While numerous tools and products are offered by most cloud providers to help tackle these challenges, it’s essential for developers to be aware of the hurdles. After all, orchestrating independent, separate pieces of software to function seamlessly as one giant unit is no easy task. But don't worry; we’ll explore this together!</p>
<p>In this article, we'll focus on a well-established distributed pattern known as Command Query Responsibility Segregation (CQRS) and combine it with Event Sourcing to make it even more relevant. Please remember that these are two distinct patterns that, while often used together, remain independent of one another.</p>
<p>I’ve kept this article approachable and jargon-light, so let’s settle in for some quality time! Grab a coffee (or your favorite drink) as we dive into the design and implementation of a complete CQRS-Event Sourcing system with tools like Kafka, MongoDB, and PostgreSQL, and for added flair, we’ll build a Next.js frontend. Though we’ll be working in .NET, the concepts are relevant and useful for any backend developer.</p>
<h1 id="heading-cqrs">CQRS</h1>
<p>Now that we know CQRS stands for Command Query Responsibility Segregation, let's take a quick look at what it is and why it’s beneficial.</p>
<h2 id="heading-what-is-cqrs-and-why-do-we-need-it">What is CQRS and Why Do We Need It?</h2>
<p>CQRS is a design pattern that allows us to separate the read operations of our domain entities from the write operations. This separation enables us to independently scale the reading operations separate from writing. If you’ve been exploring distributed system designs for a while, you may already be familiar with the famous Tiny URL system design question. It states that once a URL is generated (written), it is read at least 100 times. Thus, the read-to-write ratio for a domain entity (the tiny URL, in this case) is 1:100.</p>
<p>Doesn’t it make sense to separate the read and write functionalities so that we can scale the reading independently while maintaining lower infrastructure costs for writing? This is a classic case for CQRS, and trust me, a lot of the software we build today revolves around this concept.</p>
<p>Let’s illustrate this with a straightforward example: customers and orders.</p>
<p>Info: A customer can be created, and a customer can place many orders.</p>
<p>If I were to design the database schema for storing this data, I would create the following two tables:</p>
<pre><code class="lang-plaintext">Customers Table:
+--------------+-----------------+----------------+----------+
| CustomerId   | Name            | Email          | Phone    |
+--------------+-----------------+----------------+----------+
| 1            | Alice Smith     | alice@email.com| 123-4567 |
| 2            | Bob Johnson     | bob@email.com  | 234-5678 |
| 3            | Carol Davis     | carol@email.com| 345-6789 |
+--------------+-----------------+----------------+----------+

Orders Table:
| OrderId  | CustomerId   | OrderDate          | Amount |
+----------+--------------+--------------------+--------+
| 101      | 1            | 2024-05-01         | 150.00 |
| 102      | 1            | 2024-05-03         | 200.00 |
| 103      | 2            | 2024-05-02         | 300.00 |
| 104      | 3            | 2024-05-04         | 120.00 |
| 105      | 1            | 2024-05-05         | 50.00  |
+----------+--------------+--------------------+--------+
</code></pre>
<p>In this schema, the <code>CustomerId</code> from the Customers table serves as a foreign key in the Orders table.</p>
<p>For the requirements listed in the left column of the following table, we will perform the corresponding operations mentioned in the right column:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Requirement</strong></td><td><strong>Operation</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Add a customer</td><td>Add the entry in the Customers Table</td></tr>
<tr>
<td>A customer places an order</td><td>Add entry in the Orders Table with the Customer ID</td></tr>
<tr>
<td>Generate reports for purchases between specified dates</td><td>Join the Orders table with the Customers table to obtain information</td></tr>
<tr>
<td>Generate reports for daily purchases</td><td>Join the Orders table with the Customers table to obtain information</td></tr>
</tbody>
</table>
</div><p>This approach works well for a small store application with a limited customer base.</p>
<p>But what happens when we move to larger stores that cater to countless customers and want to leverage purchase orders to uncover meaningful patterns? These larger enterprises will require various analytical tools to gather data for multiple analyses. Here are a few examples:</p>
<ul>
<li><p>What were the total sales for each month over the last year, categorized by customer segment? [<em>Sales Performance</em>]</p>
</li>
<li><p>Which customer segments have the highest purchase amounts and frequency of orders? [<em>Customer Segmentation</em>]</p>
</li>
<li><p>What are the top five products sold by order count and total revenue for the last quarter? [<em>Product Performance</em>]</p>
</li>
<li><p>What percentage of customers made repeat purchases in the last six months? [<em>Customer Retention</em>]</p>
</li>
<li><p>What is the average time taken to fulfill orders for each customer segment? [<em>Supply Chain Assessment</em>]</p>
</li>
<li><p>How did customer orders change during and after the last promotional campaign? [<em>Marketing Analysis</em>]</p>
</li>
<li><p>What revenue is generated from different regions, and how does it compare to customer demographics? [<em>Geographic Analysis</em>]</p>
</li>
</ul>
<p>There can be numerous use cases requiring data from the databases above. Some of these services will also need real-time data. Just imagine the number of joins your database will have to perform!</p>
<p>So, we have a problem. Our database is normalized, making it well-suited for OLTP (Online Transaction Processing). However, this database would struggle under the demands of our analytical processes.</p>
<h3 id="heading-solution">Solution</h3>
<p>The solution is to break operations into two distinct flows and utilize two databases. These databases will have replicated data but distinct schemas. While write operations can continue feeding data into the previous schema (OLTP), we can have another database designed with a read-friendly schema for the OLAP (Online Analytical Processing) flow.</p>
<p>Thus, the same data captured in the earlier case would now be organized like this:</p>
<pre><code class="lang-plaintext">CustomerOrders Table
+--------------+-----------------+----------------+----------+------------+--------+
| CustomerId   | Name            | Email          | OrderId  | OrderDate  | Amount |
+--------------+-----------------+----------------+----------+------------+--------+
| 1            | Alice Smith     | alice@email.com| 101      | 2024-05-01 | 150.00 |
| 1            | Alice Smith     | alice@email.com| 102      | 2024-05-03 | 200.00 |
| 2            | Bob Johnson     | bob@email.com  | 103      | 2024-05-02 | 300.00 |
| 3            | Carol Davis     | carol@email.com| 104      | 2024-05-04 | 120.00 |
| 1            | Alice Smith     | alice@email.com| 105      | 2024-05-05 | 50.00  |
+--------------+-----------------+----------------+----------+------------+--------+
</code></pre>
<p>If you notice, while the above table schema may not be normalised, it eliminates the need for joins for read operations!</p>
<p>Here is a rough diagram of what we discussed so far:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731134504146/41674613-426d-419d-9986-9cb0a589e987.png" alt class="image--center mx-auto" /></p>
<p>You might argue that storing duplicate data is wasteful, but I would counter that data storage has never been as cost-effective as it is today! Investing in extra storage saves a great deal of computational costs and minimizes the risk of database failure.</p>
<h2 id="heading-pros-and-cons-of-cqrs">Pros and Cons of CQRS</h2>
<p>Pros:</p>
<ul>
<li><p>Separation of concerns. Separate read and write models. Enable both flows to grow independently.</p>
</li>
<li><p>Scalability increases.</p>
</li>
<li><p>Improved security. Read and Write operations are separate flows so it's easy to implement selective security.</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li><p>Increased complexity.</p>
</li>
<li><p>Data consistency issues as the read and write databases should be in sync always.</p>
</li>
<li><p>Data duplication. (But we already countered that, didn’t we?)</p>
</li>
<li><p>Not for small systems.</p>
</li>
</ul>
<p>By the way, if you’ve followed along, you now have a solid understanding of CQRS! <strong>We have successfully segregated the responsibilities of Command (Write) and Query (Read). How cool is that!</strong></p>
<h1 id="heading-event-sourcing">Event Sourcing</h1>
<p>Let's start with a clean slate! Understanding Event Sourcing is straightforward if you are already familiar with the State Pattern. The State Pattern is a behavioral design pattern that maintains the current state of an entity based on the actions taken on it.</p>
<p>In Event Sourcing, whenever we perform an action on any object, we essentially raise an event for that object. This pattern revolves around storing these events in what we call an event store. The final state of that object is then the aggregate of all those events.</p>
<h3 id="heading-example-bank-account"><strong>Example: Bank Account</strong></h3>
<p>Let's illustrate this with an example: imagine a bank account as an entity that supports the following events:</p>
<ul>
<li><p><strong>Created</strong>: The account is opened when you walk into the bank and create it, receiving an account ID.</p>
</li>
<li><p><strong>Deposit</strong>: You deposit money into the account.</p>
</li>
<li><p><strong>Withdraw</strong>: You withdraw money from the account.</p>
</li>
<li><p><strong>Closed</strong>: You close the account.</p>
</li>
</ul>
<p>Consider the following time series of events over the account's lifespan, which spans a year:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731134755119/04abcf10-35c4-4518-ae59-874abf7011ef.png" alt class="image--center mx-auto" /></p>
<p>Clearly, various events are recorded throughout the life of the account. Now, if storing an updated balance seems simpler, why not just maintain the balance directly?</p>
<p>During account closure, when a customer requests the available balance (let's say $300), one might think it could simply be stored in our banking system. However, that’s not how customers expect it to work! Banks, and many systems, require a log of all transactions to ensure complete transparency from creation to closure. This audit trail is essential for both customers and banks.</p>
<p>The bank records all transactions on the account for audit purposes and can aggregate them to derive the final balance at any given moment. For example, if I query the banking system to provide the balance as of May 28th 2023, the system will replay all the events from creation:</p>
<ul>
<li><p>Starting balance: $0</p>
</li>
<li><p>+$500 (Deposit)</p>
</li>
<li><p>-$200 (Withdrawal)</p>
</li>
<li><p>-$100 (Withdrawal)</p>
</li>
<li><p>+$100 (Deposit) = $300.</p>
</li>
</ul>
<p>In summary, Event Sourcing involves recording events for an entity in an event store. Whenever the state of the object is needed for a specific point in time, all relevant events can be replayed from the beginning to arrive at the entity's final state.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731134789587/8f227616-a597-4ac1-b5c2-c9b9d31aa6b6.png" alt class="image--center mx-auto" /></p>
<p>Now, let’s reconsider the definition of the entity. If the entity’s state is an aggregate of the events, why not refer to it as an aggregate? Indeed, we can redefine a bank account as an aggregate that has an ID (aggregate ID, also known as account ID) and contains a collection of all its events (transactions). The state (balance) can then be derived by replaying all the events associated with the aggregate.</p>
<p>If you follow along, congratulations—you now understand Event Sourcing!</p>
<h2 id="heading-pros-and-cons-of-event-sourcing"><strong>Pros and Cons of Event Sourcing</strong></h2>
<p>Like any architectural pattern, Event Sourcing has its pros and cons:</p>
<p>Pros:</p>
<ul>
<li><p><strong>History and Auditability</strong>: All changes are recorded, providing a complete history for auditing purposes.</p>
</li>
<li><p><strong>Temporal Query Support</strong>: Enables historical analysis by allowing you to query states at specific points in time.</p>
</li>
<li><p><strong>Analytical Data</strong>: Captures not only the data but also the transitions and history, making it suitable for analytics.</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li><p><strong>Storage Requirements</strong>: A large volume of events may accumulate, even for relatively simple entities, leading to increased storage needs.</p>
</li>
<li><p><strong>Complex State Retrieval</strong>: There’s no quick way to access the current state of an entity without aggregating all the events, which can be time-consuming.</p>
</li>
</ul>
<p>By considering these aspects, one can make informed decisions about whether to implement Event Sourcing in a given system.</p>
<h1 id="heading-combining-cqrs-and-event-sourcing">Combining CQRS and Event Sourcing</h1>
<p>Now that we understand what CQRS and Event Sourcing are, you may already be starting to see why and how they are often combined.</p>
<p>Event Sourcing provides a way to approach data for historical purposes and enhances auditability. However, it does come with slower read capabilities. On the other hand, CQRS allows us to separate our read and write flows.</p>
<p>By using CQRS for our writing flow and keeping aggregated data in our read flow, these two patterns empower us to scale efficiently and store more granular data simultaneously! This synergy typically operates within an event-driven architecture. Here is a complete diagram that ties everything together and fills in the missing pieces!</p>
<h2 id="heading-flow-overview">Flow Overview</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731134933709/84adc4da-7095-427b-b148-6cd089918fea.png" alt class="image--center mx-auto" /></p>
<p>Let’s iron out the flow for clarity:</p>
<ol>
<li><p><strong>The Write Service Receives an Even</strong>t: This service is responsible for handling incoming commands and generating events.</p>
</li>
<li><p><strong>Writing to the Event Source</strong>: The write service records the event in the event store, which is always append-only, meaning there are no delete or update operations.</p>
</li>
<li><p><strong>Publishing to the Event Bus</strong>: The write service then publishes a projected event to the event bus. Note that this is a projection of the received event, not the exact event itself.</p>
</li>
<li><p><strong>Listening for Events</strong>: The read service, which listens to the event bus, receives the projected event.</p>
</li>
<li><p><strong>Updating the Read Database</strong>: The read service processes the projected event and makes the necessary updates in the read database, as this database reflects the final state of the entity.</p>
</li>
</ol>
<h2 id="heading-pros-and-cons">Pros and Cons</h2>
<p>Pros:</p>
<ul>
<li><p><strong>Scalability</strong>: The architecture allows for independent scaling of the read and write flows.</p>
</li>
<li><p><strong>Access to Historical Data</strong>: You can access historical data while achieving fast reads since the read service stores the final state.</p>
</li>
<li><p><strong>Separation of Reading and Writing</strong>: The distinct paths for reading and writing enhance clarity and manageability in the system.</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li><p><strong>Consistency Issues</strong>: Consistency could become a problem if something goes wrong. For example, if the event bus goes down or the read service is not operational while the write service is publishing events, there may be discrepancies.</p>
</li>
<li><p><strong>Eventual Consistency</strong>: Data consistency is not immediate; it is eventual. This is due to the delay between when the write service receives the event and when the read service updates the read database.</p>
</li>
</ul>
<p>By understanding these dynamics, we can effectively leverage CQRS and Event Sourcing to create low latency, scalable applications that meet the demands of many large scale systems.</p>
<h1 id="heading-design-of-cqrs-plus">Design of CQRS Plus</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731135019496/43b22de7-b423-4938-b3cd-eae9d21ee03b.png" alt class="image--center mx-auto" /></p>
<p>In my attempt to learn CQRS with event sourcing, I started by developing a couple of .NET Web API applications: one for reading entities and another for writing events on them. Gradually, these applications evolved into a labor of love, leading me to integrate a gateway using YARP and a UI with Next.js, resulting in a comprehensive codebase.</p>
<p>Here is the repository :</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text"><a target="_self" href="https://github.com/OxyProgrammer/cqrs-plus/">https://github.com/OxyProgrammer/cqrs-plus/</a></div>
</div>

<p>The repository's README file contains all the instructions you need to try it out on your end. Note that Docker Desktop is required to run the entire application.</p>
<h2 id="heading-tech-stack">Tech Stack</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731135060744/4a4bb5c4-d4f9-4eca-b327-3b38ce1b21f7.png" alt class="image--center mx-auto" /></p>
<p>The services are written in C#.</p>
<ul>
<li><p>Kafka is used as the event bus for its high availability.</p>
</li>
<li><p>PostgreSQL is utilized for the query service (reading entities) to provide the final state, leveraging its ACID capabilities as a robust RDBMS.</p>
</li>
<li><p>MongoDB serves as the event store due to its ability to handle large event store growth, making it suitable for horizontal scaling.</p>
</li>
<li><p>The UI is built with Next.js using TypeScript because I enjoy working with React!</p>
</li>
<li><p>The entire application is hosted using Docker and Docker Compose, as nothing is better suited for running a distributed application on a local machine.</p>
</li>
</ul>
<h2 id="heading-design">Design</h2>
<p>If you have read the article this far, the design of CQRS Plus should be straightforward to understand:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731135132739/8727781c-c627-421e-97ec-34f71dbb7fe1.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-command-service">Command Service</h3>
<ul>
<li><p>The controllers, upon receiving requests, raise a command to the command handler.</p>
</li>
<li><p>Command handlers check the command and call the appropriate API on the event sourcing handler.</p>
</li>
<li><p>The event sourcing handler interacts with the MongoDB event store, saving an event for an aggregate and signaling the event producer to publish an event.</p>
</li>
<li><p>The event producer, an abstraction over the Kafka event bus, is responsible for placing an event in the Kafka stream.</p>
</li>
</ul>
<h3 id="heading-query-service">Query Service</h3>
<ul>
<li><p>The Query Service includes a listener called <code>EventConsumer</code>, thanks to .NET's Worker Services and <code>AddHostedService</code>. It pulls events from a Kafka topic.</p>
</li>
<li><p>Upon receiving an event, the <code>EventConsumer</code> invokes the appropriate handler in the event handler, which then writes data to PostgreSQL.</p>
</li>
<li><p>The query service handles read requests by delegating queries to the QueryDispatcher (similar to the <code>CommandDispatcher</code> in the command service).</p>
</li>
<li><p>The <code>QueryDispatcher</code> invokes the appropriate handler in the query handler, which reads data from PostgreSQL and serves the requested data.</p>
</li>
</ul>
<h1 id="heading-asp-nethttpaspnet-caveats"><a target="_blank" href="http://ASP.Net">ASP .Net</a> Caveats</h1>
<h2 id="heading-dtos-events-and-entities">DTOs, Events and Entities</h2>
<p>The entities carrying data within our applications should not be the same as those transmitting data externally. Therefore, request/response DTOs are often converted from/to data entities responsible for interacting with databases.</p>
<p>Events are models that transmit data across services through an event bus.</p>
<p>Although all are plain C# classes, they serve very different purposes, justifying their categorization.</p>
<h2 id="heading-open-api-support">Open API Support</h2>
<p>CQRS Plus adheres to the OpenAPI standard for defining request routes. For example, if you want to read a comment with a comment ID <code>cid</code> under a post with a post ID <code>pid</code>, the request route would be: <code>/api/v1/posts/{pid}/comments/{cid}</code></p>
<h2 id="heading-gateway">Gateway</h2>
<p>A gateway acts as a thin abstraction layer over the different microservices within the cluster. The YARP gateway, also functioning as a reverse proxy, ensures there are no separate API services for the frontend, presenting a unified API interface to the caller.</p>
<h1 id="heading-improvements">Improvements</h1>
<p>While CQRS Plus is nearly complete, it cannot yet be considered perfect. Currently, the operations involving saving to the event store and publishing to the event bus do not occur atomically. This process could be improved by implementing the outbox pattern. If I decide to implement it, I will update this article accordingly.</p>
<h1 id="heading-further">Further</h1>
<p>A future topic of interest could be the deployment strategies for this microservice application. While there are many options, two notable choices include:</p>
<ul>
<li><p>Kubernetes with managed instances like AKS (Azure) or EKS (AWS).</p>
</li>
<li><p>Elastic Container Service (ECS).</p>
</li>
</ul>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we explored a distributed implementation of the CQRS with Event Sourcing pattern. We delved into the intricacies of these patterns and their significance. We also examined the caveats associated with these approaches. While CQRS effectively addresses specific issues, it isn't necessary for all problems. We reviewed the implementation of a distributed CQRS with Event Sourcing and the rationale behind the technological choices.</p>
]]></content:encoded></item><item><title><![CDATA[Circular Buffers: The Smart Solution for Managing Data Streams]]></title><description><![CDATA[Introduction
In the fast-paced world of finance, technology teams often encounter situations where they must handle an extraordinarily high flow of data. For instance, when processing real-time stock market feeds, every millisecond counts. Keeping sy...]]></description><link>https://oxyprogrammer.com/circular-buffers-the-smart-solution-for-managing-data-streams</link><guid isPermaLink="true">https://oxyprogrammer.com/circular-buffers-the-smart-solution-for-managing-data-streams</guid><category><![CDATA[ Market Data feed]]></category><category><![CDATA[circular buffer]]></category><category><![CDATA[c sharp]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Sun, 03 Nov 2024 12:03:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730635290230/335fc539-13b4-48ea-bf7b-ca3f0649d747.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In the fast-paced world of finance, technology teams often encounter situations where they must handle an extraordinarily high flow of data. For instance, when processing real-time stock market feeds, every millisecond counts. Keeping systems within the permissible bounds of memory footprint and latency is essential, making efficient data structures critical. One such structure is the circular buffer. This concept, while not new, is often misunderstood, as many resources fail to address real-world caveats that arise when implementing it. Circular buffers are ideal when you need to maintain the latest n records of data from a feed rather than historical data, ensuring that you only keep what’s most relevant.</p>
<h1 id="heading-what-is-a-circular-buffer">What is a Circular Buffer?</h1>
<p>A circular buffer is a fixed-size data structure that uses a single, contiguous block of memory to maintain a dynamically changing dataset. When new data arrives, it overwrites the oldest data once the buffer is full, thereby ensuring that the buffer consistently contains the most recent entries.</p>
<h1 id="heading-background">Background</h1>
<p>There are numerous implementations of circular buffers available, but I opted for a straightforward implementation that effectively served my purpose in the most elegant way. The requirement was to display market data on a C# GUI, while the data feed provider was delivering information at an extraordinarily high speed—like to a dragon breathing fire.</p>
<p>Fortunately, the user was only concerned with the last 10 feeds, rather than the historical data. This is the perfect scenario for using a circular buffer, which excels in scenarios where only the latest entries are relevant. Among the various implementations available, the most suitable one for our needs was a circular buffer implementation that supported the overwriting feature.</p>
<h1 id="heading-real-world-applications">Real-world Applications</h1>
<p>Circular buffers are not limited to finance; they find applications in various fields:</p>
<ul>
<li><p>Networking: To handle incoming data packets without overwhelming the system.</p>
</li>
<li><p>Multimedia Streaming: For buffering audio and video streams to ensure smooth playback.</p>
</li>
<li><p>Sensor Data Management: In IoT devices, to keep track of the most recent sensor readings efficiently.</p>
</li>
</ul>
<h1 id="heading-implementation">Implementation</h1>
<p>Implementing a circular buffer is not overly complex and can be easily achieved using an array and two pointers, called head and tail. The head tracks the element that needs to be read, while the tail tracks the position where data will be added. Since it is a buffer, a fixed-size array is the best data structure for this purpose.</p>
<pre><code class="lang-plaintext">+---------------------+
|                     |
|    Circular Buffer  |
|                     |
+---------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
+---+---+---+---+---+---+---+---+---+
| x | x | x |   |   |   |   |   |   |  &lt;- Existing data
+---+---+---+---+---+---+---+---+---+
^(head)                  ^(tail)

Where:
 - 'x' indicates filled slots (buffer elements).
 - an empty slot is indicated by a blank space.
 - 'head' indicates where data is read from.
 - 'tail' indicates where new data will be added/overwritten.
</code></pre>
<p>As the tail moves to the end of the array, the new item overwrites the first element, as this is now the oldest. Since we care only about the latest n elements, overwriting the oldest data is acceptable.</p>
<pre><code class="lang-plaintext"> +---------------------+
 |                     |
 |    Circular Buffer  |
 |                     |
 +---------------------+
 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
 +---+---+---+---+---+---+---+---+---+
 | y | x | x | x | x | x | x | x | x |  &lt;- After adding new data
 +---+---+---+---+---+---+---+---+---+
 ^(head)                             ^(tail)

Where:
 - The pointer `tail` has moved to overwrite the oldest data (index 0 now contains 'y').
 - Similar operations can continue as new data is added, overwriting older data in a circular fashion.
</code></pre>
<h2 id="heading-caveats">Caveats</h2>
<p>One important consideration is that, instead of simply overwriting the reference of the first element, we could optimize our implementation by overwriting the properties of the first element. You may argue that reading and writing properties takes time, but the same can be said for garbage collection.</p>
<p>Additionally, some caveats to consider include:</p>
<ul>
<li><p>Buffer Size: Choosing an inappropriate buffer size can lead to data loss if the buffer fills up too quickly.</p>
</li>
<li><p>Concurrent Access: In multi-threaded situations, care must be taken to synchronize access to the buffer to prevent data corruption.</p>
</li>
</ul>
<p>Here is a screenshot of a comparative run conducted with 1 million objects in a buffer of size 5:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730635191661/641037c5-11c4-4799-b905-b4decda4070c.gif" alt class="image--center mx-auto" /></p>
<p>The circular buffer implementation and test code is available at the following location:<br /><a target="_blank" href="https://github.com/OxyProgrammer/circular-buffer">https://github.com/OxyProgrammer/circular-buffer</a></p>
<h1 id="heading-performance-metrics">Performance Metrics</h1>
<p>In my implementation, I found that using a circular buffer significantly improved performance in terms of memory usage and processing speed when compared to traditional array methods, which require shifting elements on every insertion.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Circular buffers are the default go-to data structure for scenarios with a heavy inflow of data when we only need to care about the last n items. They optimize memory usage and enhance processing efficiency, particularly useful in real-time applications. For smaller objects, overwriting properties is often more beneficial than overwriting the reference of an object, as it helps to avoid unnecessary garbage collection cycles.</p>
]]></content:encoded></item><item><title><![CDATA[Navigating Networking Layers: The Journey of Data Through TCP]]></title><description><![CDATA[Introduction
As backend engineers, we often take many things for granted without even realizing it, particularly what's happening beneath the surface. Although application programmers may not need to understand these underlying processes, architects ...]]></description><link>https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp</link><guid isPermaLink="true">https://oxyprogrammer.com/navigating-networking-layers-the-journey-of-data-through-tcp</guid><category><![CDATA[Application Load]]></category><category><![CDATA[networking]]></category><category><![CDATA[OSI Model]]></category><category><![CDATA[TCP]]></category><category><![CDATA[TCP/IP]]></category><category><![CDATA[Load Balancer]]></category><category><![CDATA[network load balancer]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Sat, 02 Nov 2024 15:30:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730559412025/8171c15f-d248-4003-89cf-b08ed7e23daa.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>As backend engineers, we often take many things for granted without even realizing it, particularly what's happening beneath the surface. Although application programmers may not need to understand these underlying processes, architects must grasp the internals. This understanding is crucial for several reasons:</p>
<ul>
<li><p>Choice of tools</p>
</li>
<li><p>Designing the data flow for the system</p>
</li>
<li><p>Estimating costs</p>
</li>
<li><p>Extensibility of the system</p>
</li>
</ul>
<p>Failing to properly assess any of these factors can lead to significant losses in time and cost, as well as challenging system maintenance.</p>
<p>When making an HTTP call, numerous processes occur behind the scenes. Your data is transformed from JSON to <em>bytes</em>, to <em>segments</em>, to <em>packets</em>, to <em>data frames</em>, and finally, to <em>radio waves</em> or <em>electrical signals</em>.</p>
<p>In this article, I aim to demystify the journey of an HTTP call to a remote server. As a bonus, we will also explore the differences between Application Load Balancers (ALB) and Network Load Balancers (NLB) in AWS. We will examine the cost differences between these AWS offerings with our newly acquired knowledge.</p>
<h1 id="heading-traveling-through-the-layers-of-network">Traveling through the layers of network</h1>
<p>Most of us are probably familiar with the OSI model of networking. However, it often remains an academic concept in our minds. Visualizing how data travels and transforms through the various network layers can be challenging. Revisiting the OSI model is a good starting point, but this time let's focus on visualizing how the data transforms.</p>
<p>The accompanying diagram explains how a request travels from the sender (typically the client) to the receiver (typically the server). It assumes that the receiver’s IP address and ports are known. If the IP address and port of the receiver are unknown, an additional DNS request is needed to resolve the IP address.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">You can easily perform a DNS query using <code>curl </code>to retrieve the results. For example, the IP address and port for my domain can be readily located:</div>
</div>

<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXe4YRYpnX8CYxr-dyzpxRz_5pk8BNsPKpdJ1SkhpoLqOSh1FTw-7lOQVbnwzmUTR7btp2oKXSnJm5ONxqMx4gMDqU1PN8lryKlxneX-VrsERgXhQ6lNsI4Zylzm7ZlJaZxh4tEbpHWuhSByX6Rf1wQaIW0?key=HyPyc-WXVAsAzSUUz0rLylvT" alt /></p>
<p>Before delving into the diagram, two points are worth noting:</p>
<ol>
<li><p>The layers are logical, not physical, and should be viewed as such.</p>
</li>
<li><p>The processes aren't strictly separated. Routers and switches may perform operations associated with other layers. For example, many routers (layer 3) also function as switches (layer 2). More on this later.</p>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfAqpR66GKdMmUE--o0LckrxKg66tnBniW4TbyVMlYsUweSH2YN7eQQ43QDC9V-wbl0KdIoQjLzUyq7eREwAvTlLT8Yy0zPZUMSxR8G3WeXy0GJGo2E3BN4rgRC6CiKP3C54N8DkZyJwMD6BW0jvi1P1So?key=HyPyc-WXVAsAzSUUz0rLylvT" alt /></p>
<ul>
<li><p>The application code resides at the application layer, where a fetch call is made to a Node.js API or another technology. The fetch request includes the IP, port, and request object, which is a JSON object.</p>
</li>
<li><p>At the presentation layer, the request object is transformed into bytes.</p>
</li>
<li><p>The bytes and host port information are sent to another API call in the session layer, which maintains the state of the connection as it forms in the following layer.</p>
</li>
<li><p>Now the flow enters the OS level. Each OS implements its handling from the transport layer, where a TCP connection is established with the receiver. TCP data can only be passed if a valid connection exists, meaning both sender and receiver operating systems maintain the state for integral data transmission over TCP. Data is broken into <strong>SEGMENTS</strong>, with headers marked on all segments, containing all flags and data necessary for TCP communication and maintaining data integrity. Understanding the TCP header in detail is a topic for another article.</p>
</li>
<li><p>In the network layer, segments are turned into IP <strong>PACKETS</strong>, containing the receiver IP address. The port information remains within the segments inside the IP packets. These packets are then passed into another API call that takes them to the data frame layer.</p>
</li>
<li><p>The data frame layer packages IP packets into data <strong>FRAMES</strong>, which include MAC addresses the frame should be forwarded to. In this case, it's the sender router’s MAC address.</p>
</li>
<li><p>The physical layer transmits data frames as radio waves (Wi-Fi) or light signals (optical fiber) to the sender router.</p>
</li>
<li><p>The sender router checks the data frame and underlying IP packet, recognizing the recipient IP is meant for a distant server. It performs Network Address Translation (NAT), replacing the sender’s IP and port with its public IP and a port. To perform NAT, routers need access to layer 4 data (ports), hence they operate from layer 4 to 2.</p>
</li>
<li><p>After several hops across ISPs and routers, the frame reaches the destination network and its gateway, the receiver router.</p>
</li>
<li><p>The receiver router checks its NAT table to determine the internal host and port for the request, and consults the ARP table for the server's MAC address.</p>
</li>
<li><p>The router forwards the request to the correct receiver, which extracts the IP packets from the data frames and the segments from the IP Packets, then handles the TCP segments using the port specified to deliver data to the appropriate program.</p>
</li>
<li><p>The response from the receiver travels back via a similar route. Hence, I use 'sender' and 'receiver' instead of 'client' and 'server.'</p>
</li>
</ul>
<p>You may wonder why terms like segments, packets, and frames are emphasized. This is to highlight:</p>
<ul>
<li><p>Segments for TCP</p>
</li>
<li><p>Packets for IP</p>
</li>
<li><p>Frames for Data</p>
</li>
</ul>
<p>These elements fit together like <a target="_blank" href="https://en.wikipedia.org/wiki/Matryoshka_doll">Matryoshka dolls</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730723213828/2a018f6f-4962-41ee-96f1-f4f3a2616696.png" alt class="image--center mx-auto" /></p>
<p>Questions like what NAT Tables and ARP Tables are, and how NAT works, will be explored in another article. For the OSI model and data transformation across layers, understanding the data transformation is key. Details about the nuances of network data travel will be addressed later.</p>
<h1 id="heading-load-balancers-nlb-vs-alb">Load balancers: NLB vs ALB</h1>
<p>Load balancers function as reverse proxies with the added capability of distributing traffic efficiently across instances of services. Having already explored the various layers of the OSI model and how data transforms as it travels through them, we are well-equipped to understand the differences between Network Load Balancers and Application Load Balancers. This comprehension will also help us grasp why their costs differ among AWS offerings.</p>
<h2 id="heading-network-load-balancers-nlb">Network Load Balancers (NLB)</h2>
<p>NLB operate at Layer 4 of the OSI model, which means they primarily manage connections and segments. As illustrated in the accompanying diagram, when clients establish TCP connections with the load balancer, the segments are transmitted without the network being aware of the entire request that these segments make up. Consequently, the load balancing operation is inherently sticky, meaning that all segments from a given connection are directed to a specific server. For example, all blue segments are paired with one server, while all orange segments correspond to another (refer following diagram). Additionally, since NLBs lack visibility into higher-layer data, they do not support caching or advanced routing functionalities, making them purely focused on load balancing while ensuring security by not exposing higher layer information.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730723233039/0d61d9f3-7b87-4e66-8c35-1510b8fe93a1.png" alt /></p>
<ul>
<li><p>Pros:</p>
<ol>
<li><p>Simple and efficient for distributing traffic based on TCP connections.</p>
</li>
<li><p>High performance with low latency.</p>
</li>
<li><p>Secure, as it doesn’t handle higher-layer payloads.</p>
</li>
</ol>
</li>
<li><p>Cons:</p>
<ol>
<li><p>Limited to Layer 4; lacks visibility into application-level data.</p>
</li>
<li><p>No caching support or advanced routing capabilities.</p>
</li>
<li><p>Sticky sessions by default, which may not suit all applications.</p>
</li>
</ol>
</li>
</ul>
<h2 id="heading-application-load-balancers-alb">Application Load Balancers (ALB)</h2>
<p>ALBs, in contrast, function at Layer 7 of the OSI model, allowing them to analyze the entire incoming request. As shown in the figure, ALBs maintain a buffer and act as independent receivers of segments, which enables them to implement smart routing capabilities based on specific request route keys. This capability enhances their efficiency in directing traffic to appropriate service clusters. Moreover, ALBs can also perform TLS offloading, necessitating the management of TLS certificates, which some may view as a potential security risk. Unlike NLBs, ALBs can provide caching support, further optimizing the handling of incoming requests.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730723239876/8a91ef39-d272-45cf-bb30-346fcfcdf866.png" alt /></p>
<ul>
<li><p>Pros:</p>
<ol>
<li><p>Operates at Layer 7, enabling detailed inspection of requests for smart routing.</p>
</li>
<li><p>Provides caching support, enhancing response times.</p>
</li>
<li><p>Capable of performing TLS offloading, improving security management.</p>
</li>
</ol>
</li>
<li><p>Cons:</p>
<ol>
<li><p>More complex and may introduce additional latency due to extensive processing.</p>
</li>
<li><p>Requires management of TLS certificates, which can pose a security risk if not handled properly.</p>
</li>
<li><p>Potentially higher costs compared to NLBs due to more advanced features.</p>
</li>
</ol>
</li>
</ul>
<h1 id="heading-os-facilitating-tcp-connection-under-the-hood">OS facilitating TCP connection under the hood</h1>
<p>A TCP connection is established through a three-way handshake that consists of SYN, SYN-ACK, and ACK requests. The SYN represents the synchronization of the client’s initial sequence number for transmission.</p>
<p>The diagram below illustrates the connection establishment process. This is a well-known fact, and you are likely already familiar with it.</p>
<pre><code class="lang-plaintext">Client                               Server
   |                                       |
   | ---------------- SYN ---------------&gt; |
   |                                       |
   | &lt;-------------- SYN-ACK ------------- |
   |                                       |
   | ---------------- ACK ---------------&gt; |
   |                                       |
   | &lt;-------- Connection Established ---- |
   |                                       |
</code></pre>
<p>The interesting part lies in understanding what happens under the hood in the server’s operating system while these SYN and ACK requests are exchanged. After several attempts to write this explanation, I decided to create a comic strip to illustrate the process. I believe that following the comic strip while reading along will enhance comprehension. Additionally, we will correlate this process with high-level code for easier familiarity with our everyday coding experience.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfDZDdC7_NcLKVg9v4nyk4MXzQ77aBwbH481szKGEzLc3mEunYGNkT3WSYvTRN9TizDG9qRJZWRYLLeimFPCbmgrUvc2njJs6nAuK5Vf78sVhO6kvpKfc8W8mjjIEgprG5wdrSay-lEruevS2UAlQyjKN0d?key=HyPyc-WXVAsAzSUUz0rLylvT" alt /></p>
<p>Let's first discuss the server processes starting up in the OS. It’s quite common for a single server to run multiple server programs listening on different ports. For instance, two processes—Process-1 and Process-2—could start on ports 3000 and 8000, respectively. The Node.js code to initiate these server programs would look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Import the required modules from Node.js</span>
<span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);

<span class="hljs-comment">// Create the first server instance for handling HTTP requests on port 3000</span>
<span class="hljs-keyword">const</span> process1Server = http.createServer(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-comment">// Process incoming requests for this server</span>
    res.end(<span class="hljs-string">'Hello from Process 1 on port 3000!'</span>);
});

<span class="hljs-comment">// Creating the second server instance for handling WebSocket connections on port 8000</span>
<span class="hljs-keyword">const</span> process2Server = http.createServer(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-comment">// Process incoming requests for this server</span>
    res.end(<span class="hljs-string">'Hello from Process 2 on port 8000!'</span>);
});

<span class="hljs-comment">// Making the first server listen on port 3000</span>
process1Server.listen(<span class="hljs-number">3000</span>, <span class="hljs-string">"192.168.0.5"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Server is listening on port 3000'</span>);
});

<span class="hljs-comment">// Making the second server listen on port 8000</span>
process2Server.listen(<span class="hljs-number">8000</span>, <span class="hljs-string">"192.168.0.5"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Server is listening on port 8000'</span>);
});
</code></pre>
<ol>
<li><p>With the call to the <code>listen()</code> method, Node.js makes internal system calls to create sockets bound to the addresses 192.168.0.5:3000 and 192.168.0.5:8000, respectively. For each socket, file descriptors are created. Sockets can be thought of as files, and file descriptors serve as identifiers for those files.</p>
</li>
<li><p>The OS maintains two queues: the SYN queue (also known as the half-connection queue) and the accept queue. When a SYN request arrives from a client, the OS immediately sends back a SYN-ACK to the client. the OS creates an entry in the SYN queue containing the following details:</p>
<ul>
<li><p>Sender Address</p>
</li>
<li><p>Sender Port</p>
</li>
<li><p>Receiver Address (in this case, 192.168.0.5)</p>
</li>
<li><p>Receiver Port (in this case, 3000)</p>
</li>
<li><p>Client’s Initial Sequence Number</p>
</li>
</ul>
</li>
<li><p>The client then sends an ACK, at which point the entry in the SYN queue is moved to the accept queue.</p>
</li>
<li><p>The accept queue is a data structure that server processes monitor closely, often utilizing a mutex. They repeatedly call <code>accept()</code> on its entries.</p>
<p> In this example, Process-2, which has the file descriptor for the socket created for 192.168.0.5:8000, attempts to call accept() on the entry. However, the OS checks and denies the request because the entry corresponds to 192.168.0.5:3000.</p>
</li>
<li><p>Next, Process-1 calls accept() successfully, as its file descriptor matches the entry in the queue. At this point, the OS creates a new socket and file descriptor for this connection using the data from the accept queue entry:</p>
<ul>
<li><p>Sender Address</p>
</li>
<li><p>Sender Port</p>
</li>
<li><p>Receiver Address (in this case, 192.168.0.5)</p>
</li>
<li><p>Receiver Port (in this case, 3000).</p>
</li>
</ul>
</li>
</ol>
<p>    The OS then hands over this socket to Process-1, which now has all the necessary details for communication between the client and the server.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>This article has provided an in-depth look at the layers of networking and the process of creating a TCP connection. If you've learned something new from this discussion, you likely recognize the high cost of network calls and may already be considering ways to minimize them. Truly understanding the intricacies of network calls often leads to reservations about adopting a Microservices Architecture, as it inherently involves an increase in network calls, which can introduce complexity and latency.</p>
<p>While this article explores various concepts in detail, it inevitably leaves many questions unanswered. For instance, although we examined the journey of data from sender to receiver, we only touched on the ideas of ARP and NAT without fully elaborating on them. Additionally, while we discussed TCP connection establishment, TCP encompasses much more than just this aspect. How does it guarantee data integrity? How does it regulate the flow of data between sender and receiver? After all, TCP has been a foundational protocol for nearly 40 years and continues to underpin many of the major network communications we rely on today.</p>
<p>In future articles, we will work to clarify these remaining details and further demystify the complexities of networking.</p>
]]></content:encoded></item><item><title><![CDATA[Exploring Memory Management: A .NET Developer's Insights into Golang]]></title><description><![CDATA[Introduction
Over a decade ago, as I explored the intricacies of .NET memory allocation, it struck me as intuitive and elegant. The CLR efficiently manages the Managed Heap as a contiguous slice of memory, optimizing garbage collection—a brilliant co...]]></description><link>https://oxyprogrammer.com/exploring-memory-management-a-net-developers-insights-into-golang</link><guid isPermaLink="true">https://oxyprogrammer.com/exploring-memory-management-a-net-developers-insights-into-golang</guid><category><![CDATA[escape analysis]]></category><category><![CDATA[memory-management]]></category><category><![CDATA[golang]]></category><category><![CDATA[.NET]]></category><category><![CDATA[C#]]></category><category><![CDATA[stack]]></category><category><![CDATA[heap]]></category><category><![CDATA[garbagecollection]]></category><dc:creator><![CDATA[Siddhartha S]]></dc:creator><pubDate>Sat, 26 Oct 2024 18:30:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729961643490/88d91bfe-7be0-4830-adf4-89821aa7fae6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Over a decade ago, as I explored the intricacies of .NET memory allocation, it struck me as intuitive and elegant. The CLR efficiently manages the Managed Heap as a contiguous slice of memory, optimizing garbage collection—a brilliant concept that endures. Fast forward to today, as I explore Golang, I find my understanding of "elegance" expanding.</p>
<p>Memory management in Golang differs significantly from .NET, and for good reasons. Go doesn't adhere to the OOP paradigm like C#, necessitating a unique approach to memory management and garbage collection. While the differences between Go and .NET are numerous and worthy of separate discussion, this article focuses on memory allocation and garbage collection in these two robust technologies.</p>
<h1 id="heading-background-memory-collection-in-net">Background: Memory Collection in .Net</h1>
<p>If you're reading this, you likely have some familiarity with .NET memory allocation and garbage collection. Let's briefly recap key features to set the stage for comparison:</p>
<ul>
<li><p>Functions operate on the stack, where primitive types are declared.</p>
</li>
<li><p>Reference types instantiated during function execution are allocated on the managed heap.</p>
</li>
<li><p>Primitive types encapsulated by reference types are also allocated within the heap.</p>
</li>
<li><p>When a function completes execution and no variables reference the heap location, the memory is considered orphaned and eventually freed by the Garbage Collector.</p>
</li>
</ul>
<p>This is a simplified overview of .NET Garbage Collection, omitting complexities like the Large Object Heap (LOH), yet it captures the essence of the process.</p>
<h1 id="heading-the-plight">The Plight</h1>
<p>Go's memory allocation is more complex. Unlike C#, where all classes are reference types and hence goes to Managed Heap, Go employs pointers. It may seem that using pointers in Go transforms a value type struct into a reference type, similar to .NET garbage collection.</p>
<p>Well, let's find out with an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729961521961/ec0be39a-a217-45ed-a2a3-ef71f8a6f4c8.png" alt class="image--center mx-auto" /></p>
<p>To summarize the above code:</p>
<ul>
<li><p>Line 17: The main method begins program execution.</p>
</li>
<li><p>Line 19: Declares the struct <code>SmallStruct</code>.</p>
</li>
<li><p>Line 20: Invokes <code>changeSmallStruct(&amp;smallStruct)</code>.</p>
</li>
<li><p>Line 21: Prints and verifies changes.</p>
</li>
<li><p>Line 23: Calls <code>newSmallStruct()</code>.</p>
</li>
<li><p>Line 24: Prints and confirms initialization.</p>
</li>
</ul>
<p>Here we discuss just the plight, and so I will just leave you with a question and its answer:</p>
<p><strong>Question</strong>: Which instance of the two (<code>smallStruct</code>, <code>anotherSmallStruct</code>) will get allocated to where (stack, heap)? Remember both are pointers and hence references.</p>
<p><strong>Answer</strong>: <code>smallStruct</code> → Stack, <code>anotherSmallStruct</code>→ Heap</p>
<h1 id="heading-demystifying-the-plight">Demystifying the plight</h1>
<p>To understand what's happening, we will need to have a look at how things work in the stack as the program proceeds.</p>
<p>Below ascii art diagram would explain the stack operation and the explanation follows that:</p>
<pre><code class="lang-go">[Line <span class="hljs-number">17</span> Defines the main method and that<span class="hljs-string">'s where the program execution will start from.]
Stack:
|----------------|
| main()         |
|----------------|

[Line 19: Declares the struct called SmallStruct.]
Stack:
|----------------|
| main()         |
| smallStruct    | &lt;- Allocated on stack (Name: "Sid", City: "Mumbai")
|----------------|

[Line 20: Calling changeSmallStruct(&amp;smallStruct). This is when a new stack frame is loaded.]
Stack:
|----------------|
| changeSmallStruct() |
| arg (pointer)  | &lt;- Points to smallStruct in main()'</span>s frame
|----------------|
| main()         |
| smallStruct    |
|----------------|

[After changeSmallStruct() completes]
Stack:
|----------------|
| main()         |
| smallStruct    | &lt;- Modified (Name: <span class="hljs-string">"Siddhartha"</span>, City: <span class="hljs-string">"Bangalore"</span>)
|----------------|

[Line <span class="hljs-number">23</span>: Calling newSmallStruct()]
Stack:
|----------------|
| newSmallStruct() |
|----------------|
| main()         |
| smallStruct    |
|----------------|
Heap:
[SmallStruct]    &lt;- Allocated on heap (Name: <span class="hljs-string">"James"</span>, City: <span class="hljs-string">"Panji"</span>)

[After newSmallStruct() returns]
Stack:
|----------------|
| main()         |
| smallStruct    |
| anotherSmallStruct | &lt;- Pointer to heap-allocated SmallStruct
|----------------|
Heap:
[SmallStruct]    &lt;- (Name: <span class="hljs-string">"James"</span>, City: <span class="hljs-string">"Panji"</span>)

[Program End]
Stack:
(empty)
Heap:
[SmallStruct]    &lt;- Will be garbage collected
</code></pre>
<p>Explanation of the above ascii art diagram is as follows (you may would like to refer the explanation and the diagram together):</p>
<ol>
<li>In line 19, the memory for the struct <code>SmallStruct</code> gets allocated on stack and a pointer for that is created. The pointer is assigned to the variable “smallStruct”</li>
</ol>
<ol start="2">
<li><p>In line 20, the method <code>changeSmallStruct</code> gets called and a new stackFrame is loaded for that.</p>
</li>
<li><p>In lines 12-15: The pointer gets passed to the method changeSmallStruct and this method works on the pointer by dereferencing it and then changing the fields that it is pointing to. Thus the memory in the stack frame of the main() method that the pointer is referencing to, gets updated (Field names get changed to Siddhartha and Bangalore respectively).</p>
</li>
<li><p>The stack frame for the method <code>changeSmallStruct()</code> is unloaded and the stack memory is reclaimed.</p>
</li>
<li><p>In line 23: New Stack frame gets loaded for <code>newSmallStruct()</code>.</p>
</li>
<li><p>In line 9: The same thing happens that happened in line 19. <em>The memory for the struct</em> <code>SmallStruct</code> <em>gets assigned in the stack frame of</em> <code>newSmallStruct()</code> <em>method.</em> And this pointer is returned to the stack frame of the main() method in line 23.</p>
 <div data-node-type="callout">
 <div data-node-type="callout-emoji">💡</div>
 <div data-node-type="callout-text">The italicized line in the above point is not correct, but please read on!</div>
 </div>
</li>
<li><p>The next logical step would be to unload the stack frame for method newSmallStruct. But here comes the paradox. The actual memory that had been allocated for <code>SmallStruct</code> is in the stackframe that needs to be unloaded. The main method stack frame now has the reference of a memory that needs to get unloaded! That is why the go compiler in line 9, would not allocate the memory for the struct in the stack frame of <code>newSmallStruct(</code>). It would rather create it in the Heap because the stack frame that it would get created in needs to be unloaded with a pointer still referencing it from the main method.</p>
</li>
<li><p>The main program finally exits and the memory for the <code>SmallStruct {Name: “James”, City: “Panji”}</code> heap will be garbage collected.</p>
</li>
</ol>
<p>Phew, so certainly the memory allocation and garbage collection is done quite differently in Go!</p>
<h1 id="heading-further">Further</h1>
<p>At this point there may be several questions popping in your head:</p>
<h3 id="heading-is-this-it-is-it-the-only-way-golang-decides-to-allocate-memory">Is this it, is it the only way Golang decides to allocate memory?</h3>
<p>The Go compiler uses a technique called <strong>escape analysis</strong> to determine whether a variable should be allocated on the heap or the stack. Generally, the Go compiler tries to allocate variables on the stack when possible, as it's more efficient.</p>
<p>Variables are <em>typically</em> allocated on the heap when:</p>
<ul>
<li><p>They are too large for the stack</p>
</li>
<li><p>Their size is not known at compile time</p>
</li>
<li><p>They are shared with other goroutines</p>
</li>
<li><p>They outlive the function that created them.</p>
</li>
</ul>
<p>You may refer to go documentation. Here are few links:</p>
<ul>
<li><p><a target="_blank" href="https://go.dev/doc/faq#stack_or_heap">https://go.dev/doc/faq#stack_or_heap</a></p>
</li>
<li><p><a target="_blank" href="https://go.dev/doc/go1.13#runtime">https://go.dev/doc/go1.13#runtime</a></p>
</li>
<li><p><a target="_blank" href="https://go.dev/ref/mem">https://go.dev/ref/mem</a></p>
</li>
</ul>
<h3 id="heading-why-do-we-care-where-the-memory-allocation-happens">Why do we care where the memory allocation happens?</h3>
<p>Most of the time we don’t and we shouldn't. But too much garbage collection can become a bottleneck for the application’s performance and  being aware of how the memory allocation works lets you write better code that aligns well with the go mindset. As an example I would like to draw your attention to a go standard library method:</p>
<p><a target="_blank" href="https://pkg.go.dev/io#Reader">https://pkg.go.dev/io#Reader</a></p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Reader <span class="hljs-keyword">interface</span> {
    Read(p []<span class="hljs-keyword">byte</span>) (n <span class="hljs-keyword">int</span>, err error)
}
</code></pre>
<p>Notice, the <code>Read</code> method is taking a byte slice and passing it down, rather than returning the byte slice as a return type. This is for the reasons discussed above.</p>
<h3 id="heading-how-do-we-always-know-where-the-memory-is-getting-allocated">How do we always know where the memory is getting allocated?</h3>
<p>Actually, even seasoned golang devs can be tricked while predicting the memory allocation. So it's always better to use build tools to find the memory allocation of the objects getting created in the program. One such tool is the following:</p>
<blockquote>
<p><em>go build -gcflags -m main.go</em>   </p>
</blockquote>
<p>For the above demo program it would output the following:</p>
<blockquote>
<p>./main.go:8:6: can inline newSmallStruct</p>
<p>./main.go:12:6: can inline changeSmallStruct</p>
<p>./main.go:17:6: can inline main</p>
<p>./main.go:20:19: inlining call to changeSmallStruct</p>
<p>./main.go:23:38: inlining call to newSmallStruct</p>
<p>./main.go:9:9: &amp;SmallStruct{...} escapes to heap</p>
<p>./main.go:12:24: arg does not escape</p>
<p>./main.go:19:17: &amp;SmallStruct{...} does not escape</p>
<p>./main.go:23:38: &amp;SmallStruct{...} does not escape</p>
</blockquote>
<p>You may add <code>-m=2</code> to increase verbosity.  </p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Memory management is fundamental to a programming language's efficiency and performance. Transitioning from languages like C# or Java, one might expect similar garbage collection behavior in Go, yet the approaches differ significantly. Developing proficiency in a language involves aligning with its design principles. That can only be achieved by being informed about internals.</p>
]]></content:encoded></item></channel></rss>