<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ scaling - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ scaling - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 14 Jun 2026 05:24:58 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/scaling/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How Large-Scale Platforms Handle Millions of Daily Transactions ]]>
                </title>
                <description>
                    <![CDATA[ Every day, millions of people order food, stream videos, send messages, book rides, make payments, and shop online. Most of these actions take only a few seconds from the user's perspective. A user cl ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-large-scale-platforms-handle-millions-of-daily-transactions/</link>
                <guid isPermaLink="false">6a2cfda7306003b984294a7b</guid>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                    <category>
                        <![CDATA[ infrastructure ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Reliability ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Sat, 13 Jun 2026 06:50:15 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/67e3b365-0795-4055-9a59-61e32090de3e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Every day, millions of people order food, stream videos, send messages, book rides, make payments, and shop online. Most of these actions take only a few seconds from the user's perspective. A user clicks a button, and the platform responds almost instantly.</p>
<p>Behind the scenes, however, these platforms are processing enormous numbers of transactions. A single popular application may handle thousands of requests every second and millions of transactions every day. Each transaction must be processed accurately, securely, and quickly.</p>
<p>In this article, we'll explore how large-scale platforms manage massive transaction volumes, the engineering challenges involved, and the architectural patterns developers use to build reliable systems.</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-why-transaction-volume-creates-unique-challenges">Why Transaction Volume Creates Unique Challenges</a></p>
</li>
<li><p><a href="#heading-breaking-monoliths-into-services">Breaking Monoliths Into Services</a></p>
</li>
<li><p><a href="#heading-using-load-balancers-to-distribute-traffic">Using Load Balancers to Distribute Traffic</a></p>
</li>
<li><p><a href="#heading-why-databases-become-bottlenecks">Why Databases Become Bottlenecks</a></p>
</li>
<li><p><a href="#heading-caching-frequently-accessed-data">Caching Frequently Accessed Data</a></p>
</li>
<li><p><a href="#heading-processing-tasks-asynchronously">Processing Tasks Asynchronously</a></p>
</li>
<li><p><a href="#heading-preventing-duplicate-transactions">Preventing Duplicate Transactions</a></p>
</li>
<li><p><a href="#heading-monitoring-everything">Monitoring Everything</a></p>
</li>
<li><p><a href="#heading-preparing-for-traffic-spikes">Preparing for Traffic Spikes</a></p>
</li>
<li><p><a href="#heading-building-for-failure">Building for Failure</a></p>
</li>
<li><p><a href="#heading-the-importance-of-consistency-and-reliability">The Importance of Consistency and Reliability</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-transaction-volume-creates-unique-challenges">Why Transaction Volume Creates Unique Challenges</h2>
<p>Handling a few hundred transactions per day is relatively straightforward. A single server and database can often manage the workload without difficulty. The challenge emerges as usage grows and systems begin serving thousands or even millions of users simultaneously.</p>
<p>Consider an online marketplace operating across multiple countries. At any given moment, thousands of users may be placing orders. Inventory must be updated in real time, payments must be processed accurately, notifications must be delivered, and fraud detection systems must evaluate transactions before approval. All of this happens within seconds.</p>
<p>At scale, even a minor delay can affect thousands of users. Systems must maintain low response times while preventing database bottlenecks, avoiding duplicate transactions, handling unexpected traffic spikes, and remaining reliable when failures occur.</p>
<p>To solve these problems, engineering teams rely on <a href="https://www.atlassian.com/microservices/microservices-architecture/distributed-architecture">distributed systems</a> and scalable architectural patterns.</p>
<h2 id="heading-breaking-monoliths-into-services">Breaking Monoliths Into Services</h2>
<p>Many successful platforms begin as <a href="https://www.freecodecamp.org/news/microservices-vs-monoliths-explained/#heading-what-is-a-monolith">monolithic applications</a> where all functionality exists within a single codebase. While this approach works well during the early stages of growth, it can become increasingly difficult to scale as transaction volume increases.</p>
<p>To overcome this limitation, large platforms often adopt a service-oriented architecture. Instead of one application handling every responsibility, individual services are created for specific business functions such as user management, payments, inventory, notifications, and analytics.</p>
<p>A simplified order-processing workflow might look like this:</p>
<pre><code class="language-python">def create_order(user_id, product_id):
    inventory.reserve(product_id)

    payment_result = payment.charge(user_id)

    if payment_result.success:
        order.create(user_id, product_id)
        notification.send_confirmation(user_id)

    return payment_result
</code></pre>
<p>This separation allows each service to scale independently. If payment activity suddenly increases, engineers can allocate additional resources specifically to the payment service without affecting the rest of the platform. It also lets teams develop, deploy, and maintain services independently, improving both agility and reliability.</p>
<h2 id="heading-using-load-balancers-to-distribute-traffic">Using Load Balancers to Distribute Traffic</h2>
<p>No single server can handle millions of daily transactions on its own. To distribute incoming requests efficiently, platforms place <a href="https://www.freecodecamp.org/news/auto-scaling-and-load-balancing/#heading-load-balancing-explained">load balancers</a> in front of their application servers.</p>
<p>Instead of connecting directly to a server, users send requests to a load balancer. The load balancer determines which server is best positioned to handle each request based on factors such as current load, availability, and health status.</p>
<p>A simplified architecture looks like this:</p>
<pre><code class="language-text">Users
   |
Load Balancer
   |
-------------------
|        |        |
Server1 Server2 Server3
</code></pre>
<p>If one server becomes overloaded or fails, traffic can be redirected to healthier servers. This improves both performance and availability. Modern cloud providers offer managed load-balancing solutions that automatically distribute traffic based on resource utilization and server health.</p>
<h2 id="heading-why-databases-become-bottlenecks">Why Databases Become Bottlenecks</h2>
<p>Scaling application servers is often relatively easy. But databases frequently become the most significant bottleneck in transaction-heavy systems.</p>
<p>Every transaction ultimately requires reading or writing data. Consider an <a href="https://jumptask.io/blog/guide-to-task-earning/">online task management platform</a> where users complete tasks and receive rewards. Each completed task may trigger multiple database operations, including verification of task completion, updating account balances, recording transaction history, and generating audit logs.</p>
<p>As transaction volume grows, database performance becomes critical. One common solution is read replication. Instead of relying on a single database instance, platforms create multiple replicas that handle read requests while the primary database focuses on write operations.</p>
<p>The architecture may resemble the following:</p>
<pre><code class="language-text">Primary DB
     |
-------------------------
|         |            |
Replica1 Replica2 Replica3
</code></pre>
<p>By distributing read traffic across multiple replicas, platforms reduce pressure on the primary database and improve response times for users.</p>
<h2 id="heading-caching-frequently-accessed-data">Caching Frequently Accessed Data</h2>
<p>Not every request needs to reach the database. In fact, repeatedly querying the database for the same information can significantly increase infrastructure costs and response times.</p>
<p>To address this, platforms use <a href="https://www.freecodecamp.org/news/how-in-memory-caching-works-in-redis/">caching systems such as Redis</a> to store frequently accessed data in memory. Information such as user profiles, product details, and application settings often changes infrequently and can be retrieved directly from the cache.</p>
<p>Without caching:</p>
<pre><code class="language-python">user = database.get_user(user_id)
</code></pre>
<p>With caching:</p>
<pre><code class="language-python">user = cache.get(user_id)

if not user:
    user = database.get_user(user_id)
    cache.set(user_id, user)
</code></pre>
<p>Memory access is substantially faster than database queries. When a platform processes millions of requests every day, caching can dramatically improve performance while reducing backend load.</p>
<h2 id="heading-processing-tasks-asynchronously">Processing Tasks Asynchronously</h2>
<p>Users expect immediate responses. If every operation must finish before the system responds, applications quickly become sluggish under heavy load.</p>
<p>To improve responsiveness, large-scale systems separate critical user-facing actions from background processing tasks. Consider a payment transaction. The user needs confirmation that the payment was successful, but they don't need to wait for analytics updates, report generation, or email delivery.</p>
<p>A synchronous implementation might look like this:</p>
<pre><code class="language-python">process_payment()
send_email()
update_analytics()
generate_report()
</code></pre>
<p>A more scalable approach uses <a href="https://www.freecodecamp.org/news/how-message-queues-make-distributed-systems-more-reliable/">message queues</a>:</p>
<pre><code class="language-python">process_payment()

queue.publish("send_email")
queue.publish("update_analytics")
queue.publish("generate_report")
</code></pre>
<p>Background workers consume these queued tasks and process them independently. This architecture improves user experience and enables systems to handle significantly larger transaction volumes.</p>
<h2 id="heading-preventing-duplicate-transactions">Preventing Duplicate Transactions</h2>
<p>One of the most important challenges in transaction processing is preventing duplicate execution.</p>
<p>Network interruptions can create situations where users unknowingly submit the same request multiple times. Imagine a customer making a purchase. The payment succeeds, but the confirmation never reaches the user's device because of a network failure. Believing the payment failed, the customer clicks the button again.</p>
<p>Without safeguards, the platform could charge the customer twice.</p>
<p>Many systems solve this problem through <a href="https://temporal.io/blog/idempotency-and-durable-execution">idempotency</a> keys. A simplified implementation looks like this:</p>
<pre><code class="language-python">def process_payment(request_id, amount):

    if payment_exists(request_id):
        return existing_payment(request_id)

    payment = create_payment(request_id, amount)
    return payment
</code></pre>
<p>If the same request arrives again, the system returns the original result instead of processing a second payment. This pattern is widely used in financial services, payment gateways, and banking applications.</p>
<h2 id="heading-monitoring-everything">Monitoring Everything</h2>
<p>As systems grow more complex, visibility becomes essential. Engineering teams can't effectively troubleshoot issues they can't observe.</p>
<p>Modern platforms collect metrics from every layer of their infrastructure. Engineers <a href="https://www.freecodecamp.org/news/the-front-end-monitoring-handbook/">continuously monitor</a> request latency, database response times, error rates, queue depth, CPU utilization, and memory consumption.</p>
<p>A simple monitoring rule might look like this:</p>
<pre><code class="language-python">if error_rate &gt; 5:
    alert("High error rate detected")
</code></pre>
<p>Monitoring enables teams to identify problems before they impact users. It also provides valuable data for performance optimization and future capacity planning.</p>
<h2 id="heading-preparing-for-traffic-spikes">Preparing for Traffic Spikes</h2>
<p>Traffic patterns are rarely predictable. An e-commerce platform may experience enormous demand during holiday sales, while a ticketing website can receive millions of requests within minutes when a popular event goes live.</p>
<p>To handle these surges, platforms rely on autoscaling. Cloud infrastructure can automatically add resources as demand increases and remove them when traffic subsides.</p>
<p>A simplified scaling rule might look like this:</p>
<pre><code class="language-python">if cpu_usage &gt; 70:
    add_server()
</code></pre>
<p>Autoscaling helps maintain performance during peak periods while controlling infrastructure costs during quieter times.</p>
<h2 id="heading-building-for-failure">Building for Failure</h2>
<p>One of the most important principles in distributed systems is accepting that failures are inevitable.</p>
<p>Servers crash. Databases become unavailable. Networks experience interruptions. Rather than hoping these events never occur, large-scale platforms design systems that can continue operating when failures happen.</p>
<p>For example, payment systems often include retry logic:</p>
<pre><code class="language-python">for attempt in range(3):
    try:
        charge_customer()
        break
    except:
        continue
</code></pre>
<p>In addition, platforms implement redundancy by running multiple instances of critical components across different geographic regions and availability zones. If one component fails, another can take over with minimal disruption.</p>
<p>This strategy significantly improves availability and resilience.</p>
<h2 id="heading-the-importance-of-consistency-and-reliability">The Importance of Consistency and Reliability</h2>
<p>At scale, transaction processing isn't solely about speed. Accuracy is equally important.</p>
<p>Users may tolerate a slight delay, but they won't tolerate duplicate charges, missing funds, incorrect balances, or lost transactions. For this reason, large-scale transaction systems place a strong emphasis on consistency, auditing, logging, reconciliation, and recovery mechanisms.</p>
<p>Every transaction must be traceable. Every failure must be recoverable. These requirements become particularly important in industries such as finance, e-commerce, subscription billing, and task earning platforms where money and rewards move between users and businesses every day.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The ability to handle millions of daily transactions isn't the result of a single technology. It comes from combining multiple architectural principles that work together to create reliable, scalable systems.</p>
<p>Large-scale platforms distribute traffic across multiple servers, separate responsibilities into specialized services, cache frequently accessed data, process background work asynchronously, continuously monitor system health, and design for inevitable failures.</p>
<p>For developers, understanding these patterns provides valuable insight into how modern internet platforms operate behind the scenes. Whether you're building a payment processor, a SaaS platform, an online marketplace, or a task earning application, the same foundational principles apply.</p>
<p>As systems grow, scalability becomes less about writing more code and more about designing architecture that remains reliable under increasing demand. The platforms that succeed are the ones capable of delivering fast, accurate, and consistent transactions regardless of how many users arrive.</p>
<p>Hope you enjoyed this article. You can <a href="https://linkedin.com/in/manishmshiva">connect with me on LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Scale Laravel Applications for High-Traffic Production Systems ]]>
                </title>
                <description>
                    <![CDATA[ Your first scaling problem rarely arrives with a bang. For a while, everything is fine: pages load fast, the database barely breaks a sweat, and the team ships features without thinking much about inf ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-scale-laravel-applications-for-high-traffic-production-systems/</link>
                <guid isPermaLink="false">6a2b48a3a381db4fd3f61555</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                    <category>
                        <![CDATA[ production ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Olamilekan Lamidi ]]>
                </dc:creator>
                <pubDate>Thu, 11 Jun 2026 23:45:39 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/8882176c-0420-4fc9-8d72-129640aac231.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Your first scaling problem rarely arrives with a bang. For a while, everything is fine: pages load fast, the database barely breaks a sweat, and the team ships features without thinking much about infrastructure.</p>
<p>Then traffic climbs. A campaign over-performs. A marketplace onboards a popular seller. A SaaS product signs a couple of enterprise accounts.</p>
<p>Suddenly, <code>/dashboard</code> takes two seconds instead of 300 milliseconds. Queue jobs that used to clear in seconds sit waiting for minutes. You have database CPU spikes every afternoon.</p>
<p>So you add another app server, and response time barely moves because the real culprit was a slow query on a large table all along.</p>
<p>If you have run Laravel in production, you've probably lived some version of this. The good news is that scaling Laravel almost never means abandoning the framework. It means learning where pressure builds and making the application behave predictably under load.</p>
<p>In this guide, you'll learn how to find common bottlenecks, tune the database, use Redis effectively, move slow work onto queues, optimize APIs, and monitor a Laravel application in production.</p>
<p>None of this requires a single heroic rewrite. The biggest wins usually come from practical work: removing inefficient queries, pushing slow tasks onto queues, adding the right indexes, caching carefully chosen data, and measuring whether each change actually helped.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You'll get the most out of this guide if you're already comfortable with:</p>
<ul>
<li><p>Building applications with Laravel and PHP</p>
</li>
<li><p>Writing Eloquent queries and database migrations</p>
</li>
<li><p>Using queues, jobs, and scheduled commands</p>
</li>
<li><p>Reading a basic database query plan</p>
</li>
<li><p>Deploying Laravel to a production server or platform</p>
</li>
<li><p>Working with Redis and either MySQL or PostgreSQL in a production-like setup</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-happens-when-laravel-apps-start-growing">What Happens When Laravel Apps Start Growing</a></p>
</li>
<li><p><a href="#heading-common-laravel-bottlenecks">Common Laravel Bottlenecks</a></p>
</li>
<li><p><a href="#heading-how-to-optimize-the-database">How to Optimize the Database</a></p>
</li>
<li><p><a href="#heading-how-to-scale-with-redis">How to Scale with Redis</a></p>
</li>
<li><p><a href="#heading-how-to-use-queue-driven-architectures">How to Use Queue-Driven Architectures</a></p>
</li>
<li><p><a href="#heading-how-to-optimize-api-performance">How to Optimize API Performance</a></p>
</li>
<li><p><a href="#heading-how-to-monitor-laravel-in-production">How to Monitor Laravel in Production</a></p>
</li>
<li><p><a href="#heading-an-example-high-traffic-laravel-architecture">An Example High-Traffic Laravel Architecture</a></p>
</li>
<li><p><a href="#heading-lessons-learned-the-hard-way">Lessons Learned the Hard Way</a></p>
</li>
<li><p><a href="#heading-a-pre-launch-scaling-checklist">A Pre-Launch Scaling Checklist</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a href="#heading-references">References</a></p>
</li>
</ul>
<h2 id="heading-what-happens-when-laravel-apps-start-growing">What Happens When Laravel Apps Start Growing</h2>
<p>Traffic changes a system's behavior because it turns small inefficiencies into permanent costs. A query that takes 80 milliseconds is harmless when it runs a few hundred times an hour. Run it 30 times per page view on a page that gets thousands of hits a minute, and that same query becomes a capacity problem.</p>
<p>The pressure tends to show up in predictable places. More requests mean more PHP workers, more database connections, more queue volume, and more Redis operations.</p>
<p>The database, whether MySQL or PostgreSQL, is usually the first thing to buckle. Queues back up when work is created faster than workers can drain it. Caches only help when hit rates stay high and misses stay controlled. And scaling everything horizontally can turn sloppy code into an expensive cloud bill.</p>
<p>That's why scaling work has to start with measurement, not guesswork. Before you change anything, you want to know what is actually saturated: request CPU, database I/O, lock contention, Redis latency, queue depth, an external API, or oversized payloads.</p>
<p>A typical request in a growing Laravel app travels through several layers. The user sends a request, a load balancer routes it to an app server, and Laravel checks Redis for a cached result. On a miss, it queries the database, stores the computed result back in Redis, and hands any slow follow-up work to a queue. A worker picks up that job later while Laravel returns the response right away.</p>
<p>Here's the important part: adding more app servers does nothing for a slow query, a missing index, or an overloaded queue. Horizontal scaling only pays off once the shared dependencies behind those servers can keep up.</p>
<h2 id="heading-common-laravel-bottlenecks">Common Laravel Bottlenecks</h2>
<p>Laravel itself causes very few scaling problems. Most issues come from how application code talks to the database, the network, and background workers.</p>
<h3 id="heading-n1-queries">N+1 Queries</h3>
<p>The classic offender is the N+1 query. You load a list of models, then lazily touch a relationship on each one:</p>
<pre><code class="language-php">use App\Models\Post;

$posts = Post::latest()-&gt;take(50)-&gt;get();

foreach (\(posts as \)post) {
    echo $post-&gt;author-&gt;name;
}
</code></pre>
<p>That's one query for the posts plus one query per author: 51 queries for a single page. Eager load the relationship instead:</p>
<pre><code class="language-php">use App\Models\Post;

$posts = Post::with('author')
    -&gt;latest()
    -&gt;take(50)
    -&gt;get();

foreach (\(posts as \)post) {
    echo $post-&gt;author-&gt;name;
}
</code></pre>
<p>In production, these are sneaky. They often hide inside API Resources, Blade components, and authorization checks, where the relationship access isn't obvious from the controller.</p>
<h3 id="heading-missing-indexes">Missing Indexes</h3>
<p>Adding an index is one of the highest-return fixes you can make. Take a query like this:</p>
<pre><code class="language-php">\(orders = Order::where('account_id', \)accountId)
    -&gt;where('status', 'paid')
    -&gt;whereBetween('created_at', [\(start, \)end])
    -&gt;latest()
    -&gt;paginate(50);
</code></pre>
<p>If <code>orders</code> has millions of rows and no useful compound index, the database scans far more rows than it needs to. Add an index that matches how you actually query:</p>
<pre><code class="language-php">use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void
    {
        Schema::table('orders', function (Blueprint $table) {
            $table-&gt;index(['account_id', 'status', 'created_at']);
        });
    }

    public function down(): void
    {
        Schema::table('orders', function (Blueprint $table) {
            $table-&gt;dropIndex(['account_id', 'status', 'created_at']);
        });
    }
};
</code></pre>
<p>Indexes aren't free, though. They take up space and slow down writes. Add them for real, repeated query patterns, not for every column that ever appears in a <code>where</code> clause.</p>
<h3 id="heading-inefficient-eager-loading">Inefficient Eager Loading</h3>
<p>You can also swing too far the other way. Loading every relationship "just in case" burns memory and ships data the request never uses:</p>
<pre><code class="language-php">$users = User::with([
    'profile',
    'teams',
    'roles.permissions',
    'invoices.lineItems.product',
])-&gt;get();
</code></pre>
<p>That might be fine for an admin detail page showing one user. On a list page, it's a liability. Constrain the eager loads and select only the columns you need:</p>
<pre><code class="language-php">$users = User::query()
    -&gt;select(['id', 'name', 'email'])
    -&gt;with([
        'profile:id,user_id,avatar_url',
        'teams:id,name',
    ])
    -&gt;latest()
    -&gt;paginate(25);
</code></pre>
<p>One caveat: tightly scoped select lists can break later code that expects a column you didn't load. Keep this technique close to read-heavy endpoints where the payoff is obvious.</p>
<h3 id="heading-synchronous-processing">Synchronous Processing</h3>
<p>High-traffic apps need short web requests. Sending email, generating PDFs, calling third-party APIs, resizing images, and building exports usually belong outside the request cycle. This version can hurt you:</p>
<pre><code class="language-php">public function store(Request $request)
{
    \(order = Order::create(\)request-&gt;validated());

    Mail::to(\(order-&gt;user)-&gt;send(new OrderReceipt(\)order));

    return response()-&gt;json($order, 201);
}
</code></pre>
<p>Push the work onto a queue instead:</p>
<pre><code class="language-php">public function store(StoreOrderRequest $request)
{
    \(order = Order::create(\)request-&gt;validated());

    SendOrderReceipt::dispatch($order-&gt;id);

    return response()-&gt;json([
        'id' =&gt; $order-&gt;id,
        'status' =&gt; 'accepted',
    ], 202);
}
</code></pre>
<p>Now your response time no longer depends on your mail provider. If the provider has a slow afternoon, the queue absorbs it and your users don't have to wait.</p>
<h3 id="heading-large-payloads">Large Payloads</h3>
<p>Oversized JSON responses hurt everyone in the chain: the app server serializing them, the network carrying them, and the client parsing them. A frequent mistake is returning whole models when you meant to return a summary:</p>
<pre><code class="language-php">return User::with('orders', 'invoices', 'teams')-&gt;findOrFail($id);
</code></pre>
<p>Define an explicit API Resource instead:</p>
<pre><code class="language-php">use Illuminate\Http\Resources\Json\JsonResource;

class UserSummaryResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' =&gt; $this-&gt;id,
            'name' =&gt; $this-&gt;name,
            'avatar_url' =&gt; $this-&gt;profile?-&gt;avatar_url,
            'plan' =&gt; $this-&gt;subscription_plan,
        ];
    }
}
</code></pre>
<p>A small, deliberate response contract keeps endpoint cost easy to reason about and prevents accidental coupling.</p>
<h3 id="heading-expensive-joins">Expensive Joins</h3>
<p>Joins are useful, but expensive joins across large tables can dominate your database time, especially when they sort or filter on columns that aren't indexed:</p>
<pre><code class="language-php">$rows = DB::table('orders')
    -&gt;join('users', 'users.id', '=', 'orders.user_id')
    -&gt;join('accounts', 'accounts.id', '=', 'users.account_id')
    -&gt;where('accounts.region', 'us-east')
    -&gt;where('orders.status', 'paid')
    -&gt;orderByDesc('orders.created_at')
    -&gt;limit(100)
    -&gt;get();
