Fixes #8. Replace radix v2 with v4

This commit is contained in:
ErickSkrauch 2023-12-14 02:15:59 +01:00
parent d678f61df7
commit 883a7bda3c
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
8 changed files with 187 additions and 223 deletions

View File

@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Bumped Go version to 1.21. - Bumped Go version to 1.21.
### Removed
- StatsD metrics:
- Gauges:
- `ely.skinsystem.{hostname}.app.redis.pool.available`
## [4.6.0] - 2021-03-04 ## [4.6.0] - 2021-03-04
### Added ### Added
- `/profile/{username}` endpoint, which returns a profile and its textures, equivalent of the Mojang's - `/profile/{username}` endpoint, which returns a profile and its textures, equivalent of the Mojang's

View File

@ -3,6 +3,7 @@ package redis
import ( import (
"bytes" "bytes"
"compress/zlib" "compress/zlib"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -10,23 +11,22 @@ import (
"strings" "strings"
"time" "time"
"github.com/mediocregopher/radix.v2/pool" "github.com/mediocregopher/radix/v4"
"github.com/mediocregopher/radix.v2/redis"
"github.com/mediocregopher/radix.v2/util"
"github.com/elyby/chrly/model" "github.com/elyby/chrly/model"
) )
var now = time.Now var now = time.Now
func New(addr string, poolSize int) (*Redis, error) { func New(ctx context.Context, addr string, poolSize int) (*Redis, error) {
conn, err := pool.New("tcp", addr, poolSize) client, err := (radix.PoolConfig{Size: poolSize}).New(ctx, "tcp", addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Redis{ return &Redis{
pool: conn, client: client,
context: ctx,
}, nil }, nil
} }
@ -34,27 +34,34 @@ const accountIdToUsernameKey = "hash:username-to-account-id" // TODO: this shoul
const mojangUsernameToUuidKey = "hash:mojang-username-to-uuid" const mojangUsernameToUuidKey = "hash:mojang-username-to-uuid"
type Redis struct { type Redis struct {
pool *pool.Pool client radix.Client
context context.Context
} }
func (db *Redis) FindSkinByUsername(username string) (*model.Skin, error) { func (db *Redis) FindSkinByUsername(username string) (*model.Skin, error) {
conn, err := db.pool.Get() var skin *model.Skin
err := db.client.Do(db.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
var err error
skin, err = findByUsername(ctx, conn, username)
return err
}))
return skin, err
}
func findByUsername(ctx context.Context, conn radix.Conn, username string) (*model.Skin, error) {
redisKey := buildUsernameKey(username)
var encodedResult []byte
err := conn.Do(ctx, radix.Cmd(&encodedResult, "GET", redisKey))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer db.pool.Put(conn)
return findByUsername(username, conn) if len(encodedResult) == 0 {
}
func findByUsername(username string, conn util.Cmder) (*model.Skin, error) {
redisKey := buildUsernameKey(username)
response := conn.Cmd("GET", redisKey)
if response.IsType(redis.Nil) {
return nil, nil return nil, nil
} }
encodedResult, _ := response.Bytes()
result, err := zlibDecode(encodedResult) result, err := zlibDecode(encodedResult)
if err != nil { if err != nil {
return nil, err return nil, err
@ -72,56 +79,66 @@ func findByUsername(username string, conn util.Cmder) (*model.Skin, error) {
} }
func (db *Redis) FindSkinByUserId(id int) (*model.Skin, error) { func (db *Redis) FindSkinByUserId(id int) (*model.Skin, error) {
conn, err := db.pool.Get() var skin *model.Skin
err := db.client.Do(db.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
var err error
skin, err = findByUserId(ctx, conn, id)
return err
}))
return skin, err
}
func findByUserId(ctx context.Context, conn radix.Conn, id int) (*model.Skin, error) {
var username string
err := conn.Do(ctx, radix.FlatCmd(&username, "HGET", accountIdToUsernameKey, id))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer db.pool.Put(conn)
return findByUserId(id, conn) if username == "" {
}
func findByUserId(id int, conn util.Cmder) (*model.Skin, error) {
response := conn.Cmd("HGET", accountIdToUsernameKey, id)
if response.IsType(redis.Nil) {
return nil, nil return nil, nil
} }
username, err := response.Str() return findByUsername(ctx, conn, username)
if err != nil {
return nil, err
}
return findByUsername(username, conn)
} }
func (db *Redis) SaveSkin(skin *model.Skin) error { func (db *Redis) SaveSkin(skin *model.Skin) error {
conn, err := db.pool.Get() return db.client.Do(db.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
return save(ctx, conn, skin)
}))
}
func save(ctx context.Context, conn radix.Conn, skin *model.Skin) error {
err := conn.Do(ctx, radix.Cmd(nil, "MULTI"))
if err != nil { if err != nil {
return err return err
} }
defer db.pool.Put(conn)
return save(skin, conn)
}
func save(skin *model.Skin, conn util.Cmder) error {
conn.Cmd("MULTI")
// If user has changed username, then we must delete his old username record // If user has changed username, then we must delete his old username record
if skin.OldUsername != "" && skin.OldUsername != skin.Username { if skin.OldUsername != "" && skin.OldUsername != skin.Username {
conn.Cmd("DEL", buildUsernameKey(skin.OldUsername)) err = conn.Do(ctx, radix.Cmd(nil, "DEL", buildUsernameKey(skin.OldUsername)))
if err != nil {
return err
}
} }
// If this is a new record or if the user has changed username, we set the value in the hash table // If this is a new record or if the user has changed username, we set the value in the hash table
if skin.OldUsername != "" || skin.OldUsername != skin.Username { if skin.OldUsername != "" || skin.OldUsername != skin.Username {
conn.Cmd("HSET", accountIdToUsernameKey, skin.UserId, skin.Username) err = conn.Do(ctx, radix.FlatCmd(nil, "HSET", accountIdToUsernameKey, skin.UserId, skin.Username))
} }
str, _ := json.Marshal(skin) str, _ := json.Marshal(skin)
conn.Cmd("SET", buildUsernameKey(skin.Username), zlibEncode(str)) err = conn.Do(ctx, radix.FlatCmd(nil, "SET", buildUsernameKey(skin.Username), zlibEncode(str)))
if err != nil {
return err
}
conn.Cmd("EXEC") err = conn.Do(ctx, radix.Cmd(nil, "EXEC"))
if err != nil {
return err
}
skin.OldUsername = skin.Username skin.OldUsername = skin.Username
@ -129,45 +146,45 @@ func save(skin *model.Skin, conn util.Cmder) error {
} }
func (db *Redis) RemoveSkinByUserId(id int) error { func (db *Redis) RemoveSkinByUserId(id int) error {
conn, err := db.pool.Get() return db.client.Do(db.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
if err != nil { return removeByUserId(ctx, conn, id)
return err }))
}
defer db.pool.Put(conn)
return removeByUserId(id, conn)
} }
func removeByUserId(id int, conn util.Cmder) error { func removeByUserId(ctx context.Context, conn radix.Conn, id int) error {
record, err := findByUserId(id, conn) record, err := findByUserId(ctx, conn, id)
if err != nil { if err != nil {
return err return err
} }
conn.Cmd("MULTI") err = conn.Do(ctx, radix.Cmd(nil, "MULTI"))
if err != nil {
conn.Cmd("HDEL", accountIdToUsernameKey, id) return err
if record != nil {
conn.Cmd("DEL", buildUsernameKey(record.Username))
} }
conn.Cmd("EXEC") err = conn.Do(ctx, radix.FlatCmd(nil, "HDEL", accountIdToUsernameKey, id))
if err != nil {
return err
}
return nil if record != nil {
err = conn.Do(ctx, radix.Cmd(nil, "DEL", buildUsernameKey(record.Username)))
if err != nil {
return err
}
}
return conn.Do(ctx, radix.Cmd(nil, "EXEC"))
} }
func (db *Redis) RemoveSkinByUsername(username string) error { func (db *Redis) RemoveSkinByUsername(username string) error {
conn, err := db.pool.Get() return db.client.Do(db.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
if err != nil { return removeByUsername(ctx, conn, username)
return err }))
}
defer db.pool.Put(conn)
return removeByUsername(username, conn)
} }
func removeByUsername(username string, conn util.Cmder) error { func removeByUsername(ctx context.Context, conn radix.Conn, username string) error {
record, err := findByUsername(username, conn) record, err := findByUsername(ctx, conn, username)
if err != nil { if err != nil {
return err return err
} }
@ -176,45 +193,68 @@ func removeByUsername(username string, conn util.Cmder) error {
return nil return nil
} }
conn.Cmd("MULTI") err = conn.Do(ctx, radix.Cmd(nil, "MULTI"))
if err != nil {
return err
}
conn.Cmd("DEL", buildUsernameKey(record.Username)) err = conn.Do(ctx, radix.Cmd(nil, "DEL", buildUsernameKey(record.Username)))
conn.Cmd("HDEL", accountIdToUsernameKey, record.UserId) if err != nil {
return err
}
conn.Cmd("EXEC") err = conn.Do(ctx, radix.FlatCmd(nil, "HDEL", accountIdToUsernameKey, record.UserId))
if err != nil {
return err
}
return nil return conn.Do(ctx, radix.Cmd(nil, "EXEC"))
} }
func (db *Redis) GetUuid(username string) (string, bool, error) { func (db *Redis) GetUuid(username string) (string, bool, error) {
conn, err := db.pool.Get() var uuid string
var found bool
err := db.client.Do(db.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
var err error
uuid, found, err = findMojangUuidByUsername(ctx, conn, username)
return err
}))
return uuid, found, err
}
func findMojangUuidByUsername(ctx context.Context, conn radix.Conn, username string) (string, bool, error) {
key := strings.ToLower(username)
var result string
err := conn.Do(ctx, radix.Cmd(&result, "HGET", mojangUsernameToUuidKey, key))
if err != nil { if err != nil {
return "", false, err return "", false, err
} }
defer db.pool.Put(conn)
return findMojangUuidByUsername(username, conn) if result == "" {
}
func findMojangUuidByUsername(username string, conn util.Cmder) (string, bool, error) {
key := strings.ToLower(username)
response := conn.Cmd("HGET", mojangUsernameToUuidKey, key)
if response.IsType(redis.Nil) {
return "", false, nil return "", false, nil
} }
data, _ := response.Str() parts := strings.Split(result, ":")
parts := strings.Split(data, ":")
// https://github.com/elyby/chrly/issues/28 // https://github.com/elyby/chrly/issues/28
if len(parts) < 2 { if len(parts) < 2 {
conn.Cmd("HDEL", mojangUsernameToUuidKey, key) err = conn.Do(ctx, radix.Cmd(nil, "HDEL", mojangUsernameToUuidKey, key))
return "", false, fmt.Errorf("Got unexpected response from the mojangUsernameToUuid hash: \"%s\"", data) if err != nil {
return "", false, err
}
return "", false, fmt.Errorf("got unexpected response from the mojangUsernameToUuid hash: \"%s\"", result)
} }
timestamp, _ := strconv.ParseInt(parts[1], 10, 64) timestamp, _ := strconv.ParseInt(parts[1], 10, 64)
storedAt := time.Unix(timestamp, 0) storedAt := time.Unix(timestamp, 0)
if storedAt.Add(time.Hour * 24 * 30).Before(now()) { if storedAt.Add(time.Hour * 24 * 30).Before(now()) {
conn.Cmd("HDEL", mojangUsernameToUuidKey, key) err = conn.Do(ctx, radix.Cmd(nil, "HDEL", mojangUsernameToUuidKey, key))
if err != nil {
return "", false, err
}
return "", false, nil return "", false, nil
} }
@ -222,36 +262,23 @@ func findMojangUuidByUsername(username string, conn util.Cmder) (string, bool, e
} }
func (db *Redis) StoreUuid(username string, uuid string) error { func (db *Redis) StoreUuid(username string, uuid string) error {
conn, err := db.pool.Get() return db.client.Do(db.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
if err != nil { return storeMojangUuid(ctx, conn, username, uuid)
return err }))
}
defer db.pool.Put(conn)
return storeMojangUuid(username, uuid, conn)
} }
func storeMojangUuid(username string, uuid string, conn util.Cmder) error { func storeMojangUuid(ctx context.Context, conn radix.Conn, username string, uuid string) error {
value := uuid + ":" + strconv.FormatInt(now().Unix(), 10) value := uuid + ":" + strconv.FormatInt(now().Unix(), 10)
res := conn.Cmd("HSET", mojangUsernameToUuidKey, strings.ToLower(username), value) err := conn.Do(ctx, radix.Cmd(nil, "HSET", mojangUsernameToUuidKey, strings.ToLower(username), value))
if res.IsType(redis.Err) { if err != nil {
return res.Err return err
} }
return nil return nil
} }
func (db *Redis) Ping() error { func (db *Redis) Ping() error {
r := db.pool.Cmd("PING") return db.client.Do(db.context, radix.Cmd(nil, "PING"))
if r.Err != nil {
return r.Err
}
return nil
}
func (db *Redis) Avail() int {
return db.pool.Avail()
} }
func buildUsernameKey(username string) string { func buildUsernameKey(username string) string {

View File

@ -1,17 +1,16 @@
//go:build redis //go:build redis
// +build redis
package redis package redis
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"reflect"
"strconv" "strconv"
"testing" "testing"
"time" "time"
"github.com/mediocregopher/radix.v2/redis" "github.com/mediocregopher/radix/v4"
assert "github.com/stretchr/testify/require" assert "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -36,15 +35,13 @@ func init() {
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
t.Run("should connect", func(t *testing.T) { t.Run("should connect", func(t *testing.T) {
conn, err := New(redisAddr, 12) conn, err := New(context.Background(), redisAddr, 12)
assert.Nil(t, err) assert.Nil(t, err)
assert.NotNil(t, conn) assert.NotNil(t, conn)
internalPool := reflect.ValueOf(conn.pool).Elem().FieldByName("pool")
assert.Equal(t, 12, internalPool.Cap())
}) })
t.Run("should return error", func(t *testing.T) { t.Run("should return error", func(t *testing.T) {
conn, err := New("localhost:12345", 12) // Use localhost to avoid DNS resolution conn, err := New(context.Background(), "localhost:12345", 12) // Use localhost to avoid DNS resolution
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, conn) assert.Nil(t, conn)
}) })
@ -55,21 +52,30 @@ type redisTestSuite struct {
Redis *Redis Redis *Redis
cmd func(cmd string, args ...interface{}) *redis.Resp cmd func(cmd string, args ...interface{}) string
} }
func (suite *redisTestSuite) SetupSuite() { func (suite *redisTestSuite) SetupSuite() {
conn, err := New(redisAddr, 10) ctx := context.Background()
conn, err := New(ctx, redisAddr, 10)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot establish connection to redis: %w", err)) panic(fmt.Errorf("cannot establish connection to redis: %w", err))
} }
suite.Redis = conn suite.Redis = conn
suite.cmd = conn.pool.Cmd suite.cmd = func(cmd string, args ...interface{}) string {
var result string
err := suite.Redis.client.Do(ctx, radix.FlatCmd(&result, cmd, args...))
if err != nil {
panic(err)
}
return result
}
} }
func (suite *redisTestSuite) SetupTest() { func (suite *redisTestSuite) SetupTest() {
// Cleanup database before the each test // Cleanup database before each test
suite.cmd("FLUSHALL") suite.cmd("FLUSHALL")
} }
@ -101,7 +107,7 @@ func TestRedis(t *testing.T) {
* mojangSignature: "mock-mojang-signature" * mojangSignature: "mock-mojang-signature"
* } * }
*/ */
var skinRecord = []byte{ var skinRecord = string([]byte{
0x78, 0x9c, 0x5c, 0xce, 0x4b, 0x4a, 0x4, 0x41, 0xc, 0xc6, 0xf1, 0xbb, 0x7c, 0xeb, 0x1a, 0xdb, 0xd6, 0xb2, 0x78, 0x9c, 0x5c, 0xce, 0x4b, 0x4a, 0x4, 0x41, 0xc, 0xc6, 0xf1, 0xbb, 0x7c, 0xeb, 0x1a, 0xdb, 0xd6, 0xb2,
0x9c, 0xc9, 0xd, 0x5c, 0x88, 0x8b, 0xd1, 0xb5, 0x84, 0x4e, 0xa6, 0xa7, 0xec, 0x7a, 0xc, 0xf5, 0x0, 0x41, 0x9c, 0xc9, 0xd, 0x5c, 0x88, 0x8b, 0xd1, 0xb5, 0x84, 0x4e, 0xa6, 0xa7, 0xec, 0x7a, 0xc, 0xf5, 0x0, 0x41,
0xbc, 0xbb, 0xb4, 0xd2, 0xa, 0x2e, 0xf3, 0xe3, 0x9f, 0x90, 0xf, 0xf4, 0xaa, 0xe5, 0x41, 0x40, 0xa3, 0x41, 0xbc, 0xbb, 0xb4, 0xd2, 0xa, 0x2e, 0xf3, 0xe3, 0x9f, 0x90, 0xf, 0xf4, 0xaa, 0xe5, 0x41, 0x40, 0xa3, 0x41,
@ -112,7 +118,7 @@ var skinRecord = []byte{
0xa0, 0x13, 0x87, 0xaa, 0x6, 0x31, 0xbf, 0x71, 0x9a, 0x9f, 0xf5, 0xbd, 0xf5, 0xa2, 0x15, 0x84, 0x98, 0xa7, 0xa0, 0x13, 0x87, 0xaa, 0x6, 0x31, 0xbf, 0x71, 0x9a, 0x9f, 0xf5, 0xbd, 0xf5, 0xa2, 0x15, 0x84, 0x98, 0xa7,
0x65, 0xf7, 0xa3, 0xbb, 0xb6, 0xf1, 0xd6, 0x1d, 0xfd, 0x9c, 0x78, 0xa5, 0x7f, 0x61, 0xfd, 0x75, 0x83, 0xa7, 0x65, 0xf7, 0xa3, 0xbb, 0xb6, 0xf1, 0xd6, 0x1d, 0xfd, 0x9c, 0x78, 0xa5, 0x7f, 0x61, 0xfd, 0x75, 0x83, 0xa7,
0x20, 0x2f, 0x7f, 0xff, 0xe2, 0xf3, 0x2b, 0x0, 0x0, 0xff, 0xff, 0x6f, 0xdd, 0x51, 0x71, 0x20, 0x2f, 0x7f, 0xff, 0xe2, 0xf3, 0x2b, 0x0, 0x0, 0xff, 0xff, 0x6f, 0xdd, 0x51, 0x71,
} })
func (suite *redisTestSuite) TestFindSkinByUsername() { func (suite *redisTestSuite) TestFindSkinByUsername() {
suite.RunSubTest("exists record", func() { suite.RunSubTest("exists record", func() {
@ -198,14 +204,11 @@ func (suite *redisTestSuite) TestSaveSkin() {
suite.Require().Nil(err) suite.Require().Nil(err)
usernameResp := suite.cmd("GET", "username:mock") usernameResp := suite.cmd("GET", "username:mock")
suite.Require().False(usernameResp.IsType(redis.Nil)) suite.Require().NotEmpty(usernameResp)
bytes, _ := usernameResp.Bytes() suite.Require().Equal(skinRecord, usernameResp)
suite.Require().Equal(skinRecord, bytes)
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1) idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
suite.Require().False(usernameResp.IsType(redis.Nil)) suite.Require().Equal("Mock", idResp)
str, _ := idResp.Str()
suite.Require().Equal("Mock", str)
}) })
suite.RunSubTest("save exists record with changed username", func() { suite.RunSubTest("save exists record with changed username", func() {
@ -227,9 +230,8 @@ func (suite *redisTestSuite) TestSaveSkin() {
suite.Require().Nil(err) suite.Require().Nil(err)
usernameResp := suite.cmd("GET", "username:newmock") usernameResp := suite.cmd("GET", "username:newmock")
suite.Require().False(usernameResp.IsType(redis.Nil)) suite.Require().NotEmpty(usernameResp)
bytes, _ := usernameResp.Bytes() suite.Require().Equal(string([]byte{
suite.Require().Equal([]byte{
0x78, 0x9c, 0x5c, 0x8e, 0xcb, 0x4e, 0xc3, 0x40, 0xc, 0x45, 0xff, 0xe5, 0xae, 0xa7, 0x84, 0x40, 0x18, 0x5a, 0x78, 0x9c, 0x5c, 0x8e, 0xcb, 0x4e, 0xc3, 0x40, 0xc, 0x45, 0xff, 0xe5, 0xae, 0xa7, 0x84, 0x40, 0x18, 0x5a,
0xff, 0x1, 0xb, 0x60, 0x51, 0x58, 0x23, 0x2b, 0x76, 0xd3, 0x21, 0xf3, 0xa8, 0xe6, 0x21, 0x90, 0x10, 0xff, 0xff, 0x1, 0xb, 0x60, 0x51, 0x58, 0x23, 0x2b, 0x76, 0xd3, 0x21, 0xf3, 0xa8, 0xe6, 0x21, 0x90, 0x10, 0xff,
0x8e, 0x52, 0x14, 0x90, 0xba, 0xf4, 0xd1, 0xf1, 0xd5, 0xf9, 0x42, 0x2b, 0x9a, 0x1f, 0x4, 0xd4, 0x1b, 0xb4, 0x8e, 0x52, 0x14, 0x90, 0xba, 0xf4, 0xd1, 0xf1, 0xd5, 0xf9, 0x42, 0x2b, 0x9a, 0x1f, 0x4, 0xd4, 0x1b, 0xb4,
@ -241,15 +243,14 @@ func (suite *redisTestSuite) TestSaveSkin() {
0x42, 0x1a, 0xe7, 0xcd, 0x2f, 0xdd, 0xd4, 0x15, 0xaf, 0xde, 0xde, 0x4d, 0x91, 0x17, 0x74, 0x21, 0x96, 0x3f, 0x42, 0x1a, 0xe7, 0xcd, 0x2f, 0xdd, 0xd4, 0x15, 0xaf, 0xde, 0xde, 0x4d, 0x91, 0x17, 0x74, 0x21, 0x96, 0x3f,
0x6e, 0xf0, 0xec, 0xe5, 0xf5, 0x3f, 0xf9, 0xdc, 0xfb, 0xfd, 0x13, 0x0, 0x0, 0xff, 0xff, 0xca, 0xc3, 0x54, 0x6e, 0xf0, 0xec, 0xe5, 0xf5, 0x3f, 0xf9, 0xdc, 0xfb, 0xfd, 0x13, 0x0, 0x0, 0xff, 0xff, 0xca, 0xc3, 0x54,
0x25, 0x25,
}, bytes) }), usernameResp)
oldUsernameResp := suite.cmd("GET", "username:mock") oldUsernameResp := suite.cmd("GET", "username:mock")
suite.Require().True(oldUsernameResp.IsType(redis.Nil)) suite.Require().Empty(oldUsernameResp)
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1) idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
suite.Require().False(usernameResp.IsType(redis.Nil)) suite.Require().NotEmpty(usernameResp)
str, _ := idResp.Str() suite.Require().Equal("NewMock", idResp)
suite.Require().Equal("NewMock", str)
}) })
} }
@ -262,10 +263,10 @@ func (suite *redisTestSuite) TestRemoveSkinByUserId() {
suite.Require().Nil(err) suite.Require().Nil(err)
usernameResp := suite.cmd("GET", "username:mock") usernameResp := suite.cmd("GET", "username:mock")
suite.Require().True(usernameResp.IsType(redis.Nil)) suite.Require().Empty(usernameResp)
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1) idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
suite.Require().True(idResp.IsType(redis.Nil)) suite.Require().Empty(idResp)
}) })
suite.RunSubTest("exists only id", func() { suite.RunSubTest("exists only id", func() {
@ -275,7 +276,7 @@ func (suite *redisTestSuite) TestRemoveSkinByUserId() {
suite.Require().Nil(err) suite.Require().Nil(err)
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1) idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
suite.Require().True(idResp.IsType(redis.Nil)) suite.Require().Empty(idResp)
}) })
suite.RunSubTest("error when querying skin record", func() { suite.RunSubTest("error when querying skin record", func() {
@ -296,10 +297,10 @@ func (suite *redisTestSuite) TestRemoveSkinByUsername() {
suite.Require().Nil(err) suite.Require().Nil(err)
usernameResp := suite.cmd("GET", "username:mock") usernameResp := suite.cmd("GET", "username:mock")
suite.Require().True(usernameResp.IsType(redis.Nil)) suite.Require().Empty(usernameResp)
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1) idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
suite.Require().True(idResp.IsType(redis.Nil)) suite.Require().Empty(idResp)
}) })
suite.RunSubTest("exists only username", func() { suite.RunSubTest("exists only username", func() {
@ -309,7 +310,7 @@ func (suite *redisTestSuite) TestRemoveSkinByUsername() {
suite.Require().Nil(err) suite.Require().Nil(err)
usernameResp := suite.cmd("GET", "username:mock") usernameResp := suite.cmd("GET", "username:mock")
suite.Require().True(usernameResp.IsType(redis.Nil)) suite.Require().Empty(usernameResp)
}) })
suite.RunSubTest("no records", func() { suite.RunSubTest("no records", func() {
@ -372,7 +373,7 @@ func (suite *redisTestSuite) TestGetUuid() {
suite.Require().Nil(err) suite.Require().Nil(err)
resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock") resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock")
suite.Require().True(resp.IsType(redis.Nil), "should cleanup expired records") suite.Require().Empty(resp, "should cleanup expired records")
}) })
suite.RunSubTest("exists, but corrupted record", func() { suite.RunSubTest("exists, but corrupted record", func() {
@ -388,7 +389,7 @@ func (suite *redisTestSuite) TestGetUuid() {
suite.Require().Error(err, "Got unexpected response from the mojangUsernameToUuid hash: \"corrupted value\"") suite.Require().Error(err, "Got unexpected response from the mojangUsernameToUuid hash: \"corrupted value\"")
resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock") resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock")
suite.Require().True(resp.IsType(redis.Nil), "should cleanup expired records") suite.Require().Empty(resp, "should cleanup expired records")
}) })
} }
@ -402,9 +403,7 @@ func (suite *redisTestSuite) TestStoreUuid() {
suite.Require().Nil(err) suite.Require().Nil(err)
resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock") resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock")
suite.Require().False(resp.IsType(redis.Nil)) suite.Require().Equal(resp, "d3ca513eb3e14946b58047f2bd3530fd:1587435016")
str, _ := resp.Str()
suite.Require().Equal(str, "d3ca513eb3e14946b58047f2bd3530fd:1587435016")
}) })
suite.RunSubTest("store empty uuid", func() { suite.RunSubTest("store empty uuid", func() {
@ -416,9 +415,7 @@ func (suite *redisTestSuite) TestStoreUuid() {
suite.Require().Nil(err) suite.Require().Nil(err)
resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock") resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock")
suite.Require().False(resp.IsType(redis.Nil)) suite.Require().Equal(resp, ":1587435016")
str, _ := resp.Str()
suite.Require().Equal(str, ":1587435016")
}) })
} }
@ -426,8 +423,3 @@ func (suite *redisTestSuite) TestPing() {
err := suite.Redis.Ping() err := suite.Redis.Ping()
suite.Require().Nil(err) suite.Require().Nil(err)
} }
func (suite *redisTestSuite) TestAvail() {
avail := suite.Redis.Avail()
suite.Require().True(avail > 0)
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"path" "path"
"time"
"github.com/defval/di" "github.com/defval/di"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -38,6 +37,7 @@ func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error
config.SetDefault("storage.redis.poolSize", 10) config.SetDefault("storage.redis.poolSize", 10)
conn, err := redis.New( conn, err := redis.New(
context.Background(),
fmt.Sprintf("%s:%d", config.GetString("storage.redis.host"), config.GetInt("storage.redis.port")), fmt.Sprintf("%s:%d", config.GetString("storage.redis.host"), config.GetInt("storage.redis.port")),
config.GetInt("storage.redis.poolSize"), config.GetInt("storage.redis.poolSize"),
) )
@ -45,12 +45,6 @@ func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error
return nil, err return nil, err
} }
if err := container.Provide(func() es.ReporterFunc {
return es.AvailableRedisPoolSizeReporter(conn, time.Second, context.Background())
}, di.As(new(es.Reporter))); err != nil {
return nil, err
}
if err := container.Provide(func() *namedHealthChecker { if err := container.Provide(func() *namedHealthChecker {
return &namedHealthChecker{ return &namedHealthChecker{
Name: "redis", Name: "redis",

View File

@ -1,7 +1,6 @@
package eventsubscribers package eventsubscribers
import ( import (
"context"
"net/http" "net/http"
"strings" "strings"
"sync" "sync"
@ -30,7 +29,7 @@ func (f ReporterFunc) Enable(reporter slf.StatsReporter) {
f(reporter) f(reporter)
} }
// TODO: rework all reporters in the same style as AvailableRedisPoolSizeReporter // TODO: rework all reporters in the same style as it was there: https://github.com/elyby/chrly/blob/1543e98b/di/db.go#L48-L52
func (s *StatsReporter) ConfigureWithDispatcher(d Subscriber) { func (s *StatsReporter) ConfigureWithDispatcher(d Subscriber) {
s.timersMap = make(map[string]time.Time) s.timersMap = make(map[string]time.Time)
@ -189,24 +188,3 @@ func (s *StatsReporter) finalizeTimeRecording(timeKey string, statName string) {
s.RecordTimer(statName, time.Since(startedAt)) s.RecordTimer(statName, time.Since(startedAt))
} }
type RedisPoolCheckable interface {
Avail() int
}
func AvailableRedisPoolSizeReporter(pool RedisPoolCheckable, d time.Duration, stop context.Context) ReporterFunc {
return func(reporter slf.StatsReporter) {
go func() {
ticker := time.NewTicker(d)
for {
select {
case <-stop.Done():
ticker.Stop()
return
case <-ticker.C:
reporter.UpdateGauge("redis.pool.available", int64(pool.Avail()))
}
}
}()
}
}

View File

@ -1,7 +1,6 @@
package eventsubscribers package eventsubscribers
import ( import (
"context"
"errors" "errors"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -401,30 +400,3 @@ func TestStatsReporter(t *testing.T) {
}) })
} }
} }
type redisPoolCheckableMock struct {
mock.Mock
}
func (r *redisPoolCheckableMock) Avail() int {
return r.Called().Int(0)
}
func TestAvailableRedisPoolSizeReporter(t *testing.T) {
poolMock := &redisPoolCheckableMock{}
poolMock.On("Avail").Return(5).Times(3)
reporterMock := &StatsReporterMock{}
reporterMock.On("UpdateGauge", "redis.pool.available", int64(5)).Times(3)
ctx, cancel := context.WithCancel(context.Background())
creator := AvailableRedisPoolSizeReporter(poolMock, 10*time.Millisecond, ctx)
creator(reporterMock)
time.Sleep(35 * time.Millisecond)
cancel()
poolMock.AssertExpectations(t)
reporterMock.AssertExpectations(t)
}

5
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/etherlabsio/healthcheck/v2 v2.0.0 github.com/etherlabsio/healthcheck/v2 v2.0.0
github.com/getsentry/raven-go v0.2.1-0.20190419175539-919484f041ea github.com/getsentry/raven-go v0.2.1-0.20190419175539-919484f041ea
github.com/gorilla/mux v1.8.1 github.com/gorilla/mux v1.8.1
github.com/mediocregopher/radix.v2 v0.0.0-20181115013041-b67df6e626f9 github.com/mediocregopher/radix/v4 v4.1.4
github.com/mono83/slf v0.0.0-20170919161409-79153e9636db github.com/mono83/slf v0.0.0-20170919161409-79153e9636db
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.1 github.com/spf13/viper v1.18.1
@ -32,11 +32,9 @@ require (
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/levenlabs/golib v0.0.0-20180911183212-0f8974794783 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mono83/udpwriter v1.0.2 // indirect github.com/mono83/udpwriter v1.0.2 // indirect
github.com/oschwald/geoip2-golang v1.9.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
@ -47,6 +45,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tilinna/clock v1.0.2 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect
golang.org/x/sys v0.15.0 // indirect golang.org/x/sys v0.15.0 // indirect

13
go.sum
View File

@ -35,12 +35,10 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/levenlabs/golib v0.0.0-20180911183212-0f8974794783 h1:ErBsqZpyadTBr2zGsgMau0JdyghyLdwRRHAKJukexrQ=
github.com/levenlabs/golib v0.0.0-20180911183212-0f8974794783/go.mod h1:zw8z7nRRkGDZHexz1aMbZGtwxli5so0CBVZeIa3G+RE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mediocregopher/radix.v2 v0.0.0-20181115013041-b67df6e626f9 h1:ViNuGS149jgnttqhc6XQNPwdupEMBXqCx9wtlW7P3sA= github.com/mediocregopher/radix/v4 v4.1.4 h1:Uze6DEbEAvL+VHXUEu/EDBTkUk5CLct5h3nVSGpc6Ts=
github.com/mediocregopher/radix.v2 v0.0.0-20181115013041-b67df6e626f9/go.mod h1:fLRUbhbSd5Px2yKUaGYYPltlyxi1guJz1vCmo1RQL50= github.com/mediocregopher/radix/v4 v4.1.4/go.mod h1:ajchozX/6ELmydxWeWM6xCFHVpZ4+67LXHOTOVR0nCE=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mono83/slf v0.0.0-20170919161409-79153e9636db h1:tlz4fTklh5mttoq5M+0yEc5Lap8W/02A2HCXCJn5iz0= github.com/mono83/slf v0.0.0-20170919161409-79153e9636db h1:tlz4fTklh5mttoq5M+0yEc5Lap8W/02A2HCXCJn5iz0=
@ -49,10 +47,6 @@ github.com/mono83/udpwriter v1.0.2 h1:JiQ/N646oZoJA1G0FOMvn2teMt6SdL1KwNH2mszOlQ
github.com/mono83/udpwriter v1.0.2/go.mod h1:mTDiyLtA0tXoxckkV9T4NUkJTgSQIuO8pAUKx/dSRkQ= github.com/mono83/udpwriter v1.0.2/go.mod h1:mTDiyLtA0tXoxckkV9T4NUkJTgSQIuO8pAUKx/dSRkQ=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -81,6 +75,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -90,6 +85,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/thedevsaddam/govalidator v1.9.10 h1:m3dLRbSZ5Hts3VUWYe+vxLMG+FdyQuWOjzTeQRiMCvU= github.com/thedevsaddam/govalidator v1.9.10 h1:m3dLRbSZ5Hts3VUWYe+vxLMG+FdyQuWOjzTeQRiMCvU=
github.com/thedevsaddam/govalidator v1.9.10/go.mod h1:Ilx8u7cg5g3LXbSS943cx5kczyNuUn7LH/cK5MYuE90= github.com/thedevsaddam/govalidator v1.9.10/go.mod h1:Ilx8u7cg5g3LXbSS943cx5kczyNuUn7LH/cK5MYuE90=
github.com/tilinna/clock v1.0.2 h1:6BO2tyAC9JbPExKH/z9zl44FLu1lImh3nDNKA0kgrkI=
github.com/tilinna/clock v1.0.2/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=