{
  "schema_version": 2,
  "id": "develop/clients/go/transpipe",
  "title": "Pipelines and transactions",
  "url": "https://redis.io/docs/latest/develop/clients/go/transpipe/",
  "summary": "Learn how to use Redis pipelines and transactions",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-10-12T14:54:04+01:00",
  "page_type": "content",
  "content_hash": "b435c9bdcc46ecabfc35250d4f9cbd714c2e9b6f4d3af0d0152f62258bdbddc5",
  "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.\n\nThe main difference with the pipeline commands is that their return\nvalues contain a valid result only after the pipeline has finished executing.\nYou can access the result using the `Val()` method instead of\n`Result()` (note that errors are reported by the `Exec()` method rather\nthan by the individual commands).\n\nBasic pipeline: Execute multiple commands in a single batch using Pipeline and Exec\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou can also create a pipeline using the `Pipelined()` method.\nThis executes pipeline commands in a callback function that you\nprovide and calls `Exec()` automatically after it returns:\n\nPipeline with callback: Use Pipelined method for automatic execution of batched commands\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\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. Create a\ntransaction object with the `TxPipeline()` method, call command methods\non that object, and then call the transaction object's\n`Exec()` method to execute it. You can access the results\nfrom commands in the transaction after it completes using the\n`Val()` method.\n\nBasic transaction: Execute commands atomically using TxPipeline to ensure consistency\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThere is also a `TxPipelined()` method that works in a similar way\nto `Pipelined()`, described above:\n\nTransaction with callback: Use TxPipelined for automatic atomic execution with callback syntax\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\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](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe code below reads a string\nthat represents a `PATH` variable for a command shell, then appends a new\ncommand path to the string before attempting to write it back. If the watched\nkey is modified by another client before writing, the transaction aborts.\nThe `Watch()` method receives a callback function where you execute the\ncommands you want to watch. In the body of this callback, you can execute\nread-only commands before the transaction using the usual client object\n(called `rdb` in our examples) and receive an immediate result. Start the\ntransaction itself by calling `TxPipeline()` or `TxPipelined()` on the\n`Tx` object passed to the callback. `Watch()` also receives one or more\n`string` parameters after the callback that represent the keys you want\nto watch.\n\nFor production usage, you would generally call code like the following in\na loop to retry it until it succeeds or else report or log the failure:\n\nOptimistic locking: Watch keys for changes and retry transactions when watched keys are modified\n\n**Difficulty:** Advanced\n\n**Available in:** Go\n\n##### Go\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "execute-a-pipeline-ex0",
      "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-ex1",
      "language": "go",
      "code": "var pd0Result *redis.StatusCmd\n\tvar pd3Result *redis.StatusCmd\n\tvar pd4Result *redis.StatusCmd\n\n\tcmds, err = rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {\n\t\tpd0Result = (*redis.StatusCmd)(pipe.Get(ctx, \"seat:0\"))\n\t\tpd3Result = (*redis.StatusCmd)(pipe.Get(ctx, \"seat:3\"))\n\t\tpd4Result = (*redis.StatusCmd)(pipe.Get(ctx, \"seat:4\"))\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// The results are available only after the pipeline\n\t// has finished executing.\n\tfmt.Println(pd0Result.Val()) // >>> #0\n\tfmt.Println(pd3Result.Val()) // >>> #3\n\tfmt.Println(pd4Result.Val()) // >>> #4",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-transaction-ex0",
      "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-ex1",
      "language": "go",
      "code": "var tx1Result *redis.IntCmd\n\tvar tx2Result *redis.IntCmd\n\tvar tx3Result *redis.IntCmd\n\n\tcmds, err = rdb.TxPipelined(ctx, func(trans redis.Pipeliner) error {\n\t\ttx1Result = trans.IncrBy(ctx, \"counter:1\", 1)\n\t\ttx2Result = trans.IncrBy(ctx, \"counter:2\", 2)\n\t\ttx3Result = trans.IncrBy(ctx, \"counter:3\", 3)\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(tx1Result.Val()) // >>> 2\n\tfmt.Println(tx2Result.Val()) // >>> 4\n\tfmt.Println(tx3Result.Val()) // >>> 6",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "watch-keys-for-changes-ex0",
      "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"
    }
  ]
}
