Files
InfantrySkillCalculator/internal/cache/cache.go

132 lines
3.0 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, gameTag string, score float32) error {
key := getPlayerCacheKey(playerId, gameTag)
return pc.SetValue(key, score, pc.lifetime)
}
func (pc *PlayerCache) GetScore(playerId uint, gameTag string) (float32, error) {
key := getPlayerCacheKey(playerId, gameTag)
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, gameTag string) ([]float32, error) {
vals, err := pc.cache.Pipelined(ctx, func(pipe redis.Pipeliner) error {
for _, id := range playerIds {
key := getPlayerCacheKey(id, gameTag)
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, gameTag string) error {
key := getPlayerCacheKey(playerId, gameTag)
return pc.cache.Del(ctx, key).Err()
}
func (pc *PlayerCache) PurgeCache() error {
return pc.cache.FlushAll(ctx).Err()
}
func getPlayerCacheKey(playerId uint, gameTag string) string {
return fmt.Sprintf("player:%d:game:%s", playerId, gameTag)
}
func (pc *PlayerCache) GetPlayerIdFromCacheKey(key string) (uint, error) {
var playerId uint
_, err := fmt.Sscanf(key, "player:%d:game:", &playerId)
return playerId, err
}