</code></pre>
<p>At scale, you may need to denormalize a small field, precompute a reporting table, or move analytics off the primary transactional database entirely. Do not treat denormalization as an admission of defeat. Copying a stable field like <code>account_id</code> onto <code>orders</code> can remove a costly join from a hot path. The price you pay is keeping that duplicated data consistent, which can be a worthwhile trade-off.</p>
<h2 id="heading-how-to-optimize-the-database">How to Optimize the Database</h2>
<p>When a Laravel app slows down, the database is usually the first place to look.</p>
<h3 id="heading-add-indexes-around-real-query-patterns">Add Indexes Around Real Query Patterns</h3>
<p>Start with your slow query log, database metrics, and traces rather than intuition. If the app constantly looks up active subscriptions by account, build a compound index that matches that access pattern:</p>
<pre><code class="language-php">Schema::table('subscriptions', function (Blueprint $table) {
    $table-&gt;index(['account_id', 'status', 'renews_at']);
});
</code></pre>
<p>Then write the query so it can actually use the index:</p>
<pre><code class="language-php">\(subscription = Subscription::where('account_id', \)accountId)
    -&gt;where('status', 'active')
    -&gt;where('renews_at', '&gt;=', now())
    -&gt;orderBy('renews_at')
    -&gt;first();
</code></pre>
<p>Get in the habit of running <code>EXPLAIN</code> after you add an index to confirm that the plan changed. An index the optimizer ignores is just write overhead.</p>
<h3 id="heading-use-eager-loading-deliberately">Use Eager Loading Deliberately</h3>
<p>Match eager loading to what the endpoint actually returns. For list endpoints, keep relationships shallow and constrained:</p>
<pre><code class="language-php">$projects = Project::query()
    -&gt;select(['id', 'account_id', 'name', 'updated_at'])
    -&gt;withCount('openTasks')
    -&gt;with([
        'owner:id,name',
    ])
    -&gt;where('account_id', $accountId)
    -&gt;latest('updated_at')
    -&gt;paginate(30);
</code></pre>
<p>When you only need a number, <code>withCount</code> beats loading a whole relationship to count it:</p>
<pre><code class="language-php">$teams = Team::query()
    -&gt;withCount([
        'members',
        'invitations as pending_invitations_count' =&gt; fn (\(query) =&gt; \)query-&gt;whereNull('accepted_at'),
    ])
    -&gt;paginate(25);
</code></pre>
<p>Your memory footprint stays flat, which matters much more on a list page than on a detail page.</p>
<h3 id="heading-optimize-queries-before-adding-hardware">Optimize Queries Before Adding Hardware</h3>
<p>A bigger database instance buys you time. It also hides the inefficient queries that put you there until the next traffic jump exposes them again. Before you reach for a larger machine, find your highest-cost queries. In local or staging environments, logging slow ones is easy:</p>
<pre><code class="language-php">use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

DB::listen(function (QueryExecuted $query) {
    if ($query-&gt;time &gt; 100) {
        Log::warning('Slow query detected', [
            'sql' =&gt; $query-&gt;toRawSql(),
            'time_ms' =&gt; $query-&gt;time,
        ]);
    }
});
</code></pre>
<p>Be careful doing this in production. Bindings can contain sensitive data, and verbose logging at high volume can become its own performance problem.</p>
<h3 id="heading-process-large-tables-with-chunking">Process Large Tables with Chunking</h3>
<p>Never pull an entire large table into memory for a batch job:</p>
<pre><code class="language-php">User::where('is_active', true)
    -&gt;chunkById(1000, function ($users) {
        foreach (\(users as \)user) {
            RefreshUserSearchIndex::dispatch($user-&gt;id);
        }
    });
</code></pre>
<p><code>chunkById</code> is safer than offset-based chunking when rows can change while the job runs, because it tracks the last seen ID instead of a numeric offset. For very large exports, stream the records or write them out in batches.</p>
<h3 id="heading-use-cursor-pagination-for-high-volume-feeds">Use Cursor Pagination for High-Volume Feeds</h3>
<p>Offset pagination gets slower the deeper a user scrolls, because the database still has to skip every row it's not returning. For feeds, audit logs, messages, and timelines, cursor pagination is usually the better fit:</p>
<pre><code class="language-php">$events = AuditEvent::query()
    -&gt;where('account_id', $accountId)
    -&gt;orderByDesc('id')
    -&gt;cursorPaginate(50);

return AuditEventResource::collection($events);
</code></pre>
<p>It relies on a stable, indexed ordering column and uses next/previous cursors rather than arbitrary page numbers, which is what an infinite-scroll feed usually needs.</p>
<h3 id="heading-split-reads-with-read-replicas">Split Reads with Read Replicas</h3>
<p>As read traffic grows, replicas can take load off the primary:</p>
<pre><code class="language-php">'mysql' =&gt; [
    'driver' =&gt; 'mysql',
    'read' =&gt; [
        'host' =&gt; [
            env('DB_READ_HOST', '127.0.0.1'),
        ],
    ],
    'write' =&gt; [
        'host' =&gt; [
            env('DB_WRITE_HOST', '127.0.0.1'),
        ],
    ],
    'sticky' =&gt; true,
    'database' =&gt; env('DB_DATABASE', 'laravel'),
    'username' =&gt; env('DB_USERNAME', 'root'),
    'password' =&gt; env('DB_PASSWORD', ''),
],
</code></pre>
<p>The <code>sticky</code> option keeps reads on the write connection after a write within the same request, which helps avoid some read-after-write surprises.</p>
<p>Replicas come with replication lag, and that lag matters. Don't route payment confirmations, password changes, permission checks, or anything else consistency-sensitive to a replica that might be a few seconds stale unless the business flow can genuinely tolerate seeing old data.</p>
<h2 id="heading-how-to-scale-with-redis">How to Scale with Redis</h2>
<p>Redis often does a lot in a Laravel production stack: caching, sessions, rate limiting, queues, locks, and Horizon metrics. It's fast, but it still needs thought: sensible key design, expiration policies, memory monitoring, and a real plan for invalidation.</p>
<h3 id="heading-caching">Caching</h3>
<p>Cache expensive reads that get requested often and can tolerate being slightly out of date:</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Cache;

$stats = Cache::remember(
    "accounts:{$account-&gt;id}:dashboard-stats",
    now()-&gt;addMinutes(5),
    fn () =&gt; DashboardStats::forAccount($account)-&gt;calculate()
);
</code></pre>
<p>Short time-to-live values go a surprisingly long way. A five-minute cache can wipe out thousands of duplicate queries while keeping the data fresh enough for most dashboards.</p>
<p>When the data changes after a known event, invalidate it explicitly:</p>
<pre><code class="language-php">Order::created(function (Order $order) {
    Cache::forget("accounts:{$order-&gt;account_id}:dashboard-stats");
});
</code></pre>
<p>Caching works best when your keys are predictable and your invalidation is tied to domain events rather than guesswork.</p>
<h3 id="heading-sessions">Sessions</h3>
<p>For horizontally scaled app servers, file-based sessions are a trap: the next request can land on a different server that has never seen the session. Store sessions in Redis or a database so any server can handle any request:</p>
<pre><code class="language-env">SESSION_DRIVER=redis
CACHE_STORE=redis
QUEUE_CONNECTION=redis
</code></pre>
<h3 id="heading-rate-limiting">Rate Limiting</h3>
<p>Rate limits protect you from abusive clients, runaway loops, and endpoints that get hammered:</p>
<pre><code class="language-php">use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(120)-&gt;by(
        optional(\(request-&gt;user())-&gt;id ?: \)request-&gt;ip()
    );
});
</code></pre>
<p>Expensive endpoints deserve stricter limits:</p>
<pre><code class="language-php">RateLimiter::for('exports', function (Request $request) {
    return Limit::perHour(10)-&gt;by($request-&gt;user()-&gt;id);
});
</code></pre>
<p>Let business cost drive the numbers. Login, search, export, and webhook endpoints rarely need the same limit.</p>
<h3 id="heading-queues">Queues</h3>
<p>Redis is a common queue backend because it's quick and Horizon supports it well:</p>
<pre><code class="language-env">QUEUE_CONNECTION=redis
</code></pre>
<p>Dispatch work onto named queues from the request:</p>
<pre><code class="language-php">GenerateInvoicePdf::dispatch($invoice-&gt;id)
    -&gt;onQueue('documents');
</code></pre>
<p>Split work by profile, such as <code>default</code>, <code>emails</code>, <code>webhooks</code>, <code>documents</code>, and <code>imports</code>, because each workload can need different worker counts and retry rules. Keep the names meaningful. During an incident, "the documents queue is 20 minutes behind" tells you far more than "default is slow."</p>
<h2 id="heading-how-to-use-queue-driven-architectures">How to Use Queue-Driven Architectures</h2>
<p>Queues are one of Laravel's best scaling tools. They let the app accept work quickly and process it asynchronously with controlled concurrency. They also make the system more resilient: when a third-party API goes down, jobs retry on their own instead of tying up your PHP-FPM request workers.</p>
<h3 id="heading-laravel-queues">Laravel Queues</h3>
<p>A good job is small, idempotent, and safe to retry:</p>
<pre><code class="language-php">use App\Mail\OrderReceiptMail;
use App\Models\Order;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Mail;

class SendOrderReceipt implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $backoff = 60;

    public function __construct(public int $orderId)
    {
    }

    public function handle(): void
    {
        \(order = Order::with('user')-&gt;findOrFail(\)this-&gt;orderId);

        Mail::to(\(order-&gt;user)-&gt;send(new OrderReceiptMail(\)order));
    }
}
</code></pre>
<p>Pass IDs into jobs rather than full Eloquent models. The model might change before the job runs, and serializing a whole model bloats the payload. For external APIs, add timeouts and guard against duplicate work:</p>
<pre><code class="language-php">use App\Models\Order;
use App\Services\CrmClient;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class SyncOrderToCrm implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $backoff = 60;

    public function __construct(public int $orderId)
    {
    }

    public function handle(CrmClient $crm): void
    {
        \(order = Order::findOrFail(\)this-&gt;orderId);

        if ($order-&gt;crm_synced_at) {
            return;
        }

        \(crm-&gt;upsertOrder(\)order-&gt;external_reference, [
            'total' =&gt; $order-&gt;total,
            'status' =&gt; $order-&gt;status,
        ]);

        $order-&gt;forceFill(['crm_synced_at' =&gt; now()])-&gt;save();
    }
}
</code></pre>
<p>The <code>crm_synced_at</code> check is the whole point. Jobs run more than once in real life, and idempotency is what keeps a retry from double-charging or double-syncing.</p>
<h3 id="heading-horizon">Horizon</h3>
<p>Horizon gives you visibility and control over Redis queues. A typical setup runs different supervisors for different workloads:</p>
<pre><code class="language-php">'production' =&gt; [
    'supervisor-default' =&gt; [
        'connection' =&gt; 'redis',
        'queue' =&gt; ['default', 'emails'],
        'balance' =&gt; 'auto',
        'maxProcesses' =&gt; 20,
        'tries' =&gt; 3,
    ],

    'supervisor-documents' =&gt; [
        'connection' =&gt; 'redis',
        'queue' =&gt; ['documents'],
        'balance' =&gt; 'simple',
        'maxProcesses' =&gt; 5,
        'tries' =&gt; 2,
        'timeout' =&gt; 300,
    ],
],
</code></pre>
<p>The separation matters: a long-running document job shouldn't starve a quick password-reset email.</p>
<h3 id="heading-failed-jobs-and-retries">Failed Jobs and Retries</h3>
<p>Retries only help when failures are temporary. Retrying a job that's permanently broken just burns capacity. For jobs with a business deadline, use <code>retryUntil</code>:</p>
<pre><code class="language-php">use DateTime;
use Throwable;

public function retryUntil(): DateTime
{
    return now()-&gt;addMinutes(30);
}

public function failed(Throwable $exception): void
{
    ImportBatch::whereKey($this-&gt;batchId)-&gt;update([
        'status' =&gt; 'failed',
        'failed_reason' =&gt; $exception-&gt;getMessage(),
    ]);
}
</code></pre>
<p>Use <code>failed</code> to flag the problem somewhere a human will see it. Whatever you do, don't set unlimited retries on jobs that hit a third-party service.</p>
<h3 id="heading-queue-monitoring">Queue Monitoring</h3>
<p>Track queue depth, wait time, failure rate, and processing time together. Depth alone can mislead you. When depth starts climbing, walk through it methodically: are workers keeping pace with incoming jobs? If the queue keeps growing, check how long individual jobs take. If the slow part is the database, fix the query or dial back worker concurrency. If it's an external API, add backoff or a circuit breaker. If the work is CPU-bound, scale workers or break the jobs into smaller pieces.</p>
<p>Be careful with the "scale workers" instinct, though. Adding more workers without checking the database first can make an incident worse. More workers mean more concurrent queries, more locks, and more pressure on the primary exactly when it's already struggling.</p>
<h2 id="heading-how-to-optimize-api-performance">How to Optimize API Performance</h2>
<p>APIs earn special attention because clients call them repeatedly and payloads tend to grow quietly over months.</p>
<h3 id="heading-api-resources">API Resources</h3>
<p>Resources keep your response shape intentional:</p>
<pre><code class="language-php">class OrderResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' =&gt; $this-&gt;id,
            'status' =&gt; $this-&gt;status,
            'total' =&gt; $this-&gt;total,
            'placed_at' =&gt; $this-&gt;created_at-&gt;toIso8601String(),
            'customer' =&gt; new CustomerSummaryResource($this-&gt;whenLoaded('customer')),
        ];
    }
}
</code></pre>
<p><code>whenLoaded</code> is doing real work here. It stops the resource from quietly triggering a lazy query when the relationship wasn't eager loaded:</p>
<pre><code class="language-php">$orders = Order::query()
    -&gt;with('customer:id,name')
    -&gt;where('account_id', $accountId)
    -&gt;latest()
    -&gt;paginate(50);

return OrderResource::collection($orders);
</code></pre>
<h3 id="heading-pagination">Pagination</h3>
<p>Returning unbounded collections is an easy way to create an API performance problem you won't notice until a client has a lot of data:</p>
<pre><code class="language-php">$perPage = min((int) request('per_page', 50), 100);

\(orders = Order::where('account_id', \)accountId)
    -&gt;latest()
    -&gt;paginate($perPage);
</code></pre>
<p>Cap the page size. If a client genuinely needs every record for an export, make that an async job rather than a giant synchronous response.</p>
<h3 id="heading-response-optimization">Response Optimization</h3>
<p>Stop returning fields nobody reads. On read-heavy endpoints, selecting only the columns you need cuts both database I/O and serialization cost:</p>
<pre><code class="language-php">$products = Product::query()
    -&gt;select(['id', 'name', 'slug', 'price', 'thumbnail_url'])
    -&gt;where('is_visible', true)
    -&gt;orderBy('name')
    -&gt;paginate(40);
</code></pre>
<p>It's also worth turning on compression at the web server or load balancer. JSON compresses extremely well, and that's often a small config change with a real bandwidth payoff.</p>
<h3 id="heading-rate-limiting">Rate Limiting</h3>
<p>Design API rate limits around identity and endpoint cost:</p>
<pre><code class="language-php">Route::middleware(['auth:sanctum', 'throttle:api'])
    -&gt;group(function () {
        Route::get('/orders', [OrderController::class, 'index']);
        Route::post('/exports/orders', [OrderExportController::class, 'store'])
            -&gt;middleware('throttle:exports');
    });
