Detective de'Code and the Dueling Dishes
Tags: programming, database, bug, python
Published: 2023-10-11 18:50:43

## The Call

On a foggy evening, Louis de'Code was deeply focused in the green glow of his terminal when an unexpected notification jolted him. His old-fashioned, ringtone echoed through his dimly-lit office: "Gelátoz needs your help!" Gelátoz, the talk-of-the-town food delivery app, had become synonymous with the annual Feast of Fables - a playful holiday celebrating the most treasured culinary tales from around the world.

The message was clear and urgent: "Detective! Gelátoz hangs and hiccups just as our customers are finalizing their festive orders. And just our luck, due to the new privacy law that forbids retaining user food preferences, the logs—rich with food content—had to be deleted! Feast of Fables is days away, and this hiccup could turn our grand celebration into a grand fiasco."

A holiday where mythic dishes took center stage, Feast of Fables was Gelátoz's crowning achievement, and a malfunction now would not only spell a gastronomical disaster but also might just put the final nail in their digital coffin. The stakes were high, and the clock was ticking.

With his keen senses tingling, detective de'Code felt the weight of this crisis. Clearing the code clutter on his screen, he mentally geared up for a deep dive into the realm of Gelátoz, where culinary wonders met code. The game was on.

## Tech Trio

In the heart of the city, the grand Gelátoz headquarters resembled an architectural masterpiece, where vintage aesthetics embraced the throbbing pulse of modern tech innovation. Inside this wonder, culinary fantasies transmuted into digital commands, awaiting their journey to patrons eagerly anticipating the Feast of Fables.

