{
  "id": "transpipe",
  "title": "Pipelines and transactions",
  "url": "https://redis.io/docs/latest/develop/clients/lettuce/transpipe/",
  "summary": "Learn how to use Redis pipelines and transactions",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-07-24T10:52:10-07:00",
  "page_type": "content",
  "content_hash": "914c2939c288efd91be5ff4183fe0182f6091f72682de6e7d5dc42506b33b201",
  "sections": [
    {
      "id": "execute-a-pipeline",
      "title": "Execute a pipeline",
      "role": "content",
      "text": "With Lettuce, you pipeline commands by buffering them on the client and then\nflushing them to the server together. The synchronous and asynchronous APIs do\nthis by temporarily disabling auto-flushing with `setAutoFlushCommands()` and\nthen calling `flushCommands()`; each buffered command returns a `RedisFuture`\nthat you can inspect after the batch is flushed. The reactive API pipelines\ncommands by composing the corresponding `Mono` and `Flux` publishers:\n\nFoundational: Batch multiple Lettuce commands and flush them to the server together\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), PHP, Python, Ruby, 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\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\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##### Ruby\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"
    },
    {
      "id": "execute-a-transaction",
      "title": "Execute a transaction",
      "role": "content",
      "text": "Lettuce transactions use the Redis `MULTI` and `EXEC` commands. Commands issued\nbetween `multi()` and `exec()` are queued on the server and only run when\n`exec()` completes. The return value from `exec()` is a `TransactionResult`\nthat contains the results in order:\n\nFoundational: Use MULTI and EXEC with Lettuce to execute multiple commands atomically\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), PHP, Python, Ruby, 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\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\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##### Ruby\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, Redis discards the transaction and you\nmust retry using the latest 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 watches a key, reads its current value, queues an update\ninside `MULTI`, and then checks `TransactionResult.wasDiscarded()` after\n`EXEC`:\n\nOptimistic locking: Use WATCH with EXEC and check whether Lettuce discarded the transaction\n\n**Difficulty:** Intermediate\n\n**Available in:** C#, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), PHP, Python, Ruby, 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\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\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##### Ruby\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": "// Lettuce pipelines commands by buffering them on the client while\n            // auto-flushing is disabled. Use the asynchronous API to obtain a\n            // `RedisFuture` for each buffered command, then read the results\n            // after the batch has been flushed to the server.\n            RedisAsyncCommands<String, String> async = connection.async();\n            connection.setAutoFlushCommands(false);\n\n            for (int i = 0; i < 5; i++) {\n                async.set(\"seat:\" + i, \"#\" + i);\n            }\n\n            RedisFuture<String> seat0 = async.get(\"seat:0\");\n            RedisFuture<String> seat3 = async.get(\"seat:3\");\n            RedisFuture<String> seat4 = async.get(\"seat:4\");\n\n            connection.flushCommands();\n            connection.setAutoFlushCommands(true);\n\n            System.out.println(seat0.get()); // >>> #0\n            System.out.println(seat3.get()); // >>> #3\n            System.out.println(seat4.get()); // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex6",
      "language": "java",
      "code": "// Disable auto-flushing to buffer the commands on the client, then\n            // flush them to the server as a single batch. Each command returns\n            // a `RedisFuture` that completes when its response arrives.\n            connection.setAutoFlushCommands(false);\n\n            for (int i = 0; i < 5; i++) {\n                async.set(\"seat:\" + i, \"#\" + i);\n            }\n\n            RedisFuture<String> seat0 = async.get(\"seat:0\");\n            RedisFuture<String> seat3 = async.get(\"seat:3\");\n            RedisFuture<String> seat4 = async.get(\"seat:4\");\n\n            connection.flushCommands();\n            connection.setAutoFlushCommands(true);\n\n            System.out.println(seat0.get()); // >>> #0\n            System.out.println(seat3.get()); // >>> #3\n            System.out.println(seat4.get()); // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex7",
      "language": "java",
      "code": "// Reactive commands are pipelined automatically: the writes below are\n            // sent to the server back to back, then the reads are sent as a batch\n            // once you subscribe (here, by calling `block()`).\n            List<String> seats = Flux.range(0, 5)\n                    .flatMap(i -> reactive.set(\"seat:\" + i, \"#\" + i))\n                    .thenMany(Flux.just(\"seat:0\", \"seat:3\", \"seat:4\")\n                            .flatMapSequential(reactive::get))\n                    .collectList()\n                    .block();\n\n            System.out.println(seats.get(0)); // >>> #0\n            System.out.println(seats.get(1)); // >>> #3\n            System.out.println(seats.get(2)); // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex8",
      "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-ex9",
      "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-ex10",
      "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-ex11",
      "language": "ruby",
      "code": "r.pipelined do |pipe|\n  (0..4).each { |i| pipe.set(\"seat:#{i}\", \"##{i}\") }\nend\n\nseats = r.pipelined do |pipe|\n  pipe.get('seat:0')\n  pipe.get('seat:3')\n  pipe.get('seat:4')\nend\n\nputs seats[0] # >>> #0\nputs seats[1] # >>> #3\nputs seats[2] # >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex12",
      "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": "commands.multi();\n\n            commands.incrby(\"counter:1\", 1);\n            commands.incrby(\"counter:2\", 2);\n            commands.incrby(\"counter:3\", 3);\n\n            TransactionResult transResult = commands.exec();\n\n            System.out.println((Long) transResult.get(0)); // >>> 1\n            System.out.println((Long) transResult.get(1)); // >>> 2\n            System.out.println((Long) transResult.get(2)); // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex6",
      "language": "java",
      "code": "// Commands issued between `multi()` and `exec()` are queued on the\n            // server. Their futures only complete when `exec()` runs.\n            async.multi();\n\n            RedisFuture<Long> counter1 = async.incrby(\"counter:1\", 1);\n            RedisFuture<Long> counter2 = async.incrby(\"counter:2\", 2);\n            RedisFuture<Long> counter3 = async.incrby(\"counter:3\", 3);\n\n            RedisFuture<TransactionResult> execResult = async.exec();\n            execResult.get();\n\n            System.out.println(counter1.get()); // >>> 1\n            System.out.println(counter2.get()); // >>> 2\n            System.out.println(counter3.get()); // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex7",
      "language": "java",
      "code": "// Queue the commands between `multi()` and `exec()`. Subscribing to\n            // each one inside the `multi()` callback sends it to the server, where\n            // it is held until `exec()` runs the transaction.\n            TransactionResult transResult = reactive.multi()\n                    .flatMap(ok -> {\n                        reactive.incrby(\"counter:1\", 1).subscribe();\n                        reactive.incrby(\"counter:2\", 2).subscribe();\n                        reactive.incrby(\"counter:3\", 3).subscribe();\n                        return reactive.exec();\n                    })\n                    .block();\n\n            System.out.println((Long) transResult.get(0)); // >>> 1\n            System.out.println((Long) transResult.get(1)); // >>> 2\n            System.out.println((Long) transResult.get(2)); // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex8",
      "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-ex9",
      "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-ex10",
      "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-ex11",
      "language": "ruby",
      "code": "trans_results = r.multi do |tx|\n  tx.incrby('counter:1', 1)\n  tx.incrby('counter:2', 2)\n  tx.incrby('counter:3', 3)\nend\n\nputs trans_results[0] # >>> 1\nputs trans_results[1] # >>> 2\nputs trans_results[2] # >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "execute-a-transaction-ex12",
      "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": "commands.set(\"shellpath\", \"/usr/syscmds/\");\n\n            // Watch the key for changes while we prepare the update.\n            commands.watch(\"shellpath\");\n\n            String currentPath = commands.get(\"shellpath\");\n            String newPath = currentPath + \":/usr/mycmds/\";\n\n            commands.multi();\n            commands.set(\"shellpath\", newPath);\n\n            TransactionResult watchedResult = commands.exec();\n\n            // `exec()` returns an empty result that reports `wasDiscarded()` if\n            // the watched key changed before the transaction ran.\n            if (!watchedResult.wasDiscarded()) {\n                System.out.println(commands.get(\"shellpath\"));\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex6",
      "language": "java",
      "code": "async.set(\"shellpath\", \"/usr/syscmds/\").get();\n\n            // Watch the key for changes while we prepare the update.\n            async.watch(\"shellpath\").get();\n\n            String currentPath = async.get(\"shellpath\").get();\n            String newPath = currentPath + \":/usr/mycmds/\";\n\n            async.multi().get();\n            async.set(\"shellpath\", newPath);\n\n            TransactionResult watchedResult = async.exec().get();\n\n            // `exec()` reports `wasDiscarded()` if the watched key changed\n            // before the transaction ran.\n            if (!watchedResult.wasDiscarded()) {\n                System.out.println(async.get(\"shellpath\").get());\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex7",
      "language": "java",
      "code": "reactive.set(\"shellpath\", \"/usr/syscmds/\").block();\n\n            // Watch the key, read its current value, then queue the update in a\n            // transaction. `exec()` reports `wasDiscarded()` if the watched key\n            // changed before the transaction ran.\n            TransactionResult watchedResult = reactive.watch(\"shellpath\")\n                    .then(reactive.get(\"shellpath\"))\n                    .flatMap(currentPath -> reactive.multi()\n                            .flatMap(ok -> {\n                                reactive.set(\"shellpath\", currentPath + \":/usr/mycmds/\").subscribe();\n                                return reactive.exec();\n                            }))\n                    .block();\n\n            if (!watchedResult.wasDiscarded()) {\n                System.out.println(reactive.get(\"shellpath\").block());\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex8",
      "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-ex9",
      "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-ex10",
      "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-ex11",
      "language": "ruby",
      "code": "r.set('shellpath', '/usr/syscmds/')\n\n# Watch the key, read its current value, then queue the update in a\n# transaction. `multi` returns `nil` if the watched key changed before\n# `EXEC` ran, in which case you would retry with the latest value.\nresult = r.watch('shellpath') do |client|\n  current_path = client.get('shellpath')\n  new_path = current_path + ':/usr/mycmds/'\n\n  client.multi do |tx|\n    tx.set('shellpath', new_path)\n  end\nend\n\nif result.nil?\n  puts 'Transaction aborted; retry with the latest value'\nelse\n  puts r.get('shellpath')\n  # >>> /usr/syscmds/:/usr/mycmds/\nend",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex12",
      "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"
    }
  ]
}
