132 lines
2.8 KiB
Go
132 lines
2.8 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/redis/go-redis/v9"
|
|
"log"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
var ctx = context.Background()
|
|
|
|
type PlayerCache struct {
|
|
cache *redis.Client
|
|
lifetime time.Duration
|
|
expireSub *redis.PubSub
|
|
expireCallback func(key string)
|
|
}
|
|
|
|
func NewPlayerCache(address string, lifetime time.Duration, expireCallback func(key string)) *PlayerCache {
|
|
pc := &PlayerCache{
|
|
redis.NewClient(
|
|
&redis.Options{
|
|
Addr: address,
|
|
Password: "",
|
|
DB: 0,
|
|
}),
|
|
lifetime,
|
|
nil,
|
|
expireCallback,
|
|
}
|
|
pc.expireSub = pc.cache.PSubscribe(ctx, "__keyevent@0__:expired")
|
|
|
|
go func() {
|
|
for {
|
|
msg, err := pc.expireSub.ReceiveMessage(ctx)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
} else {
|
|
pc.expireCallback(msg.Payload)
|
|
}
|
|
}
|
|
}()
|
|
|
|
return pc
|
|
}
|
|
|
|
func (pc *PlayerCache) Connect() error {
|
|
_, err := pc.cache.Ping(ctx).Result()
|
|
return err
|
|
}
|
|
|
|
func (pc *PlayerCache) GetValue(key string) (string, error) {
|
|
val, err := pc.cache.Get(ctx, key).Result()
|
|
if err != nil {
|
|
return "", err // cache miss or error
|
|
} else {
|
|
return val, nil // cache hit
|
|
}
|
|
}
|
|
|
|
func (pc *PlayerCache) SetValue(key string, value interface{}, lifetime time.Duration) error {
|
|
return pc.cache.Set(ctx, key, value, lifetime).Err()
|
|
}
|
|
|
|
func (pc *PlayerCache) SetScore(playerId uint, score float32) error {
|
|
key := getPlayerCacheKey(playerId)
|
|
return pc.SetValue(key, score, pc.lifetime)
|
|
}
|
|
|
|
func (pc *PlayerCache) GetScore(playerId uint) (float32, error) {
|
|
key := getPlayerCacheKey(playerId)
|
|
val, err := pc.GetValue(key)
|
|
if errors.Is(err, redis.Nil) {
|
|
return -1.0, nil // cache miss
|
|
} else if err != nil {
|
|
return -1.0, err // cache error
|
|
} else {
|
|
valFloat, _ := strconv.ParseFloat(val, 32)
|
|
return float32(valFloat), nil // cache hit
|
|
}
|
|
}
|
|
|
|
func (pc *PlayerCache) GetScores(playerIds []uint) ([]float32, error) {
|
|
vals, err := pc.cache.Pipelined(ctx, func(pipe redis.Pipeliner) error {
|
|
for _, id := range playerIds {
|
|
key := getPlayerCacheKey(id)
|
|
pipe.Get(ctx, key)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil && !errors.Is(err, redis.Nil) {
|
|
return nil, err
|
|
}
|
|
|
|
var scores []float32
|
|
for _, val := range vals {
|
|
score, err := val.(*redis.StringCmd).Float32()
|
|
|
|
if errors.Is(err, redis.Nil) { // cache miss
|
|
score = -1
|
|
err = nil
|
|
} else if err != nil { // cache error
|
|
score = -1
|
|
}
|
|
|
|
scores = append(scores, score)
|
|
}
|
|
return scores, nil
|
|
}
|
|
|
|
func (pc *PlayerCache) DeleteScore(playerId uint) error {
|
|
key := getPlayerCacheKey(playerId)
|
|
return pc.cache.Del(ctx, key).Err()
|
|
}
|
|
|
|
func (pc *PlayerCache) PurgeCache() error {
|
|
return pc.cache.FlushAll(ctx).Err()
|
|
}
|
|
|
|
func getPlayerCacheKey(playerId uint) string {
|
|
return fmt.Sprintf("player:%d", playerId)
|
|
}
|
|
|
|
func (pc *PlayerCache) GetPlayerIdFromCacheKey(key string) (uint, error) {
|
|
var playerId uint
|
|
_, err := fmt.Sscanf(key, "player:%d", &playerId)
|
|
return playerId, err
|
|
}
|