Redis optimistic lock with golang demo

redis transaction command MULTI: Open a transaction EXEC...
links

redis transaction command

  • MULTI: Open a transaction
  • EXEC: Transaction execution, which executes all commands within a transaction at once
  • DISCARD: Cancel Transaction

Optimistic locking using WATCH+MULTI

WATCH: Monitors one or more keys, and if a key changes before the transaction executes, the transaction is interrupted
UNWATCH: Suppress WATCH command from monitoring all keys

Use go-redis package to simulate user ticket-grabbing process

  • Open multiple goroutine simulations and preempt invoices
  • go-redis TxPipelined Execute Transaction
  • Go-redisClient.WatchMonitor a key
package main import ( "errors" "fmt" "sync" "github.com/go-redis/redis/v7" ) func main() { client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password set DB: 0, // use default DB }) key := "ticket_count" client.Set(key, "5", 0).Err() val, _ := client.Get(key).Result() fmt.Println("current ticket_count key val: ", val) getTicket(client, key) } func runTx(key string, id int) func(tx *redis.Tx) error { txf := func(tx *redis.Tx) error { n, err := tx.Get(key).Int() if err != nil && err != redis.Nil { return err } if n == 0 { return errors.New("No tickets left") } // actual opperation (local in optimistic lock) n = n - 1 // 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 } return txf } func getTicket(client *redis.Client, key string) { routineCount := 8 var wg sync.WaitGroup wg.Add(routineCount) for i := 0; i < routineCount; i++ { go func(id int) { defer wg.Done() for { err := client.Watch(runTx(key, id), key) if err == nil { fmt.Println(id, "Success") return } else if err.Error() == "No tickets left" { fmt.Println(id, "No tickets left") return } else { fmt.Println(err, "retry") } } }(i) } wg.Wait() }

Github code: https://github.com/defp/redis...

current ticket_count key val: 5 7 Success 6 Success redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry 3 Success redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry 2 Success redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry 5 Success redis: transaction failed retry redis: transaction failed retry redis: transaction failed retry 4 No tickets left 1 No tickets left 0 No tickets left

8 June 2020, 12:23 | Views: 8867

Add new comment

For adding a comment, please log in
or create account

0 comments