summaryrefslogtreecommitdiffstats
path: root/iredis/data/commands/blpop.md
blob: b48ace78f2b7384b930f6bc4ca49700c45eb62e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
`BLPOP` is a blocking list pop primitive. It is the blocking version of `LPOP`
because it blocks the connection when there are no elements to pop from any of
the given lists. An element is popped from the head of the first list that is
non-empty, with the given keys being checked in the order that they are given.

## Non-blocking behavior

When `BLPOP` is called, if at least one of the specified keys contains a
non-empty list, an element is popped from the head of the list and returned to
the caller together with the `key` it was popped from.

Keys are checked in the order that they are given. Let's say that the key
`list1` doesn't exist and `list2` and `list3` hold non-empty lists. Consider the
following command:

```
BLPOP list1 list2 list3 0
```

`BLPOP` guarantees to return an element from the list stored at `list2` (since
it is the first non empty list when checking `list1`, `list2` and `list3` in
that order).

## Blocking behavior

If none of the specified keys exist, `BLPOP` blocks the connection until another
client performs an `LPUSH` or `RPUSH` operation against one of the keys.

Once new data is present on one of the lists, the client returns with the name
of the key unblocking it and the popped value.

When `BLPOP` causes a client to block and a non-zero timeout is specified, the
client will unblock returning a `nil` multi-bulk value when the specified
timeout has expired without a push operation against at least one of the
specified keys.

**The timeout argument is interpreted as an integer value specifying the maximum
number of seconds to block**. A timeout of zero can be used to block
indefinitely.

## What key is served first? What client? What element? Priority ordering details.

- If the client tries to blocks for multiple keys, but at least one key contains
  elements, the returned key / element pair is the first key from left to right
  that has one or more elements. In this case the client is not blocked. So for
  instance `BLPOP key1 key2 key3 key4 0`, assuming that both `key2` and `key4`
  are non-empty, will always return an element from `key2`.
- If multiple clients are blocked for the same key, the first client to be
  served is the one that was waiting for more time (the first that blocked for
  the key). Once a client is unblocked it does not retain any priority, when it
  blocks again with the next call to `BLPOP` it will be served accordingly to
  the number of clients already blocked for the same key, that will all be
  served before it (from the first to the last that blocked).
- When a client is blocking for multiple keys at the same time, and elements are
  available at the same time in multiple keys (because of a transaction or a Lua
  script added elements to multiple lists), the client will be unblocked using
  the first key that received a push operation (assuming it has enough elements
  to serve our client, as there may be other clients as well waiting for this
  key). Basically after the execution of every command Redis will run a list of
  all the keys that received data AND that have at least a client blocked. The
  list is ordered by new element arrival time, from the first key that received
  data to the last. For every key processed, Redis will serve all the clients
  waiting for that key in a FIFO fashion, as long as there are elements in this
  key. When the key is empty or there are no longer clients waiting for this
  key, the next key that received new data in the previous command / transaction
  / script is processed, and so forth.

## Behavior of `!BLPOP` when multiple elements are pushed inside a list.

There are times when a list can receive multiple elements in the context of the
same conceptual command:

- Variadic push operations such as `LPUSH mylist a b c`.
- After an `EXEC` of a `MULTI` block with multiple push operations against the
  same list.
- Executing a Lua Script with Redis 2.6 or newer.

When multiple elements are pushed inside a list where there are clients
blocking, the behavior is different for Redis 2.4 and Redis 2.6 or newer.

For Redis 2.6 what happens is that the command performing multiple pushes is
executed, and _only after_ the execution of the command the blocked clients are
served. Consider this sequence of commands.

    Client A:   BLPOP foo 0
    Client B:   LPUSH foo a b c

If the above condition happens using a Redis 2.6 server or greater, Client **A**
will be served with the `c` element, because after the `LPUSH` command the list
contains `c,b,a`, so taking an element from the left means to return `c`.

Instead Redis 2.4 works in a different way: clients are served _in the context_
of the push operation, so as long as `LPUSH foo a b c` starts pushing the first
element to the list, it will be delivered to the Client **A**, that will receive
`a` (the first element pushed).

The behavior of Redis 2.4 creates a lot of problems when replicating or
persisting data into the AOF file, so the much more generic and semantically
simpler behavior was introduced into Redis 2.6 to prevent problems.

Note that for the same reason a Lua script or a `MULTI/EXEC` block may push
elements into a list and afterward **delete the list**. In this case the blocked
clients will not be served at all and will continue to be blocked as long as no
data is present on the list after the execution of a single command,
transaction, or script.

## `!BLPOP` inside a `!MULTI` / `!EXEC` transaction

`BLPOP` can be used with pipelining (sending multiple commands and reading the
replies in batch), however this setup makes sense almost solely when it is the
last command of the pipeline.

Using `BLPOP` inside a `MULTI` / `EXEC` block does not make a lot of sense as it
would require blocking the entire server in order to execute the block
atomically, which in turn does not allow other clients to perform a push
operation. For this reason the behavior of `BLPOP` inside `MULTI` / `EXEC` when
the list is empty is to return a `nil` multi-bulk reply, which is the same thing
that happens when the timeout is reached.

If you like science fiction, think of time flowing at infinite speed inside a
`MULTI` / `EXEC` block...

@return

@array-reply: specifically:

- A `nil` multi-bulk when no element could be popped and the timeout expired.
- A two-element multi-bulk with the first element being the name of the key
  where an element was popped and the second element being the value of the
  popped element.

@examples

```
redis> DEL list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BLPOP list1 list2 0
1) "list1"
2) "a"
```

## Reliable queues

When `BLPOP` returns an element to the client, it also removes the element from
the list. This means that the element only exists in the context of the client:
if the client crashes while processing the returned element, it is lost forever.

This can be a problem with some application where we want a more reliable
messaging system. When this is the case, please check the `BRPOPLPUSH` command,
that is a variant of `BLPOP` that adds the returned element to a target list
before returning it to the client.

## Pattern: Event notification

Using blocking list operations it is possible to mount different blocking
primitives. For instance for some application you may need to block waiting for
elements into a Redis Set, so that as far as a new element is added to the Set,
it is possible to retrieve it without resort to polling. This would require a
blocking version of `SPOP` that is not available, but using blocking list
operations we can easily accomplish this task.

The consumer will do:

```
LOOP forever
    WHILE SPOP(key) returns elements
        ... process elements ...
    END
    BRPOP helper_key
END
```

While in the producer side we'll use simply:

```
MULTI
SADD key element
LPUSH helper_key x
EXEC
```