Redis offers blocking list commands such as BLPOP and BLMOVE. Explain what blocking means here, what happens to the server and to other clients while one client is blocked, and how these commands behave inside a MULTI/EXEC transaction.
answer
- client blocks, server does not
- multiple keys = priority order, left to right
- reply = [key, value]; nil on timeout; 0 = forever
- inside MULTI/Lua it never blocks
- FIFO wake-up; write command, rejected on replicas
basics
~20 sBLPOP parks the calling client until an element arrives on one of the given keys or the timeout expires, returning key and value, or nil on timeout. Only that client waits; the server keeps serving everyone else. Inside MULTI/EXEC and Lua they never block — they behave like the non-blocking version and return nil immediately.
solid answer
~60 sBLPOP/BRPOP take one or more keys plus a timeout. If any key has an element the command returns immediately, checking keys in the order given, so key order encodes priority. Otherwise the client is suspended: Redis records it as waiting on those keys and moves on. The connection is idle, not spinning, and the server continues serving all other clients — blocking is per client, not global. When a push arrives, the longest-waiting blocked client is served first, in FIFO order. A timeout of 0 means wait forever; since Redis 6.0 the timeout may be fractional seconds. On timeout the reply is nil; on success it is the key plus the popped value, which matters because you may have passed several keys. Inside MULTI/EXEC there is nobody to unblock you — the queued commands run as one atomic step — so blocking commands degrade to their non-blocking form and return nil when the list is empty. Same inside Lua scripts. BLPOP is also a write command, so a read-only replica rejects it.
code
text · 8 linesBLPOP q:high q:normal q:low 5
1) "q:normal"
2) "job-17" # nil after 5s if all empty
MULTI
BLPOP q:empty 30
EXEC
1) (nil) # did not wait 30s - no blocking inside EXECgo deeper
Know that BLPOP waits for an element instead of polling, takes a timeout, and returns the key together with the value.
Explain that only the client blocks, the multi-key priority order, FIFO wake-up, and the non-blocking behaviour inside MULTI and Lua.
Add connection-count and replica constraints, CLIENT UNBLOCK, and why plain BLPOP is at-most-once so real queues use BLMOVE.
Judge whether a blocking-consumer fleet is the right primitive at all versus a structure with acknowledgements, and size the connection budget accordingly.
## What blocking means A blocking list command is a way to wait for work without polling. Instead of a consumer looping on LPOP with a sleep — which wastes round trips and adds latency equal to the poll interval — the consumer issues BLPOP queue 0 and Redis holds the reply until an element exists. The critical mechanic is that the client blocks, not the server. When Redis cannot satisfy a blocking pop it puts that client into a blocked state, records which keys it is waiting on, and returns to serving other connections. Nothing is stalled globally. When another client pushes to a watched key, Redis wakes waiting clients as part of handling that push. ## The command family - BLPOP key [key ...] timeout and BRPOP: blocking forms of LPOP/RPOP. Multiple keys are checked left to right, so BLPOP high normal low 0 always drains high first. - BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout: blocking form of LMOVE, which atomically pops from one list and pushes to another. It replaces BRPOPLPUSH, deprecated in Redis 6.2. - BLMPOP (Redis 7.0): blocking form of LMPOP, popping up to count elements from the first non-empty key. BLPOP's reply is a two-element array of key and value, precisely because you may have supplied many keys and need to know which fired. On timeout you get a nil reply, which client libraries usually surface as null rather than an error. ## Fairness and wake-up order If several clients are blocked on the same key, Redis serves them in the order they blocked: first to wait is first served. A single pushed element therefore wakes exactly one waiter, which is what makes BLPOP a work-distribution primitive rather than a broadcast — each job goes to one consumer. There is a subtlety when a push and a pop happen inside the same atomic unit. If a transaction or script pushes and then does other work, blocked clients are served after the whole unit finishes, not in the middle of it. A push followed by a delete of the same key inside one transaction can leave the waiter unserved, because the element was never observable between commands. ## Inside MULTI/EXEC and scripts Blocking commands cannot block during EXEC. The server executes the queued commands as one atomic step, so waiting would deadlock the instance. Redis therefore runs BLPOP as if it were LPOP: on an empty list the queued command's result is nil and EXEC proceeds. The same rule applies inside Lua scripts and Functions. Candidates who claim BLPOP inside MULTI waits for its timeout are wrong in an instructive way — the reason it cannot is that transaction execution is a single indivisible step. ## Operational notes Blocked clients still hold a connection, so a large consumer fleet means a large connection count; CLIENT LIST shows them and CLIENT UNBLOCK releases one by client id. Because BLPOP mutates a key it carries the write flag, so it must target the primary and fails on a read-only replica. And a blocking pop gives at-most-once delivery: the element is gone the moment the consumer receives it, so a crash mid-processing loses the job — which is exactly why the reliable pattern uses BLMOVE into a processing list instead.
- Twenty consumers are blocked on the same key and one element is pushed. Who gets it?Exactly one consumer: the one blocked longest, since Redis serves waiters in FIFO order. The other nineteen stay blocked. This is what makes BLPOP a work-distribution primitive rather than a broadcast mechanism.
- Why does BLPOP fail on a read-only replica?Because popping mutates the list, so BLPOP carries the write flag and read-only replicas reject write commands. Consumers must connect to the primary; replicas can only serve reads such as LRANGE or LLEN.
saying these in an interview costs you the question
- Saying the whole Redis server is blocked while a client waits on BLPOP
- Believing BLPOP inside MULTI/EXEC waits for its timeout
- Expecting every blocked client to receive the same pushed element
- Forgetting the reply includes the key name and parsing it as a bare value
- Assuming a blocking pop is safe against consumer crashes