diff --git a/content/develop/clients/jedis/transpipe.md b/content/develop/clients/jedis/transpipe.md new file mode 100644 index 0000000000..a98ad52ae1 --- /dev/null +++ b/content/develop/clients/jedis/transpipe.md @@ -0,0 +1,86 @@ +--- +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +description: Learn how to use Redis pipelines and transactions +linkTitle: Pipelines/transactions +title: Pipelines and transactions +weight: 2 +--- + +Redis lets you send a sequence of commands to the server together in a batch. +There are two types of batch that you can use: + +- **Pipelines** avoid network and processing overhead by sending several commands + to the server together in a single communication. The server then sends back + a single communication with all the responses. See the + [Pipelining]({{< relref "/develop/use/pipelining" >}}) page for more + information. +- **Transactions** guarantee that all the included commands will execute + to completion without being interrupted by commands from other clients. + See the [Transactions]({{< relref "/develop/interact/transactions" >}}) + page for more information. + +## Execute a pipeline + +To execute commands in a pipeline, you first create a pipeline object +and then add commands to it using methods that resemble the standard +command methods (for example, `set()` and `get()`). The commands are +buffered in the pipeline and only execute when you call the `sync()` +method on the pipeline object. + +The main difference with the pipeline commands is that they return +`Response` objects, where `Type` is the return type of the +standard command method. A `Response` object contains a valid result +only after the pipeline has finished executing. You can access the +result using the `Response` object's `get()` method. + +{{< clients-example pipe_trans_tutorial basic_pipe Java-Sync >}} +{{< /clients-example >}} + +## Execute a transaction + +A transaction works in a similar way to a pipeline. Create a +transaction object with the `multi()`, call command methods +on that object, and then call the transaction object's +`exec()` method to execute it. You can access the results +from commands in the transaction using `Response` objects, as +you would with a pipeline. However, the `exec()` method also +returns a `List` value that contains all the result +values in the order the commands were executed (see +[Watch keys for changes](#watch-keys-for-changes) below for +an example that uses the results list). + +{{< clients-example pipe_trans_tutorial basic_trans Java-Sync >}} +{{< /clients-example >}} + +## Watch keys for changes + +Redis supports *optimistic locking* to avoid inconsistent updates +to different keys. The basic idea is to watch for changes to any +keys that you use in a transaction while you are are processing the +updates. If the watched keys do change, you must restart the updates +with the latest data from the keys. See +[Transactions]({{< relref "/develop/interact/transactions" >}}) +for more information about optimistic locking. + +The code below reads a string +that represents a `PATH` variable for a command shell, then appends a new +command path to the string before attempting to write it back. If the watched +key is modified by another client before writing, the transaction aborts. +Note that you should call read-only commands for the watched keys synchronously on +the usual client object (called `jedis` in our examples) but you still call commands +for the transaction on the transaction object. + +For production usage, you would generally call code like the following in +a loop to retry it until it succeeds or else report or log the failure. + +{{< clients-example pipe_trans_tutorial trans_watch Java-Sync >}} +{{< /clients-example >}} diff --git a/content/develop/clients/redis-py/transpipe.md b/content/develop/clients/redis-py/transpipe.md index e6d83c90a3..139c143516 100644 --- a/content/develop/clients/redis-py/transpipe.md +++ b/content/develop/clients/redis-py/transpipe.md @@ -41,31 +41,8 @@ Note that the command methods for a pipeline always return the original pipeline object, so you can "chain" several commands together, as the example below shows: - -```python -import redis - -r = redis.Redis(decode_responses=True) - -pipe = r.pipeline() - -for i in range(5): - pipe.set(f"seat:{i}", f"#{i}") - -set_5_result = pipe.execute() -print(set_5_result) # >>> [True, True, True, True, True] - -pipe = r.pipeline() - -# "Chain" pipeline commands together. -get_3_result = pipe.get("seat:0").get("seat:3").get("seat:4").execute() -print(get_3_result) # >>> ['#0', '#3', '#4'] -``` +{{< clients-example pipe_trans_tutorial basic_pipe Python >}} +{{< /clients-example >}} ## Execute a transaction @@ -96,43 +73,8 @@ key is modified by another client before writing, the transaction aborts with a `WatchError` exception, and the loop executes again for another attempt. Otherwise, the loop terminates successfully. - -```python -r.set("shellpath", "/usr/syscmds/") - -with r.pipeline() as pipe: - # Repeat until successful. - while True: - try: - # Watch the key we are about to change. - pipe.watch("shellpath") - - # The pipeline executes commands directly (instead of - # buffering them) from immediately after the `watch()` - # call until we begin the transaction. - current_path = pipe.get("shellpath") - new_path = current_path + ":/usr/mycmds/" - - # Start the transaction, which will enable buffering - # again for the remaining commands. - pipe.multi() - - pipe.set("shellpath", new_path) - - pipe.execute() - - # The transaction succeeded, so break out of the loop. - break - except redis.WatchError: - # The transaction failed, so continue with the next attempt. - continue - -get_path_result = r.get("shellpath") -print(get_path_result) # >>> '/usr/syscmds/:/usr/mycmds/' -``` +{{< clients-example pipe_trans_tutorial trans_watch Python >}} +{{< /clients-example >}} Because this is a common pattern, the library includes a convenience method called `transaction()` that handles the code to watch keys, @@ -144,26 +86,5 @@ using `transaction()`. Note that `transaction()` can't add the `multi()` call automatically, so you must still place this correctly in your transaction function. - -```python -r.set("shellpath", "/usr/syscmds/") - - -def watched_sequence(pipe): - current_path = pipe.get("shellpath") - new_path = current_path + ":/usr/mycmds/" - - pipe.multi() - - pipe.set("shellpath", new_path) - - -trans_result = r.transaction(watched_sequence, "shellpath") -print(trans_result) # True - -get_path_result = r.get("shellpath") -print(get_path_result) # >>> '/usr/syscmds/:/usr/mycmds/' -``` +{{< clients-example pipe_trans_tutorial watch_conv_method Python >}} +{{< /clients-example >}}