<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Abubakar Hassan</title>
    <description>The latest articles on Forem by Abubakar Hassan (@itssadon).</description>
    <link>https://forem.com/itssadon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F241462%2Fdfa0157e-18a6-4c7d-b45b-d354bff04b4b.jpeg</url>
      <title>Forem: Abubakar Hassan</title>
      <link>https://forem.com/itssadon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/itssadon"/>
    <language>en</language>
    <item>
      <title>How to Prevent Race Conditions in a Node.js System Using Mutexes and RabbitMQ</title>
      <dc:creator>Abubakar Hassan</dc:creator>
      <pubDate>Fri, 20 Dec 2024 21:39:31 +0000</pubDate>
      <link>https://forem.com/itssadon/how-to-prevent-race-conditions-in-a-nodejs-system-using-mutexes-and-rabbitmq-3mkn</link>
      <guid>https://forem.com/itssadon/how-to-prevent-race-conditions-in-a-nodejs-system-using-mutexes-and-rabbitmq-3mkn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In distributed systems, ensuring data consistency across multiple concurrent processes is critical. When dealing with sensitive operations like account withdrawals, race conditions can result in corrupted data or financial discrepancies. This article explains how to use mutexes (mutual exclusion locks) with RabbitMQ and Node.js to safely handle concurrent operations while maintaining efficient memory management.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Imagine a scenario where multiple RabbitMQ workers process withdrawal requests from the same account simultaneously. Without proper synchronization, two workers might read the same initial balance, subtract different amounts, and then update the account, leading to an incorrect balance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Race condition example:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Initial balance: $1000&lt;/li&gt;
&lt;li&gt;Worker A reads the balance: $1000&lt;/li&gt;
&lt;li&gt;Worker B reads the balance: $1000&lt;/li&gt;
&lt;li&gt;Worker A subtracts $300 and updates the balance: $700&lt;/li&gt;
&lt;li&gt;Worker B subtracts $200 and updates the balance: $800 (overwriting Worker A’s update)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Final balance: $800 (should have been $500).&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: Using Mutexes
&lt;/h2&gt;

&lt;p&gt;A mutex ensures that only one worker can process operations on a specific account at any time. Here’s how we can implement a mutex-based solution in a RabbitMQ-driven Node.js system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Implementation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;async-mutex amqplib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;async-mutex: Provides an easy-to-use mutex for asynchronous operations.&lt;/li&gt;
&lt;li&gt;amqplib: Enables interaction with RabbitMQ.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Code Implementation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below is the complete implementation, including robust memory management for mutex cleanup.&lt;/p&gt;

&lt;p&gt;a. Mutex Management&lt;/p&gt;

&lt;p&gt;We use a Map to maintain mutexes per account. Mutexes are created on demand and removed when they are no longer needed.&lt;/p&gt;

&lt;p&gt;b. Memory Optimization&lt;/p&gt;

&lt;p&gt;To avoid memory bloat, we implement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Idle Timeouts: Automatically remove mutexes for inactive accounts after 5 minutes.&lt;/li&gt;
&lt;li&gt;Periodic Cleanup: A background process ensures stale mutexes are removed every 1 minute.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;c. Full Implementation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Mutex&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;async-mutex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amqp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqplib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Mutex for account operations with automatic cleanup&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountMutexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Store one mutex per account&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountTimeouts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Store timeout references for cleanup&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLEANUP_INTERVAL_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1-minute cleanup interval&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;IDLE_TIMEOUT_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 5-minute idle timeout per account&lt;/span&gt;

