# Blocking operations

> Wait for data with BLPOP, BLMOVE, BZPOPMIN, XREAD BLOCK and friends, wait for replicas with WAIT, and handle timeouts.

URL: https://docs.hotkv.com/develop/blocking-operations/


A blocking command waits on the server until there is something to return, or
until its timeout passes. It lets a worker sleep until a job arrives instead
of polling in a loop, which saves round trips and reacts immediately when data
shows up. The connection is busy while it waits, so use a dedicated
connection (or a pool) for blocking calls.

## Blocking commands

| Command | Waits for | Timeout unit |
| --- | --- | --- |
| [BLPOP](https://docs.hotkv.com/commands/blpop/index.md), [BRPOP](https://docs.hotkv.com/commands/brpop/index.md) | An element at the head or tail of the first non-empty list | Seconds (decimals allowed) |
| [BLMOVE](https://docs.hotkv.com/commands/blmove/index.md) | An element to move from one list to another | Seconds |
| [BRPOPLPUSH](https://docs.hotkv.com/commands/brpoplpush/index.md) | Same as `BLMOVE source destination RIGHT LEFT` (deprecated) | Seconds |
| [BLMPOP](https://docs.hotkv.com/commands/blmpop/index.md) | One or more elements from the first non-empty list | Seconds |
| [BZPOPMIN](https://docs.hotkv.com/commands/bzpopmin/index.md), [BZPOPMAX](https://docs.hotkv.com/commands/bzpopmax/index.md) | The lowest or highest scored member of the first non-empty sorted set | Seconds |
| [BZMPOP](https://docs.hotkv.com/commands/bzmpop/index.md) | One or more members from the first non-empty sorted set | Seconds |
| [XREAD](https://docs.hotkv.com/commands/xread/index.md) `BLOCK ms` | New entries in one or more streams | Milliseconds |
| [XREADGROUP](https://docs.hotkv.com/commands/xreadgroup/index.md) `BLOCK ms` | New entries for a consumer group | Milliseconds |
| [WAIT](https://docs.hotkv.com/commands/wait/index.md) | Replicas to acknowledge this client's writes | Milliseconds |
| [WAITAOF](https://docs.hotkv.com/commands/waitaof/index.md) | This client's writes to reach the append-only file on disk | Milliseconds |

## A simple work queue

Producers push jobs onto a list; workers block on it.

```
127.0.0.1:6379> BLPOP jobs 5
```

The worker now waits. When a producer runs:

```
127.0.0.1:6379> RPUSH jobs "resize:img-881"
(integer) 1
```

the worker's `BLPOP` returns at once with the key and the element:

```
1) "jobs"
2) "resize:img-881"
```

If nothing arrives within 5 seconds, `BLPOP` replies with a null array
(`(nil)` in `redis-cli`; a RESP3 null on a RESP3 connection).

To make sure a job is not lost if the worker crashes after taking it, move it
to a processing list atomically with `BLMOVE` and remove it from there once
done:

```
127.0.0.1:6379> BLMOVE jobs jobs:processing LEFT RIGHT 5
"resize:img-881"
127.0.0.1:6379> LREM jobs:processing 1 "resize:img-881"
(integer) 1
```

For queues that need acknowledgements, retries and several consumer groups,
a [stream](https://docs.hotkv.com/data-types/streams/index.md) with `XREADGROUP BLOCK` is usually a better
fit.

## Timeouts

- A timeout of `0` waits forever.
- List and sorted set commands take seconds and accept decimals, so `0.1` is
  100 ms. A negative value replies `ERR timeout is negative`, and text that is
  not a number replies `ERR timeout is not a float or out of range`.
- `XREAD`, `XREADGROUP`, `WAIT` and `WAITAOF` take whole milliseconds; a
  value that is not an integer replies
  `ERR timeout is not an integer or out of range`.
- Set your client library's socket read timeout longer than the blocking
  timeout, or the library may give up before the server answers.

When several clients wait on the same key, they are served in the order they
started waiting. A command given several keys first checks them in the
order you listed them and returns from the first one that has data; if none
has, it waits for whichever receives data first.

## Ending a wait early

[CLIENT UNBLOCK](https://docs.hotkv.com/commands/client-unblock/index.md) `client-id [TIMEOUT | ERROR]`,
sent from another connection, ends a blocked command. With `TIMEOUT` (the
default) the blocked client gets its normal timeout reply; with `ERROR` it
gets `UNBLOCKED client unblocked via CLIENT UNBLOCK`. `CLIENT UNBLOCK`
replies `1` if the client was blocked and `0` otherwise. Find the client id
with [CLIENT LIST](https://docs.hotkv.com/commands/client-list/index.md).

## Inside transactions and scripts

A blocking command never waits inside `MULTI`/`EXEC`, a Lua script or a
function, because those run as one isolated step. It answers immediately as
if its timeout had already passed: with data if there is some, otherwise
with the null reply. Likewise, `WAIT` inside a transaction or script returns
the number of replicas that have acknowledged so far without waiting.

## WAIT and WAITAOF

`WAIT numreplicas timeout` blocks until at least `numreplicas` replicas have
acknowledged every write this connection made so far, or the timeout passes,
and replies with the number of replicas that did. On a server that is not a
primary with replicas it replies `0` at once. `WAITAOF numlocal numreplicas
timeout` does the same for the append-only file on disk, locally and on
replicas. Neither makes a write durable by itself: they tell you how far it
got, so your code can decide what to do. See
[Replication](https://docs.hotkv.com/operate/replication/index.md) and [Persistence](https://docs.hotkv.com/persistence/index.md).

## Blocking in a cluster

All the keys of one blocking command must hash to the same slot, served by
the node you send it to, as for any multi-key command. Use a hash tag, such
as `jobs:{eu}:high` and `jobs:{eu}:low`, to block on several queues at once.

If the slot a client is waiting on moves to another node while it waits (a
resharding or a failover), HotKV ends the wait with a `MOVED` redirect to the
new owner, or with `CLUSTERDOWN` if the cluster goes down. The client should
then retry, just as for any redirected command.

> **Note**
> When a primary is turned into a replica during a failover, its blocked clients
> get `MOVED` to the new primary, as in Valkey. Redis 7 ends those waits with an
> `UNBLOCKED` error instead.

## Compatibility

The blocking commands, their timeouts, replies and errors follow Redis 7.
The only difference is the `MOVED` reply blocked clients get when their
primary is demoted, described above.