Files
go/doctests/pipe_trans_example_test.go
zeekay 6994cf05aa kv, not redis: rename the package, the extras and the URL scheme
We do not use Redis. The client was a go-redis fork and still said so
everywhere: `package redis`, `redis.NewClient`, `redis://`, `extra/redisotel`.
Consumers wrote `redis.Client` against a Valkey-backed store called KV.

  package redis            -> package kv          (93 decls)
  redis.X                  -> kv.X                (2,794 call sites)
  "redis: …" errors        -> "kv: …"             (206)
  extra/redis{otel,cmd,prometheus,census} -> extra/kv{otel,cmd,prometheus,census}
  redis:// , rediss://     -> kv:// , kvs://      (ParseURL)
  Redis*/‌*Redis* idents    -> KV*/*KV*
  REDIS_VERSION, REDIS_ENDPOINTS_CONFIG_PATH -> KV_*

I previously argued this rename was unsafe because much of it was "wire
contract". That was wrong, and the categorisation shows why: RESP command names
are GET/SET/HSET — they never contain the word. Of 5,771 occurrences only two
kinds were server-visible at all: the ParseURL scheme (we choose what we pass;
our configs already use kv://) and the CLIENT SETINFO lib-name label, which the
server merely records. Nothing here changes a byte of protocol.

Two things deliberately keep the old spelling:
  RediSearch  — names a specific external module the FT.* commands map to.
  StoreDist   — contains the letters by accident (sto-reDis-t), not the word.

Breaking for consumers by design: every import site must move redis.X -> kv.X.
Minor bump, never a major — this module stays v9 forever.
2026-07-26 08:34:19 -07:00

183 lines
3.6 KiB
Go

// EXAMPLE: pipe_trans_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/hanzokv/go/v9"
)
// HIDE_END
func ExampleClient_transactions() {
ctx := context.Background()
rdb := kv.NewClient(&kv.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
for i := 0; i < 5; i++ {
rdb.Del(ctx, fmt.Sprintf("seat:%d", i))
}
rdb.Del(ctx, "counter:1", "counter:2", "counter:3", "shellpath")
// REMOVE_END
// STEP_START basic_pipe
pipe := rdb.Pipeline()
for i := 0; i < 5; i++ {
pipe.Set(ctx, fmt.Sprintf("seat:%v", i), fmt.Sprintf("#%v", i), 0)
}
cmds, err := pipe.Exec(ctx)
if err != nil {
panic(err)
}
for _, c := range cmds {
fmt.Printf("%v;", c.(*kv.StatusCmd).Val())
}
fmt.Println("")
// >>> OK;OK;OK;OK;OK;
pipe = rdb.Pipeline()
get0Result := pipe.Get(ctx, "seat:0")
get3Result := pipe.Get(ctx, "seat:3")
get4Result := pipe.Get(ctx, "seat:4")
cmds, err = pipe.Exec(ctx)
// The results are available only after the pipeline
// has finished executing.
fmt.Println(get0Result.Val()) // >>> #0
fmt.Println(get3Result.Val()) // >>> #3
fmt.Println(get4Result.Val()) // >>> #4
// STEP_END
// STEP_START basic_pipe_pipelined
var pd0Result *kv.StatusCmd
var pd3Result *kv.StatusCmd
var pd4Result *kv.StatusCmd
cmds, err = rdb.Pipelined(ctx, func(pipe kv.Pipeliner) error {
pd0Result = (*kv.StatusCmd)(pipe.Get(ctx, "seat:0"))
pd3Result = (*kv.StatusCmd)(pipe.Get(ctx, "seat:3"))
pd4Result = (*kv.StatusCmd)(pipe.Get(ctx, "seat:4"))
return nil
})
if err != nil {
panic(err)
}
// The results are available only after the pipeline
// has finished executing.
fmt.Println(pd0Result.Val()) // >>> #0
fmt.Println(pd3Result.Val()) // >>> #3
fmt.Println(pd4Result.Val()) // >>> #4
// STEP_END
// STEP_START basic_trans
trans := rdb.TxPipeline()
trans.IncrBy(ctx, "counter:1", 1)
trans.IncrBy(ctx, "counter:2", 2)
trans.IncrBy(ctx, "counter:3", 3)
cmds, err = trans.Exec(ctx)
for _, c := range cmds {
fmt.Println(c.(*kv.IntCmd).Val())
}
// >>> 1
// >>> 2
// >>> 3
// STEP_END
// STEP_START basic_trans_txpipelined
var tx1Result *kv.IntCmd
var tx2Result *kv.IntCmd
var tx3Result *kv.IntCmd
cmds, err = rdb.TxPipelined(ctx, func(trans kv.Pipeliner) error {
tx1Result = trans.IncrBy(ctx, "counter:1", 1)
tx2Result = trans.IncrBy(ctx, "counter:2", 2)
tx3Result = trans.IncrBy(ctx, "counter:3", 3)
return nil
})
if err != nil {
panic(err)
}
fmt.Println(tx1Result.Val()) // >>> 2
fmt.Println(tx2Result.Val()) // >>> 4
fmt.Println(tx3Result.Val()) // >>> 6
// STEP_END
// STEP_START trans_watch
// Set initial value of `shellpath`.
rdb.Set(ctx, "shellpath", "/usr/syscmds/", 0)
const maxRetries = 1000
// Retry if the key has been changed.
for i := 0; i < maxRetries; i++ {
err := rdb.Watch(ctx,
func(tx *kv.Tx) error {
currentPath, err := rdb.Get(ctx, "shellpath").Result()
newPath := currentPath + ":/usr/mycmds/"
_, err = tx.TxPipelined(ctx, func(pipe kv.Pipeliner) error {
pipe.Set(ctx, "shellpath", newPath, 0)
return nil
})
return err
},
"shellpath",
)
if err == nil {
// Success.
break
} else if err == kv.TxFailedErr {
// Optimistic lock lost. Retry the transaction.
continue
} else {
// Panic for any other error.
panic(err)
}
}
fmt.Println(rdb.Get(ctx, "shellpath").Val())
// >>> /usr/syscmds/:/usr/mycmds/
// STEP_END
// Output:
// OK;OK;OK;OK;OK;
// #0
// #3
// #4
// #0
// #3
// #4
// 1
// 2
// 3
// 2
// 4
// 6
// /usr/syscmds/:/usr/mycmds/
}