Skip to content

Commit fc38085

Browse files
Merge pull request #1224 from redis/DOC-4560-node-trans-pipe
DOC-4560 JavaScript transaction/pipeline page
2 parents 4126fd7 + 92c5d21 commit fc38085

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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: 5
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+
There are two ways to execute commands in a pipeline. The first is
34+
to include the commands in a
35+
[`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
36+
call, as shown in the following example. The chained `then(...)` callback is optional
37+
and you can often omit it for commands that write data and only return a
38+
status result.
39+
40+
```js
41+
await Promise.all([
42+
client.set('seat:0', '#0'),
43+
client.set('seat:1', '#1'),
44+
client.set('seat:2', '#2'),
45+
]).then((results) =>{
46+
console.log(results);
47+
// >>> ['OK', 'OK', 'OK']
48+
});
49+
50+
await Promise.all([
51+
client.get('seat:0'),
52+
client.get('seat:1'),
53+
client.get('seat:2'),
54+
]).then((results) =>{
55+
console.log(results);
56+
// >>> ['#0', '#1', '#2']
57+
});
58+
```
59+
60+
You can also create a pipeline object using the
61+
[`multi()`]({{< relref "/commands/multi" >}}) method
62+
and then add commands to it using methods that resemble the standard
63+
command methods (for example, `set()` and `get()`). The commands are
64+
buffered in the pipeline and only execute when you call the
65+
`execAsPipeline()` method on the pipeline object. Again, the
66+
`then(...)` callback is optional.
67+
68+
```js
69+
await client.multi()
70+
.set('seat:3', '#3')
71+
.set('seat:4', '#4')
72+
.set('seat:5', '#5')
73+
.execAsPipeline()
74+
.then((results) => {
75+
console.log(results);
76+
// >>> ['OK', 'OK', 'OK']
77+
});
78+
```
79+
80+
The two approaches are almost equivalent, but they have different behavior
81+
when the connection is lost during the execution of the pipeline. After
82+
the connection is re-established, a `Promise.all()` pipeline will
83+
continue execution from the point where the interruption happened,
84+
but a `multi()` pipeline will discard any remaining commands that
85+
didn't execute.
86+
87+
## Execute a transaction
88+
89+
A transaction works in a similar way to a pipeline. Create a
90+
transaction object with the `multi()` command, call command methods
91+
on that object, and then call the transaction object's
92+
`exec()` method to execute it.
93+
94+
```js
95+
const [res1, res2, res3] = await client.multi()
96+
.incrBy("counter:1", 1)
97+
.incrBy("counter:2", 2)
98+
.incrBy("counter:3", 3)
99+
.exec();
100+
101+
console.log(res1); // >>> 1
102+
console.log(res2); // >>> 2
103+
console.log(res3); // >>> 3
104+
```
105+
106+
## Watch keys for changes
107+
108+
Redis supports *optimistic locking* to avoid inconsistent updates
109+
to different keys. The basic idea is to watch for changes to any
110+
keys that you use in a transaction while you are are processing the
111+
updates. If the watched keys do change, you must restart the updates
112+
with the latest data from the keys. See
113+
[Transactions]({{< relref "/develop/interact/transactions" >}})
114+
for more information about optimistic locking.
115+
116+
The code below reads a string
117+
that represents a `PATH` variable for a command shell, then appends a new
118+
command path to the string before attempting to write it back. If the watched
119+
key is modified by another client before writing, the transaction aborts.
120+
Note that you should call read-only commands for the watched keys synchronously on
121+
the usual `client` object but you still call commands for the transaction on the
122+
transaction object created with `multi()`.
123+
124+
For production usage, you would generally call code like the following in
125+
a loop to retry it until it succeeds or else report or log the failure.
126+
127+
```js
128+
// Set initial value of `shellpath`.
129+
client.set('shellpath', '/usr/syscmds/');
130+
131+
// Watch the key we are about to update.
132+
await client.watch('shellpath');
133+
134+
const currentPath = await client.get('shellpath');
135+
const newPath = currentPath + ':/usr/mycmds/';
136+
137+
// Attempt to write the watched key.
138+
await client.multi()
139+
.set('shellpath', newPath)
140+
.exec()
141+
.then((result) => {
142+
// This is called when the pipeline executes
143+
// successfully.
144+
console.log(result);
145+
}, (err) => {
146+
// This is called when a watched key was changed.
147+
// Handle the error here.
148+
console.log(err);
149+
});
150+
151+
const updatedPath = await client.get('shellpath');
152+
console.log(updatedPath);
153+
// >>> /usr/syscmds/:/usr/mycmds/
154+
```

0 commit comments

Comments
 (0)