Scenario advanced

CPU-Bound Task Blocking the Async Event Loop

Fix a FastAPI service that becomes unresponsive during image thumbnail generation by offloading CPU-bound work to a process pool or task queue.

2 min read ~15 min to complete
Steps
5 Services Used
~15 min Duration
Advanced Difficulty
The Situation

Senior Python developer / backend engineering interviews

Context: Your FastAPI service processes image thumbnails on upload. When a user uploads a large image, the API becomes unresponsive for all other users for ~3 seconds. Why and how do you fix it?

# BROKEN — CPU-bound work on the async event loop blocks everything
from PIL import Image

@app.post("/upload")
async def upload_image(file: UploadFile):
    data = await file.read()
    img = Image.open(io.BytesIO(data))
    thumbnail = img.resize((200, 200))   # CPU-bound — blocks event loop!
    # During this 3 seconds, no other requests are served
    return save_thumbnail(thumbnail)
# FIX — run CPU-bound work in a thread pool executor
import asyncio
from concurrent.futures import ProcessPoolExecutor
from PIL import Image

process_pool = ProcessPoolExecutor(max_workers=4)

def generate_thumbnail(data: bytes) -> bytes:
    """Pure function — safe to run in subprocess."""
    img = Image.open(io.BytesIO(data))
    thumbnail = img.resize((200, 200), Image.LANCZOS)
    buf = io.BytesIO()
    thumbnail.save(buf, format="JPEG", quality=85)
    return buf.getvalue()

@app.post("/upload")
async def upload_image(file: UploadFile):
    data = await file.read()
    loop = asyncio.get_event_loop()
    # Run in process pool — event loop stays free
    thumbnail_data = await loop.run_in_executor(
        process_pool, generate_thumbnail, data
    )
    return store_thumbnail(thumbnail_data)
# For truly heavy workloads — use a task queue (Celery + Redis)
from celery import Celery

celery = Celery("tasks", broker="redis://localhost/0")

@celery.task
def process_image_task(image_data: bytes):
    return generate_thumbnail(image_data)

@app.post("/upload")
async def upload_image(file: UploadFile):
    data = await file.read()
    task = process_image_task.delay(data)
    return {"task_id": task.id, "status": "processing"}

@app.get("/upload/{task_id}")
async def get_result(task_id: str):
    task = AsyncResult(task_id)
    return {"status": task.status, "result": task.result}
Services Used
PythonasyncioFastAPICeleryRedis
Prerequisites
  • Python 3.10+
  • Basic understanding of async programming
What You Learned
  • Why CPU-bound work blocks the event loop
  • run_in_executor with a process pool
  • Offloading heavy work to Celery

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form