![Gelátoz](https://roman-right.dev/articles/series/detective-de-code/detective-de-code-and-the-dueling-dishes/assets/office.webp)

As de'Code made his entry, the symphony of coding keystrokes and server hums hinted at the technological dance unfolding. Immediately, he discerned the trio that wove the tech tapestry of Gelátoz:

1. **Fiona, the Frontend Maestro**: Donning an artsy beret and a demeanor of fierce focus, Fiona was fluent in the poetry of React. She delicately crafted user interfaces that felt less like screens and more like tantalizing culinary presentations. When not immersed in her digital creations, Fiona lost herself to the rhythm of dance, her body painting stories in the air with grace.

2. **Benny, the Backend Virtuoso**: His quirky "Life's better in Python" tee told tales of his expertise. Orchestrating the data dynamics using FastAPI, Benny ensured that the heartbeats of Gelátoz's operations always played in rhythm.  Away from the digital waves, Benny felt a deep connection to the vastness of the seas, his heart echoing the call of seafaring adventures.

3. **Alice, the Database Guardian**: With her serene aura, and a mug that cleverly quipped, "Keep calm and query on," Alice guarded the sanctum of Postgres. Ensuring every data point, every transaction was as meticulous as a chef's recipe, she made sure the digital ingredients always sang in harmony. In her free time, the world of chess captivated her – where she meticulously planned moves and strategies, mirroring her precision at work.

Surveying the trio and the digital masterpiece they'd crafted, detective de'Code braced himself. With a malfunctioning app, mysteriously vanished logs, and the Feast of Fables ticking closer, his plate was full. The culinary coding caper had just begun.

## Rhythmic Riddles

The grand room of Gelátoz's headquarters was punctuated by the determined pacing of detective de'Code. "The culprit must lie somewhere in the path of promo dish recommendation," he mused. First stop: the Frontend - the shop window of the app. Fiona, with a nervous glance, handed him access to the console, where the notorious JavaScript snippet lay in wait.

```javascript
import { useState, useEffect } from 'react';

function PromoDishDetails({ dishId }) {
 const [dish, setDish] = useState(null);
 const [chefDetails, setChefDetails] = useState(null);

 useEffect(() => {
   async function fetchData() {
     let response = await fetch(`/api/promoDishes/${dishId}`);
     let data = await response.json();
     setDish(data);

     // Fetching chef details after getting the dish details
     let chefResponse = await fetch(`/api/chefs/${data.chefId}`);
     let chefData = await chefResponse.json();
     setChefDetails(chefData);
   }

   fetchData();
 }, [dishId]);

 return (
   <div>
     Dish Name: {dish?.name} <br/>
     Promo Price: {dish?.promoPrice} <br/>
     Chef: {chefDetails?.fullName}
   </div>
 );
}

```

Examining the lines, de'Code felt the rhythm of the sequential API calls, like notes in a well-composed melody. "Sequential requests," he thought aloud. "But executed gracefully."

He imagined it as a choreographed dance: a performer moves to the stage, dances, and only when the routine is done does the next performer take their turn. Each step is well-rehearsed; there's no tripping or overstepping. 

![Fiona](https://roman-right.dev/articles/series/detective-de-code/detective-de-code-and-the-dueling-dishes/assets/fiona.webp)

Fiona sighed with relief. "So, our front-end dance is smooth?"

Detective de'Code nodded, appreciating the finesse in the script. "It's not here that our villain hides. The choreography is well-executed, no step out of place."

## Tales from the Depths

Pivoting, Louis de'Code ventured into the darker, more enigmatic world of the backend. Like a sailor about to navigate uncharted waters, Benny, holding his code snippet, handed over the map to the next mystery.

```python
async def fetch_promo_dishes():
  async with httpx.AsyncClient() as client:
     for attempt in range(MAX_RETRIES):
        try:
            response = await client.get(f"{SUPPLIER_URL}/promo")
            response.raise_for_status()
            return response.json()
        except httpx.RequestError:
            if attempt < (MAX_RETRIES - 1):
                await asyncio.sleep(RETRY_DELAY * (2 ** attempt))
            else:
                raise


@app.get("/promoDishes/")
async def get_promo_dishes():
   try:
       dishes = await fetch_promo_dishes()
       return dishes
   except httpx.RequestError:
       return {"error": "Failed to fetch promotional dishes."}
```

The detective's fingers trailed over the lines detailing retries, a clever system reminiscent of a sailor adjusting his course amidst changing winds and tides. "Resilient code," he admired. "Just like a captain not letting a few storms dissuade his journey, but knowing when to dock for shelter. Benny, this is masterful navigation. You've safeguarded against turbulent third-party waters."

![Benny](https://roman-right.dev/articles/series/detective-de-code/detective-de-code-and-the-dueling-dishes/assets/benny2.webp)

Benny, beaming with a mix of pride and relief, responded, "We don't want to be those sailors who fight against a storm until their ship sinks. We adjust the sails, make a few attempts, then find safe harbor."

As the backend's innocence became evident, de'Code's gaze wandered, seeking another thread. "Considering the system stalls post-order but prior to promo dish recommendation, our focus should now be on the order placement mechanism." The game was still afoot.

## The Stalemate Standoff

Amid the dim lighting of the server room, detective de'Code, with narrowed eyes, stared at the enigma on the screen. The cursor blinked in a rhythmic pattern, but what unfolded was nothing short of a fierce chess match between two pieces: `process_normal_order` and `process_combo_order`.

"Notice," the detective whispered, fingers caressing the keyboard, "how these two functions handle transactions in reverse order. The devil, as they say, is in the details."

```python
async def process_normal_order(conn, order_dish_ids):
    async with conn.transaction():
        await conn.execute(
            """
            UPDATE 
                inventory
            SET 
                stock = stock - 1
            WHERE
                id = ANY($1::int[]);
            """,
            order_dish_ids,
        )
        await conn.execute(
            """
            INSERT INTO
                orders (status, dish_ids)
            VALUES
                ('created', $1);
            """,
            order_dish_ids,
        )


async def process_combo_order(conn, combo_dish_ids):
    combo_dish_ids = combo_dish_ids + [COLA_ID, FRIES_ID]
    async with conn.transaction():
        await conn.execute(
            """
            INSERT INTO
                orders (status, dish_ids)
            VALUES 
                ('created', $1);
            """,
            combo_dish_ids,
        )
        await conn.execute(
            """
            UPDATE 
                inventory
            SET
                stock = stock - 1
            WHERE
                id = ANY($1::int[]);
            """,
            combo_dish_ids,
        )


@app.post("/placeOrder/")
async def place_order(order: Order):
    conn = await asyncpg.connect(DATABASE_URL)
    if order.tag == "combo":
        await process_combo_order(conn, order.dish_ids)
    else:
        await process_normal_order(conn, order.dish_ids)
    await conn.close()
    return {"status": "Order placed successfully!"}
  ```

The room, filled with developers and stakeholders, buzzed with confusion. Alice, the database expert, stepped forward. "When two transactions compete for the same resources but in reverse order, it's a classic recipe for a deadlock. It's as if two chess players tried to checkmate each other, but their moves end up in a stalemate."

![Alice](https://roman-right.dev/articles/series/detective-de-code/detective-de-code-and-the-dueling-dishes/assets/alice3.webp)

Detective de'Code nodded in agreement. "Exactly. In a high traffic scenario, when both functions are invoked almost simultaneously, one waits for the other indefinitely, creating a standstill - our mastermind antagonist: the deadlock."

## Resolution Recipe

With the problem identified, Louis de'Code, ever the genius, set forth on rectifying it. He swiftly rearranged the code, ensuring consistency in transaction order, preventing the chess pieces from getting trapped.

```python
async def process_order(conn, order_dish_ids):
    # Let's combine the logic so that 
    # transaction order is consistent
    async with conn.transaction():
        await conn.execute(
            """
            INSERT INTO
                orders (status, dish_ids)
            VALUES
                ('created', $1);
            """,
            order_dish_ids,
        )
        await conn.execute(
            """
            UPDATE
                inventory
            SET
                stock = stock - 1 
            WHERE 
                id = ANY($1::int[]);
            """,
            order_dish_ids,
        )


@app.post("/placeOrder/")
async def place_order(order: Order):
    conn = await asyncpg.connect(DATABASE_URL)
    dish_ids = order.dish_ids
    if order.tag == "combo":
        dish_ids += [COLA_ID, FRIES_ID]
    await process_order(conn, dish_ids)
    await conn.close()

    return {"status": "Order placed successfully!"}
```

The room erupted in applause. Alice, impressed, remarked, "By ensuring that both types of orders follow the same transaction pattern, you've eliminated the potential for a deadlock. Brilliant!"

But amidst the cheers, de'Code, ever modest, simply adjusted his hat. "It's all about ensuring the right recipe, a harmonized blend of ingredients. Tech problems, after all, are like puzzles waiting to be solved."

And with that, the detective packed his toolkit. "Another tech enigma awaits," he remarked, leaving the Gelátoz team in awe, their gratitude palpable, their system more robust.