{
  "id": "transpipe",
  "title": "Pipelines and transactions",
  "url": "https://redis.io/docs/latest/develop/clients/rust/transpipe/",
  "summary": "Learn how to use Redis pipelines and transactions",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-04-01T08:10:08-05:00",
  "page_type": "content",
  "content_hash": "ef8a6a9c81f125fc2cb0157da5147ba8f3da9f9b660346c21df0b3e4a7eb6eed",
  "sections": [
    {
      "id": "execute-a-pipeline",
      "title": "Execute a pipeline",
      "role": "content",
      "text": "To execute commands in a pipeline, you first create a pipeline object\nand then add commands to it using methods that resemble the standard\ncommand methods (for example, `set()` and `get()`). The commands are\nbuffered in the pipeline and only execute when you call the `exec()`\nmethod on the pipeline object. If you need the results from the\ncommands, use the `query()` method, which returns\nthe results from all the commands in order.\n\nNote that the command methods for a pipeline always return the original\npipeline object, so you can \"chain\" several commands together, as the\nexample below shows:\n\n#### Code Examples\n\n**Redis CLI:**\n\n[code example]\n\n**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), Python, Rust (Synchronous)\n\n**C# (Synchronous):**\n\n[code example]\n\n**Go:**\n\n[code example]\n\n**Java (Synchronous - Jedis):**\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": "You can execute a simple transaction by adding the `atomic()` method to a pipeline.\n\n#### Code Examples\n\n**Redis CLI:**\n\n[code example]\n\n**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), Python, Rust (Synchronous)\n\n**C# (Synchronous):**\n\n[code example]\n\n**Go:**\n\n[code example]\n\n**Java (Synchronous - Jedis):**\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\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions]()\nfor more information about optimistic locking.\n\nThe example below shows how to use the `transaction()` function to\nautomatically retry a transaction when watched keys are modified.\nPass the list of keys you want to watch and a closure representing the transaction.\nThe closure receives the original connection and a pipeline as parameters.\nUse the connection to read the latest values from the watched keys,\nbut always use the pipeline to add all the commands that make up the watched transaction.\nIf the watched keys are modified during the transaction, the `transaction()` function\nautomatically retries the transaction until it succeeds.\n\n#### Code Examples\n\n**Redis CLI:**\n\n[code example]\n\n**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), Python, Rust (Synchronous)\n\n**C# (Synchronous):**\n\n[code example]\n\n**Go:**\n\n[code example]\n\n**Java (Synchronous - Jedis):**\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 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-ex1",
      "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-ex2",
      "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-ex3",
      "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-ex4",
      "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 = 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-ex1",
      "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-ex2",
      "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-ex3",
      "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-ex4",
      "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 = 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-ex1",
      "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-ex2",
      "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-ex3",
      "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-ex4",
      "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"
    }
  ]
}