</code></pre>
<p>This keeps casual browsing and expensive exports under separate policies, so one heavy user can't squeeze out everyone else.</p>
<h3 id="heading-caching-api-responses">Caching API Responses</h3>
<p>Cache responses that are expensive to compute and can tolerate being a little stale:</p>
<pre><code class="language-php">public function index(Request $request)
{
    \(accountId = \)request-&gt;user()-&gt;account_id;
    \(page = \)request-&gt;integer('page', 1);

    \(cacheKey = "api:accounts:{\)accountId}:orders:v1:page:{$page}";

    return Cache::remember(\(cacheKey, now()-&gt;addSeconds(60), function () use (\)accountId) {
        return OrderResource::collection(
            Order::with('customer:id,name')
                -&gt;where('account_id', $accountId)
                -&gt;latest()
                -&gt;paginate(50)
        )-&gt;response()-&gt;getData(true);
    });
}
</code></pre>
<p>Notice the <code>v1</code> in the key. Bumping that version number lets you invalidate an entire response format at once when the shape changes. Always scope the key to the tenant or user for anything that's not truly global.</p>
<h2 id="heading-how-to-monitor-laravel-in-production">How to Monitor Laravel in Production</h2>
<p>The teams that catch problems before customers do are the ones collecting signals from everywhere: Laravel, queues, the database, Redis, the infrastructure, and external services.</p>
<p>Laravel gives you several good starting points. Horizon shows queue throughput, failed jobs, wait times, and worker balancing. Telescope surfaces request details, queries, exceptions, jobs, mail, and cache events. Your logs capture slow operations, unexpected retries, and external failures. Your metrics track latency, error rate, queue depth, job runtime, database CPU, lock waits, cache hit ratio, and Redis memory. Your alerting ties all of it back to something a customer would actually feel.</p>
<p>That last part is where teams often make mistakes. The best alerts are about symptoms, not machines being busy: p95 API latency over 800ms for 10 minutes, checkout error rate above 1%, the emails queue waiting more than 5 minutes, database CPU over 85% with slow queries rising, Redis memory over 80%, or failed payment webhooks crossing a threshold.</p>
<p>A useful mental model is this: logs tell you what happened, metrics tell you whether the system is healthy, and traces tell you where the time went. In practice, wrapping your expensive business operations in a bit of instrumentation pays off quickly:</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Log;

$startedAt = microtime(true);

\(report = \)builder-&gt;forAccount($account)-&gt;build();

Log::info('Billing report generated', [
    'account_id' =&gt; $account-&gt;id,
    'duration_ms' =&gt; (int) ((microtime(true) - $startedAt) * 1000),
    'invoice_count' =&gt; $report-&gt;invoiceCount(),
]);
</code></pre>
<p>When something is failing at 2am, a log line like that can tell you which account, import, or report is causing the pressure.</p>
<p>One more thing worth internalizing: monitor wait time, not just throughput. A queue can process thousands of jobs a minute and still be unhealthy if important jobs sit waiting too long before they start. Users feel the wait, not the throughput.</p>
<h2 id="heading-an-example-high-traffic-laravel-architecture">An Example High-Traffic Laravel Architecture</h2>
<p>A high-traffic Laravel setup generally separates four things: stateless web requests, shared cache and session storage, asynchronous workers, and database roles.</p>
<p>Users hit a load balancer, which spreads traffic across a fleet of stateless Laravel app servers. Those servers use Redis for cache, sessions, rate limits, queues, and Horizon data. Queue workers handle slow or unreliable work off to the side. A MySQL primary takes all writes and any consistency-sensitive reads, while a read replica absorbs read-heavy endpoints that can tolerate some replication lag.</p>
<p>The flow looks like this:</p>
<pre><code class="language-text">Users
  -&gt; Load balancer
  -&gt; Stateless Laravel app servers
  -&gt; Redis for cache, sessions, rate limits, queues, and Horizon data
  -&gt; Primary database for writes and consistency-sensitive reads
  -&gt; Read replica for safe read-heavy endpoints

Redis queue
  -&gt; Queue workers
  -&gt; Database, external APIs, mail providers, object storage, and other services
</code></pre>
<p>This isn't the only valid shape. PostgreSQL can stand in for MySQL, Amazon SQS can replace Redis queues, a CDN can serve static assets and cache public responses, and object storage should hold user uploads. The principle that matters is that each layer has one clear job and can be scaled or tuned on its own.</p>
<p>The flip side of stateless app servers is that anything a user needs after the request ends has to live in shared storage. Uploads, generated files, and session state shouldn't sit on a single server's local disk, or they may disappear from the user's point of view when the load balancer sends the next request somewhere else.</p>
<h2 id="heading-lessons-learned-the-hard-way">Lessons Learned the Hard Way</h2>
<h3 id="heading-1-premature-optimization">1. Premature Optimization</h3>
<p>This usually shows up as elaborate infrastructure built before the app has any real visibility into itself.</p>
<p>The practical path works better: measure, rank the bottlenecks, fix the biggest one, repeat. For most Laravel apps, the first round of scaling is mostly indexes, N+1 fixes, queue separation, and trimming payloads.</p>
<h3 id="heading-2-over-caching">2. Over-caching</h3>
<p>Caching can make a system faster and harder to reason about at the same time. One team cached an account-settings response for 30 minutes, then later folded role changes into that same response. The result was that users who had just lost access could still see features until the cache expired.</p>
<p>The fix was splitting stable account metadata away from permission-sensitive state. The lesson is to avoid caching authorization data unless you have thought carefully about invalidation.</p>
<h3 id="heading-3-missing-indexes">3. Missing Indexes</h3>
<p>These hide until a table crosses a size threshold. A query that scanned 20,000 rows in development can scan 20 million in production. Bake index review into feature work, and plan big index migrations carefully so they don't lock a hot table at the worst possible time.</p>
<h3 id="heading-4-queue-overload">4. Queue Overload</h3>
<p>Queues don't remove work, they move it. The classic failure is letting one noisy workload block everything else. A big CSV import floods the default queue, and password-reset emails get stuck behind it. Separate queues are cheap insurance against that entire class of incident.</p>
<h3 id="heading-5-large-transactions">5. Large Transactions</h3>
<p>Long transactions hold locks longer and make failures more expensive. Dispatching a job inside a transaction is especially risky because a worker can grab it before the transaction commits:</p>
<pre><code class="language-php">DB::transaction(function () use ($request) {
    $order = Order::create([...]);
    \(order-&gt;items()-&gt;createMany(\)request-&gt;items);

    GenerateInvoicePdf::dispatch($order-&gt;id);
    SyncOrderToCrm::dispatch($order-&gt;id);
});
</code></pre>
<p>Use after-commit dispatching for any job that depends on committed data:</p>
<pre><code class="language-php">GenerateInvoicePdf::dispatch($order-&gt;id)-&gt;afterCommit();
SyncOrderToCrm::dispatch($order-&gt;id)-&gt;afterCommit();
</code></pre>
<p>Keep transactions scoped to the data that genuinely has to change atomically, and nothing more.</p>
<h3 id="heading-6-treating-symptoms-as-causes">6. Treating Symptoms as Causes</h3>
<p>This is the expensive one. If latency is high because an endpoint runs 300 queries, adding app servers adds database pressure. If jobs are slow because an external API is rate-limiting you, adding workers multiplies the failures.</p>
<p>Good scaling work keeps asking the same questions: What resource is saturated? Which endpoint, job, tenant, or query is causing it? Is this work necessary during the request? Can I reduce it, defer it, cache it, or isolate it? How will I know whether the change helped?</p>
<h2 id="heading-a-pre-launch-scaling-checklist">A Pre-Launch Scaling Checklist</h2>
<p>Run through this before a big launch, a traffic campaign, or an enterprise rollout.</p>
<p><strong>Application and runtime:</strong> Cache config, routes, and views during deploy. Set <code>APP_DEBUG=false</code>. Turn on OPcache. Keep web requests short and move slow work to queues. Store uploads in object storage, not on app-server disk. Keep servers stateless. Set timeouts on every external HTTP call.</p>
<p><strong>Database:</strong> Review slow query logs first. Add indexes for your high-volume filters, joins, and ordering. Hunt for N+1 queries in controllers, resources, policies, and views. Paginate every list endpoint. Use <code>chunkById</code> or cursors for batch work. Avoid long transactions and external calls inside transactions. Confirm your backup and restore process works. Test stale-read behavior if you use replicas.</p>
<p><strong>Redis and cache:</strong> Use Redis for cache, sessions, rate limiting, and queues where it fits. Set TTLs unless you have a clear reason not to. Include tenant, user, locale, and version in keys when relevant. Watch memory and the eviction policy. Avoid caching permission-sensitive responses without careful invalidation. Guard against cache stampedes on expensive recomputation.</p>
<p><strong>Queues:</strong> Separate queues by workload. Configure Horizon supervisors per queue. Set timeouts, retries, and backoff on purpose. Make jobs idempotent where you can. Use <code>afterCommit</code> for jobs that depend on committed data. Monitor wait time, runtime, failures, and retries. Review failed jobs instead of ignoring them.</p>
<p><strong>APIs:</strong> Use Resources to control response shape. Cap <code>per_page</code>. Use cursor pagination for big feeds and logs. Cache expensive reads with safe, versioned keys and short TTLs. Apply rate limits by endpoint cost. Don't return raw Eloquent models. Compress responses at the edge.</p>
<p><strong>Observability:</strong> Track p50, p95, and p99 latency on the endpoints that matter. Track error rates by route and job class. Alert on queue wait time, not just size. Watch database CPU, connections, slow queries, and lock waits. Watch Redis memory, latency, and evictions. Log important business operations with durations and identifiers. Test your alerts before launch night because a silent alert is worse than no alert.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Laravel runs high-traffic production systems well when you design around the real costs of data, concurrency, and external dependencies. Just make sure you measure before you optimize, because guessing wastes time and tends to complicate the wrong layer.</p>
<p>Fix the database first: indexes, query shape, pagination, and eager loading usually deliver the biggest early wins. Lean on queues to keep requests fast and push slow work into controlled background workers. Cache deliberately, with clear keys, sane TTLs, and a plan for invalidation. Keep watching latency, errors, queue wait time, database health, Redis memory, and your external dependencies.</p>
<p>The best scaling work is practical and repeatable. You study the system you actually have, remove waste, isolate slow parts, and give yourself enough visibility to make the next change with confidence. Do that on a loop, and you rarely need the big rewrite.</p>
<h2 id="heading-references">References</h2>
<ul>
<li><p><a href="https://laravel.com/docs/eloquent-relationships">Laravel documentation: Eloquent relationships</a></p>
</li>
<li><p><a href="https://laravel.com/docs/queries">Laravel documentation: Database queries</a></p>
</li>
<li><p><a href="https://laravel.com/docs/cache">Laravel documentation: Cache</a></p>
</li>
<li><p><a href="https://laravel.com/docs/queues">Laravel documentation: Queues</a></p>
</li>
<li><p><a href="https://laravel.com/docs/redis">Laravel documentation: Redis</a></p>
</li>
<li><p><a href="https://laravel.com/docs/routing#rate-limiting">Laravel documentation: Rate limiting</a></p>
</li>
<li><p><a href="https://laravel.com/docs/eloquent-resources">Laravel documentation: Eloquent API resources</a></p>
</li>
<li><p><a href="https://laravel.com/docs/horizon">Laravel Horizon documentation</a></p>
</li>
<li><p><a href="https://laravel.com/docs/telescope">Laravel Telescope documentation</a></p>
</li>
<li><p><a href="https://dev.mysql.com/doc/refman/8.4/en/optimization.html">MySQL documentation: Optimization</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/">Redis documentation</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Amazon EC2 Auto Scaling? ]]>
                </title>
                <description>
                    <![CDATA[ Auto scaling is like having a smart system that keeps an eye on how many people are visiting your website. When you have a lot of people, it quickly adds more servers to handle the extra traffic. And when things quiet down, it scales back to save you... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-amazon-ec2-auto-scaling/</link>
                <guid isPermaLink="false">66b906c577c23fa04d7098f7</guid>
                
                    <category>
                        <![CDATA[ Amazon ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ec2 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Destiny Erhabor ]]>
                </dc:creator>
                <pubDate>Mon, 06 May 2024 16:32:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/christophe-hautier-902vnYeoWS4-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Auto scaling is like having a smart system that keeps an eye on how many people are visiting your website. When you have a lot of people, it quickly adds more servers to handle the extra traffic. And when things quiet down, it scales back to save you money.</p>
<p>In AWS, there are two important services that help with this: Amazon EC2 Auto Scaling and AWS Auto Scaling. Amazon EC2 Auto Scaling is specifically for managing your EC2 servers, while AWS Auto Scaling can also handle other things like DynamoDB tables and Amazon Aurora databases.</p>
<p>In this article, we'll dive deeper into how Amazon EC2 Auto Scaling works and how you can use it to keep your website running smoothly without overspending on servers.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Have an AWS account</li>
<li>Basic understanding of EC2 instance</li>
</ul>
<h2 id="heading-table-of-content">Table of Content</h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-example-use-case">Example Use case</a></li>
<li><a class="post-section-overview" href="#advantages-of-amazon-ec2-auto-scaling">Advantages of Amazon EC2 Auto Scaling</a></li>
<li><a class="post-section-overview" href="#heading-components-of-ec2-auto-scaling">Components of EC2 Auto Scaling</a></li>
<li><a class="post-section-overview" href="#what-is-launch-configurations-vs-launch-templates">What is Launch Configurations vs Launch Templates</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-launch-template">How to create a launch template</a></li>
<li><a class="post-section-overview" href="#heading-what-are-auto-scaling-groups-asgs">What are Auto Scaling Groups (ASGs)</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-an-auto-scaling-group">How to create an Auto Scaling Group</a></li>
<li><a class="post-section-overview" href="#heading-what-are-scaling-policies">What are Scaling Policies</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-example-use-case">Example Use Case</h2>
<h3 id="heading-scenario">Scenario:</h3>
<p>Imagine running a website that sells trendy clothes. Sometimes, lots of people visit your site at once, especially during lunch breaks or evenings. Other times, it's pretty quiet.</p>
<h3 id="heading-problem">Problem:</h3>
<p>You need enough servers to handle busy times, but you don't want to waste money on too many servers when it's quiet.</p>
<h3 id="heading-solution-with-amazon-ec2-auto-scaling">Solution with Amazon EC2 Auto Scaling:</h3>
<p><strong>Traffic Analysis</strong>: Look at when people visit your site the most. This helps you understand when you need more servers.</p>
<p><strong>Set Rules</strong>: Decide when to add or remove servers automatically. For example, you might say, "If more than 70% of our servers are busy for more than 5 minutes, add one more server."</p>
<p><strong>Adjust Server Numbers</strong>: Tell Amazon the smallest and biggest number of servers you need. You can also say how many you'd like on average. For instance, you might say, "Keep at least 2 servers running all the time. But if it's busy, go up to 10 servers. And usually, we need around 4."</p>
<p><strong>Load Balancing</strong>: Make sure all servers get some work. Use a load balancer to send visitors to the least busy server. This keeps everything running smoothly even if you have many servers.</p>
<p><strong>Test and Watch</strong>: Before trusting everything, test to see if it works as planned. Keep an eye on it afterward to make sure it's doing its job right.</p>
<p><strong>Save Money</strong>: With auto scaling, you don't pay for servers you're not using. When traffic is low, it reduces the number of servers, saving you money. When traffic picks up, it adds more servers, so your site stays fast.</p>
<h2 id="heading-advantages-of-using-amazon-ec2-auto-scaling">Advantages of Using Amazon EC2 Auto Scaling</h2>
<p><strong>Cost Optimization</strong>: EC2 Auto Scaling helps optimize costs by automatically adjusting the number of EC2 instances based on demand. During periods of low traffic, it reduces the number of instances, saving on operational costs. Conversely, during high traffic, it scales up to ensure optimal performance without over-provisioning resources.</p>
<p><strong>Improved Availability</strong>: By automatically distributing incoming traffic across multiple instances and fault tolerance of your application. If any instance fails/is unhealthy, the Auto Scaling group replaces it with a new one, ensuring minimal disruption to your services.</p>
<p><strong>Scalability</strong>: EC2 Auto Scaling allows your application to handle sudden spikes in traffic or increased workload without manual intervention. </p>
<p><strong>Enhanced Performance</strong>: With EC2 Auto Scaling, you can maintain consistent performance levels even during peak usage periods. By automatically adding more instances when traffic increases, it prevents performance degradation and ensures a smooth user experience.</p>
<p><strong>Ease of Management</strong>: EC2 Auto Scaling simplifies the management of your EC2 fleet by automating instance provisioning, scaling, and monitoring.</p>
<p><strong>Integration with AWS Services</strong>: EC2 Auto Scaling integrates seamlessly with other AWS services such as Elastic Load Balancing (ELB) and Amazon CloudWatch.</p>
<p><strong>Highly Customizable</strong>: EC2 Auto Scaling offers flexibility and customization options to meet the specific needs of your application.</p>
<h2 id="heading-components-of-ec2-auto-scaling">Components of EC2 Auto Scaling</h2>
<p>Let's get a better understanding on how the Auto Scaling works through its different components. </p>
<p>There are two distinct steps to configuration. The first step is the creation of a launch configuration or launch template. The second is the creation of an Auto Scaling group.</p>
<h2 id="heading-launch-configurations-and-launch-templates">Launch Configurations and Launch Templates</h2>
<p>Launch configurations or launch templates define the configuration settings for the EC2 instances that will be launched by the Auto Scaling group. </p>
<p>These settings include the AMI (Amazon Machine Image), instance type, security groups, key pair, and user data. </p>
<p>Launch configurations are older and being phased out in favor of launch templates, which offer more features and flexibility.</p>
<h3 id="heading-how-to-create-a-launch-template">How to Create a Launch Template</h3>
<p>First, navigate to EC2 Instance page</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/launch-template-1.png" alt="AWS instance page" width="600" height="400" loading="lazy">
<em>AWS instance page</em></p>
<p>Select the Launch Templates under the instances and click the create button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/launch-template-2.png" alt="AWS launch templates" width="600" height="400" loading="lazy">
<em>AWS launch templates</em></p>
<p>The following screen should show up, almost similar to launching an <code>EC2 instance</code>. You can fill the required information accordingly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/screencapture-us-east-1-console-aws-amazon-ec2-home-2024-05-03-22_52_38-1.png" alt="Create AWS launch templates" width="600" height="400" loading="lazy">
<em>Create AWS launch templates</em></p>
<p>After configuration, click the "Create Launch" template button and allow it to create, then view your newly created launch template with default and latest version as 1. You can use this launch template to create another launch template and specify a different version for it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/launch-template-3-1.png" alt="View AWS launch templates" width="600" height="400" loading="lazy">
<em>View AWS launch templates</em></p>
<p>Auto scaling requires either a launch template or launch configuration to identify the instance it's launching and its configurations.</p>
<h2 id="heading-what-are-auto-scaling-groups-asgs">What are Auto Scaling Groups (ASGs)</h2>
<p>Auto Scaling groups are the core component of EC2 Auto Scaling. They define the group of EC2 instances that are managed together and share the same scaling policies. ASGs ensure that your application can automatically scale out (add instances) or scale in (remove instances) based on demand.</p>
<h3 id="heading-how-to-create-an-auto-scaling-group">How to create an Auto Scaling Group</h3>
<p>First, navigate to EC2 Instance page and under the Auto Scaling group, select and click the create button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/asg-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>creating an Auto Scaling group</em></p>
<p>On the create screen, the first step is to give your ASG a <code>Name</code> and then select your <code>launch template</code> created from the steps above. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/asg-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>creating a launch template</em></p>
<p>The next step requires you to select or override an instance launch template. You also select a VPC and subnet.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/asg-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>selecting instance launch template</em></p>
<p>The next step is to configure advanced options such as adding a load balancer and monitoring. You can attach or add a new load balancer but for this article we will skip this part.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/asg-4.png" alt="Image" width="600" height="400" loading="lazy">
<em>configuring advanced options</em></p>
<p>Next, configure the group size and scaling. Here, we want to configure the scale between minimum of 2 and maximum of 5. Also, set the metrics type to track the CPU utilization (set to 50 – you can increase to 70 or more) for scaling.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/screencapture-us-east-1-console-aws-amazon-ec2-home-2024-05-03-23_41_58.png" alt="Image" width="600" height="400" loading="lazy">
<em>configuring group size and scaling</em></p>
<p>Next two steps are for adding notifications (you will need to create an SNS service for this) and tags. In this article, we are going to skip these and create our ASG.</p>
<p>Create and view the ASG created. From its <strong>activity</strong> folder, you can see those two instances launched. Also, from the instances page, you should see two EC2 instances. This is because we set our desired state to 2.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/asg-5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Auto Scaling groups</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/asg-6.png" alt="Image" width="600" height="400" loading="lazy">
<em>Auto Scaling groups</em></p>
<h2 id="heading-what-are-scaling-policies">What are Scaling Policies?</h2>
<p>Scaling policies define the rules that govern how the Auto Scaling group scales in or out in response to changing demand. There are four types of scaling policies:</p>
<p>Let's break down each type of scaling with examples:</p>
<h3 id="heading-manual-scaling">Manual Scaling</h3>
<p>Manual scaling involves adjusting the number of EC2 instances in your Auto Scaling group manually, without relying on automated triggers or policies. This type of scaling is typically done in response to predictable events or planned changes in demand.</p>
<p><strong>Example</strong>: Assuming you run an e-commerce website, and you know that there will be a flash sale event that will attract a large number of visitors. To handle the expected surge in traffic, you can manually increase the desired capacity of your Auto Scaling group before the event, adding more EC2 instances in advance of the anticipated demand spike. After the event is over, you can manually reduce the desired capacity back to its normal level.</p>
<h5 id="heading-pros">Pros:</h5>
<ul>
<li><strong>Control</strong>: Offers direct control over the number of EC2 instances in the Auto Scaling group.</li>
<li><strong>Flexibility</strong>: Allows for immediate adjustments based on specific requirements or events.</li>
</ul>
<h5 id="heading-cons">Cons:</h5>
<ul>
<li><strong>Manual Intervention</strong>: Relies on human intervention, which can be time-consuming and prone to errors.</li>
<li><strong>Lack of Automation</strong>: Not suitable for handling dynamic or unpredictable fluctuations in demand efficiently.</li>
</ul>
<h3 id="heading-schedule-scaling">Schedule Scaling</h3>
<p>Schedule scaling involves defining predefined schedules to adjust the number of EC2 instances in your Auto Scaling group automatically. This type of scaling is useful for applications with predictable traffic patterns, such as daily or weekly fluctuations in demand.</p>
<p><strong>Example</strong>: Consider a video streaming service that experiences peak traffic during evenings and weekends. You can set up a schedule scaling policy to increase the desired capacity of your Auto Scaling group every evening at 6 PM and decrease it every morning at 6 AM. This ensures that you have enough capacity to handle peak demand periods without overspending on resources during off-peak hours.</p>
<h5 id="heading-pros-1">Pros:</h5>
<ul>
<li><strong>Predictability</strong>: Well-suited for applications with predictable traffic patterns, such as daily or weekly fluctuations.</li>
<li><strong>Cost Optimization</strong>: Helps optimize costs by aligning resources with expected demand patterns.</li>
</ul>
<h5 id="heading-cons-1">Cons:</h5>
<ul>
<li><strong>Limited Adaptability</strong>: May not be responsive to sudden changes in demand or unexpected traffic spikes.</li>
<li><strong>Requires Planning</strong>: Requires upfront planning and configuration of schedules based on historical data or business insights.</li>
</ul>
<h3 id="heading-dynamic-scaling">Dynamic Scaling</h3>
<p>Dynamic scaling adjusts the number of EC2 instances in your Auto Scaling group automatically based on real-time metrics, such as CPU utilization, network traffic, or other application-specific metrics. This type of scaling is responsive to fluctuations in demand and helps ensure optimal performance and cost-effectiveness.</p>
<h5 id="heading-types">Types:</h5>
<ul>
<li><strong>Step Scaling</strong>: This policy scales the number of instances based on a series of scaling adjustments defined by step adjustments and associated metrics thresholds. </li>
<li><strong>Target Tracking</strong>: This policy automatically adjusts the number of instances to maintain a specified target metric, such as average CPU utilization or network traffic.</li>
</ul>
<p>When adding instances to the ASG, it will take a few minutes for them to come online and handle load. This is why a cooldown policy has to be set.</p>
<p><strong>Scaling Cooldowns:</strong> Scaling cooldowns help prevent rapid fluctuations in the number of instances by imposing a cooldown period after a scaling activity is triggered. During this cooldown period, EC2 Auto Scaling will not launch or terminate additional instances, allowing time for the newly launched instances to stabilize or for the impact of terminated instances to be observed.</p>
<p><strong>Example</strong>: Let's say you operate a ride-sharing platform where demand can vary unpredictably throughout the day. With dynamic scaling, you can configure Auto Scaling policies to add more EC2 instances when the number of ride requests exceeds a certain threshold, and remove instances when demand decreases. This allows you to dynamically adapt to changing traffic patterns in real-time, ensuring a seamless experience for both drivers and passengers.</p>
<h5 id="heading-pros-2">Pros:</h5>
<ul>
<li><strong>Real-Time Responsiveness</strong>: Adjusts resource allocation dynamically in response to actual demand, ensuring optimal performance.</li>
<li><strong>Cost Efficiency</strong>: Automatically scales resources up or down, helping to optimize costs by only using what is needed.</li>
</ul>
<h5 id="heading-cons-2">Cons:</h5>
<ul>
<li><strong>Potential Over-Provisioning</strong>: May lead to over-provisioning during sudden spikes in demand if scaling policies are not properly configured.</li>
<li><strong>Complexity</strong>: Requires careful configuration of scaling policies and monitoring of metrics to ensure effective scaling behavior.</li>
</ul>
<h3 id="heading-predictive-scaling">Predictive Scaling</h3>
<p>Predictive scaling uses machine learning algorithms and historical data to forecast future demand and proactively adjust the number of EC2 instances in your Auto Scaling group. This type of scaling helps prevent under-provisioning or over-provisioning of resources by anticipating changes in demand before they occur.</p>
<p><strong>Example</strong>: Suppose you operate a weather forecasting application that experiences increased demand during severe weather events. By analyzing historical data on weather patterns and user behavior, predictive scaling can predict when a surge in traffic is likely to occur and automatically scale up the capacity of your Auto Scaling group ahead of time. This ensures that your application remains responsive and available during peak usage periods without unnecessary resource waste.</p>
<h5 id="heading-pros-3">Pros:</h5>
<ul>
<li><strong>Proactive Optimization</strong>: Anticipates future demand based on historical data, ensuring resources are provisioned ahead of time.</li>
<li><strong>Improved Cost Management</strong>: Helps prevent under-provisioning and over-provisioning, optimizing resource usage and costs.</li>
</ul>
<h4 id="heading-cons-3">Cons:</h4>
<ul>
<li><strong>Data Dependence</strong>: Relies on accurate historical data and effective machine learning models for accurate predictions.</li>
<li><strong>Initial Setup</strong>: Requires initial setup and configuration of predictive scaling models, which can be complex and resource-intensive.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, Amazon EC2 Auto Scaling offers a range of strategies to effectively manage and optimize the performance of applications running on EC2 instances.</p>
<p>Whether it's through manual adjustments, scheduled scaling, dynamic responses to real-time metrics, or proactive measures based on predictive analytics, EC2 Auto Scaling provides the flexibility and automation needed to ensure that resources are aligned with demand. </p>
<p>By leveraging these scaling capabilities, businesses can enhance availability, improve cost efficiency, and deliver a seamless user experience, ultimately driving better outcomes for their applications and customers on the AWS platform.</p>
<p>As always, I hope you enjoyed the article and learned something new. If you want, you can also follow me on <a target="_blank" href="https://www.linkedin.com/in/destiny-erhabor">LinkedIn</a> or <a target="_blank" href="https://twitter.com/caesar_sage">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Best Practices for Scaling Your Node.js REST APIs ]]>
                </title>
                <description>
                    <![CDATA[ By Rishabh Rawat There is more to scalability than using cluster mode. In this tutorial, we'll explore 10 ways you can make your Node.js API ready to scale. When working on a project, we often get a few real nuggets here and there on how to do someth... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/nodejs-api-best-practices-for-scaling/</link>
                <guid isPermaLink="false">66d460c747a8245f78752ab5</guid>
                
                    <category>
                        <![CDATA[ best practices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scalability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 15 Sep 2022 16:59:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/Node.js-Best-Practices-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Rishabh Rawat</p>
<p>There is more to scalability than using cluster mode. In this tutorial, we'll explore 10 ways you can make your Node.js API ready to scale.</p>
<p>When working on a project, we often get a few real nuggets here and there on how to do something in a better way. We get to learn retrospectively, and then we're fully prepared to apply it next time around. </p>
<p>But how often does that actually work out? I don't even remember what I did yesterday sometimes. So I wrote this article.</p>
<p>This is my attempt to document some of the best Node.js scalability practices that are not talked about as often.</p>
<p>You can adopt these practices at any stage in your Node.js project. It doesn't have to be a last-minute patch.</p>
<p>With that said, here's what we will cover in this article:</p>
<ol>
<li>🚦Use throttling</li>
<li>🐢 Optimize your database queries</li>
<li>䷪ Fail fast with circuit breaker</li>
<li>🔍 Log your checkpoints</li>
<li>🌠 Use Kafka over HTTP requests</li>
<li>🪝 Look out for memory leaks</li>
<li>🐇 Use caching</li>
<li>🎏 Use connection pooling</li>
<li>🕋 Seamless scale-ups</li>
<li>💎 OpenAPI compliant documentation</li>
</ol>
<h2 id="heading-use-throttling">Use Throttling</h2>
<p>Throttling allows you to limit access to your services to prevent them from being overwhelmed by too many requests. It has some clear benefits – you can safeguard your application whether it's a large burst of users or a <a target="_blank" href="https://en.wikipedia.org/wiki/Denial-of-service_attack">denial-of-service attack</a>.</p>
<p>The common place to implement a throttling mechanism is where the rate of input and output don't match. Particularly, when there is more inbound traffic than what a service can (or wants to) handle.</p>
<p>Let’s understand with a visualization.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/throttling-nodejs-best-practices-nodejs.drawio--5-.png" alt="throttling between services" width="600" height="400" loading="lazy">
<em>Your application is throttling requests from News Feed Service</em></p>
<p>There's throttling at the first junction point between your application and the News Feed Service:</p>
<ol>
<li><em>News Feed Service (NFS)</em> subscribes to your application for sending notifications.</li>
<li>It sends 1000 requests to your application every second.</li>
<li>Your application only handles 500 requests/sec based on the billing plan NFS subscribed to.</li>
<li>Notifications are sent for the first 500 requests.</li>
</ol>
<p>Now it is very important to note that all the requests by NFS that exceed the quota of 500 requests/sec should fail and have to be retried by the NFS.</p>
<p><strong>Why reject the extra requests when you can queue them?</strong> There are a couple of reasons:</p>
<ol>
<li>Accepting all the requests will cause your application to start accumulating them. It will become a single point of failure (by RAM/disk exhaustion) for all the clients subscribed to your application, including NFS.</li>
<li>You should not accept requests that are greater than the scope of the subscription plan of your clients (in this case, NFS).</li>
</ol>
<p>For application level rate limiting, you can use <a target="_blank" href="https://www.npmjs.com/package/express-rate-limit">express-rate-limit</a> middleware for your Express.js API. For network level throttling, you can find solutions like <a target="_blank" href="https://aws.amazon.com/waf/">WAF</a>.</p>
<p>If you are using a pub-sub mechanism, you can throttle your consumers or subscribers as well. For instance, you can choose to consume only limited bytes of data when consuming a Kafka topic by setting the <a target="_blank" href="https://kafka.js.org/docs/consuming#a-name-options-a-options">maxBytes option</a>.</p>
<h2 id="heading-optimize-your-database-queries">Optimize Your Database Queries</h2>
<p>There will be times when querying the database is the only choice. You might have not cached the data or it could be stale.</p>
<p>When that happens, make sure your database is prepared for it. Having enough RAM and disk IOPS is a good first step.</p>
<p>Secondly, optimize your queries as much as possible. For starters, here are a couple of things that will set you on the right path:</p>
<ol>
<li>Try to use indexed fields when querying. Don't over-index your tables in hopes of the best performance. <a target="_blank" href="https://www.mongodb.com/blog/post/performance-best-practices-indexing#:~:text=Eliminate%20Unnecessary%20Indexes">Indexes have their cost</a>.</li>
<li>For deletes, stick to soft deletes. If permanent deletion is necessary, delay it. (<a target="_blank" href="https://httpie.io/blog/stardust">interesting story</a>)</li>
<li>When reading data, only fetch the required fields using projection. If possible, strip away the unnecessary metadata and methods (for example, Mongoose has <a target="_blank" href="https://mongoosejs.com/docs/tutorials/lean.html">lean</a>).</li>
<li>Try to decouple database performance from the user experience. If CRUD on the database can happen in the background (that is, non-blocking), do it. Don't leave the user waiting.</li>
<li>Directly update the desired fields using update queries. Don't fetch the document, update the field, and save the whole document back to the database. It has network and database overhead.</li>
</ol>
<h2 id="heading-fail-fast-with-a-circuit-breaker">Fail Fast with a Circuit Breaker</h2>
<p>Imagine you get burst traffic on your Node.js application, and one of the external services required to fulfill the requests is down. Would you want to keep hitting the dead end for every request thereafter? Definitely Not. We don't want to waste time and resources on the requests destined to fail.</p>
<p>This is the whole idea of a circuit breaker. <strong>Fail early.</strong> <strong>Fail fast</strong>.</p>
<p>For example, if 50 out of 100 requests fail, it doesn't allow any more requests to that external service for the next X seconds. It prevents firing requests that are bound to fail.</p>
<p>Once the circuit resets, it allows requests to go through. If they fail again, the circuit breaks and the cycle repeats.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/circuit-breaker-nodejs-best-practices-for-scale.drawio--1-.png" alt="Node.js Opposum circuit breaker states" width="600" height="400" loading="lazy">
<em>Node.js Opposum circuit breaker states</em></p>
<p>To learn more about how to add a circuit breaker to your Node.js application, check out <a target="_blank" href="https://github.com/nodeshift/opossum">Opposum</a>. You can read more on circuit breakers <a target="_blank" href="https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern">here</a>.</p>
<h2 id="heading-log-your-checkpoints">Log Your Checkpoints</h2>
<p>A good logging setup allows you to spot errors quickly. You can create visualizations to understand your app's behavior, set up alerts, and debug efficiently.</p>
<p>You can check out the <a target="_blank" href="https://www.elastic.co/what-is/elk-stack">ELK stack</a> for setting up a good logging and alerting pipeline.</p>
<p>While logging is an essential tool, it is very easy to overdo it. If you start logging everything, you can end up exhausting your disk IOPS causing your application to suffer.</p>
<p><strong>As a good rule of thumb is to only log checkpoints.</strong></p>
<p>Checkpoints can be:</p>
<ol>
<li>Requests, as they enter the main control flow in your application and after they are validated and sanitized.</li>
<li>Request and response when interacting with an external service/SDK/API.</li>
<li>The final response to that request.</li>
<li>Helpful error messages for your catch handlers (with sane defaults for error messages).</li>
</ol>
<p><strong>PS:</strong> If a request goes through multiple services during the lifecycle, you can pass along a unique ID in the logs to capture a particular request across all the services.</p>
<h2 id="heading-use-kafka-over-http-requests">Use Kafka Over HTTP Requests</h2>
<p>While HTTP has its use-cases, it is easy to overdo it. Avoid using HTTP requests where it is not necessary.</p>
<p>Let's understand this with the help of an example.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/kafka-over-http-nodejs-best-practices-for-scale.drawio.png" alt="Overview of Kafka pub-sub using topics" width="600" height="400" loading="lazy">
<em>Overview of Kafka pub-sub using topics</em></p>
<p>Let's say you are building a product like Amazon and there are two services:</p>
<ol>
<li>Vendor service</li>
<li>Inventory service</li>
</ol>
<p>Whenever you receive new stock from the vendor service, you push the stock details to a <a target="_blank" href="https://kafka.apache.org/intro">Kafka</a> topic. The inventory service listens to that topic and updates the database acknowledging the fresh restock.</p>
<p>To note that, you push the new stock data into the pipeline and move on. It is consumed by the inventory service at its own pace. <strong>Kafka allows you to decouple services.</strong></p>
<p>Now, what happens if your inventory service goes down? It is not straightforward with HTTP requests. Whereas in the case of Kafka, you can replay the intended messages (for example using <a target="_blank" href="https://github.com/edenhill/kcat">kcat</a>). <strong>With Kafka, you do not lose data after consumption.</strong></p>
<p>When an item comes back in stock, you might want to send out notifications to the users who wishlisted it. To do that, your notification service can listen to the same topic as the inventory service. This way, <strong>a single message bus is consumed at various places without</strong> <strong>HTTP overhead</strong>.</p>
<p>The <a target="_blank" href="https://kafka.js.org/docs/getting-started">Getting Started page</a> of KafkaJS shares the exact snippet to get you started with a basic setup in your Node.js application. I’d highly recommend checking it out, as there's a lot to explore.</p>
<h2 id="heading-look-out-for-memory-leaks">Look Out for Memory Leaks</h2>
<p>If you don't write memory-safe code and don't <a target="_blank" href="https://nodejs.org/en/docs/guides/simple-profiling/">profile</a> your application often, you may end up with a crashed server.</p>
<p>You do not want your profiling results to look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Image-Pasted-at-2022-9-6-14-58.png" alt="setTimeout retaining 98% memory after execution is over" width="600" height="400" loading="lazy">
<em>setTimeout retaining 98% memory after execution is over</em></p>
<p>For starters, I would recommend the following:</p>
<ol>
<li>Run your Node.js API with the <code>--inspect</code> flag.</li>
<li>Open <code>chrome://inspect/#devices</code> in your Chrome browser.</li>
<li>Click inspect &gt; <code>Memory</code> tab &gt; <code>Allocation instrumentation on timeline</code>.</li>
<li>Perform some operations on your app. You can use apache bench on macOS to fire off multiple requests. Run <code>curl cheat.sh/ab</code> in your terminal to learn how to use it.</li>
<li>Stop the recording and analyze the memory retainers.</li>
</ol>
<p>If you find any large blocks of retained memory, try to minimize it. There are a lot of resources on this topic. Start by googling "how to prevent memory leaks in Node.js".</p>
<p>Profiling your Node.js application and looking for memory utilization patterns should be regular practice. Let's make "Profiling Driven Refactor" (PDR) a thing?</p>
<h2 id="heading-use-caching-to-prevent-excessive-database-lookup">Use Caching to Prevent Excessive Database Lookup</h2>
<p>The goal is to not hit the database for every request your application gets. Storing the results in cache decreases the load on your database and boosts performance.</p>
<p>There are two strategies when working with caching.</p>
<p><strong>Write through</strong> caching makes sure the data is inserted into the database and the cache when a write operation happens. This keeps the cache relevant and leads to better performance. Downsides? Expensive cache as you store infrequently used data to the cache as well.</p>
<p>Whereas in <strong>Lazy loading</strong>, the data is only written to the cache when it is first read. The first request serves the data from the database but the consequent requests use the cache. It has a smaller cost but increased response time for the first request.</p>
<p>To decide the TTL (or Time To Live) for the cached data, ask yourself:</p>
<ol>
<li>How often the underlying data changes?</li>
<li>What is the risk of returning outdated data to the end user?</li>
</ol>
<p>If it is okay, <strong>having more TTL will help you with a better performance</strong>.</p>
<p>Importantly, <strong>add a slight delta to your TTLs</strong>. If your application receives a large burst of traffic and all of your cached data expires at once, it can lead to unbearable load on the database, affecting user experience.</p>
<pre><code>final TTL = estimated value <span class="hljs-keyword">of</span> TTL + small random delta
</code></pre><p>There are a number of policies to perform <a target="_blank" href="https://redis.io/docs/manual/eviction/">cache eviction</a>. But leaving it on default settings is a valid and accepted approach.</p>
<h2 id="heading-use-connection-pooling">Use Connection Pooling</h2>
<p>Opening a standalone connection to the database is costly. It involves TCP handshake, SSL, authentication and authorization checks, and so on.</p>
<p>Instead, you can leverage connection pooling.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/conection-pooling-nodejs-best-practices-for-scale.drawio--1-.png" alt="Database connection pool" width="600" height="400" loading="lazy">
<em>Database connection pool</em></p>
<p>A connection pool holds multiple connections at any given time. Whenever you need it, the pool manager assigns any available/idle connection. You get to skip the cold start phase of a brand new connection.</p>
<p>Why not max out the number of connections in the pool, then? Because it highly depends on your hardware resources. If you ignore it, performance can take a massive toll.</p>
<p>The more the connections, the less RAM each connection has, and the slower the queries that leverage RAM (for example sort). The same principle applies to your disk and CPU. With every new connection, you are spreading your resources thin across the connections.</p>
<p>You can tweak the number of connections till it matches your needs. For starters, you can get an estimate on the size you need from <a target="_blank" href="https://www.cybertec-postgresql.com/en/tuning-max_connections-in-postgresql/">here</a>.</p>
<p>Read about the MongoDB connection pool <a target="_blank" href="https://www.mongodb.com/docs/manual/administration/connection-pool-overview/">here</a>. For PostgreSQL, you can use the <code>node-postgres</code> package. It has built-in support for <a target="_blank" href="https://node-postgres.com/features/pooling">connection pooling</a>. </p>
<h2 id="heading-seamless-scale-ups">Seamless Scale-ups</h2>
<p>When your application's user base is starting to grow and you have already hit the ceiling on vertical scaling, what do you do? You scale horizontally.</p>
<blockquote>
<p>Vertical scaling means increasing the resources of a node (CPU, memory, etc.) whereas horizontal scaling involves adding more nodes to balance out the load on each node.</p>
</blockquote>
<p>If you’re using AWS, you can leverage Automatic Scaling Groups (ASG) which horizontally scales the number of servers based on a predefined rule (for example when CPU utilization is more than 50%).</p>
<p>You can even pre-schedule the scale up and scale down using <a target="_blank" href="https://docs.aws.amazon.com/autoscaling/application/userguide/examples-scheduled-actions.html">scheduled actions</a> in case of predictable traffic patterns (for example during the World Cup finals for a streaming service).</p>
<p>Once you have your ASG in place, adding a load balancer in front will make sure the traffic is routed to all the instances based on a chosen strategy (like <a target="_blank" href="https://en.wikipedia.org/wiki/Round-robin_scheduling">round robin</a>, for example).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/alb-nodejs-best-practices-for-scale.drawio--2-.png" alt="Load balancing multiple targets based on predefined rules" width="600" height="400" loading="lazy">
<em>Load balancing multiple targets based on predefined rules</em></p>
<p><strong>PS:</strong> It is always a good idea to estimate the requests your single server can handle (CPU, memory, disk, and so on) and allocate at least 30% more.</p>
<h2 id="heading-openapi-compliant-documentation">OpenAPI Compliant Documentation</h2>
<p>It might not directly affect your ability to scale a Node.js application, but I had to include this in the list. If you've ever done an API integration, you know it.</p>
<p>It is crucial to know everything about the API before you take a single step forward. It makes it easy to integrate, iterate, and reason about the design. Not to mention the gains in the speed of development.</p>
<p>Make sure to <strong>create OpenAPI Specification (OAS) for your Node.js API</strong>.</p>
<p>It allows you to create API documentation in an industry-standard manner. It acts as a single source of truth. When defined properly, it makes interacting with the API much more productive.</p>
<p>I have created and published a sample API documentation <a target="_blank" href="https://app.swaggerhub.com/apis/Rishabh570/test-API/0.1">here</a>. You can even inspect any API using the <a target="_blank" href="https://swagger.io/tools/swagger-inspector/">swagger inspector</a>.</p>
<p>You can find all of your API documentations and create new ones from the <a target="_blank" href="https://app.swaggerhub.com/home">Swagger Hub dashboard</a>.</p>
<h2 id="heading-now-you-go-captain">Now you go, captain!</h2>
<p>We have looked at ten lesser-known best practices to prepare Node.js for scale and how you can take your first steps with each one of them.</p>
<p>Now it is your turn to go through the checklist and explore the ones you find lacking in your Node.js application.</p>
<p></p>

  <div data-style="clean">
    <ul></ul>
    <div data-element="fields" data-stacked="false" class="seva-fields formkit-fields">
      <div class="formkit-field"></div>
      
        <div class="formkit-spinner">
          <div></div>
          <div></div>
          <div></div>
        </div><span>Grab your checklist ✨</span>
      
    </div>
  </div>
  


<p>I hope you found this helpful and it gave you some pointers to move forward in your scalability endeavor. This is not an exhaustive list of all the best practices – I have just included the ones I found are not talked about as much based on my experience.</p>
<p>Feel free to reach out on <a target="_blank" href="https://twitter.com/Rishabh570">Twitter</a>. I'd love to hear your feedback and suggestions on other best practices that you are using.</p>
<p>Liked the article? <a target="_blank" href="https://rrawat.com/newsletter">Get the improvement pills on backend web development</a> 💌.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Horizontal vs. Vertical Scaling – How to Scale a Database ]]>
                </title>
                <description>
                    <![CDATA[ Data Scalability Data scalability refers to the ability of a database to manipulate changing demands by adding and removing data. In this way, the database grows at the same pace as the software.  Via scaling, the database can expand or contract the ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/horizontal-vs-vertical-scaling-in-database/</link>
                <guid isPermaLink="false">66c4c63e034a1005e6c59644</guid>
                
                    <category>
                        <![CDATA[ vertical ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Horizontal ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scalability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sophia Iroegbu ]]>
                </dc:creator>
                <pubDate>Thu, 09 Jun 2022 15:26:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/06/Tech-Blog-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-data-scalability">Data Scalability</h2>
<p>Data scalability refers to the ability of a database to manipulate changing demands by adding and removing data. In this way, the database grows at the same pace as the software. </p>
<p>Via scaling, the database can expand or contract the capacity of the system's resources to support the application's frequently changing usage.</p>
<p><strong>There are two ways a database can be scaled:</strong></p>
<ul>
<li>Horizontal scaling (scale-out)</li>
<li>Vertical scaling (scale-up)</li>
</ul>
<p>In this article, we'll look at both methods of scaling and discuss the advantages and disadvantages of each to help you choose.</p>
<h2 id="heading-horizontal-scaling">Horizontal Scaling</h2>
<p>This scaling approach adds more database nodes to handle the increased workload. It decreases the load on the server rather than expanding the individual servers. </p>
<p>When you need more capacity, you can add more servers to the cluster. Another name for this scaling method is <strong>Scaling out</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/scaling-out.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-advantages-of-horizontal-scaling">Advantages of Horizontal Scaling:</h3>
<ul>
<li>It is easy to upgrade</li>
<li>It is simple to implement and costs less</li>
<li>It offers flexible, scalable tools</li>
<li>It has limitless scaling with unlimited addition of server instances</li>
<li>Upgrading a horizontally scaled database is easy – just add a node to the server</li>
</ul>
<h3 id="heading-disadvantages-of-horizontal-scaling">Disadvantages of Horizontal Scaling:</h3>
<ul>
<li>Any bugs in the code will become more complex to debug and understand</li>
<li>The licensing fee is expensive as you will have more nodes that are licensed</li>
<li>The cost of the data center will increase significantly because of the increased space, cooling, and power required</li>
</ul>
<h3 id="heading-when-to-use-horizontal-scaling">When to use horizontal scaling:</h3>
<p>If you are dealing with more than a thousand users, it is best to use this scaling system because when the servers receive multiple user requests, everything will scale well.</p>
<p>It will also not crash because there are multiple servers.</p>
<h2 id="heading-vertical-scaling">Vertical Scaling</h2>
<p>The vertical scaling approach increases the capacity of a single machine by increasing the resources in the same logical server. This involves adding resources like memory, storage, and processing power to existing software, enhancing its performance. </p>
<p>This is the traditional method of scaling a database. Another name for this approach is <strong>Scale-up</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/02vertical-scaling-software-scalability.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-advantages-of-vertical-scaling">Advantages of Vertical Scaling:</h3>
<ul>
<li>The cost of the data center for the space, cooling, and power will be smaller</li>
<li>It is a cost-efficient software</li>
<li>It is easy to use and implement – the administrator can easily manage and maintain the software</li>
<li>The resources for this approach are flexible</li>
</ul>
<h3 id="heading-disadvantages-of-vertical-scaling">Disadvantages of Vertical Scaling:</h3>
<ul>
<li>The cost may be low, but you will need to pay for a license each time you scale up</li>
<li>The hardware costs more because of high-end servers</li>
<li>There is a limit to the amount you can upgrade</li>
<li>You are restricted to a single database vendor, and migration is challenging, or you may need to start over</li>
</ul>
<h3 id="heading-when-to-use-vertical-scaling">When to use vertical scaling:</h3>
<p>The vertical scaling approach is for you if you need a system with unique data consistency.</p>
<p>If you don't want to worry about balancing the server's workload, vertical scaling is the best option.</p>
<h2 id="heading-differences-between-vertical-and-horizontal-scaling">Differences Between Vertical and Horizontal Scaling</h2>
<table>
<thead>
<tr>
<th>Vertical</th>
<th>Horizontal</th>
</tr>
</thead>
<tbody>
<tr>
<td>The license costs less</td>
<td>The license costs more</td>
</tr>
<tr>
<td>This method increases the power of the server with additional individual servers</td>
<td>This method increases the power of the server with the existing server</td>
</tr>
<tr>
<td>This data is present on one single node, and it is scaled through a multicore</td>
<td>This is based on partitioning each node that contains a single part of data</td>
</tr>
</tbody>
</table>

<h2 id="heading-which-scaling-method-is-best-for-your-app">Which scaling method is best for your app?</h2>
<p>When choosing how to scale your database, you must consider what's at stake when you scale up and out. </p>
<p>Now we'll take a look at some factors to consider so you can choose which scaling system is best for your app:</p>
<h3 id="heading-load-balancing">Load balancing</h3>
<p>The vertical scaling system is best for balancing loads because you have a single server (vertical scaling), and there is no need to balance your load. Horizontal scaling requires you to balance the workload evenly.</p>
<h3 id="heading-point-of-failure">Point of failure</h3>
<p>The horizontal scaling system has more than one server, so when one server crashes, the next one picks up the slack. This means that there is no <em>single point of failure</em> which makes the system resilient.</p>
<p>But in the vertical scaling system, there is only one server, so once the server crashes, everything goes offline.</p>
<h3 id="heading-speed">Speed</h3>
<p>In terms of speed, the vertical scaling system is faster because, since it runs on one server, the vertical scaling system has an <em>interprocess communication</em> – that is, the server communicates within itself and it's fast. </p>
<p>The horizontal scaling system has network calls between two or more servers. This is also known as <em>Remote Procedure Calls (RPC).</em> RPCs are slow, though.</p>
<h3 id="heading-data-consistency">Data consistency</h3>
<p>When dealing with servers, you'll need to make sure that the data stored in them is consistent when end users send a request. </p>
<p>The vertical scaling system is data consistent because all information is on a single server. But the horizontal scaling system is scaled out with multiple servers, so data consistency can be a huge issue.</p>
<h3 id="heading-hardware-limitations">Hardware limitations</h3>
<p>The horizontal scaling system scales well because the number of servers you throw at a request is linear to the number of users in the database or server. The vertical scaling system, on the other hand, has a limitation because everything runs on a single server.</p>
<p>When choosing a system to scale your database, make sure to make a pros and cons list of the information in this article. It will help you decide which to use.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>A cloud computing model's scalability is the ability to quickly and instantly increase or decrease an IT capacity. Knowing how the two types of scaling work is crucial as this plays a massive role in your database or server management.</p>
<p>Quick recap...</p>
<ul>
<li>A server's role is to enhance its capacity to handle the increased workload, called <strong>Vertical scaling.</strong></li>
<li>A system's job is to add new nodes to manage the distributed workload, termed <strong>Horizontal scaling.</strong></li>
<li>The horizontal scaling system scales well as the number of users increases.</li>
<li>The vertical scaling system is faster due to its ability to inter-process communication.</li>
</ul>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Scale a Distributed System ]]>
                </title>
                <description>
                    <![CDATA[ By Apoorv Tyagi Designing a distributed system that supports millions of users is a complex task, and one that requires continuous improvement and refinement.  Recently I read a book by Alex Xu called "System Design Interview – An Insider's Guide". T... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-scale-a-distributed-system/</link>
                <guid isPermaLink="false">66d45d9acc7f04d2549a371e</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ distributed systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scalability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 13 Dec 2021 23:37:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/photo-1515378960530-7c0da6231fb1-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Apoorv Tyagi</p>
<p>Designing a distributed system that supports millions of users is a complex task, and one that requires continuous improvement and refinement. </p>
<p>Recently I read a book by Alex Xu called "<em>System Design Interview – An Insider's Guide</em>". This article, inspired by the first part of the book, shares some popular techniques used by many large tech companies to scale their architecture to support up to a million users.</p>
<p>This is not an exhaustive list, but if you're a newer developer who's just getting started, this can help you build a stronger foundation for your career.</p>
<h2 id="heading-use-a-load-balancer"><strong>Use a </strong>Load<strong> B</strong>alancer<em>**</em></h2>
<p>A load balancer is a device that evenly distributes network traffic across several web servers. In this architecture, the clients do not connect to the servers directly – instead they connect to the public IP of the load balancer. </p>
<p>Using a load balancer also protects your site in the event of web server failure – and this, in turn, improves availability. For example,</p>
<ul>
<li>If one server goes down, all the traffic can be routed to the second server. This prevents the overall system from going offline.</li>
<li>If in the future the traffic grows and these two servers are not enough to handle all the requests properly, then you just need to add more servers to your pool of web servers and the load balancer automatically starts distributing requests to them.</li>
</ul>
<h3 id="heading-load-balancing-algorithms">Load Balancing Algorithms</h3>
<p>Let's look at some of the algorithms which a load balancer can use to choose a web server from a pool for an incoming request:</p>
<ul>
<li><strong>Round Robin</strong> – You start from the first server in the pool, move down to the next server, and when you're done with the last server you loop back up to the first and start working down the pool again.</li>
<li><strong>Load-based server</strong> – You assign a server based on whichever server has the smallest load currently, thereby increasing throughput.</li>
<li><strong>IP Hashing</strong> – You assign a server by hashing the IP address of incoming requests and using the hash value to do the modulo operation with the number of servers available in the server pool.</li>
</ul>
<h2 id="heading-use-caching"><strong>Use </strong>Cach<strong>ing</strong></h2>
<p>A cache stores the result of the previous responses so that any subsequent requests for the same data can be served faster. So you can use caching to minimize the network latency of a system.</p>
<p>You can significantly improve the performance of an application by decreasing the network calls to the database. This is because repeated database calls are expensive and cost time. </p>
<p>For example, every time a new user loads a website's home page, one or more database calls are made to fetch the data. This increases the response time. Caching can alleviate this problem by storing the results you know will get called often and those whose results get modified infrequently.</p>
<p>Here are a few considerations to keep in mind before using a cache:</p>
<ul>
<li><strong>Set an expiration policy:</strong> You should always have an expiration policy on your cache. If you don't have one, the data will get stored in the cache permanently and it will become stale.</li>
<li><strong>Sync the cache and database:</strong> You should build a mechanism to keep the database and the cache in sync. If any data modifying operations occur in the databases and the same change doesn't reflect in the cache then it will introduce inconsistencies in your system. </li>
<li><strong>Set an eviction policy</strong>: You should have an algorithm that can decide which existing items will get removed once the cache is full and you get a request to add other items to the cache. Least-recently-used (LRU) is one of the most popular cache eviction policies used today.</li>
</ul>
<h2 id="heading-use-a-content-delivery-network-cdn"><strong>Use a </strong>Content<strong> D</strong>elivery<strong> N</strong>etwork (CDN)<em>**</em></h2>
<p>A CDN or a Content Delivery Network is a network of geographically distributed servers that help improve the delivery of static content from a performance perspective. CDN servers are generally used to cache content like images, CSS, and JavaScript files.</p>
<p>Here is how a CDN works:</p>
<ul>
<li>When a client sends a request, a CDN server to the client will deliver all the static content related to the request.</li>
<li>If the CDN server does not have the required file, it then sends a request to the original web server.</li>
<li>The CDN caches the file and returns it to the client.</li>
<li>Let's say now another client sends the same request, then the file is returned from the CDN.</li>
</ul>
<p>Here are a few considerations to keep in mind before using a CDN:</p>
<ul>
<li><strong>Cost</strong>: CDNs are generally run by third-party providers and they charge you for the data transfers in and out of the CDN. So caching infrequently used assets should not be stored in the CDN.</li>
<li><strong>Fallback Mechanism</strong>: If a CDN fails, you should be able to detect it and start sending requests for resources from the original web server. So you should build a mechanism for how your application copes with a CDN failure.</li>
</ul>
<h2 id="heading-set-up-a-message-queue"><strong>Set Up a </strong>Message<strong> Q</strong>ueue<em>**</em></h2>
<p>A message queue allows an asynchronous form of communication. It acts as a buffer for the messages to get stored on the queue until they are processed.</p>
<p>The architecture of a message queue includes an input service, called publishers, that creates messages, publishes them to a message queue, and sends an event. Another service called subscribers receives these events and performs actions defined by the messages.</p>
<p>Both publishers and subscribers are decoupled from each other and that's what makes the message queue a preferred architecture for building scalable applications.</p>
<h3 id="heading-message-queue-example">Message queue example</h3>
<p>Consider the following use case:</p>
<p>You are building an application for ticket booking. As soon as a user completes their booking, a message confirming their payment and ticket should be triggered. This task may take some time to complete and it should not make our system wait for processing the next request.</p>
<p>Here, we can push the message details along with other metadata like the user's phone number to the message queue. Another worker service picks up the jobs from the message queue and asynchronously performs the message creation and sending tasks.</p>
<p>The publishers and the subscribers can be scaled independently. When the size of the queue increases, you can add more consumers to reduce the processing time.</p>
<h2 id="heading-choose-your-database-wisely"><strong>Choose Your </strong>Database<strong> Wisely</strong></h2>
<p>According to <a target="_blank" href="https://en.wikipedia.org/wiki/Database">Wikipedia</a>:</p>
<blockquote>
<p>A database is an organized collection of data stored and accessed via a computer system. </p>
</blockquote>
<p>Databases are used for the persistent storage of data. We generally have two types of databases, relational and non-relational.</p>
<h3 id="heading-relational-database">➔ Relational Database</h3>
<p>A relational database has strict relationships between entries stored in the database and they are highly structured. This is to ensure data integrity. For example, adding a new field to the table when its schema doesn't allow for it will throw an error.</p>
<p>Another important feature of relational databases is ACID transactions.</p>
<h4 id="heading-acid-transactions">ACID transactions</h4>
<p>These are a set of features that describe any given transactions (a set of read or write operations) that a good relational database should support.</p>
<p><strong>Atomicity</strong> means that when a transaction that comprises more than one operation takes place, the database must guarantee that if one operation fails the entire transaction fails. Either it happens completely or doesn't happen at all.</p>
<p><strong>Consistency</strong> means that each transaction in a database does not violate the data integrity constraints whenever the database changes state and does not corrupt the data. In simple terms, consistency means for every "read" operation, you'll receive the most recent "write" operation results.</p>
<p><strong>Isolation</strong> means that you can run multiple concurrent transactions on a database, without leading to any kind of inconsistency. All these multiple transactions will occur independently of each other.</p>
<p><strong>Durability</strong> means that once the transaction has completed execution, the updated data remains stored in the database. It will be saved on a disk and will be persistent even if a system failure occurs.</p>
<h3 id="heading-non-relational-databases">➔ Non-Relational Databases</h3>
<p>A non-relational database has a less rigid structure and may or may not have strict relationships between the entries stored in the database. The data typically is stored as key-value pairs. For example:</p>
<pre><code>[
    { 
        <span class="hljs-attr">firstName</span>: <span class="hljs-string">"Apoorv"</span>,
        <span class="hljs-attr">lastName</span>: <span class="hljs-string">"Tyagi"</span>,
        <span class="hljs-attr">gender</span>: <span class="hljs-string">"M"</span>
    },
    { 
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Judit"</span>,
        <span class="hljs-attr">rank</span>: <span class="hljs-string">"Polgar"</span>,
        <span class="hljs-attr">gender</span>: <span class="hljs-string">"F"</span>
    },
    {
      <span class="hljs-comment">//...</span>
    },
]
</code></pre><p>Similar to the ACID properties of relational databases, the non-relational database offers BASE properties:</p>
<p><strong>Basically Available (BA)</strong> which states that the system guarantees availability even in the presence of multiple failures. </p>
<p><strong>Soft State (S)</strong> means the state of the system may change over time, even without application interaction due to eventual consistency. In NoSQL, unlike RDBMS, it is believed that data consistency is the developer's responsibility and should not be handled by the database.</p>
<p><strong>Eventual Consistency (E)</strong> means that the system will become consistent "eventually". However, there's no guarantee of when this will happen.</p>
<h3 id="heading-nosql-vs-sql">NoSQL vs SQL</h3>
<p>Non-relational databases (also often referred to as NoSQL databases) might be a better choice if:</p>
<ul>
<li>Your application requires low latency. Since there are no complex JOIN queries.</li>
<li>You have a large amount of unstructured data, or you do not have any relation among your data.</li>
</ul>
<h2 id="heading-how-to-scale-a-database">How to Scale a Database</h2>
<p>Let's now look at the various ways you can scale your database:</p>
<h3 id="heading-vertical-vs-horizontal-database-scaling">Vertical vs horizontal database scaling</h3>
<p>In vertical scaling, you scale by adding more power (CPU, RAM) to a single server.</p>
<p>In horizontal scaling, you scale by simply adding more servers to your pool of servers.</p>
<p>For low-scale applications, vertical scaling is a great option because of its simplicity. But vertical scaling has a hard limit. It is practically not possible to add unlimited RAM, CPU, and memory to a single server. </p>
<p>Because of this, it is recommended that you go for horizontal scaling (also known as sharding) for large-scale applications.</p>
<h3 id="heading-database-replication">Database replication</h3>
<p>This is the process of copying data from your central database to one or more databases.</p>
<p>You do database replication using primary-replica (formerly known as master-slave) architecture. The primary database generally only supports write operations. All the data modifying operations like insert or update will be sent to the primary database.</p>
<p>On the other hand, the replica databases get copies of the data from the primary database and only support read operations. All the data querying operations like read, fetch will be served by replica databases.</p>
<p>Advantages of database replication:</p>
<ul>
<li><strong>Performance Improvements</strong>: Database replication improves performance significantly as all the writes and updates happen in the primary node and all the read operations are distributed to replica nodes, thereby allowing more queries to run in parallel.</li>
<li><strong>High Availability</strong>: Since we create replicas of data across different nodes available in different parts of the world, the application remains functional even if one database node goes offline as you can access data from other nodes. In case the failure occurs in the primary node, any one of the replica nodes will get promoted to a primary node and serve the write/update operations until the original primary node comes back online.</li>
</ul>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>That's it. Thanks for stopping by. I hope you found this article interesting and informative!</p>
<p>My DMs are always open if you want to discuss further on any tech topic or if you've got any questions, suggestions, or feedback in general:</p>
<ul>
<li><a target="_blank" href="https://twitter.com/apoorv__tyagi">Twitter</a></li>
<li><a target="_blank" href="https://www.linkedin.com/in/apoorvtyagi/">LinkedIn</a></li>
<li><a target="_blank" href="https://github.com/apoorvtyagi">GitHub</a></li>
<li><a target="_blank" href="https://apoorvtyagi.tech/">Blog</a></li>
</ul>
<p>Happy learning! 💻 😄</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A guide to understanding database scaling patterns ]]>
                </title>
                <description>
                    <![CDATA[ By Kousik Nath There are lot of articles online describing database scalability patterns, but they are mostly scattered articles — just techniques that are defined haphazardly without much context. I find that they are not defined in a step by step m... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/understanding-database-scaling-patterns/</link>
                <guid isPermaLink="false">66d45f6951f567b42d9f8461</guid>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 30 Jul 2019 17:07:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/database-1954920_1920.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Kousik Nath</p>
<p>There are lot of articles online describing database scalability patterns, but they are mostly scattered articles — just techniques that are defined haphazardly without much context. I find that they are not defined in a step by step manner, and don't discuss when to choose which scaling option, which scaling options are feasible in practise, and why. </p>
<p>Therefore, I am planing to discuss some of the techniques in detail in future articles. To start, I feel it’s better if I discuss step by step techniques with some context in my own way. This article is a high level article — I will not discuss scaling techniques in details here, but will provide an overview. So let's get started.</p>
<h3 id="heading-a-case-study">A case study</h3>
<p>Assume you have built a startup which offers ride sharing at a cheap cost. Initially when you start, you target a city and hardly have tens of customers after your initial advertisement. </p>
<p>You save all customers, trips, locations, bookings data, and customer trip history in the same database or most likely in a single physical machine. There is no fancy caching or big data pipeline to solve problems since your app is very new. This is perfect for your use case at this moment since there are very few customers and your system hardly books 1 trip in 5 minutes, for example.</p>
<p>But as time goes on, more people start signing up in your system since you are the cheapest service in the market and thanks to your promotion and ads. You start booking, say, 10 bookings per minute, and slowly the number increases to 20, 30 bookings per minute. </p>
<p>At this point of time, you realize that the system has started performing poorly: API latency has increased a lot, and some transactions deadlock or starve and eventually they fail. Your app is taking more time to respond, causing customer dissatisfaction. What can you do to solve the problem?</p>
<h2 id="heading-pattern-1-query-optimization-amp-connection-pool-implementation">Pattern 1 - Query Optimization &amp; Connection Pool implementation:</h2>
<p>The first solution that comes in mind is that the cache frequently uses non-dynamic data like booking history, payment history, user profiles and so on. But after this application layer caching, you can’t solve the latency problem of APIs exposing dynamic data like the current driver's location or the nearest cabs for a given customer or current trip cost at a certain moment in time after the trip starts. </p>
<p>You identify that your database is probably heavily normalized, so you introduce some redundant columns (these columns frequently appear in <code>WHERE</code> or <code>JOIN ON</code> clause in queries) in highly used tables for the sake of denormalization. This reduces join queries, breaks a big query into multiple smaller queries, and adds their results up in the application layer.</p>
<p>Another parallel optimization that you can do is tweaking around database connections. Database client libraries and external libraries are available in almost all programming languages. You can use connection pool libraries to cache database connections or can configure connection pool size in the database management system itself. </p>
<p>Creating any network connection is costly since it requires some back &amp; forth communication between client &amp; server. Pooling connections may help you to optimize the number of connections. Connection pool libraries may help you to multiplex connections — multiple application threads can use the same database connection. I shall see if I can explain connection pooling in detail in a separate article later.</p>
<p>Your measure latency of your APIs &amp; find probably 20–50% or more reduced latency. This is good optimization at this point in time.</p>
<p>You have now scaled your business to one more city, more customer sign up, you slowly start to do 80–100 bookings per minute. Your system is not able to handle this scale. Again you see API latency has increased, database layer has given up, but this time, no query optimization is giving you any significant performance gain. You check the system metric, you find disk space is almost full, CPU is busy 80% of the time, RAM fills up very quickly.</p>
<h2 id="heading-pattern-2-vertical-scaling-or-scale-up">Pattern 2 - Vertical Scaling or Scale Up:</h2>
<p>After examining all system metrics, you know there is no other easy solution rather than upgrading the hardware of the system. You upgrade your RAM size by 2 times, upgrade disk space by, say, 3 times or more. This is called vertical scaling or scaling up your system. You inform your infrastructure team or devops team or third party data centre agents to upgrade your machine.</p>
<p><strong>But how do you set up machine for vertical scaling?</strong></p>
<p>You allocate a bigger machine. One approach is not to migrate data manually from old machine rather set the new machine as <code>replica</code> to the existing machine (<code>primary</code>)-make a temporary <code>primary replica</code> configuration. Let the replication happen naturally. Once the replication is done, <a target="_blank" href="https://blog.pythian.com/mysql-recipes-promoting-a-slave-to-master-or-changing-masters/?source=post_page---------------------------">promote the new machine to primary</a> &amp; take the older machine offline. Since the bigger machine is expected to serve all request, all read / write will happen on this machine.</p>
<p>Cool. Your system is up &amp; running again with increased performance.</p>
<p>Your business is doing very well &amp; you decide to scale to 3 more cities — you are now operational in 5 cities total. Traffic is 3x times than earlier, you are expected to do around 300 bookings per minute. Before even achieving this target booking, you again hit the performance crunch, database index size is increasing heavily in memory, it needs constant maintenance, table scanning with index is getting slower than ever. You calculate the cost of scaling up the machine further but not convinced with the cost. What do you do now?</p>
<h2 id="heading-pattern-3-command-query-responsibility-segregation-cqrs">Pattern 3 - Command Query Responsibility Segregation (CQRS):</h2>
<p>You identify that the big machine is not able to handle all <code>read/write</code> requests. Also in most of the cases, any company needs transactional capability on <code>write</code> but not on <code>read</code> operations. You are also fine with a little bit of inconsistent or delayed <code>read</code> operations &amp; your business has no issue with that either. You see an opportunity where it might be a good option to separate the <code>read</code> &amp; <code>write</code> operations physical machine wise. It will create scope for individual machines to handle more <code>read/write</code> operations. </p>
<p>You now take two more big machines &amp; set them up as <code>replica</code> to the current machine. Database replication will take care of distributing data from <code>primary</code> machine to <code>replica</code> machines. You navigate all read queries (Query (<code>Q</code>) in <code>CQRS</code>) to the replicas — any <code>replica</code> can serve any read request, you navigate all write queries (Command (<code>C</code>) in <code>CQRS</code>) to the <code>primary</code>. There might be little lag in the replication, but according to your business use case that’s fine.</p>
<p>Most of the medium scale startups which serve few hundred thousand requests everyday can survive with primary-replica set up provided that they periodically archive older data.</p>
<p>Now you scale to 2 more cities, you see that your <code>primary</code> is not able to handle all <code>write</code> requests. Many <code>write</code> requests are having latency. Moreover, the lag between <code>primary</code> &amp; <code>replica</code> sometimes impact customers &amp; drivers ex — when trip ends, customer pays the driver successfully, but the driver is not able to see the payment since customer’s activity is a <code>write</code> request that goes to the <code>primary</code>, while driver’s activity is a <code>read</code> request that goes to one of the replicas. Your overall system is so slow that driver is not able to see the payment for at least half a minute — frustrating for both driver &amp; customer. How do you solve it?</p>
<h2 id="heading-pattern-4-multi-primary-replication">Pattern 4 - Multi Primary Replication</h2>
<p>You scaled really well with <code>primary-replica</code> configuration, but now you need more write performance. You might be ready to compromise a little bit on <code>read</code> request performance. Why not distribute the write request to a <code>replica</code> also?</p>
<p>In a <code>multi-primary</code> configuration, all the machines can work as both <code>primary</code>&amp; <code>replica</code>. You can think of <code>multi-primary</code> as a circle of machines say <code>A-&gt;B-&gt;C-&gt;D-&gt;A</code>. <code>B</code> can replicate data from <code>A</code>, <code>C</code> can replicate data from <code>B</code>, <code>D</code> can replicate data from <code>C</code>, <code>A</code> can replicate data from <code>D</code>. You can write data to any node, while reading data, you can broadcast the query to all nodes, whoever replies return that. All nodes will have same database schema, same set of tables, index etc. So you have to make sure there are no collision in <code>id</code> across nodes in the same table, otherwise during broadcasting, multiple nodes would return different data for the same <code>id</code>. </p>
<p>Generally it’s better to use <code>UUID</code> or <code>GUID</code> for id. One more disadvantage of this technique is — <code>read</code> queries might be inefficient since it involves broadcasting query &amp; getting the correct result — basically scatter gather approach.</p>
<p>Now you scale to 5 more cities &amp; your system is in pain again. You are expected to handle roughly 50 request per second. You are in desperate need to handle heavy number of concurrent requests. How do you achieve that?</p>
<h2 id="heading-pattern-5-partitioning">Pattern 5 - Partitioning:</h2>
<p>You know that your <code>location</code> database is something which is getting high <code>write</code> &amp; <code>read</code> traffic. Probably <code>write:read</code> ratio is <code>7:3</code>. This is putting a lot of pressure on the existing databases. The <code>location</code> tables contain few primary data like <code>longitude</code>, <code>latitude</code>, <code>timestamp</code>, <code>driver id</code>, <code>trip id</code> etc. It does not have a much to do with user trips, user data, payment data etc. What about separating the <code>location</code> tables in a separate database schema? What about putting that database in separate machines with proper <code>primary-replica</code> or <code>multi-primary</code> configuration? </p>
<p>This is called partitioning of data by functionality. Different database can host data categorized by different functionality, if required the result can be aggregated in the back end layer. Using this technique, you can focus on scaling those functionalities well which demand high <code>read/write</code> requests. Although the back end or application layer has to take the responsibility to join the results when necessary resulting in more code changes probably.</p>
<p>Now imagine you have expanded your business to a total of 20 cities in your country &amp; planning to expand to Australia soon. Your increasing demand of app requires faster &amp; faster response. None of the above method can help you to the extreme now. You must scale your system in such a way that expanding to other countries / regions does not always need you to do frequent engineering or architecture changes. How do you do that?</p>
<h2 id="heading-pattern-6-horizontal-scaling">Pattern 6 - Horizontal Scaling:</h2>
<p>You do lot of googling, read a lot on how other companies have solved the issue — and come to the conclusion that you need to scale horizontally. You allocate say 50 machines — all have the same database schema which in turn contains the same set of tables. All the machines just hold a part of data. </p>
<p>Since all databases contain same set of tables, you can design the system in such a way that locality of data is there i.e; all related data lands in the same machine. Each machine can have their own replicas, replicas can be used in failure recovery. Each of the databases is called <code>shard</code>. A physical machine can have one or multiple <code>shards</code> — it’s up to your design how you want. You need to decide on <code>sharding key</code> in such a way that a single <code>sharding key</code> always refers to the same machine. So you can imagine lot of machines all holding related data in same set of tables, <code>read/write</code> requests for the same row or same set of resource land in the same database machine.</p>
<p>Sharding is in general hard — at least engineers from different companies say that. But when you serve millions or billions of requests, you have to make such tough decision.</p>
<p>I will discuss <code>sharding</code> in greater detail in my next post, so holding back my temptation to discuss more in this post.</p>
<p>Now since you have sharding in place, you are confident that you can scale to many countries. Your business has grown so much that investors are pushing you to scale the business across continents. You again see some problem here. API latency again. Your service is hosted in USA &amp; people from Vietnam are having difficult time book rides. Why? What do you do about it?</p>
<h2 id="heading-pattern-7-data-centre-wise-partition">Pattern 7 - Data Centre Wise Partition:</h2>
<p>Your business is growing in America, South Asia &amp; in few countries in Europe. You are doing millions of bookings daily with billions of request hitting your server. Congrats - this is a peak moment for your business. </p>
<p>But since requests from the app have to travel across continents through hundreds or thousands of servers in the internet, the latency arises. What about distributing traffic across data centres? You can set up a data centre in Singapore that handles all requests from South Asia, data centre in Germany can handle all requests from European countries, and a California data centre can handle all USA requests. </p>
<p>Also you enable cross data centre replication which helps disaster recovery. So if California data centre does replication to Singapore data centre, in case California data centre crashes due to electricity issue or natural calamity, all USA requests can fall back to Singapore data centre and so on. </p>
<p>This scaling technique is useful when you have millions of customers to serve across countries and you can’t accommodate any data loss, you have to always maintain availability of the system.</p>
<p>These are some general step by step techniques for database scaling. Although most of the engineers don’t get enough chance to implement these techniques, but as a whole it’s better to get a broader idea about such system which in future may help you to do better system &amp; architecture designing.</p>
<p>In my next articles, I will try to discuss some of the concepts in details. Please feel free to give appropriate feedback for this post if any.</p>
<p>The article is originally published on the author's medium account: <a target="_blank" href="https://medium.com/@kousiknath/understanding-database-scaling-patterns-ac24e5223522">https://medium.com/@kousiknath/understanding-database-scaling-patterns-ac24e5223522</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Scale Elm Views with Master View Types ]]>
                </title>
                <description>
                    <![CDATA[ A concept to help Elm Views scale as applications grow larger and more complicated. In Elm, there are a lot of great ways to scale the Model, and update, but there is more controversy around scaling the view. A lot of the debate is around Reusable Vi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/scaling-elm-views-with-master-view-types/</link>
                <guid isPermaLink="false">66bb9271deef71ff683a6d48</guid>
                
                    <category>
                        <![CDATA[ ELM ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Thu, 18 Jul 2019 07:29:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/2014-Haute-Route-Imperiale51.JPG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A concept to help Elm Views scale as applications grow larger and more complicated.</p>
<p>In Elm, there are a lot of great ways to scale the <code>Model</code>, and <code>update</code>, but there is more controversy around scaling the <code>view</code>. A lot of the debate is around <a target="_blank" href="https://gist.github.com/rofrol/fd46e9570728193fddcc234094a0bd99#reusable-views-instead-of-nested-components">Reusable Views versus Components</a>. Components are not recommended, but a lot of people are still advocating for them.  This article presents an idea that hopefully strengthens the argument for Resuable Views.</p>
<p>In almost all cases, the scaling problem comes down to enforcing consistency, which usually means allowing child views to make some adjustments to the master view, while at the same time not allowing child views to make a mess.</p>
<p>I will be using Richard Feldman's excellent <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example">Real World app</a> (specifically written to demonstrate scaling in Elm) as an example, as it is contains a lot of current best practice techniques, it is well known (2000+ stars and 300+ forks) and Richard is a well known Elm expert. </p>
<p>I will be suggesting some improvements to this code, so I want make a clear at this point that I mean no disrespect by this (I would bet large sums of money that he did it in about one tenth of the time it would have taken me!). You could also argue that the problems are small and not worth fixing. Ultimately, this decision is yours, but by the end of the article I hope to persuade you that there are problems, and that they are fixable if you think it is worthwhile.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/ski-touring.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-master-view-functions-with-conditionals">Master view functions with conditionals</h2>
<p>One option is to define a master view function. This function takes care of shared concerns, like the header bar and overall layout. Then it calls child view functions depending on the current view and / or has parameters to control child specific behaviour.</p>
<p>This works, but can quickly lead to:</p>
<ul>
<li>An explosion of parameters, potentially forcing your child views to return a lot of things they don't care about.</li>
<li>A mixing of responsibilities between master and child views.</li>
<li>Extra code and duplication.</li>
</ul>
<p>In the Real World App, a parameter of type <code>Page</code> is passed to the master view so that it can render a navbar link as active. There is a <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page.elm#L113">large case statement</a> that uses this parameter to work out what which link is active, and it would be a lot easier for the child just to specify this.</p>
<p>The <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Main.elm#L85">line below</a> shows the master view passing <code>Page.Home</code>, which has to match up with <code>Home.view home</code>. This is easy to get wrong, there is no help from the compiler or type system, and really it is the responsibility of the child view the specify this.</p>
<p><code>viewPage Page.Home GotHomeMsg (Home.view home)</code></p>
<p>There is some duplication when <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page.elm#L62">creating the NavBarLink Html</a>, and the <code>linkTo</code> function will accept any Html, although only very particular Html is valid.</p>
<h2 id="heading-convention-and-trust">Convention and trust</h2>
<p>Another possibility is for child views to be responsible for keeping shared elements consistent, by convention and trust. </p>
<p>Arguably this also happens in the Real World App. The <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Home.elm#L146">Home</a>, <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Article.elm#L119">Article</a> and <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Profile.elm#L197">Profile</a> views all have the concept of a banner. The banner is different in each view, but presumably is meant to be a consistent and recognisable visual element (essentially, it's the title / header for the view). The views don't share any code for these banners, and as a result of this they are not the same size or colour. You could theoretically try and enforce a convention using tests, but it would be difficult, and probably not worthwhile.</p>
<h2 id="heading-helper-functions">Helper functions</h2>
<p>Another possibility is for child views to be responsible for keeping shared elements consistent, but by using some helper functions. This is definitely a step forward, and is probably the most common solution I see in the wild. The functions can go in the same file and be next to each other. This makes it easier to see that they are related and are representing the same visual element, and easier to make them consistent. </p>
<p>However, there are still some drawbacks. The main one is that the child views have to know to use the helper functions, and there is nothing enforcing this. This isn't a huge deal when you only have one shared element and one function to call, but as applications get bigger, you end up with a combinatorial explosion of differences in the shared visual elements. Most people tame this by providing a number of small, focused functions for the various differences. Then the child view has to know about all these functions, and how to compose them, and there no help from the compiler. </p>
<p>Again, this arguably occurs in the Real World App: for example in <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Profile.elm#L211">this part of the Profile.view function</a>, which needs to know how to use the <code>viewTabs</code>, <code>Feed.viewArticles</code> and <code>Feed.viewPagination</code> helper functions, and what Html they need to be contained in.</p>
<h2 id="heading-scaling-with-master-view-types">Scaling with Master View Types</h2>
<p>In order to overcome these problems, I propose using a <code>Type</code> to define your site structure (I rather pompously call this a "Master View Type"). Child views then return this type, and the master view takes it as a parameter and returns the html. </p>
<p>For the Real World App examples we have been looking at, the Master View Type is below (<code>Viewer</code> is the person viewing the page in the Real World App). You could arguably have more general banner types here, such as AvatarBanner, or even IconBanner (instead of ViewerBanner) depending on your domain.</p>
<pre><code class="lang-elm"><span class="hljs-keyword">type</span> <span class="hljs-keyword">alias</span> <span class="hljs-type">Page</span> =
    {   activeNavBarLink: <span class="hljs-type">NavBarLink</span>
        , banner: <span class="hljs-type">Banner</span>
        , body: <span class="hljs-type">Html</span> <span class="hljs-type">Msg</span>
    }

<span class="hljs-keyword">type</span> <span class="hljs-type">Banner</span> =
    <span class="hljs-type">TextBanner</span> <span class="hljs-type">TextBannerProperties</span>
    | <span class="hljs-type">ViewerBanner</span> <span class="hljs-type">Viewer</span>
    | <span class="hljs-type">ArticleBanner</span> <span class="hljs-type">Viewer</span> <span class="hljs-type">ArticlePreview</span>

<span class="hljs-keyword">type</span> <span class="hljs-type">NavBarLink</span> =
    <span class="hljs-type">NavBarLink</span> <span class="hljs-type">NavBarLinkProperties</span>
</code></pre>
<p>To demonstrate this, I have create a repository with just the <a target="_blank" href="https://github.com/ceddlyburge/elm-without-master-view-types">Header and Banner parts of the Real World App</a>  and then created a new repository after refactoring to use a  <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/Page.elm">Master Page Type</a>, <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/NavBarLink.elm">NavBarLink Type</a> and <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/Banner.elm">Banner Type</a>. You can peruse the code to get a feel for how it works.</p>
<p>To my mind, using a Master Page Type has the following benefits:</p>
<ul>
<li>Writing the master view code is easier</li>
<li>Writing the child view code is easier</li>
<li>Communication and understanding are improved, as UI concepts now have names</li>
<li>Theming / redesigning a site is a lot easier</li>
<li>Elm packages can provide UI templates</li>
</ul>
<p>The master view can precisely define what it will accept / support via the types, with <a target="_blank" href="https://guide.elm-lang.org/types/custom_types.html">union types</a> and <a target="_blank" href="https://medium.com/@ckoster22/advanced-types-in-elm-opaque-types-ec5ec3b84ed2">opaque types</a>. Non supported combinations can be made unrepresentable or uncreatable. </p>
<p>In my example repository the <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/NavBarLink.elm">NavBarLink type is opaque</a>, so it is only possible to create supported NavBarLinks (<code>home</code>, <code>article</code> and <code>viewer</code>). In a similar way <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/Banner.elm">Banner is a union type</a>, which means that only supported variants can be represented. </p>
<p>It would be possible for a programmer to simply change these files, but a proficient programmer would recognise the patterns and follow them. If this isn't enough and you are feeling paranoid, then you can require stricter code review on such files, potentially taking advantage of <a target="_blank" href="https://help.github.com/en/articles/about-code-owners">CODEOWNERS</a> functionality on GitHub and GitLab. In the extreme you can  provide the modules via an elm package, and restrict push access to the underlying repository.</p>
<p>Child views don't have to do anything more than create an instance of the types. The helper functions all return types, so it's easy to see which functions can be used in a particular context, and is impossible to use functions in the wrong context. For example, if a function returns a <code>HeaderBarLink</code>, it is impossible to mistakenly use this function to create a link in the <code>FooterBar</code>, or elsewhere on the page. Child views can also leave some of the complexity to the master view. For example, the child view can define a list of options to choose from, and the master view can render this using buttons, a drop down list or an autocomplete list, depending on the number of options. </p>
<p>The master page type also provides names for UI concepts, which can then be discussed. For example, a designer could say "Let's move the NavBarLinks to the left hand side", and everybody would know what they meant. A product owner could say "Let's create a new page with an IconBanner, and we'll use the current weather api for the icon" and again, everybody would know what they mean. You can look at this <a target="_blank" href="https://www.thoughtworks.com/insights/blog/ui-components-design">excellent thoughtworks article</a> for more details of this.</p>
<p>Since the responsibility for turning the Master View Type in to html is all in the same place, it is easy to make drastic changes to the look and feel of a website, and to do theming. These changes and themes can alter the Css <em>and the Html</em>, which is something that the normal theming techniques just can't do. Pragmatically, your Master View Type will often have a <code>body: Html Msg</code> property (to allow child views complete flexibility on the child specific parts of the page) so there would still be some sprawling code to fix up, but it will definitely be a lot easier.</p>
<p>Finally, it opens up possibility of providing ready made themes and site layouts as packages. This would allow you to just do the following to get a working app, complete with layout and styling:</p>
<ul>
<li><code>create-elm-app</code></li>
<li><code>elm install elm-bootstrap-starter-template</code></li>
<li>Write some code to create the Master Page Type</li>
<li><code>elm-app start</code></li>
</ul>
<p>Companies could create packages like these to ensure a consistent look and feel across their applications. Open source designs and layouts could emerge and become commonplace, similar to the way that Bootstrap has revolutionised html and css design. Developers with limited design skills (like me) could concentrate on the the bits they are best at (the logic), but still produce produce elegant websites using these packages.</p>
<p>To demonstrate this I have created a <a target="_blank" href="https://package.elm-lang.org/packages/ceddlyburge/elm-bootstrap-starter-master-view/latest/">bootstrap starter master view package</a>. It mimics the layout and design of the <a target="_blank" href="https://getbootstrap.com/docs/4.0/examples/starter-template/">bootstrap starter template</a>. I have then used this package in a demo elm application. You can <a target="_blank" href="https://elm-bootstrap-starter.netlify.com/">browse the demo application</a> to see how it looks, and <a target="_blank" href="https://github.com/ceddlyburge/elm-bootstrap-starter-demo">view the source</a> to see how it works.</p>
<p>All these advantages come at a small to negative cost. There is a little more code for the new types, but some duplication is removed. You can view the source of the Real World App repositories <a target="_blank" href="https://github.com/ceddlyburge/elm-without-master-view-types">from before</a> and <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types">after refactoring to use a Master Page Type</a> for the full details.</p>
<h2 id="heading-conclusions">Conclusions</h2>
<p>Master View Types bring a lot of benefits (view code is easier to write and maintain, UI concepts are named and UI packages are possible) for little or no cost. They should improve the code of any Elm application that has issues around enforcing consistency (while allowing flexibility) in their view code, which in my experience is most medium and large applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Unity Dashboard — lessons learned scaling our frontends, development culture and processes ]]>
                </title>
                <description>
                    <![CDATA[ By Maciej Gurban At Unity, we’ve recently set out to improve our Dashboards — an undertaking which dramatically changed not only our frontend tech stack, but also the ways we work and collaborate. We’ve developed best practices and tooling to help us... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/unity-dashboard-lessons-learned-scaling-our-frontends-development-culture-and-processes-d28f429bd70e/</link>
                <guid isPermaLink="false">66c3640e0cede4e9b1329d23</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 14 Mar 2019 14:41:41 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*rUtnJRnd_CvejLsYapq3-g.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Maciej Gurban</p>
<p>At Unity, we’ve recently set out to improve our Dashboards — an undertaking which dramatically changed not only our frontend tech stack, but also the ways we work and collaborate.</p>
<p>We’ve developed best practices and tooling to help us scale our frontend architecture, build products with great UX and performance, and to ship new features sooner.</p>
<p>This article gathers these practices and aims to provide as much reasoning behind each decision as possible. But first, some context.</p>
<h3 id="heading-the-legacy">The Legacy</h3>
<p>Looking at the number of engineers, Unity more than quadrupled its headcount in the last 4 years. As the company grew both organically and through acquisitions, its product offering grew as well. While the products developed originally at Unity were largely uniform in terms of tech and design language, the newly acquired ones naturally were not.</p>
<p>As a result we had multiple visually distinct dashboards which worked and behaved differently and which shared no common navigational elements. This resulted in poor user experience and frustrated users. In the very literal sense, the state of frontends of our products was costing us revenue.</p>
<p>After analyzing the portfolio of our products, we’ve elicited three distinct sections Unity Dashboard would be split into: Develop, Operate and Acquire , each satisfying a different business need and meant for different customer groups, thus containing feature sets largely independent from each other.</p>
<p>This new structure, and the introduction of common navigational elements aimed to solve the first major issue our users were facing — where to find the information and configuration options they’re looking for, and while it all looked good on paper, the journey how to get there were far from obvious.</p>
<h4 id="heading-considerations">Considerations</h4>
<p>Many of our developers were very excited about the possibility of moving to React and its more modern tech stack. As these solutions had been battle tested in large applications, and had their best practices and conventions mostly ironed out, things looked very promising.</p>
<p>Nevertheless, what our developers knew best and what most of our actively developed applications were written in was AngularJS. Deciding to start migrating everything in one go would have been a disaster waiting to happen. Instead we set out to test our assumptions on a much smaller scale first.</p>
<p>Perhaps the most disjointed group of products we’ve had were the <strong>Monetization dashboards</strong>. These projects, which would eventually end up under the umbrella of the <strong>Operate dashboard,</strong> were vastly different in almost any way possible: technologies used, approach to UI/UX, development practices, coding conventions — you name it.</p>
<p>Here’s what the situation roughly looked like:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/M-8Rbct9m1hh-Nzbknh2DsLBsRerT9yr2sOv" alt="Image" width="800" height="395" loading="lazy">
<em>State of our dashboards in April 2018. Projects using Angular vs those using React.</em></p>
<p>After some brainstorming we identified the main areas which we’d need to work on to bring all the products together:</p>
<h4 id="heading-1-a-single-product">1. A single product</h4>
<p>We needed these dashboards (split across multiple applications, domains and tech stacks) to:</p>
<ul>
<li>Feel like a single product (no full page redirects as the user navigates through pages of all the different applications)</li>
<li>Have a consistent look and feel</li>
<li>Include common navigational elements are always visible and look the same, no matter which part of the dashboard the user is visiting</li>
</ul>
<h4 id="heading-2-legacy-support">2. Legacy support</h4>
<p>While we did have a clean slate when it comes to the technology choice of our new frontend solution, we had to accommodate for the legacy projects which needed to be integrated into the new system. A solution, which didn’t involve big refactoring efforts, and which wouldn’t stop feature development, or drag for months without end in sight.</p>
<h4 id="heading-3-practices-and-tooling">3. Practices and tooling</h4>
<p>While nearly all the teams used AngularJS, different tools were being used to address the same set of challenges. Different test runners and assertion libraries, state management solutions or lack thereof, jQuery vs native browser selectors, SASS vs LESS, charting libraries etc.</p>
<h4 id="heading-4-developer-productivity">4. Developer productivity</h4>
<p>Since every team had their own solution to developing, testing and building their application, the development environment was often riddled with bugs, manual steps, and inefficiencies.</p>
<p>Additionally, many of our teams work in locations separated by a 10 hour difference (Helsinki, Finland and San Francisco), which makes efficient decision-making on any shared pieces a real challenge.</p>
<h3 id="heading-the-new">The New</h3>
<p>Our main areas of focus were to:</p>
<ol>
<li>Encourage and preserve agile ways of working in our teams, and to let the teams be largely independent from one another</li>
<li>Leverage and develop common tooling and conventions as much as possible, to document them, and make them easily accessible and usable</li>
</ol>
<p>We believed that achieving these the goals would significantly improve our time to market and developer productivity. For that to happen, we required a solution which would:</p>
<ul>
<li><strong>Build product features with better user experience</strong></li>
<li><strong>Improve code quality</strong></li>
<li><strong>Allow for better collaboration</strong> without blocking anybody’s work progress in the process.</li>
</ul>
<p>We also wanted to encourage and ease-in the move to a modern tech stack to make our developers more satisfied with their work, and to over time move away from our antiquated frameworks and tooling.</p>
<p>The ever-evolving result of our work is a React-based SPA built inside a monorepository where all the pages and bigger features get built into largely independent code bundles loaded on demand, and which can be developed and deployed by multiple teams at the same time.</p>
<p>As a means of sandboxing all the legacy applications but still displaying them in the context of the same new application, we load them inside an iframe from within which they can communicate with the main SPA using a message bus implemented using the <code>[postMessage()](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)</code> API.</p>
<h3 id="heading-the-monorepository">The monorepository</h3>
<p>Here’s the directory structure we started out with:</p>
<pre><code>/src   /components  /scenes    /foo      /components      package.json      foo.js    /bar      /components      package.json      bar.js package.json index.js
</code></pre><p>The <code>package.json</code> in the root directory contains a set of <code>devDependencies</code> responsible for development, test and build environment of the whole application, but also contains <code>dependencies</code> of the core of the application (more on that a bit later).</p>
<p>All the larger UI chunks are referred to as <em>scenes</em>. Each <em>scene</em> contains a <code>package.json</code> where <code>dependencies</code> used by that scene’s components are defined. This makes two things possible:</p>
<ol>
<li><strong>Deployment updates only the files which have changed</strong><br>The build step compiles separate vendor and app bundles for each scene, naming each using a hash which will change only when contents of the file have changed. This means our users only download files which have changed since their last visit, and nothing more.</li>
<li><strong>Scenes are loaded only when needed</strong><br>We load all scenes asynchronously and on demand which drastically improves the load times of the whole application. The “on demand” here usually means visiting a specific route, or performing a UI action which performs a <a target="_blank" href="https://github.com/tc39/proposal-dynamic-import">dynamic module import</a>.</li>
</ol>
<p>Here’s how such setup looks in practice (simplified for readability):</p>
<pre><code><span class="hljs-comment">// In src/routes.jsconst FooLoader = AsyncLoadComponent( () =&gt; import(‘src/scenes/foo/foo’), GenericPagePreloader,);</span>
</code></pre><pre><code>&lt;Route path=”/foo” component={FooLoader) /&gt;
</code></pre><pre><code><span class="hljs-comment">// In src/scenes/foo/foo.js&lt;React.Suspense fallback={GenericPagePreloader}&gt; &lt;Component /&gt;&lt;/React.Suspense&gt;</span>
</code></pre><p>The <code>AsyncLoadComponent</code> is a thin wrapper around <code>[React.lazy()](https://reactjs.org/docs/code-splitting.html#reactlazy)</code>, additionally accepting a preloader component, the same one passed through fallback to <code>[React.Suspense()](https://reactjs.org/docs/code-splitting.html#suspense)</code>, and a delay after which the preloader should be rendered if the scene hasn’t finished loading.</p>
<p>This is useful when making sure our users see the same preloader without any interruption or flash of content from the moment a scene is requested to the moment when all of its files have been downloaded, all of the critical API requests have completed, and the component has finished rendering.</p>
<h3 id="heading-component-tiers">Component tiers</h3>
<p>As each application grows, its directory structure and abstractions evolve along with it. After roughly half a year of building and moving features to the new codebase, having a single <em>components</em> directory proved insufficient.<br>We needed our directory structure to inform us about:</p>
<ul>
<li>Have the components been developed to be generic, or are they meant only for a specific use-case?</li>
<li>Are they generic enough to be used across all the application, or should they used only in the certain contexts?</li>
<li>Who’s responsible for and most knowledgable about the code?</li>
</ul>
<p>Based on that we’ve defined the following <strong>Component Tiers</strong>:</p>
<h4 id="heading-1-application-specific-srcapp">1. Application-specific (src/app)</h4>
<p>Single-use components which cater to specific use-cases within this application, and which are not meant to be re-used or extracted to the component library (routes, footer, page header etc.).</p>
<h4 id="heading-2-generic-srccomponents">2. Generic (src/components)</h4>
<p>Generic multi-purpose components to be used all across the application and its scenes. Once we’ve arrived at a stable API for these components, they could be moved into the common component library (more on that below)</p>
<h4 id="heading-3-components-of-a-single-scene-srcscenesmy-scenecomponents">3. Components of a single scene (src/scenes/my-scene/components)</h4>
<p>Components developed with a specific use case in mind; not meant to be used in any other scenes. For cases when a component from one scene needs to be used in another one, we’d use:</p>
<h4 id="heading-4-multi-scene-components-srcscenescomponentsmy-feature">4. Multi-scene components (src/scenes/components/my-feature)</h4>
<p>Components used across multiples scenes, but not meant to be generic enough to be used anywhere else. To illustrate why simply moving them to <code>src/components</code> isn’t good enough:</p>
<p>Imagine that so far you’ve had a single scene which contained components used to build some rather specific data charts. Your team is now building a second scene which will use different data for the charts, but visually the two will look pretty much the same.</p>
<p>Importing components from one scene into another would break the encapsulation of the scene and would mean that we can no longer be certain whether changes made to a single scene’s components only affect that one scene.</p>
<p>For this purpose, any component or group of components, roughly referred to as a feature, would be placed in <code>src/scenes/components</code> from where it can be imported and used by any other team, however:</p>
<p>Whenever a team would like to start using scene components which another team developed, the best practice would be to reach out to that team first to figure out whether the use case you intend these components for can safely be supported in the future. Giving a heads up to the team who originally developed the code will prevent shipping broken features in the future when code you’ve taken into use inevitably gets changed in ways you didn’t expect (because of course, how could you!), and which might not always be caught by the unit tests.</p>
<h4 id="heading-5-common-library">5. Common library</h4>
<p>Components which we’ve battle-tested in production and want to extract to our shared component library, used by other dashboard teams at Unity.</p>
<h3 id="heading-ode-to-shared-dependencies">Ode to shared dependencies</h3>
<p>While it would be very convenient to be able to build and deploy every piece of our application in a fully isolated environment, certain dependencies — both external libraries and internal application code — are simply going to be used all across the codebase. Things like React itself, Redux and all redux-related logic, common navigational components etc.</p>
<h4 id="heading-rolling-out-the-changes">Rolling out the changes</h4>
<p>At the moment, fully encapsulating the scenes isn’t practical and in many cases simply impossible. It would take either shipping many dependencies multiple times over and in the process slowing down pages loads, or building abstractions meant to make certain libraries work in aways they’ve not been designed to.</p>
<p>As the web development and its ecosystem evolves though, the libraries seem to become more and more standalone and encapsulated, which we hope in the future will mean little to no shared dependencies, and true isolation between all the modules.</p>
<blockquote>
<p>Perhaps the biggest drawback of authoring large-scale applications is performing code changes and dependency updates without breaking something in the process</p>
</blockquote>
<p>Using a monorepository makes it possible (though not mandatory) to roll out changes and updates to the code in more gradual and safe manner — if a change causes issues, these issues will only affect a small part of the application, not the whole system.</p>
<p>And while for some the ability to perform updates on multiple unrelated areas of the codebase at the same time would come off as a benefit, the reality of having multiple teams working on the same codebase and not knowing all the other teams’ features thoroughly means that a great deal of caution is needed when building the application scaffolding and taking measures to minimize the risk of breakage.</p>
<h4 id="heading-how-to-avoid-breaking-things">How to avoid breaking things</h4>
<p>Perhaps the most fundamental strategy which helps us to do so, other than scene isolation, is having a <strong>high unit test coverage</strong>.</p>
<ol>
<li><strong>Testing</strong></li>
</ol>
<p>The unit tests aren’t of course everything — many mature products on even a moderate scale do after all invest in suites of integration and e2e tests which do a better job at verifying whether the application works as expected overall. However, as the number of features grows so does the maintenance cost and time needed to run them — a cost which cannot always be justified for less crucial but still important features.</p>
<p><strong>Some lessons we’ve learned from various testing strategies:</strong></p>
<ul>
<li>Try to unit test as much of the code as possible, especially: conditional logic, data transformations and function calls</li>
<li>Invest in and leverage integration tests to their full extent before deciding to write any e2e tests. The initial cost of integration tests is much higher, but pales in comparison to the price of upkeep of an e2e suite</li>
<li>Try not to over-react by starting to write e2e tests for things that weren’t caught by unit or integration tests. Sometimes, the processes or tooling are at fault</li>
<li>Let test cases explain UI behavior rather than implementation details</li>
<li>Automated tests cannot fully replace manual testing</li>
</ul>
<p><strong>2. Minimize the surface of shared code</strong></p>
<p>Aside from testing, code re-used across the whole application is kept to a reasonable minimum. One of the most useful strategies so far has been to move the most commonly used components and code to a shared component library, from where they are used as dependencies in scenes which need them. This allows us to roll out most of the changes progressively, on a per team- or page-basis.</p>
<p><strong>3. Accountability</strong></p>
<p>Last but not least, a huge factor in multiple teams being able to collaborate within the same codebase comes from encouraging and having <strong>developers take personal responsibility and accountability for the product</strong>, instead of offloading the responsibility for properly testing that everything works to Q.A., testers or automation.</p>
<p>This carries over to code reviews as well. Making sure each change is carefully reviewed is harder than it might seem on the surface. As team works closely together, a healthy degree of trust is developed between its members. This trust however, can sometimes translate into people being less diligent about changes made by the more experienced or otherwise trustworthy developers.</p>
<p>To encourage diligence, we emphasize that <strong>the author of the PR and the reviewer are equally responsible for ensuring everything works</strong>.</p>
<h3 id="heading-component-library">Component library</h3>
<p>To achieve the same look and feel across all the pages of our dashboards, we’ve developed a component library. What stands in our approach, is that new components are almost never developed within that library.</p>
<p>Every component, after being developed within the dashboard’s codebase, is taken into use in a bunch of features within that codebase first. Usually after a few weeks we begin to feel more confident that the component could be moved over, given that:</p>
<ul>
<li>The API is flexible enough to support the foreseeable use-cases</li>
<li>The component has been tested in a variety of contexts</li>
<li>The performance, responsiveness, and UX are all accounted for</li>
</ul>
<p>This process follows the <a target="_blank" href="https://blog.codinghorror.com/rule-of-three/">Rule of Three</a> and aims to help us <strong>release only components which are truly reusable</strong> and have been taken into use in a variety of contexts before being moved to our common library.</p>
<p>Some of the examples of the components we’d move over would include: footer, page header, side and top navigation elements, layout building blocks, banners, powered-up versions of buttons, typography elements etc.</p>
<p>In the early days, the component library used to be located in the same codebase as the application itself. We’ve since then extracted it to a separate repository to make the development process more democratized for other teams at Unity — important when driving for its adoption.</p>
<h4 id="heading-modular-component-design">Modular component design</h4>
<p>For the longest time, building reusable components meant dealing with multiple challenges, many of which often didn’t have good solutions:</p>
<ul>
<li>How to easily import the component along with its styles, and only that</li>
<li>How to override default styles without selector specificity wars</li>
<li>In bigger components consisting of multiple smaller ones, how to override the styling of the smaller component</li>
</ul>
<p>Our dashboard, as well as our component library heavily depend on and utilize <a target="_blank" href="https://material-ui.com/">Material UI</a>. What’s uniquely compelling in Material UI’s styling solution is the potential brought by <a target="_blank" href="https://cssinjs.org">JSS</a> and their <a target="_blank" href="https://medium.com/seek-blog/a-unified-styling-language-d0c208de2660">Unified Styling Language</a> (well worth the read), which make it possible to develop UIs <em>encapsulated by design</em> like in the case of <a target="_blank" href="https://github.com/css-modules/css-modules">CSS Modules</a>, and solve of the above mentioned issues in a stride.</p>
<p>This differs significantly from approaches like <a target="_blank" href="http://getbem.com/">BEM</a> which provide <em>encapsulation by convention</em> which tend to be less extensible and less encapsulated.</p>
<h3 id="heading-living-styleguide">Living styleguide</h3>
<p>A component library wouldn’t be complete without a way to showcase the components it contains and being able to see the components as they change throughout the releases.</p>
<p>We’ve had pretty good experience with <a target="_blank" href="https://storybook.js.org/">Storybook</a> which was ridiculously easy to setup and get started with, but after some time we realized a more robust and end-to-end solution was needed. Pretty close to what <a target="_blank" href="https://react-styleguidist.js.org/">Styleguidist</a> offers, but more tailored to our needs.</p>
<h4 id="heading-existing-design-docs">Existing design docs</h4>
<p>The documentation serving as the main source of information about the latest design specification was located in Confluence, where designers kept an up-to-date specification for each component using screenshots illustrating permitted use-cases, states and variations the component could be in, listed best practices, as well as details like dimensions, used colors etc. Following that approach we’ve faced a number of challenges:</p>
<ul>
<li><strong>Material design specification keeps evolving</strong> and because of that we oftentimes found ourselves either spending time on updating all the screenshots and guidelines, or let our design guidelines become outdated</li>
<li><strong>Figuring out which is more correct: implementation or specification</strong> wasn’t always an easy task. Because we’ve been publishing Storybook demos of every component and for every library version, we could see what and how changed. We couldn’t do the same for the design spec.</li>
<li><strong>Screenshots and videos can only communicate as much</strong>. To provide components of high quality and which can be used by multiple teams it’s necessary to review whether each component works in all resolutions, is bug-free and has good UX — this was difficult without having the designer sit literally next to you to see the implementation demo being shown on the screen</li>
</ul>
<h3 id="heading-component-documentation-app">Component documentation app</h3>
<p>Our documentation app aims to provide the means of efficient collaboration between designers and engineers to make it simpler and less time-consuming for both parties to document, review and develop components. To be more specific, we needed to:</p>
<ul>
<li><strong>Have a single point of reference showcasing the components</strong>, how should they look, behave, and be used — provided for every release — replacing detailed descriptions with live demos</li>
<li><strong>Make it as easy for designers and developers to collaborate</strong> on components and their docs and do so before the components are released — without the need of sharing videos, screenshots, or being physically in the same location</li>
<li><strong>Separate the designs into what we plan to do vs what has been done</strong></li>
</ul>
<p>Similarly like before, each release of the component library causes a new version of the living styleguide to be published. This time over however, there are a few differences:</p>
<ol>
<li><strong>Designers contribute to component documentation directly</strong> by editing documentation files through the Github UI, committing changes to the latest release.</li>
<li><strong>Component demos as WYSIWYG</strong> — the same code you see as an example of how to implement the component is used to render the demo, including any intermediate file imports, variable declarations etc. As an added bonus, components wrapped in <code>withStyles()</code> are displayed correctly (<a target="_blank" href="https://github.com/storybooks/storybook/issues/3851">issue</a> present in Storybook at the moment).</li>
<li><strong>Changes to the docs and the code are almost instantly visible</strong> without checking out the branch locally and starting the documentation app — the app is rebuilt and published on and for every commit.</li>
</ol>
<p><img src="https://cdn-media-1.freecodecamp.org/images/QPddvKiDlGWgFTSYYHzPqWd5znUsU1BcHJ10" alt="Image" width="800" height="418" loading="lazy"></p>
<h3 id="heading-development-experience">Development experience</h3>
<p>One of the main goals of code reviews is making sure that each change is carefully reviewed, considered and tested before being merged and deployed.</p>
<p>To make this task as obstacle-free as possible we’ve developed a <strong>Preview Server</strong> capable of creating a new build of our application every time a PR is created or updated.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/NDRV4o2ldxNzhA2UKhGJzKoWhieO4xfioA0k" alt="Image" width="800" height="286" loading="lazy">
<em>A comment containing version links gets added to every PR and is updated on every pushed change</em></p>
<p>Our designers, product managers and engineers can test each change before merging it in, in both staging and production environments and within minutes of making the change.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/YT6YyLFIVEkURWwR0bNvMlnnf1v1-A43bff5" alt="Image" width="800" height="275" loading="lazy">
<em>Browsing production version of the application before merging the PR</em></p>
<h3 id="heading-closing-words">Closing words</h3>
<p>It’s been nearly a year since we’ve undertaken to consolidate our dashboards. We’ve spent that time learning how to grow a large but healthy software project, how to get better at collaboration and communication, and how to raise the quality bar for ourselves.</p>
<p>We scaled a frontend project not only in terms of lines of code, but also in terms of number of engineers who work within its codebase — a number which quadrupled since the beginning.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/qP03wrJ8qgRo6ewiwac2oQDZCai50SmXiG6a" alt="Image" width="800" height="552" loading="lazy">
<em>Code frequency from the beginning of project’s existence until now</em></p>
<p>We did a 180 degree change in dealing with time differences between our teams, moving away from a model where our teams worked in full isolation to one where close collaboration and communication are an everyday occurrence.</p>
<p>While we still have a long road ahead to ensure we can scale our approach to more teams and to bigger challenges, we’ve noticed a number of improvements already:</p>
<ul>
<li><strong>Roadmap and work visibility</strong><br>Due to having one place where all the work is happening, the progress gets tracked, and all the issues are gathered in</li>
<li><strong>Development velocity and time-to-market</strong><br>New features can be created in large part from already existing and well-tested components — easily findable through our documentation app</li>
<li><strong>Code quality &amp; test coverage</strong><br>When building new things, a solution to a similar problem usually already exists and is at a hand’s reach, along with examples how to test it</li>
<li><strong>Overall quality &amp; UX</strong><br>Testing features and ensuring their quality is now easier than ever, as designers, product managers and other stakeholders can test each change on their own machine, with their own accounts and data sets</li>
</ul>
<p>Naturally, along the way we’ve encountered a number of challenges which we need to solve, or which will need solving in the future:</p>
<ul>
<li><strong>Build &amp; CI performance</strong><br>As the numbers of dependencies, build bundles, and tests grow, as does the time needed to do a deployment. In the future, we’ll need to develop tooling to help us only build, test and deploy the pieces which changed.</li>
<li><strong>Development culture</strong><br>To build healthy software, we need to continuously work on healthy ways of communicating and exchanging ideas, and text-based communications make this task more difficult. We’re working to address this issue through a series regular leadership training sessions and embracing a more open-source ways of working, as well as organizing a few get together sessions per year for the teams to meet each other face to face.</li>
<li><strong>Breakage isolation &amp; updates</strong><br>As the number of features and pages grows, we’ll need a more robust way of isolating our application modules to prevent damage from spreading for when things go wrong. This could be achieved by versioning all the shared code (redux logic, src/components), or in extreme cases producing standalone builds of certain features.</li>
</ul>
<h4 id="heading-state-then-now-and-in-the-future">State then, now and in the future</h4>
<p>The migration has involved moving away from AngularJS to React. Here’s how the situation changed over the past year:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/hrtCCbYM4vq2Pcm8YXGwJj9ZxT9Cc9zxk7QL" alt="Image" width="800" height="395" loading="lazy">
<em>April 2018</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/vCd4c0YHkqK3HQT7PBM1ZiHbGQyXTBTVcKIp" alt="Image" width="800" height="423" loading="lazy">
<em>February 2019</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Ak8ZHR-gW1rx70m7nKLZV6szLNZxtpLjVDrA" alt="Image" width="800" height="496" loading="lazy">
<em>Where we hope our dashboards will be by the end of 2019</em></p>
<p>It’s a wrap! Thank you for reading! You can find me on LinkedIn <a target="_blank" href="https://www.linkedin.com/in/maciejgurban/">here</a>.</p>
<p>If working on similar challenges sounds interesting to you, we’re always looking for talented engineers to join our teams <a target="_blank" href="https://careers.unity.com/">all around the world</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Scaling Node.js Applications ]]>
                </title>
                <description>
                    <![CDATA[ By Samer Buna Everything you need to know about Node.js built-in tools for scalability Update: This article is now part of my book “Node.js Beyond The Basics”. Read the updated version of this content and more about Node at jscomplete.com/node-beyon... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/scaling-node-js-applications-8492bd8afadc/</link>
                <guid isPermaLink="false">66d46107768263422736e8bd</guid>
                
                    <category>
                        <![CDATA[ distributed systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 14 Jul 2017 01:32:54 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*C7ICI8d7aAna_zTZvZ64MA.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Samer Buna</p>
<h4 id="heading-everything-you-need-to-know-about-nodejs-built-in-tools-for-scalability">Everything you need to know about Node.js built-in tools for scalability</h4>
<blockquote>
<p><strong>Update:</strong> This article is now part of my book “Node.js Beyond The Basics”.</p>
<p>Read the updated version of this content and more about Node at <a target="_blank" href="https://jscomplete.com/g/scaling-node"><strong>jscomplete.com/node-beyond-basics</strong></a>.</p>
</blockquote>
<p>Scalability in Node.js is not an afterthought. It’s something that’s baked into the core of the runtime. Node is named Node to emphasize the idea that a Node application should comprise multiple small distributed <em>nodes</em> that communicate with each other.</p>
<p>Are you running multiple nodes for your Node applications? Are you running a Node process on every CPU core of your production machines and load balancing all the requests among them? Did you know that Node has a built-in module to help with that?</p>
<p>Node’s <em>cluster</em> module not only provides an out-of-the-box solution to utilizing the full CPU power of a machine, but it also helps with increasing the availability of your Node processes and provides an option to restart the whole application with a zero downtime. This article covers all that goodness and more.</p>
<blockquote>
<p>This article is a write-up of part of <a target="_blank" href="https://www.pluralsight.com/courses/nodejs-advanced">my Pluralsight course about Node.js</a>. I cover similar content in video format there.</p>
</blockquote>
<h3 id="heading-strategies-of-scalability">Strategies of Scalability</h3>
<p>The workload is the most popular reason we scale our applications, but it’s not the only reason. We also scale our applications to increase their availability and tolerance to failure.</p>
<p>There are mainly three different things we can do to scale an application:</p>
<h4 id="heading-1-cloning">1 — Cloning</h4>
<p>The easiest thing to do to scale a big application is to clone it multiple times and have each cloned instance handle part of the workload (with a load balancer, for example). This does not cost a lot in term of development time and it’s highly effective. This strategy is the minimum you should do and Node.js has the built-in module, <code>cluster</code>, to make it easier for you to implement the cloning strategy on a single server.</p>
<h4 id="heading-2-decomposing">2 — Decomposing</h4>
<p>We can also scale an application by <a target="_blank" href="https://builttoadapt.io/whats-your-decomposition-strategy-e19b8e72ac8f">decomposing</a> it based on functionalities and services. This means having multiple, different applications with different code bases and sometimes with their own dedicated databases and User Interfaces.</p>
<p>This strategy is commonly associated with the term <em>Microservice</em>, where micro indicates that those services should be as small as possible, but in reality, the size of the service is not what’s important but rather the enforcement of loose coupling and high cohesion between services. The implementation of this strategy is often not easy and could result in long-term unexpected problems, but when done right the advantages are great.</p>
<h4 id="heading-3-splitting">3 — Splitting</h4>
<p>We can also split the application into multiple instances where each instance is responsible for only a part of the application’s data. This strategy is often named <em>horizontal partitioning</em>, or <em>sharding</em>, in databases. Data partitioning requires a lookup step before each operation to determine which instance of the application to use. For example, maybe we want to partition our users based on their country or language. We need to do a lookup of that information first.</p>
<p>Successfully scaling a big application should eventually implement all three strategies. Node.js makes it easy to do so but I am going to focus on the cloning strategy in this article and explore the built-in tools available in Node.js to implement it.</p>
<p>Please note that you need a good understanding of Node.js <em>child processes</em> before reading this article. If you haven’t already, I recommend that you read this other article first:</p>
<p><a target="_blank" href="https://medium.freecodecamp.org/node-js-child-processes-everything-you-need-to-know-e69498fe970a"><strong>Node.js Child Processes: Everything you need to know</strong></a><br><a target="_blank" href="https://medium.freecodecamp.org/node-js-child-processes-everything-you-need-to-know-e69498fe970a">_How to use spawn(), exec(), execFile(), and fork()_medium.freecodecamp.org</a></p>
<h3 id="heading-the-cluster-module">The Cluster Module</h3>
<p>The cluster module can be used to enable load balancing over an environment’s multiple CPU cores. It’s based on the child process module <code>fork</code> method and it basically allows us to fork the main application process as many times as we have CPU cores. It will then take over and load balance all requests to the main process across all forked processes.</p>
<p>The cluster module is Node’s helper for us to implement the cloning scalability strategy, but only on one machine. When you have a big machine with a lot of resources or when it’s easier and cheaper to add more resources to one machine rather than adding new machines, the cluster module is a great option for a really quick implementation of the cloning strategy.</p>
<p>Even small machines usually have multiple cores and even if you’re not worried about the load on your Node server, you should enable the cluster module anyway to increase your server availability and fault-tolerance. It’s a simple step and when using a process manager like PM2, for example, it becomes as simple as just providing an argument to the launch command!</p>
<p>But let me tell you how to use the cluster module natively and explain how it works.</p>
<p>The structure of what the cluster module does is simple. We create a <em>master</em> process and that master process forks a number of <em>worker</em> processes and manages them. Each worker process represents an instance of the application that we want to scale. All incoming requests are handled by the master process, which is the one that decides which worker process should handle an incoming request.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*C7ICI8d7aAna_zTZvZ64MA.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>The master process’s job is easy because it actually just uses a <em>round-robin</em> algorithm to pick a worker process. This is enabled by default on all platforms except Windows and it can be globally modified to let the load-balancing be handled by the operation system itself.</p>
<p>The round-robin algorithm distributes the load evenly across all available processes on a rotational basis. The first request is forwarded to the first worker process, the second to the next worker process in the list, and so on. When the end of the list is reached, the algorithm starts again from the beginning.</p>
<p>This is one of the simplest and most used load balancing algorithms. But it’s not the only one. More featured algorithms allow assigning priorities and selecting the least loaded server or the one with the fastest response time.</p>
<h4 id="heading-load-balancing-an-http-server">Load-Balancing an HTTP Server</h4>
<p>Let’s clone and load balance a simple HTTP server using the cluster module. Here’s the simple Node’s hello-world example server slightly modified to simulate some CPU work before responding:</p>
<pre><code><span class="hljs-comment">// server.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-keyword">const</span> pid = process.pid;

http.createServer(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>; i&lt;<span class="hljs-number">1e7</span>; i++); <span class="hljs-comment">// simulate CPU work</span>
  res.end(<span class="hljs-string">`Handled by process <span class="hljs-subst">${pid}</span>`</span>);
}).listen(<span class="hljs-number">8080</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Started process <span class="hljs-subst">${pid}</span>`</span>);
});
</code></pre><p>To verify that the balancer we’re going to create is going to work, I’ve included the process <code>pid</code> in the HTTP response to identify which instance of the application is actually handling a request.</p>
<p>Before we create a cluster to clone this server into multiple workers, let’s do a simple benchmark of how many requests this server can handle per second. We can use the <a target="_blank" href="https://httpd.apache.org/docs/2.4/programs/ab.html">Apache benchmarking tool</a> for that. After running the simple <code>server.js</code> code above, run this <code>ab</code> command:</p>
<pre><code>ab -c200 -t10 http:<span class="hljs-comment">//localhost:8080/</span>
</code></pre><p>This command will test-load the server with 200 concurrent connections for 10 seconds.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*w8VmzV81atlTzHn7pDXu1g.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>On my machine, the single node server was able to handle about 51 requests per second. Of course, the results here will be different on different platforms and this is a very simplified test of performance that’s not a 100% accurate, but it will clearly show the difference that a cluster would make in a multi-core environment.</p>
<p>Now that we have a reference benchmark, we can scale the application with the cloning strategy using the cluster module.</p>
<p>On the same level as the <code>server.js</code> file above, we can create a new file (<code>cluster.js</code>) for the master process with this content (explanation follows):</p>
<pre><code class="lang-js"><span class="hljs-comment">// cluster.js</span>
<span class="hljs-keyword">const</span> cluster = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cluster'</span>);
<span class="hljs-keyword">const</span> os = <span class="hljs-built_in">require</span>(<span class="hljs-string">'os'</span>);

<span class="hljs-keyword">if</span> (cluster.isMaster) {
  <span class="hljs-keyword">const</span> cpus = os.cpus().length;

  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Forking for <span class="hljs-subst">${cpus}</span> CPUs`</span>);
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i&lt;cpus; i++) {
    cluster.fork();
  }
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">require</span>(<span class="hljs-string">'./server'</span>);
}
</code></pre>
<p>In <code>cluster.js</code>, we first required both the <code>cluster</code> module and the <code>os</code> module. We use the <code>os</code> module to read the number of CPU cores we can work with using <code>os.cpus()</code>.</p>
<p>The <code>cluster</code> module gives us the handy Boolean flag <code>isMaster</code> to determine if this <code>cluster.js</code> file is being loaded as a master process or not. The first time we execute this file, we will be executing the master process and that <code>isMaster</code> flag will be set to true. In this case, we can instruct the master process to fork our server as many times as we have CPU cores.</p>
<p>Now we just read the number of CPUs we have using the <code>os</code> module, then with a for loop over that number, we call the <code>cluster.fork</code> method. The for loop will simply create as many workers as the number of CPUs in the system to take advantage of all the available processing power.</p>
<p>When the <code>cluster.fork</code> line is executed from the master process, the current file, <code>cluster.js</code>, is run again, but this time in <em>worker mode</em> with the <code>isMaster</code> flag set to false. <em>There is actually another flag set to true in this case if you need to use it, which is the <code>isWorker</code> flag.</em></p>
<p>When the application runs as a worker, it can start doing the actual work. This is where we need to define our server logic, which, for this example, we can do by requiring the <code>server.js</code> file that we have already.</p>
<p>That’s basically it. That’s how easy it is to take advantage of all the processing power in a machine. To test the cluster, run the <code>cluster.js</code> file:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*c0S-W4GYgCGB_maJ94ZLPw.png" alt="Image" width="708" height="548" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>I have 8 cores on my machine so it started 8 processes. It’s important to understand that these are completely different Node.js processes. Each worker process here will have its own event loop and memory space.</p>
<p>When we now hit the web server multiple times, the requests will start to get handled by different worker processes with different process ids. The workers will not be exactly rotated in sequence because the cluster module performs some optimizations when picking the next worker, but the load will be somehow distributed among the different worker processes.</p>
<p>We can use the same <code>ab</code> command above to load-test this cluster of processes:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*5_EogHG-Egf2uAMOj9PmCA.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>The cluster I created on my machine was able to handle 181 requests per second in comparison to the 51 requests per second that we got using a single Node process. The performance of this simple application tripled with just a few lines of code.</p>
<h4 id="heading-broadcasting-messages-to-all-workers">Broadcasting Messages to All Workers</h4>
<p>Communicating between the master process and the workers is simple because under the hood the cluster module is just using the <code>child_process.fork</code> API, which means we also have communication channels available between the master process and each worker.</p>
<p>Based on the <code>server.js</code>/<code>cluster.js</code> example above, we can access the list of worker objects using <code>cluster.workers</code>, which is an object that holds a reference to all workers and can be used to read information about these workers. Since we have communication channels between the master process and all workers, to broadcast a message to all them we just need a simple loop over all the workers. For example:</p>
<pre><code class="lang-js"><span class="hljs-built_in">Object</span>.values(cluster.workers).forEach(<span class="hljs-function"><span class="hljs-params">worker</span> =&gt;</span> {
  worker.send(<span class="hljs-string">`Hello Worker <span class="hljs-subst">${worker.id}</span>`</span>);
});
</code></pre>
<p>We simply used <code>Object.values</code> to get an array of all workers from the <code>cluster.workers</code> object. Then, for each worker, we can use the <code>send</code> function to send over any value that we want.</p>
<p>In a worker file, <code>server.js</code> in our example, to read a message received from this master process, we can register a handler for the <code>message</code> event on the global <code>process</code> object. For example:</p>
<pre><code class="lang-js">process.on(<span class="hljs-string">'message'</span>, <span class="hljs-function"><span class="hljs-params">msg</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Message from master: <span class="hljs-subst">${msg}</span>`</span>);
});
</code></pre>
<p>Here is what I see when I test these two additions to the cluster/server example:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*6XfoWiNKTCiDjqar7L5_xw.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>Every worker received a message from the master process. <em>Note how the workers did not start in order.</em></p>
<p>Let’s make this communication example a little bit more practical. Let’s say we want our server to reply with the number of users we have created in our database. We’ll create a mock function that returns the number of users we have in the database and just have it square its value every time it’s called (dream growth):</p>
<pre><code class="lang-js"><span class="hljs-comment">// **** Mock DB Call</span>
<span class="hljs-keyword">const</span> numberOfUsersInDB = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-built_in">this</span>.count = <span class="hljs-built_in">this</span>.count || <span class="hljs-number">5</span>;
  <span class="hljs-built_in">this</span>.count = <span class="hljs-built_in">this</span>.count * <span class="hljs-built_in">this</span>.count;
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.count;
}
<span class="hljs-comment">// ****</span>
</code></pre>
<p>Every time <code>numberOfUsersInDB</code> is called, we’ll assume that a database connection has been made. What we want to do here — to avoid multiple DB requests — is to cache this call for a certain period of time, such as 10 seconds. However, we still don’t want the 8 forked workers to do their own DB requests and end up with 8 DB requests every 10 seconds. We can have the master process do just one request and tell all of the 8 workers about the new value for the user count using the communication interface.</p>
<p>In the master process mode, we can, for example, use the same loop to broadcast the users count value to all workers:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Right after the fork loop within the isMaster=true block</span>
<span class="hljs-keyword">const</span> updateWorkers = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> usersCount = numberOfUsersInDB();
  <span class="hljs-built_in">Object</span>.values(cluster.workers).forEach(<span class="hljs-function"><span class="hljs-params">worker</span> =&gt;</span> {
    worker.send({ usersCount });
  });
};

updateWorkers();
<span class="hljs-built_in">setInterval</span>(updateWorkers, <span class="hljs-number">10000</span>);
</code></pre>
<p>Here we’re invoking <code>updateWorkers</code> for the first time and then invoking it every 10 seconds using a <code>setInterval</code>. This way, every 10 seconds, all workers will receive the new user count value over the process communication channel and only one database connection will be made.</p>
<p>In the server code, we can use the <code>usersCount</code> value using the same <code>message</code> event handler. We can simply cache that value with a module global variable and use it anywhere we want.</p>
<p>For example:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);
<span class="hljs-keyword">const</span> pid = process.pid;

<span class="hljs-keyword">let</span> usersCount;

http.createServer(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>; i&lt;<span class="hljs-number">1e7</span>; i++); <span class="hljs-comment">// simulate CPU work</span>
  res.write(<span class="hljs-string">`Handled by process <span class="hljs-subst">${pid}</span>\n`</span>);
  res.end(<span class="hljs-string">`Users: <span class="hljs-subst">${usersCount}</span>`</span>);
}).listen(<span class="hljs-number">8080</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Started process <span class="hljs-subst">${pid}</span>`</span>);
});

process.on(<span class="hljs-string">'message'</span>, <span class="hljs-function"><span class="hljs-params">msg</span> =&gt;</span> {
  usersCount = msg.usersCount;
});
</code></pre>
<p>The above code makes the worker web server respond with the cached <code>usersCount</code> value. If you test the cluster code now, during the first 10 seconds you’ll get “25” as the users count from all workers (and only one DB request would be made). Then after another 10 seconds, all workers would start reporting the new user count, 625 (and only one other DB request would be made).</p>
<p>This is all possible thanks to the communication channels between the master process and all workers.</p>
<h4 id="heading-increasing-server-availability">Increasing Server Availability</h4>
<p>One of the problems in running a single instance of a Node application is that when that instance crashes, it has to be restarted. This means some downtime between these two actions, even if the process was automated as it should be.</p>
<p>This also applies to the case when the server has to be restarted to deploy new code. With one instance, there will be downtime which affects the availability of the system.</p>
<p>When we have multiple instances, the availability of the system can be easily increased with just a few extra lines of code.</p>
<p>To simulate a random crash in the server process, we can simply do a <code>process.exit</code> call inside a timer that fires after a random amount of time:</p>
<pre><code class="lang-js"><span class="hljs-comment">// In server.js</span>
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
  process.exit(<span class="hljs-number">1</span>) <span class="hljs-comment">// death by random timeout</span>
}, <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">10000</span>);
</code></pre>
<p>When a worker process exits like this, the master process will be notified using the <code>exit</code> event on the <code>cluster</code> model object. We can register a handler for that event and just fork a new worker process when any worker process exits.</p>
<p>For example:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Right after the fork loop within the isMaster=true block</span>
cluster.on(<span class="hljs-string">'exit'</span>, <span class="hljs-function">(<span class="hljs-params">worker, code, signal</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (code !== <span class="hljs-number">0</span> &amp;&amp; !worker.exitedAfterDisconnect) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Worker <span class="hljs-subst">${worker.id}</span> crashed. `</span> +
                <span class="hljs-string">'Starting a new worker...'</span>);
    cluster.fork();
  }
});
</code></pre>
<p>It’s good to add the if condition above to make sure the worker process actually crashed and was not manually disconnected or killed by the master process itself. For example, the master process might decide that we are using too many resources based on the load patterns it sees and it will need to kill a few workers in that case. To do so, we can use the <code>disconnect</code> methods on any worker and, in that case, the <code>exitedAfterDisconnect</code> flag will be set to true. The if statement above will guard to not fork a new worker for that case.</p>
<p>If we run the cluster with the handler above (and the random crash in <code>server.js</code>), after a random number of seconds, workers will start to crash and the master process will immediately fork new workers to increase the availability of the system. You can actually measure the availability using the same <code>ab</code> command and see how many requests the server will not be able to handle overall (because some of the unlucky requests will have to face the crash case and that’s hard to avoid.)</p>
<p>When I tested the code, only 17 requests failed out of over 1800 in the 10-second testing interval with 200 concurrent requests.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*B72o6QhsyiNnEQU5Wx20RQ.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>That’s over 99% availability. By just adding a few lines of code, we now don’t have to worry about process crashes anymore. The master guardian will keep an eye on those processes for us.</p>
<h4 id="heading-zero-downtime-restarts">Zero-downtime Restarts</h4>
<p>What about the case when we want to restart all worker processes when, for example, we need to deploy new code?</p>
<p>We have multiple instances running, so instead of restarting them together, we can simply restart them one at a time to allow other workers to continue to serve requests while one worker is being restarted.</p>
<p>Implementing this with the cluster module is easy. Since we don’t want to restart the master process once it’s up, we need a way to send this master process a command to instruct it to start restarting its workers. This is easy on Linux systems because we can simply listen to a process signal like <code>SIGUSR2</code>, which we can trigger by using the <code>kill</code> command on the process id and passing that signal:</p>
<pre><code class="lang-js"><span class="hljs-comment">// In Node</span>
process.on(<span class="hljs-string">'SIGUSR2'</span>, <span class="hljs-function">() =&gt;</span> { ... });
<span class="hljs-comment">// To trigger that</span>
$ kill -SIGUSR2 PID
</code></pre>
<p>This way, the master process will not be killed and we have a way to instruct it to start doing something. <code>SIGUSR2</code> is a proper signal to use here because this will be a user command. If you’re wondering why not <code>SIGUSR1</code>, it’s because Node uses that for its debugger and you want to avoid any conflicts.</p>
<p>Unfortunately, on Windows, these process signal are not supported and we would have to find another way to command the master process to do something. There are some alternatives. We can, for example, use standard input or socket input. Or we can monitor the existence of a <code>process.pid</code> file and watch that for a remove event. But to keep this example simple, we’ll just assume this server is running on a Linux platform.</p>
<p>Node works very well on Windows, but I think it’s a much safer option to host production Node applications on a Linux platform. This is not just because of Node itself, but many other production tools that are much more stable on Linux. This is my personal opinion and feel free to completely ignore it.</p>
<p><em>By the way, on recent versions of Windows, you can actually use a Linux subsystem and it works very well. I’ve tested it myself and it was nothing short of impressive. If you’re developing a Node applications on Windows, check out <a target="_blank" href="https://msdn.microsoft.com/en-us/commandline/wsl/about">Bash on Windows</a> and give it a try.</em></p>
<p>In our example, when the master process receives the <code>SIGUSR2</code> signal, that means it’s time for it to restart its workers, but we want to do that one worker at a time. This simply means the master process should only restart the next worker when it’s done restarting the current one.</p>
<p>To begin this task, we need to get a reference to all current workers using the <code>cluster.workers</code> object and we can simply just store the workers in an array:</p>
<pre><code><span class="hljs-keyword">const</span> workers = <span class="hljs-built_in">Object</span>.values(cluster.workers);
</code></pre><p>Then, we can create a <code>restartWorker</code> function that receives the index of the worker to be restarted. This way we can do the restarting in sequence by having the function call itself when it’s ready for the next worker. Here’s an example <code>restartWorker</code> function that we can use (explanation follows):</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> restartWorker = <span class="hljs-function">(<span class="hljs-params">workerIndex</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> worker = workers[workerIndex];
  <span class="hljs-keyword">if</span> (!worker) <span class="hljs-keyword">return</span>;

  worker.on(<span class="hljs-string">'exit'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!worker.exitedAfterDisconnect) <span class="hljs-keyword">return</span>;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Exited process <span class="hljs-subst">${worker.process.pid}</span>`</span>);

    cluster.fork().on(<span class="hljs-string">'listening'</span>, <span class="hljs-function">() =&gt;</span> {
      restartWorker(workerIndex + <span class="hljs-number">1</span>);
    });
  });

  worker.disconnect();
};

restartWorker(<span class="hljs-number">0</span>);
</code></pre>
<p>Inside the <code>restartWorker</code> function, we got a reference to the worker to be restarted and since we will be calling this function recursively to form a sequence, we need a stop condition. When we no longer have a worker to restart, we can just return. We then basically want to disconnect this worker (using <code>worker.disconnect</code>), but before restarting the next worker, we need to fork a new worker to replace this current one that we’re disconnecting.</p>
<p>We can use the <code>exit</code> event on the worker itself to fork a new worker when the current one exists, but we have to make sure that the exit action was actually triggered after a normal disconnect call. We can use the <code>exitedAfetrDisconnect</code> flag. If this flag is not true, the exit was caused by something else other than our disconnect call and in that case, we should just return and do nothing. But if the flag is set to true, we can go ahead and fork a new worker to replace the one that we’re disconnecting.</p>
<p>When this new forked worker is ready, we can restart the next one. However, remember that the fork process is not synchronous, so we can’t just restart the next worker after the fork call. Instead, we can monitor the <code>listening</code> event on the newly forked worker, which tells us that this worker is connected and ready. When we get this event, we can safely restart the next worker in sequence.</p>
<p>That’s all we need for a zero-downtime restart. To test it, you’ll need to read the master process id to be sent to the <code>SIGUSR2</code> signal:</p>
<pre><code><span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Master PID: <span class="hljs-subst">${process.pid}</span>`</span>);
</code></pre><p>Start the cluster, copy the master process id, and then restart the cluster using the <code>kill -SIGUSR2 PID</code> command. You can also run the same <code>ab</code> command while restarting the cluster to see the effect that this restart process will have on availability. Spoiler alert, you should get ZERO failed requests:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*NjG0e2ARIDQiYSHWNvdNPQ.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>Process monitors like PM2, which I personally use in production, make all the tasks we went through so far extremely easy and give a lot more features to monitor the health of a Node.js application. For example, with PM2, to launch a cluster for any app, all you need to do is use the <code>-i</code> argument:</p>
<pre><code>pm2 start server.js -i max
</code></pre><p>And to do a zero downtime restart you just issue this magic command:</p>
<pre><code>pm2 reload all
</code></pre><p>However, I find it helpful to first understand what actually will happen under the hood when you use these commands.</p>
<h4 id="heading-shared-state-and-sticky-load-balancing">Shared State and Sticky Load Balancing</h4>
<p>Good things always come with a cost. When we load balance a Node application, we lose some features that are only suitable for a single process. This problem is somehow similar to what’s known in other languages as thread safety, which is about sharing data between threads. In our case, it’s sharing data between worker processes.</p>
<p>For example, with a cluster setup, we can no longer cache things in memory because every worker process will have its own memory space. If we cache something in one worker’s memory, other workers will not have access to it.</p>
<p>If we need to cache things with a cluster setup, we have to use a separate entity and read/write to that entity’s API from all workers. This entity can be a database server or if you want to use in-memory cache you can use a server like Redis or create a dedicated Node process with a read/write API for all other workers to communicate with.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*dIR_CAkmtPFgtaGTOKBFkA.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>Don’t look at this as a disadvantage though, because using a separate entity for your application caching needs is part of <em>decomposing</em> your app for scalability. You should probably be doing that even if you’re running on a single core machine.</p>
<p>Other than caching, when we’re running on a cluster, stateful communication in general becomes a problem. Since the communication is not guaranteed to be with the same worker, creating a stateful channel on any one worker is not an option.</p>
<p>The most common example for this is authenticating users.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*jKAmrLPMer6_kmpIjyGzxA.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>With a cluster, the request for authentication comes to the master balancer process, which gets sent to a worker, assuming that to be A in this example.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*dNUlcuEXPkk44A63ct0s0g.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>Worker A now recognizes the state of this user. However, when the same user makes another request, the load balancer will eventually send them to other workers, which do not have them as authenticated. Keeping a reference to an authenticated user session in one instance memory is not going to work anymore.</p>
<p>This problem can be solved in many ways. We can simply share the state across the many workers we have by storing these sessions’ information in a shared database or a Redis node. However, applying this strategy requires some code changes, which is not always an option.</p>
<p>If you can’t do the code modifications needed to make a shared storage of sessions here, there is a less invasive but not as efficient strategy. You can use what’s known as Sticky Load Balancing. This is much simpler to implement as many load balancers support this strategy out of the box. The idea is simple. When a user authenticates with a worker instance, we keep a record of that relation on the load balancer level.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*P4LNRLkZ9n_p8OKtmRM9LA.png" alt="Image" width="800" height="450" loading="lazy">
<em>Screenshot captured from my Pluralsight course — Advanced Node.js</em></p>
<p>Then, when the same user sends a new request, we do a lookup in this record to figure out which server has their session authenticated and keep sending them to that server instead of the normal distributed behavior. This way, the code on the server side does not have to be changed, but we don’t really get the benefit of load balancing for authenticated users here so only use sticky load balancing if you have no other option.</p>
<p>The cluster module actually does not support sticky load balancing, but a few other load balancers can be configured to do sticky load balancing by default.</p>
<p>Thanks for reading.</p>
<p>Learning React or Node? Checkout my books:</p>
<ul>
<li><a target="_blank" href="http://amzn.to/2peYJZj">Learn React.js by Building Games</a></li>
<li><a target="_blank" href="http://amzn.to/2FYfYru">Node.js Beyond the Basics</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
