feat: join room, rooms.Update transactional
This commit is contained in:
@@ -3,6 +3,7 @@ package rooms
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
@@ -37,11 +38,10 @@ func (r *Room) UnmarshalBinary(data []byte) error {
|
||||
|
||||
// let's check whether it will be possible to save nested structs
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
type RoomManager interface {
|
||||
Get(roomName string) (Room, bool, error)
|
||||
Save(room Room) error
|
||||
Update(ctx context.Context, roomName string, f func(fromRoom Room) (toRoom Room)) error
|
||||
}
|
||||
const roomRedisPrefix = "room"
|
||||
func roomNameToRedisId(roomName string) string {
|
||||
@@ -55,7 +55,7 @@ type RedisRM struct {
|
||||
|
||||
func (redisRM RedisRM) Get(roomName string) (Room, bool, error) {
|
||||
var readRoom Room
|
||||
err := redisRM.Rdb.Get(ctx, roomNameToRedisId(roomName)).Scan(&readRoom)
|
||||
err := redisRM.Rdb.Get(context.TODO(), roomNameToRedisId(roomName)).Scan(&readRoom)
|
||||
if err == redis.Nil {
|
||||
return Room{}, false, nil
|
||||
}
|
||||
@@ -67,7 +67,43 @@ func (redisRM RedisRM) Get(roomName string) (Room, bool, error) {
|
||||
return readRoom, true, nil
|
||||
}
|
||||
|
||||
var maxRetries int = 20
|
||||
func (redisRM RedisRM) Update(ctx context.Context, roomName string, f func(fromRoom Room) (toRoom Room)) error {
|
||||
// transactional function
|
||||
roomKey := roomNameToRedisId(roomName)
|
||||
txf := func (tx *redis.Tx) error {
|
||||
var savedRoom Room
|
||||
err := tx.Get(ctx, roomKey).Scan(&savedRoom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
room := f(savedRoom)
|
||||
|
||||
_, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
|
||||
pipe.Set(ctx, roomKey, &room, 0)
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
err := redisRM.Rdb.Watch(ctx, txf, roomKey)
|
||||
if err == nil {
|
||||
return nil // success
|
||||
}
|
||||
if err == redis.TxFailedErr {
|
||||
// optimistic lock will keep spinning
|
||||
continue
|
||||
}
|
||||
// non tx errror are returned, including redis.Nil
|
||||
return err
|
||||
}
|
||||
|
||||
return errors.New("update reached maximum amount of retries")
|
||||
}
|
||||
|
||||
func (redisRM RedisRM) Save(room Room) error {
|
||||
err := redisRM.Rdb.Set(ctx, roomNameToRedisId(room.Name), &room, 0).Err() // maybe even set expiration?
|
||||
err := redisRM.Rdb.Set(context.TODO(), roomNameToRedisId(room.Name), &room, 0).Err() // maybe even set expiration?
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user