&lt;span class="c1"&gt;// Function to get or create a mutex for a specific account&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAccountMutex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;accountMutexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mutex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;accountMutexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mutex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;resetAccountTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Start idle timeout cleanup&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;accountMutexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Function to reset idle timeout for an account&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resetAccountTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountTimeouts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountTimeouts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;accountMutexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;accountTimeouts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Mutex for account &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; removed due to inactivity.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;IDLE_TIMEOUT_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;accountTimeouts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Periodic cleanup process&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startPeriodicCleanup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;accountTimeouts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;accountMutexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;accountTimeouts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;CLEANUP_INTERVAL_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Periodic cleanup started: checking every &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CLEANUP_INTERVAL_MS&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; seconds.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Simulated database of accounts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;456&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Process withdrawal&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processWithdrawal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mutex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAccountMutex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;release&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Processing withdrawal for account &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Account not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Insufficient funds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Withdrawal successful! New balance for account &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error processing withdrawal for account &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;resetAccountTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// RabbitMQ message handler&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processWithdrawal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Connect to RabbitMQ and consume messages&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;amqp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqp://localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createChannel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queueName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;withdrawals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queueName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;durable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Waiting for messages in queue: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;queueName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queueName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Acknowledge message after processing&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;startPeriodicCleanup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Start periodic cleanup&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Account-Specific Mutex:

&lt;ul&gt;
&lt;li&gt;Each account has its own mutex (accountMutexes), allowing safe concurrency for different accounts.&lt;/li&gt;
&lt;li&gt;Mutexes are dynamically created when accessed for the first time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Critical Section:

&lt;ul&gt;
&lt;li&gt;The processWithdrawal function locks the mutex to ensure that only one worker can modify the account’s balance.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Memory Management:

&lt;ul&gt;
&lt;li&gt;Idle Timeout: Removes mutexes after 5 minutes of inactivity.&lt;/li&gt;
&lt;li&gt;Periodic Cleanup: A background process runs every minute to clean up stale or unreferenced mutexes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvi4fe94a7kgbpo2nti0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvi4fe94a7kgbpo2nti0.png" alt="Image description" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Race Condition Prevention:

&lt;ul&gt;
&lt;li&gt;Ensures that only one worker processes withdrawals for a given account at a time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Efficient Memory Management:

&lt;ul&gt;
&lt;li&gt;Automatically removes mutexes for inactive accounts, preventing memory bloat.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;High Throughput:

&lt;ul&gt;
&lt;li&gt;Concurrent processing of different accounts is unaffected, maintaining system scalability.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Robust Error Handling:

&lt;ul&gt;
&lt;li&gt;Proper handling of account errors and lock release in the finally block ensures the system stays consistent.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sample Output
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Input Queue Messages:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"accountId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"accountId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"accountId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Console Output:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for messages in queue: withdrawals
Periodic cleanup started: checking every 60 seconds.
Processing withdrawal for account 123
Withdrawal successful! New balance for account 123: 900
Processing withdrawal for account 456
Withdrawal successful! New balance for account 456: 1800
Processing withdrawal for account 123
Withdrawal successful! New balance for account 123: 600
Mutex for account 123 removed due to inactivity.
Mutex for account 456 removed due to inactivity.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;By combining mutexes with RabbitMQ, you can safely handle concurrent operations in Node.js systems.&lt;/p&gt;

&lt;p&gt;Adding idle timeouts and periodic cleanup ensures efficient memory management, making this solution scalable and robust for real-world use cases.&lt;/p&gt;

</description>
      <category>node</category>
      <category>eventdriven</category>
      <category>tutorial</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to end a telnet session (Linux, MAC, Windows)</title>
      <dc:creator>Abubakar Hassan</dc:creator>
      <pubDate>Tue, 13 Dec 2022 16:33:17 +0000</pubDate>
      <link>https://forem.com/itssadon/how-to-end-a-telnet-session-linux-mac-windows-hbb</link>
      <guid>https://forem.com/itssadon/how-to-end-a-telnet-session-linux-mac-windows-hbb</guid>
      <description>&lt;p&gt;Hello there 👋&lt;/p&gt;

&lt;p&gt;Like me, you have started a telnet connection through the terminal and have no idea how to close it 🤣&lt;/p&gt;

