SERIES 1 • PART 3 OF 4
The Life of a Backend Request - How Real APIs Work

The tech fest registration system is now popular.

But a bug appears.

Two students manage to register for the last seat of an event.

How did that happen?

Race conditions

Imagine there is one seat left.

Two workers check the database simultaneously.


Worker 1 → seats_available = 1
Worker 2 → seats_available = 1

Both proceed to reserve the seat.

Now the event is overbooked.

This problem is called a race condition. Multiple processes race to update the same resource.

Using locks

A lock ensures only one worker accesses a critical section at a time.


Worker A acquires lock
↓
Checks seat count
↓
Reserves seat
↓
Releases lock

Worker B must wait until the lock is released.

The worker that gets the lock first wins.

Implementing locks in Python


import redis

redis_client = redis.Redis()

def reserve_seat(event_id, student_id):
    lock_key = f"lock:event:{event_id}"
    
    # Try to acquire lock
    lock = redis_client.lock(lock_key, timeout=5)
    
    if lock.acquire(blocking=True):
        try:
            # Critical section
            seats = get_available_seats(event_id)
            if seats > 0:
                save_registration(event_id, student_id)
                return True
            return False
        finally:
            lock.release()

Redis locks work across multiple workers because Redis is a shared resource.

Database-level locks

Databases also provide locking mechanisms.


SELECT * FROM events 
WHERE event_id = 123 
FOR UPDATE;

The FOR UPDATE clause locks the row until the transaction completes.

Other workers trying to read that row will wait.

Atomic writes

Another issue appears when writing data.

Suppose the server crashes while writing a file.

The file may look like this:


{
 "name": "Rahul",
 "email":

This is a partial write.

To prevent corruption, systems use atomic writes.

Instead of writing directly to the final file:


registrations.json

We write to a temporary file:


registrations.tmp

Then rename it:


registrations.tmp → registrations.json

File renaming is atomic, meaning the file appears completely or not at all.

Atomic writes in Python


import os
import json

def save_registration_safely(data):
    temp_file = "registrations.tmp"
    final_file = "registrations.json"
    
    # Write to temp file
    with open(temp_file, 'w') as f:
        json.dump(data, f)
    
    # Atomic rename
    os.replace(temp_file, final_file)

Database transactions

Databases handle atomicity through transactions.


BEGIN TRANSACTION;

UPDATE events SET seats_available = seats_available - 1 
WHERE event_id = 123;

INSERT INTO registrations (student_id, event_id) 
VALUES (456, 123);

COMMIT;

Either both operations succeed, or both fail. There is no partial state.

But backend systems interact with external services too.

And external systems fail.

That introduces the next challenge.