From 004659da1072be1b40837e71563a79c623e14778 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Fri, 6 Dec 2024 14:14:15 +0000 Subject: [PATCH] DOC-4560 added pipe/transaction examples --- doctests/pipe_trans_example_test.go | 170 ++++++++++++++++++++++------ 1 file changed, 136 insertions(+), 34 deletions(-) diff --git a/doctests/pipe_trans_example_test.go b/doctests/pipe_trans_example_test.go index 61f1bcbc7..ea1dd5b48 100644 --- a/doctests/pipe_trans_example_test.go +++ b/doctests/pipe_trans_example_test.go @@ -5,7 +5,6 @@ package example_commands_test import ( "context" "fmt" - "time" "github.com/redis/go-redis/v9" ) @@ -13,7 +12,6 @@ import ( // HIDE_END func ExampleClient_transactions() { - // STEP_START basic_trans ctx := context.Background() rdb := redis.NewClient(&redis.Options{ @@ -21,58 +19,162 @@ func ExampleClient_transactions() { Password: "", // no password docs DB: 0, // use default DB }) - // REMOVE_START - rdb.Del(ctx, "RateCounter") + 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 - setResult, err := rdb.Set(ctx, "RateCounter", 0, 0).Result() + // 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) } - fmt.Println(setResult) // >>> OK + for _, c := range cmds { + fmt.Printf("%v;", c.(*redis.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 *redis.StatusCmd + var pd3Result *redis.StatusCmd + var pd4Result *redis.StatusCmd + + cmds, err = rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error { + pd0Result = (*redis.StatusCmd)(pipe.Get(ctx, "seat:0")) + pd3Result = (*redis.StatusCmd)(pipe.Get(ctx, "seat:3")) + pd4Result = (*redis.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() - // The values of `incrResult` and `expResult` are not available - // until the transaction has finished executing. - incrResult := trans.Incr(ctx, "RateCounter") - expResult := trans.Expire(ctx, "RateCounter", time.Second*10) + trans.IncrBy(ctx, "counter:1", 1) + trans.IncrBy(ctx, "counter:2", 2) + trans.IncrBy(ctx, "counter:3", 3) + + cmds, err = trans.Exec(ctx) - cmdsExecuted, err := trans.Exec(ctx) + for _, c := range cmds { + fmt.Println(c.(*redis.IntCmd).Val()) + } + // >>> 1 + // >>> 2 + // >>> 3 + // STEP_END + + // STEP_START basic_trans_txpipelined + var tx1Result *redis.IntCmd + var tx2Result *redis.IntCmd + var tx3Result *redis.IntCmd + + cmds, err = rdb.TxPipelined(ctx, func(trans redis.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) } - // Values are now available. - fmt.Println(incrResult.Val()) // >>> 1 - fmt.Println(expResult.Val()) // >>> true - - // You can also use the array of command data returned - // by the `Exec()` call. - fmt.Println(len(cmdsExecuted)) // >>> 2 - - fmt.Printf("%v: %v\n", - cmdsExecuted[0].Name(), - cmdsExecuted[0].(*redis.IntCmd).Val(), - ) - // >>> incr: 1 - - fmt.Printf("%v: %v\n", - cmdsExecuted[1].Name(), - cmdsExecuted[1].(*redis.BoolCmd).Val(), - ) - // >>> expire: true + 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 *redis.Tx) error { + currentPath, err := rdb.Get(ctx, "shellpath").Result() + newPath := currentPath + ":/usr/mycmds/" + + _, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error { + pipe.Set(ctx, "shellpath", newPath, 0) + return nil + }) + + return err + }, + "shellpath", + ) + + if err == nil { + // Success. + break + } else if err == redis.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;OK; + // #0 + // #3 + // #4 + // #0 + // #3 + // #4 // 1 - // true // 2 - // incr: 1 - // expire: true + // 3 + // 2 + // 4 + // 6 + // /usr/syscmds/:/usr/mycmds/ }