&lt;p&gt;If you currently don't have an active telnet connection, you can start one on the terminal by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ telnet address port
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;To end your current telnet session you must reach the telnet prompt and type quit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here are the steps for doing so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the telnet prompt by holding down the 'Ctrl' key and push the ']' key. (prompt: telnet&amp;gt;)&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;quit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Push the &lt;code&gt;Enter&lt;/code&gt; key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope this helped.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You're welcome&lt;/em&gt; 💪&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>linux</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to build a minimal production-ready infrastructure with Terraform on DigitalOcean</title>
      <dc:creator>Abubakar Hassan</dc:creator>
      <pubDate>Sun, 11 Dec 2022 23:21:32 +0000</pubDate>
      <link>https://forem.com/itssadon/how-to-build-a-minimal-production-ready-infrastructure-with-terraform-on-digitalocean-3id6</link>
      <guid>https://forem.com/itssadon/how-to-build-a-minimal-production-ready-infrastructure-with-terraform-on-digitalocean-3id6</guid>
      <description>&lt;p&gt;This how-to will help you deploy a production-ready infrastructure on Digital Ocean using Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://www.terraform.io/downloads.html" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://m.do.co/c/72deb6aa82de" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt; account if you don't already have one (Use this link to get $100 credit)&lt;/li&gt;
