Skip to content

Commit b9a93e6

Browse files
Merge pull request #907 from redis/DOC-4560-trans-pipe-examples
DOC-4560 pipeline/transaction examples page
2 parents fe3950f + c3d17f7 commit b9a93e6

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
categories:
3+
- docs
4+
- develop
5+
- stack
6+
- oss
7+
- rs
8+
- rc
9+
- oss
10+
- kubernetes
11+
- clients
12+
description: Learn how to use Redis pipelines and transactions
13+
linkTitle: Pipelines/transactions
14+
title: Pipelines and transactions
15+
weight: 2
16+
---
17+
18+
Redis lets you send a sequence of commands to the server together in a batch.
19+
There are two types of batch that you can use:
20+
21+
- **Pipelines** avoid network and processing overhead by sending several commands
22+
to the server together in a single communication. The server then sends back
23+
a single communication with all the responses. See the
24+
[Pipelining]({{< relref "/develop/use/pipelining" >}}) page for more
25+
information.
26+
- **Transactions** guarantee that all the included commands will execute
27+
to completion without being interrupted by commands from other clients.
28+
See the [Transactions]({{< relref "/develop/interact/transactions" >}})
29+
page for more information.
30+
31+
## Execute a pipeline
32+
33+
To execute commands in a pipeline, you first create a pipeline object
34+
and then add commands to it using methods that resemble the standard
35+
command methods (for example, `set()` and `get()`). The commands are
36+
buffered in the pipeline and only execute when you call the `execute()`
37+
method on the pipeline object. This method returns a list that contains
38+
the results from all the commands in order.
39+
40+
Note that the command methods for a pipeline always return the original
41+
pipeline object, so you can "chain" several commands together, as the
42+
example below shows:
43+
44+
<!-- Tested examples will replace the inline ones when they are approved.
45+
Markup removed to stop warnings.
46+
47+
clients-example pipe_trans_tutorial basic_pipe Python
48+
/clients-example
49+
-->
50+
```python
51+
import redis
52+
53+
r = redis.Redis(decode_responses=True)
54+
55+
pipe = r.pipeline()
56+
57+
for i in range(5):
58+
pipe.set(f"seat:{i}", f"#{i}")
59+
60+
set_5_result = pipe.execute()
61+
print(set_5_result) # >>> [True, True, True, True, True]
62+
63+
pipe = r.pipeline()
64+
65+
# "Chain" pipeline commands together.
66+
get_3_result = pipe.get("seat:0").get("seat:3").get("seat:4").execute()
67+
print(get_3_result) # >>> ['#0', '#3', '#4']
68+
```
69+
70+
## Execute a transaction
71+
72+
A pipeline actually executes as a transaction by default (that is to say,
73+
all commands are executed in an uninterrupted sequence). However, if you
74+
need to switch this behavior off, you can set the `transaction` parameter
75+
to `False` when you create the pipeline:
76+
77+
```python
78+
pipe = r.pipeline(transaction=False)
79+
```
80+
81+
## Watch keys for changes
82+
83+
Redis supports *optimistic locking* to avoid inconsistent updates
84+
to different keys. The basic idea is to watch for changes to any
85+
keys that you use in a transaction while you are are processing the
86+
updates. If the watched keys do change, you must restart the updates
87+
with the latest data from the keys. See
88+
[Transactions]({{< relref "/develop/interact/transactions" >}})
89+
for more information about optimistic locking.
90+
91+
The example below shows how to repeatedly attempt a transaction with a watched
92+
key until it succeeds. The code reads a string
93+
that represents a `PATH` variable for a command shell, then appends a new
94+
command path to the string before attempting to write it back. If the watched
95+
key is modified by another client before writing, the transaction aborts
96+
with a `WatchError` exception, and the loop executes again for another attempt.
97+
Otherwise, the loop terminates successfully.
98+
99+
<!--
100+
clients-example pipe_trans_tutorial trans_watch Python
101+
/clients-example
102+
-->
103+
```python
104+
r.set("shellpath", "/usr/syscmds/")
105+
106+
with r.pipeline() as pipe:
107+
# Repeat until successful.
108+
while True:
109+
try:
110+
# Watch the key we are about to change.
111+
pipe.watch("shellpath")
112+
113+
# The pipeline executes commands directly (instead of
114+
# buffering them) from immediately after the `watch()`
115+
# call until we begin the transaction.
116+
current_path = pipe.get("shellpath")
117+
new_path = current_path + ":/usr/mycmds/"
118+
119+
# Start the transaction, which will enable buffering
120+
# again for the remaining commands.
121+
pipe.multi()
122+
123+
pipe.set("shellpath", new_path)
124+
125+
pipe.execute()
126+
127+
# The transaction succeeded, so break out of the loop.
128+
break
129+
except redis.WatchError:
130+
# The transaction failed, so continue with the next attempt.
131+
continue
132+
133+
get_path_result = r.get("shellpath")
134+
print(get_path_result) # >>> '/usr/syscmds/:/usr/mycmds/'
135+
```
136+
137+
Because this is a common pattern, the library includes a convenience
138+
method called `transaction()` that handles the code to watch keys,
139+
execute the transaction, and retry if necessary. Pass
140+
`transaction()` a function that implements your main transaction code,
141+
and also pass the keys you want to watch. The example below implements
142+
the same basic transaction as the previous example but this time
143+
using `transaction()`. Note that `transaction()` can't add the `multi()`
144+
call automatically, so you must still place this correctly in your
145+
transaction function.
146+
147+
<!--
148+
clients-example pipe_trans_tutorial watch_conv_method Python
149+
/clients-example
150+
*-->
151+
```python
152+
r.set("shellpath", "/usr/syscmds/")
153+
154+
155+
def watched_sequence(pipe):
156+
current_path = pipe.get("shellpath")
157+
new_path = current_path + ":/usr/mycmds/"
158+
159+
pipe.multi()
160+
161+
pipe.set("shellpath", new_path)
162+
163+
164+
trans_result = r.transaction(watched_sequence, "shellpath")
165+
print(trans_result) # True
166+
167+
get_path_result = r.get("shellpath")
168+
print(get_path_result) # >>> '/usr/syscmds/:/usr/mycmds/'
169+
```

0 commit comments

Comments
 (0)