Backpressure when consumers fall behind

Queues, Kafka and event-driven systems Mid-level Python 3.13+ PostgreSQL 17+ Reviewed By Best Latest Apps

Predict the output

A bounded asyncio queue of size 2. What does this print?

Python 3.13
import asyncio


async def main() -> None:
    q: asyncio.Queue[int] = asyncio.Queue(maxsize=2)
    q.put_nowait(1)
    q.put_nowait(2)
    try:
        q.put_nowait(3)
    except asyncio.QueueFull:
        print("full: producer must wait")
    print(q.qsize())


asyncio.run(main())

Options
Hint 1 of 1

What does maxsize do?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A full: producer must wait then 2

Correct. The third put is refused, so memory stays bounded.

A bounded queue makes producers feel when consumers are behind. put_nowait raises QueueFull instead of growing past maxsize, so the queue still holds just 2 items.

Why not the others?
  • B 3. maxsize=2 refuses a third item.
  • C It blocks forever. put_nowait raises instead of waiting; await put() would wait.
  • D full: producer must wait then 3. The refused item isn't added.

30-second answer

A durable queue absorbs bursts, but if the input rate stays above processing capacity the backlog grows without limit. Options: scale consumers (up to the partition count), make processing cheaper (batching, fewer round trips), slow the producers (bounded queues, 429 or 503 to callers), or shed low-value load. Inside a process, bounded queues make producers wait instead of using unbounded memory.

Practise saying it · 30 s. No recording, just a timer

How do you handle backpressure?

Say your answer out loud, then check which of these points you covered:

  • Queues absorb bursts
  • Scale, optimise
  • Push back upstream
  • Shed optional load
  • Measure lag

Deep dive

Watch consumer lag (messages or time behind) and alert on growth, not just size.

Autoscale on lag, but cap it at the partition count and downstream capacity.

Load shedding: drop or defer optional work (analytics) to protect critical paths.

asyncio.Queue(maxsize=N) blocks put when full, which is backpressure in one process.

Common mistakes

  • Unbounded in-memory queues that end in an out-of-memory kill.
  • Scaling consumers beyond the database's capacity.
  • Hiding growing lag because 'it catches up at night'.

Senior take & follow-ups

Absorb, then scale, optimise, push back or shed; measure lag.

Follow-up questions

Think of your answer first, then open the hint.

  1. What does a bounded queue do when full?

    Show hint

    Makes the producer wait or fail fast, pushing the pressure upstream.

  2. Why is lag in seconds more useful than count?

    Show hint

    It says how stale results are, regardless of message size.

Suggest a fix if something here is wrong or out of date.