&lt;li&gt;Generate a Personal Access Token for your Digital Ocean account to access the DigitalOcean API. Go to &lt;code&gt;API =&amp;gt; Tokens/Keys =&amp;gt; Generate New Token&lt;/code&gt;. Save the &lt;code&gt;string&lt;/code&gt; generated.&lt;/li&gt;
&lt;li&gt;Create a domain for your project. Go to &lt;code&gt;Networking =&amp;gt; Domains =&amp;gt; Add domain&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Initial setup terraform files
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open your terminal and create a new project directory, and open with you code editor.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir minimal-prod`
$ cd minimal-prod`
$ code .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create the following terraform files:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch versions.tf
$ touch main.tf
$ touch variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;versions.tf&lt;/code&gt;, specify the Digital Ocean terraform provider as follows:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  required_providers {
    digitalocean = {
      source  = "digitalocean/digitalocean"
      version = "2.25.2"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;main.tf&lt;/code&gt;, add the token required by the provider like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "digitalocean" {
  token = var.do_token
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;variables.tf&lt;/code&gt;, create a new variable called &lt;code&gt;do_token&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "do_token" {
  type        = string
  description = "Digital Ocean personal access token"
  default     = "&amp;lt;token_string&amp;gt;"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you wish to commit &lt;code&gt;variables.tf&lt;/code&gt; to your version control system, you might want to use a different file for more sensitive information, such as your Digital Ocean's personal access token. Create a new file &lt;code&gt;terraform.tfvars&lt;/code&gt; and save the following to it:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;do_token = "&amp;lt;digital_ocean_token&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;lt;digital_ocean_token&amp;gt;&lt;/code&gt; with the actual value generated.&lt;br&gt;
⚠️ Make sure *.tfvars is in your .gitignore file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Now prepare your working directory for other commands by running the following command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Architecture Diagram
&lt;/h2&gt;

&lt;p&gt;Below is a diagram representing the architecture that will be produced by executing the Terraform files at the end of this tutorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                       |                                                   
                     https                                                
                       |                                                   
                       v                                                    
              +--------------------+                                        
              |    Load Balancer   |                                        
 +--------------------------------------------------+                          
 |            |                    |                |                     
 |            +--------------------+                |                     
 |                     |                            |                     
 |                     |                            |                     
 |                    http              +---------+ |                     
 |                     |                |         | |                     
 |                     |                |         | |                     
 |                     +-------SSH------| Bastion |&amp;lt;---SSH---
 |                     |                |         | |                     
 |                     |                |         | |                     
 |                     |                +---------+ |                     
 |          +----------+---------+                  |                     
 |          |          |         |                  |                     
 |          v          v         v                  |                     
 |      +-------+  +-------+  +-------+             |                     
 |      |  web  |  |  web  |  |  web  |             |                     
 |      +---+---+  +---+---+  +---+---+             |                     
 |          |          |          |                 |                     
 |          |          v          |                 |                     
 |          |     +----------+    |                 |                     
 |          |     |          |    |                 |                     
 |          +----&amp;gt;| database |&amp;lt;---+                 |                     
 |                |          |                      |                     
 |                +----------+                      |                     
 +--------------------------------------------------+  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Virtual Private Cloud (VPC) setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a file &lt;code&gt;network.tf&lt;/code&gt; and add the following to build the VPC
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "digitalocean_vpc" "web" {
  name     = "${var.name}-vpc"
  region   = var.region
  ip_range = var.ip_range
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add new variables to &lt;code&gt;variables.tf&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

variable "name" {
  type        = string
  description = "Infrastructure project name"
  default     = "minimal-prod"
}

variable "region" {
  type    = string
  default = "ams2"
}

variable "ip_range" {
  type        = string
  description = "IP range for VPC"
  default     = "192.168.22.0/24"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the command below to see the execution plan so far
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Web Servers setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add a few more variables to &lt;code&gt;variables.tf&lt;/code&gt; to be used in the future
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

variable "droplet_count" {
  type    = number
  default = 1
}

variable "image" {
  type        = string
  description = "OS to install on the servers"
  default     = "ubuntu-20-04-x64"
}

variable "droplet_size" {
  type    = string
  default = "s-1vcpu-1gb"
}

variable "ssh_key" {
  type = string
}

variable "subdomain" {
  type = string
}

variable "domain_name" {
  type = string
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a new file &lt;code&gt;data.tf&lt;/code&gt; and add a data resource for our ssh key which will be pulled from Digital Ocean directly:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data "digitalocean_ssh_key" "main" {
  name = var.ssh_key
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a file &lt;code&gt;servers.tf&lt;/code&gt; hold all the resources we'll be creating for our web servers as follows:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "digitalocean_droplet" "web" {
  count     = var.droplet_count
  image     = var.image
  name      = "web-${var.name}-${var.region}-${count.index + 1}"
  region    = var.region
  size      = var.droplet_size
  ssh_keys  = [data.digitalocean_ssh_key.main.id]
  vpc_uuid  = digitalocean_vpc.web.id
  tags      = ["${var.name}-webserver"]
  user_data = &amp;lt;&amp;lt;EOF
  #cloud-config
  packages:
    - nginx
    - postgresql
    - postgresql-contrib
  runcmd:
    - [ sh, -xc, "echo '&amp;lt;h1&amp;gt;web-${var.region}-${count.index + 1}&amp;lt;/h1&amp;gt;' &amp;gt;&amp;gt; /var/www/html/index.html"]
  EOF
  lifecycle {
    create_before_destroy = true
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update &lt;code&gt;terraform.tfvars&lt;/code&gt; with the following variables for our environment:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
region        = "ams3"
droplet_count = 3
ssh_key       = "&amp;lt;ssh_key_name_on_digitalocean&amp;gt;"
domain_name   = "&amp;lt;domain_added_on_digitalocean&amp;gt;"
subdomain     = "app"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add a value for the domain you want to use in &lt;code&gt;data.tf&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

data "digitalocean_domain" "web" {
  name = var.domain_name
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a lets encrypt certificate to be used by the load balancer. Add the following to the &lt;code&gt;servers.tf&lt;/code&gt; file
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "digitalocean_certificate" "web" {
  name    = "${var.name}-certificate"
  type    = "lets_encrypt"
  domains = ["${var.subdomain}.${data.digitalocean_domain.web.name}"]
  lifecycle {
    create_before_destroy = true
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can use the Digital Ocean terraform provider to provide your own certificate if you want to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Next, we create our load balancer with the correct forwarding rules and the firewall setup in &lt;code&gt;servers.tf&lt;/code&gt;. This is to block all inbound traffic directly to the web servers from the internet.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

resource "digitalocean_loadbalancer" "web" {
  name                   = "web-${var.region}"
  region                 = var.region
  droplet_ids            = digitalocean_droplet.web.*.id
  vpc_uuid               = digitalocean_vpc.web.id
  redirect_http_to_https = true
  forwarding_rule {
    entry_port       = 443
    entry_protocol   = "https"
    target_port      = 80
    target_protocol  = "http"
    certificate_name = digitalocean_certificate.web.name
  }
  forwarding_rule {
    entry_port       = 80
    entry_protocol   = "http"
    target_port      = 80
    target_protocol  = "http"
    certificate_name = digitalocean_certificate.web.name
  }
  lifecycle {
    create_before_destroy = true
  }
}

resource "digitalocean_firewall" "web" {
  name        = "${var.name}-only-vpc-traffic"
  droplet_ids = digitalocean_droplet.web.*.id
  inbound_rule {
    protocol         = "tcp"
    port_range       = "1-65535"
    source_addresses = [digitalocean_vpc.web.ip_range]
  }
  inbound_rule {
    protocol         = "udp"
    port_range       = "1-65535"
    source_addresses = [digitalocean_vpc.web.ip_range]
  }
  inbound_rule {
    protocol         = "icmp"
    source_addresses = [digitalocean_vpc.web.ip_range]
  }
  outbound_rule {
    protocol              = "tcp"
    port_range            = "1-65535"
    destination_addresses = [digitalocean_vpc.web.ip_range]
  }
  outbound_rule {
    protocol              = "udp"
    port_range            = "1-65535"
    destination_addresses = [digitalocean_vpc.web.ip_range]
  }
  outbound_rule {
    protocol              = "icmp"
    destination_addresses = [digitalocean_vpc.web.ip_range]
  }
  outbound_rule {
    protocol              = "udp"
    port_range            = "53"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }
  outbound_rule {
    protocol              = "tcp"
    port_range            = "80"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }
  outbound_rule {
    protocol              = "tcp"
    port_range            = "443"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }
  outbound_rule {
    protocol              = "icmp"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, we create the record for the subdomain
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

resource "digitalocean_record" "web" {
  domain = data.digitalocean_domain.web.name
  type   = "A"
  name   = var.subdomain
  value  = digitalocean_loadbalancer.web.ip
  ttl    = 30
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Database resource
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next, we add a few more variables to be used to setup our database in &lt;code&gt;variables.tf&lt;/code&gt; as follows:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

variable "db_count" {
  type    = number
  default = 1
}

variable "database_size" {
  type    = string
  default = "db-s-1vcpu-1gb"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, create &lt;code&gt;database.tf&lt;/code&gt; to build the database. For this example we will be creating a Postgres database.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "digitalocean_database_cluster" "postgres-cluster" {
  name                 = "${var.name}-database-cluster"
  engine               = "pg"
  version              = "11"
  size                 = var.database_size
  region               = var.region
  node_count           = var.db_count
  private_network_uuid = digitalocean_vpc.web.id
}

resource "digitalocean_database_firewall" "postgress-cluster-firewall" {
  cluster_id = digitalocean_database_cluster.postgres-cluster.id
  rule {
    type  = "tag"
    value = "${var.name}-webserver"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Jump server (Bastion) for accessing our infrastructure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next, we need to create a jump server (Bastion) to access our infrastructure. Create a &lt;code&gt;bastion.tf&lt;/code&gt; with the following:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "digitalocean_droplet" "bastion" {
  image    = var.image
  name     = "bastion-${var.name}-${var.region}"
  region   = var.region
  size     = "s-1vcpu-1gb"
  ssh_keys = [data.digitalocean_ssh_key.main.id]
  vpc_uuid = digitalocean_vpc.web.id
  tags     = ["${var.name}-webserver"]
  lifecycle {
    create_before_destroy = true
  }
}

resource "digitalocean_record" "bastion" {
  domain = data.digitalocean_domain.web.name
  type   = "A"
  name   = "bastion-${var.name}-${var.region}"
  value  = digitalocean_droplet.bastion.ipv4_address
  ttl    = 300
}

resource "digitalocean_firewall" "bastion" {
  name        = "${var.name}-only-ssh-bastion"
  droplet_ids = [digitalocean_droplet.bastion.id]
  inbound_rule {
    protocol         = "tcp"
    port_range       = "22"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }
  outbound_rule {
    protocol              = "tcp"
    port_range            = "22"
    destination_addresses = [digitalocean_vpc.web.ip_range]
  }
  outbound_rule {
    protocol              = "icmp"
    destination_addresses = [digitalocean_vpc.web.ip_range]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now you can apply the files and let terraform create the infrastructure on Digital Ocean
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can use the &lt;code&gt;--auto-approve&lt;/code&gt; flag to let terraform automatically continue without asking you for approval.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Access the server through the bastion host
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Copy the domain name of the bastion host and ssh into it from your terminal.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh -A root@&amp;lt;FQDN of bastion host&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc95exma5x0z43n40dmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc95exma5x0z43n40dmk.png" alt="Screenshot of bastion host after logging in"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy the IP address of any of the web servers and ssh into it from the bastion host
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh root@&amp;lt;private_ip_address_of_server&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Delete infrastructure
&lt;/h3&gt;

&lt;p&gt;This will undo everything. You can delete the infrastructure by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thanks for the time reading this!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me know if you there's a way you think this infrastructure can be improved.&lt;br&gt;
👍&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>digitalocean</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to recursively remove .DS_Store</title>
      <dc:creator>Abubakar Hassan</dc:creator>
      <pubDate>Sat, 05 Nov 2022 12:58:56 +0000</pubDate>
      <link>https://forem.com/itssadon/how-to-recursively-remove-dsstore-4kdd</link>
      <guid>https://forem.com/itssadon/how-to-recursively-remove-dsstore-4kdd</guid>
      <description>&lt;p&gt;Lately, I've been doing a lot of development on Mac. With that comes a lot of use of the Finder app.&lt;/p&gt;

&lt;p&gt;In order keep your repo clean, you need to remove .DS_Store files from the folders created using the Mac GUI. Doing that by hand is really boring, painful, and repetitive. Enter that awesome thing we call the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursively Remove .DS_Store
&lt;/h2&gt;

&lt;p&gt;Open up Terminal&lt;/p&gt;

&lt;p&gt;In the command line, type: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd to/your/directory&lt;/code&gt; - then press enter.&lt;/p&gt;

&lt;p&gt;Finally, in the command line, type: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;find . -name '.DS_Store' -type f -delete&lt;/code&gt; - then press enter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: Never use a wildcard (*) with this command, unless you know what you're doing. Bad things can happen if you don't.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This snippet will remove .DS_Store files from the selected folder, as well as all of the folders that it contains. All of them. At once.&lt;/p&gt;

&lt;p&gt;This command can be used for other types of files, as well. Just replace '&lt;strong&gt;.DS_Store&lt;/strong&gt;' with whatever file name, or type, that you want to delete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other uses
&lt;/h2&gt;

&lt;p&gt;You can do this for other file types, as well.&lt;/p&gt;

&lt;p&gt;For example, you could use the below to delete all JavaScript files within a containing folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;find . -name '*.js' -type f -delete&lt;/code&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the asterisk is a wildcard, meaning the Terminal is looking for any file with an extension of .js... it can be dangerous to use wildcards...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;be careful.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>bash</category>
    </item>
  </channel>
</rss>
