• AI CODING CLUB
  • Posts
  • 👨‍💻 Xmas Computer Crash & Code Refactoring

👨‍💻 Xmas Computer Crash & Code Refactoring

I had a very busy holiday season, dealing with a nasty computer crash and some forced code refactoring.

Christmas Night Crash

On December 25th at 23:15, my AMD Ryzen Beelink Mini PC decided to call it a day, permanently.

Inside was an NVMe SSD holding a full year of projects. I was abroad at the time, which added to the challenge.

inside the Beelink, after removing the SSD

Extracting the Essentials

First, I had to remove the SSD from the Beelink.

One year of projects

While most of my code was safely backed up on GitHub, there were some recent local files I hadn’t yet pushed to the cloud.

These local .py files were critical, and retrieving them became my top priority.

Next, I tracked down a compatible external NVMe SSD enclosure.

If you ever need an SSD enclosure, make sure to verify whether your drive is a legacy SATA SSD or a more recent NVMe SSD, as the enclosures for these two types are not interchangeable.

Once I had it, I could safely transfer my scripts…

Good To Know to pick the right enclosure

M.2 Form Factor

  • M.2 is a physical form factor, not a protocol or technology. It defines the physical size and connection interface of the SSD.

  • M.2 drives can use either the SATA protocol or the NVMe protocol, depending on the drive.

M.2 SATA SSDs

  • M.2 SATA SSDs use the SATA protocol for data transfer, which means they are limited to SATA III speeds (~600 MB/s).

  • Internally, these drives connect to the motherboard's SATA controller, even though they plug into an M.2 slot.

  • They are typically labeled with terms like "M.2 SATA" to differentiate them from NVMe drives.

  • An M.2 SATA SSD will not work in an M.2 slot that only supports NVMe.

M.2 NVMe SSDs

  • M.2 NVMe SSDs use the NVMe protocol over PCIe lanes for significantly faster speeds (up to several GB/s).

  • They require an M.2 slot that supports NVMe and PCIe connectivity.

Upgrading to the Mac Mini M4

Back in London, it was clear I needed a replacement. Enter the Mac Mini M4.

The Mac Mini M4 offers incredible value at ÂŁ475, featuring a 10-core CPU and 10-core GPU. It's remarkable how much computing power Apple has managed to pack into such a compact device.

🙏 On a side note, Amazon agreed to refund 100% of the Beelink purchase price (I was lucky—only 4 days left before the end of the 12-month warranty).

I formatted the SSD, reinstalled it in the mini PC, and returned the device to AMZ.

But switching systems meant setting up my development environment from scratch:

  • installing Homebrew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • Installing ffmpeg via Homebrew

brew install ffmpeg
  • Installing node.js to use npm 

brew install node
  • Installing all Python packages from my requirements.txt files after setting up a virtual environment for each project (I’ve used Python 3.10.3 for most projects).

python3.10 -m venv venv

source venv/bin/activate

pip install -r requirements.txt

It was tedious but necessary to get back to work quickly.

Refactoring the AI Jingle Maker

Adding to the complexity, two days before the Christmas Crash, my deployment platform, Railway, made a “small” infrastructure change that forced a refactor of the AI Jingle Maker.

My web app and MySQL database, which had previously operated in the same region, were now in two different regions, even though they remained connected via Railway’s private network.

This introduced a slight lag that required more efficient handling of database queries. To mitigate this, I implemented MySQL DB Connection Pooling and improved the efficiency of my SQL queries.

from flask import current_app, g
import mysql.connector
from mysql.connector import pooling, Error
import os
from dotenv import load_dotenv
from contextlib import contextmanager
from threading import Lock
from uuid import uuid4

load_dotenv()

POOL_CONFIG = {
    'pool_name': 'mypool',
    'pool_size': 32,
    'pool_reset_session': True,
    'host': os.getenv('MYSQLHOST'),
    'user': 'root',
    'password': os.getenv('MYSQLPASSWORD'),
    'database': os.getenv('MYSQLDATABASE'),
    'port': int(os.getenv('MYSQLPORT', 3306)),
    'connect_timeout': 3,
    'autocommit': True
}

connection_pool = mysql.connector.pooling.MySQLConnectionPool(**POOL_CONFIG)

active_connection_ids = set()
connection_lock = Lock()

@contextmanager
def get_db_connection():
    conn = None
    conn_id = str(uuid4())
    try:
        with connection_lock:
            conn = connection_pool.get_connection()
            active_connection_ids.add(conn_id)
        yield conn
    except Error as e:
        raise
    finally:
        if conn:
            try:
                with connection_lock:
                    active_connection_ids.discard(conn_id)
                conn.close()
            except Error as e:
                pass

def init_app(app):
    app.teardown_appcontext(close_db)
    app.teardown_request(close_db)

def get_db():
    if 'db' not in g:
        try:
            conn_id = str(uuid4())
            with connection_lock:
                conn = connection_pool.get_connection()
                active_connection_ids.add(conn_id)
            g.db = conn
            g.db_id = conn_id
            g.db_used = True
            return g.db
        except Exception as e:
            raise
    return g.db

def close_db(e=None):
    try:
        db = g.pop('db', None)
        conn_id = g.pop('db_id', None)
        db_used = g.pop('db_used', False)
        
        if db is not None and db_used and conn_id in active_connection_ids:
            try:
                with connection_lock:
                    if db and hasattr(db, 'is_connected'):
                        try:
                            if db.is_connected():
                                db.reset_session()
                                db.close()
                        except Exception as e:
                            pass
                    active_connection_ids.discard(conn_id)
            except Exception as e:
                pass
    except Exception as e:
        pass

Example of a connection:

    with get_db_connection() as db_conn:
        cursor = db_conn.cursor()

Why should you implement connection pooling?

Every interaction between your Flask application and the database involves opening and closing connections. While this may seem trivial for a single request, the cumulative cost becomes significant under high traffic.

Opening a connection involves establishing a TCP handshake, authenticating credentials, and preparing the session—steps that add latency.

Connection pooling eliminates these repetitive operations by maintaining a pool of reusable connections, significantly improving response times.

Here’s a quick comparison:

Without Connection Pooling:

  • Create a new connection → Execute Query → Close the connection (Repeat for every query).

With Connection Pooling:

  • Reuse an existing connection from the pool → Execute Query → Return the connection to the pool.

By avoiding the overhead of constant connection creation and teardown, you save valuable resources and ensure consistent performance.

While the changes were unplanned, they significantly optimized the codebase and prepared it for future scalability.

Lessons Learned

This Christmas Night crash reinforced the importance of backups and having contingency plans for your hardware and workflow.

Holding the SSD was a stark reminder of how much we rely on (tiny pieces of) technology to store our work and creativity.

Even with most scripts backed up, the local files I recovered were a crucial part of keeping everything intact.

I also learned a lot from the forced refactoring episode, thanks to the kind assistance of Brody from Railway’s Support Team, who generously granted me some free credit for the hassle.

It’s been a hectic holiday season, but I’m starting the new year with an upgraded setup, a refactored project, and a better appreciation for robust systems.

Here’s to a smoother year ahead!

Happy New Coding Year! 🥂

PS: Next time, I’ll share the results of my first experiments with Claude AI, which I’ve just added to my toolbox.