I want to execute multiple redis commmand with transaction using MULTI and EXEC, so I can DISCARD it if something bad happens.
I've looking for the example of how to do redis transaction usinggo-redis/redis package and found nothing.
And I also look into the documentation here and I got nothing related to how to do redis transaction like this for example using that package. Or maybe I missing something from the documentation because yeah you know that godoc is only explain every function in package mostly using one liner.
Even though I found some example to do redis transaction using other Go Redis library, I won't modify my programs to use another library since the effort will be much larger to port whole application using another library.
Can anyone help me to do that using go-redis/redis package?
22 Answers
You get a Tx value for a transaction when you use Client.Watch
err := client.Watch(func(tx *redis.Tx) error { n, err := tx.Get(key).Int64() if err != nil && err != redis.Nil { return err } _, err = tx.Pipelined(func(pipe *redis.Pipeline) error { pipe.Set(key, strconv.FormatInt(n+1, 10), 0) return nil }) return err
}, key) 2 You can find an example how to create a Redis transaction here:
Code:
pipe := rdb.TxPipeline()
incr := pipe.Incr("tx_pipeline_counter")
pipe.Expire("tx_pipeline_counter", time.Hour)
// Execute
//
// MULTI
// INCR pipeline_counter
// EXPIRE pipeline_counts 3600
// EXEC
//
// using one rdb-server roundtrip.
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)Output:
1 <nil>And if you prefer to use the watch (optimistic locking) You can see an example here
Code:
const routineCount = 100
// Transactionally increments key using GET and SET commands.
increment := func(key string) error { txf := func(tx *redis.Tx) error { // get current value or zero n, err := tx.Get(key).Int() if err != nil && err != redis.Nil { return err } // actual opperation (local in optimistic lock) n++ // runs only if the watched keys remain unchanged _, err = tx.TxPipelined(func(pipe redis.Pipeliner) error { // pipe handles the error case pipe.Set(key, n, 0) return nil }) return err } for retries := routineCount; retries > 0; retries-- { err := rdb.Watch(txf, key) if err != redis.TxFailedErr { return err } // optimistic lock lost } return errors.New("increment reached maximum number of retries")
}
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i < routineCount; i++ { go func() { defer wg.Done() if err := increment("counter3"); err != nil { fmt.Println("increment error:", err) } }()
}
wg.Wait()
n, err := rdb.Get("counter3").Int()
fmt.Println("ended with", n, err)Output:
ended with 100 <nil> 0