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 using go-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?
GitHub - redis-go/redis: Redis server written in Go / Golang (prototype)
Redis is an in-memory data store used as a database, cache, or message broker. Go-redis/redis is a type-safe, Redis client library for Go with support for features like Pub/Sub, sentinel, and pipelining.
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>
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With