Concurrency 👨🏻‍💻, Sorting ⏳, Best practices📜, ML cases🤖, Upgrading DB ⛁

By Prajwal Haniya

Techletter #50 | December 9, 2023

How does nodejs handle concurrency?

Node.js is a single-threaded, event-driven, non-blocking I/O model programming language that makes it efficient for handling multiple requests simultaneously. This is achieved by using an event loop.

Node internally uses multiple POSIX threads.

what the heck is event loop?

How nodejs handles concurrency & scalability?

A complete guide to threads in Node.js

Sorting Algorithms

Credits: Algomonster

Sorting is one of the most fundamental parts of computer programming. While many programming languages support inbuilt sort, understanding how the sort works can be important during its real-life applications. Some of the sorting algorithms are:

Insertion Sort:

In insertion sort, only the first item is considered sorted. Then, for each item in the sequence, we “insert” that item into the sorted list by swapping it with the item before it until it is smaller than the current item.

function sortList(unsortedList) {
    for (var i = 0; i < unsortedList.length; i++) {
        let current = i;
        // gets the smallest element and inserts at current index 
        while (current > 0 && unsortedList[current] < unsortedList[current - 1]) {
            const temp = unsortedList[current];
            // swaps current smaller element with the element before it
            unsortedList[current] = unsortedList[current - 1];
            unsortedList[current - 1] = temp;
            current--;
        }
    }
    return unsortedList;
}

Selection Sort

The idea for this sorting algorithm is that we find the smallest item from the unsorted pile during each cycle and add it to the sorted pile.

function sortList(unsortedList) {
    const n = unsortedList.length;
    for (var i = 0; i < n; i++) {
        // Assume the current position as minimum
        let minIndex = i;

        // Find the minimum element's index from the rest of the list
        for (var j = i; j < n; j++) {
            if (unsortedList[j] < unsortedList[minIndex]) {
                minIndex = j;
            }
        }

        // Swap the minimum element with the first element
        [unsortedList[i], unsortedList[minIndex]] = [unsortedList[minIndex], unsortedList[i]];
    }
    return unsortedList;
}

Bubble Sort

In bubble sort, for each pass, we use a pointer to point at the first element of the list. For each cycle, we compare it to the next element in the list and swap them if the current item is greater, then move the pointer by one until it reaches the end of the list. We repeat this process until the list becomes sorted.

function sortList(unsortedList) {
    const n = unsortedList.length;
    // Iterate through all list elements in reversed order
    for (var i = n - 1; i >= 0; i--) {

        // Track whether a swap occurred in this pass
        let swapped = false;
        for (var j = 0; j < i; j++) {

            // Swap if the element found is greater than the next element
            if (unsortedList[j] > unsortedList[j + 1]) {
                const temp = unsortedList[j];
                unsortedList[j] = unsortedList[j + 1];
                unsortedList[j + 1] = temp;
                swapped = true;
            }
        }

        // If no two elements were swapped, the list is sorted
        if (!swapped) return unsortedList;
    }
    return unsortedList;
}

Merge Sort

The idea of a merge sort is divide and conquer: We divide the array into two almost equally, sort them (usually another merge sort), and merge the two sorted lists into one.

function sortList(unsortedList) {
    const n = unsortedList.length;

    // Base case: A list of size 1 or 0 is already sorted
    if (n <= 1) return unsortedList;

    // Split the list into left and right halves
    const midpoint = Math.floor(n / 2);
    const leftList = sortList(unsortedList.slice(0, midpoint));
    const rightList = sortList(unsortedList.slice(midpoint));

    const res = [];
    let leftPtr = 0, rightPtr = 0;

    // Merge the sorted left and right lists with two pointers
    while (leftPtr < midpoint || rightPtr < n - midpoint) {
        if (leftPtr === midpoint) {  // If left list is empty, append element from right
            res.push(rightList[rightPtr]);
            rightPtr++;
        } else if (rightPtr === n - midpoint) {  // If right list is empty, append element from left
            res.push(leftList[leftPtr]);
            leftPtr++;
        } else if (leftList[leftPtr] <= rightList[rightPtr]) {  // Append smaller element from left
            res.push(leftList[leftPtr]);
            leftPtr++;
        } else {  // Append smaller element from right
            res.push(rightList[rightPtr]);
            rightPtr++;
        }
    }

    return res;
}

Nodejs Best practices

Just writing the code isn’t enough. It should be both scalable & maintainable. For it to be both scalable & maintainable we need best practices followed by many in the industry who have already built scalable applications using nodejs. Here is one such git repository that teaches us the best practices of nodejs.

https://github.com/goldbergyoni/nodebestpractices

ML system design: 300 case studies to learn from

Recently, I have started gaining interest in Machine Learning as well. As a field artificial intelligence is a very vast subject. This site contains a database of 300 case studies from over 80 companies that share practical machine learning use cases and learnings from designing ML systems.

https://www.evidentlyai.com/ml-system-design

Upgrading database version

GitHub is a very complex software system. They updagraded nearlly 1200+ hosts and store 300+ TB of data and serve 5.5 million queries per second across 50+ database clusters. Upgrading their databases is not an easy task. But, still they successfully upgraded to MySQL 8.0

https://github.blog/2023-12-07-upgrading-github-com-to-mysql-8-0/