{
  "id": "transpipe",
  "title": "Pipelines and transactions",
  "url": "https://redis.io/docs/latest/develop/clients/php/transpipe/",
  "summary": "Learn how to use Redis pipelines and transactions",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-05-05T09:23:06-05:00",
  "page_type": "content",
  "content_hash": "7b1806fb590e8f16108d2984fafcffa4ccb1968aecd5b6f71058b4d075c77a5b",
  "sections": [
    {
      "id": "execute-a-pipeline",
      "title": "Execute a pipeline",
      "role": "content",
      "text": "To execute commands in a pipeline, create a pipeline object with\n[`pipeline()`](https://github.com/predis/predis) and then add commands to it\nusing methods that resemble the standard command methods (for example,\n`set()` and `get()`). The commands are buffered in the pipeline and only\nexecute when you call the `execute()` method on the pipeline object. This\nmethod returns an array containing the results from all the commands in order.\n\nThe command methods for a pipeline always return the original pipeline object,\nso you can chain several commands together. You can also pass a callback to\n`pipeline()` and let Predis execute the batch automatically when the callback\nreturns, as the example below shows:\n\nFoundational: Use pipelines to batch multiple commands together and reduce network round trips\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#, Go, Java (Synchronous - Jedis), PHP, Python, Rust (Synchronous)\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"
    },
    {
      "id": "execute-a-transaction",
      "title": "Execute a transaction",
      "role": "content",
      "text": "A transaction works in a similar way to a pipeline, but all the queued commands\nexecute atomically. With Predis, you can create a transaction by calling\n`transaction()` and adding commands in a callback. Predis wraps those commands\nwith `MULTI` and `EXEC` automatically:\n\nBasic transaction: Execute commands atomically using transaction() to group related writes\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#, Go, Java (Synchronous - Jedis), PHP, Python, Rust (Synchronous)\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"
    },
    {
      "id": "watch-keys-for-changes",
      "title": "Watch keys for changes",
      "role": "content",
      "text": "Redis supports *optimistic locking* to avoid inconsistent updates to keys that\nseveral clients may modify at the same time. The basic idea is to watch for\nchanges to any keys that you use in a transaction while you are preparing the\nupdate. If the watched keys do change, you must restart the update using the\nlatest value from Redis. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe example below reads a string that represents a `PATH` variable for a\ncommand shell, appends a new command path, and then writes it back inside a\ntransaction. The `cas` option enables check-and-set behavior, `watch` tells\nPredis which key to monitor for changes, and `retry` lets Predis retry the\ntransaction automatically if another client changes the watched key before\n`EXEC` runs:\n\nOptimistic locking: Use WATCH with a CAS transaction to retry updates when another client modifies the key\n\n**Difficulty:** Intermediate\n\n**Available in:** C#, C#, Go, Java (Synchronous - Jedis), PHP, Python, Rust (Synchronous)\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "execute-a-pipeline-ex0",
      "language": "csharp",
      "code": "var setTasks = new[]\n        {\n            db.StringSetAsync(\"seat:0\", \"#0\"),\n            db.StringSetAsync(\"seat:1\", \"#1\"),\n            db.StringSetAsync(\"seat:2\", \"#2\"),\n            db.StringSetAsync(\"seat:3\", \"#3\"),\n            db.StringSetAsync(\"seat:4\", \"#4\")\n        };\n        foreach (var setTask in setTasks)\n        {\n            db.Wait(setTask);\n        }\n\n        var resp1Task = db.StringGetAsync(\"seat:0\");\n        var resp2Task = db.StringGetAsync(\"seat:3\");\n        var resp3Task = db.StringGetAsync(\"seat:4\");\n\n        var resp1 = db.Wait(resp1Task);\n        Console.WriteLine(resp1); // >>> #0\n\n        var resp2 = db.Wait(resp2Task);\n        Console.WriteLine(resp2); // >>> #3\n\n        var resp3 = db.Wait(resp3Task);\n        Console.WriteLine(resp3); // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex1",
      "language": "csharp",
      "code": "var pipeline = new Pipeline(db);\n\n        for (int i = 0; i < 5; i++)\n        {\n            _ = pipeline.Db.StringSetAsync($\"seat:{i}\", $\"#{i}\");\n        }\n        pipeline.Execute();\n\n        var resp1 = db.StringGet(\"seat:0\");\n        Console.WriteLine(resp1); // >>> #0\n\n        var resp2 = db.StringGet(\"seat:3\");\n        Console.WriteLine(resp2); // >>> #3\n\n        var resp3 = db.StringGet(\"seat:4\");\n        Console.WriteLine(resp2); // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex2",
      "language": "csharp",
      "code": "var setTasks = new[]\n        {\n            db.StringSetAsync(\"seat:0\", \"#0\"),\n            db.StringSetAsync(\"seat:1\", \"#1\"),\n            db.StringSetAsync(\"seat:2\", \"#2\"),\n            db.StringSetAsync(\"seat:3\", \"#3\"),\n            db.StringSetAsync(\"seat:4\", \"#4\")\n        };\n        foreach (var setTask in setTasks)\n        {\n            db.Wait(setTask);\n        }\n\n        var resp1Task = db.StringGetAsync(\"seat:0\");\n        var resp2Task = db.StringGetAsync(\"seat:3\");\n        var resp3Task = db.StringGetAsync(\"seat:4\");\n\n        var resp1 = db.Wait(resp1Task);\n        Console.WriteLine(resp1); // >>> #0\n\n        var resp2 = db.Wait(resp2Task);\n        Console.WriteLine(resp2); // >>> #3\n\n        var resp3 = db.Wait(resp3Task);\n        Console.WriteLine(resp3); // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex3",
      "language": "csharp",
      "code": "var pipeline = new Pipeline(db);\n\n        for (int i = 0; i < 5; i++)\n        {\n            _ = pipeline.Db.StringSetAsync($\"seat:{i}\", $\"#{i}\");\n        }\n        pipeline.Execute();\n\n        var resp1 = db.StringGet(\"seat:0\");\n        Console.WriteLine(resp1); // >>> #0\n\n        var resp2 = db.StringGet(\"seat:3\");\n        Console.WriteLine(resp2); // >>> #3\n\n        var resp3 = db.StringGet(\"seat:4\");\n        Console.WriteLine(resp2); // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex4",
      "language": "go",
      "code": "pipe := rdb.Pipeline()\n\n\tfor i := 0; i < 5; i++ {\n\t\tpipe.Set(ctx, fmt.Sprintf(\"seat:%v\", i), fmt.Sprintf(\"#%v\", i), 0)\n\t}\n\n\tcmds, err := pipe.Exec(ctx)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, c := range cmds {\n\t\tfmt.Printf(\"%v;\", c.(*redis.StatusCmd).Val())\n\t}\n\n\tfmt.Println(\"\")\n\t// >>> OK;OK;OK;OK;OK;\n\n\tpipe = rdb.Pipeline()\n\n\tget0Result := pipe.Get(ctx, \"seat:0\")\n\tget3Result := pipe.Get(ctx, \"seat:3\")\n\tget4Result := pipe.Get(ctx, \"seat:4\")\n\n\tcmds, err = pipe.Exec(ctx)\n\n\t// The results are available only after the pipeline\n\t// has finished executing.\n\tfmt.Println(get0Result.Val()) // >>> #0\n\tfmt.Println(get3Result.Val()) // >>> #3\n\tfmt.Println(get4Result.Val()) // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex5",
      "language": "java",
      "code": "// Make sure you close the pipeline after use to release resources\n        // and return the connection to the pool.\n        try (AbstractPipeline pipe = jedis.pipelined()) {\n\n            for (int i = 0; i < 5; i++) {\n                pipe.set(String.format(\"seat:%d\", i), String.format(\"#%d\", i));\n            }\n\n            pipe.sync();\n        }\n\n        try (AbstractPipeline pipe = jedis.pipelined()) {\n\n            Response<String> resp0 = pipe.get(\"seat:0\");\n            Response<String> resp3 = pipe.get(\"seat:3\");\n            Response<String> resp4 = pipe.get(\"seat:4\");\n\n            pipe.sync();\n\n            // Responses are available after the pipeline has executed.\n            System.out.println(resp0.get()); // >>> #0\n            System.out.println(resp3.get()); // >>> #3\n            System.out.println(resp4.get()); // >>> #4\n\n\n        }",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex6",
      "language": "php",
      "code": "$pipe = $r->pipeline();\n$pipe->set('seat:0', '#0')\n    ->set('seat:1', '#1')\n    ->set('seat:2', '#2')\n    ->set('seat:3', '#3')\n    ->set('seat:4', '#4');\n$pipe->execute();\n\n$pipe = $r->pipeline();\n$pipe->get('seat:0')\n    ->get('seat:1')\n    ->get('seat:2')\n    ->get('seat:3')\n    ->get('seat:4');\n$seats = $pipe->execute();\n\necho implode(', ', $seats), PHP_EOL;\n// >>> #0, #1, #2, #3, #4\n\n$responses = $r->pipeline(function ($pipe) {\n    $pipe->set('seat:5', '#5');\n    $pipe->set('seat:6', '#6');\n    $pipe->set('seat:7', '#7');\n    $pipe->get('seat:5');\n    $pipe->get('seat:6');\n    $pipe->get('seat:7');\n});\n\necho implode(', ', array_slice($responses, 3)), PHP_EOL;\n// >>> #5, #6, #7",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex7",
      "language": "python",
      "code": "r = redis.Redis(decode_responses=True)\n\npipe = r.pipeline()\n\nfor i in range(5):\n    pipe.set(f\"seat:{i}\", f\"#{i}\")\n\nset_5_result = pipe.execute()\nprint(set_5_result)  # >>> [True, True, True, True, True]\n\npipe = r.pipeline()\n\n# \"Chain\" pipeline commands together.\nget_3_result = pipe.get(\"seat:0\").get(\"seat:3\").get(\"seat:4\").execute()\nprint(get_3_result)  # >>> ['#0', '#3', '#4']",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex8",
      "language": "rust",
      "code": "// Check the success of the pipeline without checking the results\n        // individually.\n        match redis::pipe()\n            .set(\"seat:0\", \"#0\")\n            .set(\"seat:1\", \"#1\")\n            .set(\"seat:2\", \"#2\")\n            .set(\"seat:3\", \"#3\")\n            .set(\"seat:4\", \"#4\")\n            .exec(&mut r)\n        {\n            Ok(_) => {\n                println!(\"Pipe executed successfully\");\n            },\n            Err(e) => {\n                println!(\"Error executing pipe: {e}\");\n                return;\n            }\n        };\n        \n        // Check the success of the pipeline and the results individually.\n        let (seat_0, seat_1, seat_2, seat_3, seat_4) :\n        (String, String, String, String, String) = match redis::pipe()\n            .get(\"seat:0\")\n            .get(\"seat:1\")\n            .get(\"seat:2\")\n            .get(\"seat:3\")\n            .get(\"seat:4\")\n            .query(&mut r) {\n                Ok(res) => res,\n                Err(e) => {\n                    println!(\"Error executing pipe: {e}\");\n                    return;\n                }\n            };\n\n        println!(\"{seat_0}, {seat_1}, {seat_2}, {seat_3}, {seat_4}\");\n        // >>> #0, #1, #2, #3, #4\n\n        // Use `ignore()` to ignore the result of specific commands.\n        let (seat_5, seat_6, seat_7) :\n            (String, String, String) = match redis::pipe()\n            .set(\"seat:5\", \"#5\").ignore()\n            .set(\"seat:6\", \"#6\").ignore()\n            .set(\"seat:7\", \"#7\").ignore()\n            .get(\"seat:5\")\n            .get(\"seat:6\")\n            .get(\"seat:7\")\n            .query(&mut r) {\n                Ok(res) => res,\n                Err(e) => {\n                    println!(\"Error executing pipe: {e}\");\n                    return;\n                }\n            };\n\n        println!(\"{seat_5}, {seat_6}, {seat_7}\");\n        // >>> #5, #6, #7",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-transaction-ex0",
      "language": "csharp",
      "code": "var trans = db.CreateTransaction();\n\n        var incr1 = trans.StringIncrementAsync(\"counter:1\", 1);\n        var incr2 = trans.StringIncrementAsync(\"counter:2\", 2);\n        var incr3 = trans.StringIncrementAsync(\"counter:3\", 3);\n\n        bool committed = db.Wait(trans.ExecuteAsync());\n\n        var resp4 = db.Wait(incr1);\n        Console.WriteLine(resp4); // >>> 1\n\n        var resp5 = db.Wait(incr2);\n        Console.WriteLine(resp5); // >>> 2\n\n        var resp6 = db.Wait(incr3);\n        Console.WriteLine(resp6);  // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex1",
      "language": "csharp",
      "code": "var trans = new Transaction(db);\n\n        _ = trans.Db.StringIncrementAsync(\"counter:1\", 1);\n        _ = trans.Db.StringIncrementAsync(\"counter:2\", 2);\n        _ = trans.Db.StringIncrementAsync(\"counter:3\", 3);\n\n        trans.Execute();\n\n        var resp4 = db.StringGet(\"counter:1\");\n        Console.WriteLine(resp4); // >>> 1\n\n        var resp5 = db.StringGet(\"counter:2\");\n        Console.WriteLine(resp5); // >>> 2\n\n        var resp6 = db.StringGet(\"counter:3\");\n        Console.WriteLine(resp6);  // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex2",
      "language": "csharp",
      "code": "var trans = db.CreateTransaction();\n\n        var incr1 = trans.StringIncrementAsync(\"counter:1\", 1);\n        var incr2 = trans.StringIncrementAsync(\"counter:2\", 2);\n        var incr3 = trans.StringIncrementAsync(\"counter:3\", 3);\n\n        bool committed = db.Wait(trans.ExecuteAsync());\n\n        var resp4 = db.Wait(incr1);\n        Console.WriteLine(resp4); // >>> 1\n\n        var resp5 = db.Wait(incr2);\n        Console.WriteLine(resp5); // >>> 2\n\n        var resp6 = db.Wait(incr3);\n        Console.WriteLine(resp6);  // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex3",
      "language": "csharp",
      "code": "var trans = new Transaction(db);\n\n        _ = trans.Db.StringIncrementAsync(\"counter:1\", 1);\n        _ = trans.Db.StringIncrementAsync(\"counter:2\", 2);\n        _ = trans.Db.StringIncrementAsync(\"counter:3\", 3);\n\n        trans.Execute();\n\n        var resp4 = db.StringGet(\"counter:1\");\n        Console.WriteLine(resp4); // >>> 1\n\n        var resp5 = db.StringGet(\"counter:2\");\n        Console.WriteLine(resp5); // >>> 2\n\n        var resp6 = db.StringGet(\"counter:3\");\n        Console.WriteLine(resp6);  // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex4",
      "language": "go",
      "code": "trans := rdb.TxPipeline()\n\n\ttrans.IncrBy(ctx, \"counter:1\", 1)\n\ttrans.IncrBy(ctx, \"counter:2\", 2)\n\ttrans.IncrBy(ctx, \"counter:3\", 3)\n\n\tcmds, err = trans.Exec(ctx)\n\n\tfor _, c := range cmds {\n\t\tfmt.Println(c.(*redis.IntCmd).Val())\n\t}\n\t// >>> 1\n\t// >>> 2\n\t// >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex5",
      "language": "java",
      "code": "try ( AbstractTransaction trans = jedis.multi()) {\n\n           trans.incrBy(\"counter:1\", 1);\n           trans.incrBy(\"counter:2\", 2);\n           trans.incrBy(\"counter:3\", 3);\n\n           trans.exec();\n        }\n        System.out.println(jedis.get(\"counter:1\")); // >>> 1\n        System.out.println(jedis.get(\"counter:2\")); // >>> 2\n        System.out.println(jedis.get(\"counter:3\")); // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex6",
      "language": "php",
      "code": "$r->transaction(function (MultiExec $tx) {\n    $tx->incr('counter:1');\n    $tx->incrby('counter:2', 2);\n    $tx->incrby('counter:3', 3);\n});\n\necho implode(', ', $r->mget('counter:1', 'counter:2', 'counter:3')), PHP_EOL;\n// >>> 1, 2, 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex7",
      "language": "python",
      "code": "\"\"\"\nCode samples for vector database quickstart pages:\n    https://redis.io/docs/latest/develop/get-started/vector-database/\n\"\"\"\nimport redis\n\nr = redis.Redis(decode_responses=True)\n\npipe = r.pipeline()\n\nfor i in range(5):\n    pipe.set(f\"seat:{i}\", f\"#{i}\")\n\nset_5_result = pipe.execute()\nprint(set_5_result)  # >>> [True, True, True, True, True]\n\npipe = r.pipeline()\n\n# \"Chain\" pipeline commands together.\nget_3_result = pipe.get(\"seat:0\").get(\"seat:3\").get(\"seat:4\").execute()\nprint(get_3_result)  # >>> ['#0', '#3', '#4']\n\nr.set(\"shellpath\", \"/usr/syscmds/\")\n\nwith r.pipeline() as pipe:\n    # Repeat until successful.\n    while True:\n        try:\n            # Watch the key we are about to change.\n            pipe.watch(\"shellpath\")\n\n            # The pipeline executes commands directly (instead of\n            # buffering them) from immediately after the `watch()`\n            # call until we begin the transaction.\n            current_path = pipe.get(\"shellpath\")\n            new_path = current_path + \":/usr/mycmds/\"\n\n            # Start the transaction, which will enable buffering\n            # again for the remaining commands.\n            pipe.multi()\n\n            pipe.set(\"shellpath\", new_path)\n\n            pipe.execute()\n\n            # The transaction succeeded, so break out of the loop.\n            break\n        except redis.WatchError:\n            # The transaction failed, so continue with the next attempt.\n            continue\n\nget_path_result = r.get(\"shellpath\")\nprint(get_path_result)  # >>> '/usr/syscmds/:/usr/mycmds/'\n\nr.set(\"shellpath\", \"/usr/syscmds/\")\n\n\ndef watched_sequence(pipe):\n    current_path = pipe.get(\"shellpath\")\n    new_path = current_path + \":/usr/mycmds/\"\n\n    pipe.multi()\n\n    pipe.set(\"shellpath\", new_path)\n\n\ntrans_result = r.transaction(watched_sequence, \"shellpath\")\nprint(trans_result)  # True\n\nget_path_result = r.get(\"shellpath\")\nprint(get_path_result)  # >>> '/usr/syscmds/:/usr/mycmds/'",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex8",
      "language": "rust",
      "code": "match redis::pipe()\n            .atomic()\n            .incr(\"counter:1\", 1)\n            .incr(\"counter:2\", 2)\n            .incr(\"counter:3\", 3)\n            .exec(&mut r)\n        {\n            Ok(_) => {\n                println!(\"Transaction executed successfully\");\n            },\n            Err(e) => {\n                println!(\"Error executing transaction: {e}\");\n                return;\n            }\n        };",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "watch-keys-for-changes-ex0",
      "language": "csharp",
      "code": "var watchedTrans = db.CreateTransaction();\n\n        watchedTrans.AddCondition(Condition.KeyNotExists(\"customer:39182\"));\n\n        var hashSetTask = watchedTrans.HashSetAsync(\n            \"customer:39182\",\n            [\n                new(\"name\", \"David\"),\n                new(\"age\", \"27\")\n            ]\n        );\n\n        bool succeeded = db.Wait(watchedTrans.ExecuteAsync());\n        db.Wait(hashSetTask);\n        Console.WriteLine(succeeded); // >>> true",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex1",
      "language": "csharp",
      "code": "var watchedTrans = new Transaction(db);\n\n        watchedTrans.AddCondition(Condition.KeyNotExists(\"customer:39182\"));\n\n        _ = watchedTrans.Db.HashSetAsync(\n            \"customer:39182\",\n            [\n                new(\"name\", \"David\"),\n                new(\"age\", \"27\")\n            ]\n        );\n\n        bool succeeded = watchedTrans.Execute();\n        Console.WriteLine(succeeded); // >>> true",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex2",
      "language": "csharp",
      "code": "var watchedTrans = db.CreateTransaction();\n\n        watchedTrans.AddCondition(Condition.KeyNotExists(\"customer:39182\"));\n\n        var hashSetTask = watchedTrans.HashSetAsync(\n            \"customer:39182\",\n            [\n                new(\"name\", \"David\"),\n                new(\"age\", \"27\")\n            ]\n        );\n\n        bool succeeded = db.Wait(watchedTrans.ExecuteAsync());\n        db.Wait(hashSetTask);\n        Console.WriteLine(succeeded); // >>> true",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex3",
      "language": "csharp",
      "code": "var watchedTrans = new Transaction(db);\n\n        watchedTrans.AddCondition(Condition.KeyNotExists(\"customer:39182\"));\n\n        _ = watchedTrans.Db.HashSetAsync(\n            \"customer:39182\",\n            [\n                new(\"name\", \"David\"),\n                new(\"age\", \"27\")\n            ]\n        );\n\n        bool succeeded = watchedTrans.Execute();\n        Console.WriteLine(succeeded); // >>> true",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex4",
      "language": "go",
      "code": "// Set initial value of `shellpath`.\n\trdb.Set(ctx, \"shellpath\", \"/usr/syscmds/\", 0)\n\n\tconst maxRetries = 1000\n\n\t// Retry if the key has been changed.\n\tfor i := 0; i < maxRetries; i++ {\n\t\terr := rdb.Watch(ctx,\n\t\t\tfunc(tx *redis.Tx) error {\n\t\t\t\tcurrentPath, err := rdb.Get(ctx, \"shellpath\").Result()\n\t\t\t\tnewPath := currentPath + \":/usr/mycmds/\"\n\n\t\t\t\t_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {\n\t\t\t\t\tpipe.Set(ctx, \"shellpath\", newPath, 0)\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\n\t\t\t\treturn err\n\t\t\t},\n\t\t\t\"shellpath\",\n\t\t)\n\n\t\tif err == nil {\n\t\t\t// Success.\n\t\t\tbreak\n\t\t} else if err == redis.TxFailedErr {\n\t\t\t// Optimistic lock lost. Retry the transaction.\n\t\t\tcontinue\n\t\t} else {\n\t\t\t// Panic for any other error.\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tfmt.Println(rdb.Get(ctx, \"shellpath\").Val())\n\t// >>> /usr/syscmds/:/usr/mycmds/",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex5",
      "language": "java",
      "code": "// Set initial value of `shellpath`.\n        jedis.set(\"shellpath\", \"/usr/syscmds/\");\n\n        // Start the transaction and watch the key we are about to update.\n        try (AbstractTransaction trans = jedis.transaction(false)) { // create a Transaction object without sending MULTI command\n            trans.watch(\"shellpath\"); // send WATCH command(s)\n            trans.multi(); // send MULTI command\n\n            String currentPath = jedis.get(\"shellpath\");\n            String newPath = currentPath + \":/usr/mycmds/\";\n\n            // Commands added to the `trans` object\n            // will be buffered until `trans.exec()` is called.\n            Response<String> setResult = trans.set(\"shellpath\", newPath);\n            List<Object> transResults = trans.exec();\n\n            // The `exec()` call returns null if the transaction failed.\n            if (transResults != null) {\n                // Responses are available if the transaction succeeded.\n                System.out.println(setResult.get()); // >>> OK\n\n                // You can also get the results from the list returned by\n                // `trans.exec()`.\n                for (Object item: transResults) {\n                    System.out.println(item);\n                }\n                // >>> OK\n\n                System.out.println(jedis.get(\"shellpath\"));\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }\n        }",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex6",
      "language": "php",
      "code": "$r->set('shellpath', '/usr/syscmds/');\n\n$r->transaction(\n    ['cas' => true, 'watch' => 'shellpath', 'retry' => 3],\n    function (MultiExec $tx) {\n        $path = $tx->get('shellpath');\n        $tx->multi();\n        $tx->set('shellpath', $path . ':/usr/mycmds/');\n    }\n);\n\necho $r->get('shellpath'), PHP_EOL;\n// >>> /usr/syscmds/:/usr/mycmds/",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex7",
      "language": "python",
      "code": "r.set(\"shellpath\", \"/usr/syscmds/\")\n\nwith r.pipeline() as pipe:\n    # Repeat until successful.\n    while True:\n        try:\n            # Watch the key we are about to change.\n            pipe.watch(\"shellpath\")\n\n            # The pipeline executes commands directly (instead of\n            # buffering them) from immediately after the `watch()`\n            # call until we begin the transaction.\n            current_path = pipe.get(\"shellpath\")\n            new_path = current_path + \":/usr/mycmds/\"\n\n            # Start the transaction, which will enable buffering\n            # again for the remaining commands.\n            pipe.multi()\n\n            pipe.set(\"shellpath\", new_path)\n\n            pipe.execute()\n\n            # The transaction succeeded, so break out of the loop.\n            break\n        except redis.WatchError:\n            # The transaction failed, so continue with the next attempt.\n            continue\n\nget_path_result = r.get(\"shellpath\")\nprint(get_path_result)  # >>> '/usr/syscmds/:/usr/mycmds/'",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex8",
      "language": "rust",
      "code": "let key = \"shellpath\";\n        let _: () = r.set(key, \"/usr/syscmds/\").unwrap();\n        \n        let Ok(_,): Result<((),), _> = redis::transaction(&mut r, &[key], |r, pipe| {\n            let mut path: String = r.get(key).unwrap();\n            path.push_str(\":/usr/mycmds/\");\n            pipe.set(key, path).query(r)\n        }) else {\n            println!(\"Error executing transaction\");\n            return;\n        };\n\n        match r.get(\"shellpath\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");\n                // >>> /usr/syscmds/:/usr/mycmds/\n            },\n            Err(e) => {\n                println!(\"Error getting shellpath: {e}\");\n                return;\n            }\n        };",
      "section_id": "watch-keys-for-changes"
    }
  ]
}
