mirror of
https://github.com/elyby/chrly.git
synced 2025-01-03 18:51:49 +05:30
Adjust Mojang's queue behavior
This commit is contained in:
parent
f3a8af6866
commit
9dde5715f5
@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Changed
|
||||||
|
- Improved Keep-Alive settings for HTTP client used to perform requests to Mojang's APIs
|
||||||
|
- Mojang's textures queue now has static delay of 1 second after each iteration to prevent strange `429` errors.
|
||||||
|
- Mojang's textures queue now caches even errored responses for signed textures to avoid `429` errors.
|
||||||
|
- Mojang's textures queue now caches textures data for 70 seconds to avoid strange `429` errors.
|
||||||
|
- Mojang's textures queue now doesn't log timeout errors.
|
||||||
|
|
||||||
## [4.2.0] - 2019-05-02
|
## [4.2.0] - 2019-05-02
|
||||||
### Added
|
### Added
|
||||||
|
@ -10,6 +10,9 @@ import (
|
|||||||
|
|
||||||
var HttpClient = &http.Client{
|
var HttpClient = &http.Client{
|
||||||
Timeout: 3 * time.Second,
|
Timeout: 3 * time.Second,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
MaxIdleConnsPerHost: 1024,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignedTexturesResponse struct {
|
type SignedTexturesResponse struct {
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/tevino/abool"
|
"github.com/tevino/abool"
|
||||||
)
|
)
|
||||||
|
|
||||||
var inMemoryStorageGCPeriod = time.Second
|
var inMemoryStorageGCPeriod = 10 * time.Second
|
||||||
var inMemoryStoragePersistPeriod = time.Second * 60
|
var inMemoryStoragePersistPeriod = time.Minute + 10*time.Second
|
||||||
var now = time.Now
|
var now = time.Now
|
||||||
|
|
||||||
type inMemoryItem struct {
|
type inMemoryItem struct {
|
||||||
@ -59,25 +59,33 @@ func (s *inMemoryTexturesStorage) GetTextures(uuid string) (*mojang.SignedTextur
|
|||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
item, exists := s.data[uuid]
|
item, exists := s.data[uuid]
|
||||||
if !exists || now().Add(inMemoryStoragePersistPeriod*time.Duration(-1)).UnixNano()/10e5 > item.timestamp {
|
validRange := getMinimalNotExpiredTimestamp()
|
||||||
|
if !exists || validRange > item.timestamp {
|
||||||
return nil, &ValueNotFound{}
|
return nil, &ValueNotFound{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item.textures, nil
|
return item.textures, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryTexturesStorage) StoreTextures(textures *mojang.SignedTexturesResponse) {
|
func (s *inMemoryTexturesStorage) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
|
||||||
|
var timestamp int64
|
||||||
|
if textures != nil {
|
||||||
|
decoded := textures.DecodeTextures()
|
||||||
|
if decoded == nil {
|
||||||
|
panic("unable to decode textures")
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp = decoded.Timestamp
|
||||||
|
} else {
|
||||||
|
timestamp = unixNanoToUnixMicro(now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
decoded := textures.DecodeTextures()
|
s.data[uuid] = &inMemoryItem{
|
||||||
if decoded == nil {
|
|
||||||
panic("unable to decode textures")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.data[textures.Id] = &inMemoryItem{
|
|
||||||
textures: textures,
|
textures: textures,
|
||||||
timestamp: decoded.Timestamp,
|
timestamp: timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,10 +93,18 @@ func (s *inMemoryTexturesStorage) gc() {
|
|||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
maxTime := now().Add(inMemoryStoragePersistPeriod*time.Duration(-1)).UnixNano() / 10e5
|
maxTime := getMinimalNotExpiredTimestamp()
|
||||||
for uuid, value := range s.data {
|
for uuid, value := range s.data {
|
||||||
if maxTime > value.timestamp {
|
if maxTime > value.timestamp {
|
||||||
delete(s.data, uuid)
|
delete(s.data, uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMinimalNotExpiredTimestamp() int64 {
|
||||||
|
return unixNanoToUnixMicro(now().Add(inMemoryStoragePersistPeriod * time.Duration(-1)).UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
func unixNanoToUnixMicro(unixNano int64) int64 {
|
||||||
|
return unixNano / 10e5
|
||||||
|
}
|
||||||
|
@ -59,7 +59,7 @@ func TestInMemoryTexturesStorage_GetTextures(t *testing.T) {
|
|||||||
assert := testify.New(t)
|
assert := testify.New(t)
|
||||||
|
|
||||||
storage := CreateInMemoryTexturesStorage()
|
storage := CreateInMemoryTexturesStorage()
|
||||||
storage.StoreTextures(texturesWithSkin)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithSkin)
|
||||||
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
|
|
||||||
assert.Equal(texturesWithSkin, result)
|
assert.Equal(texturesWithSkin, result)
|
||||||
@ -70,7 +70,7 @@ func TestInMemoryTexturesStorage_GetTextures(t *testing.T) {
|
|||||||
assert := testify.New(t)
|
assert := testify.New(t)
|
||||||
|
|
||||||
storage := CreateInMemoryTexturesStorage()
|
storage := CreateInMemoryTexturesStorage()
|
||||||
storage.StoreTextures(texturesWithSkin)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithSkin)
|
||||||
|
|
||||||
now = func() time.Time {
|
now = func() time.Time {
|
||||||
return time.Now().Add(time.Minute * 2)
|
return time.Now().Add(time.Minute * 2)
|
||||||
@ -90,7 +90,7 @@ func TestInMemoryTexturesStorage_StoreTextures(t *testing.T) {
|
|||||||
assert := testify.New(t)
|
assert := testify.New(t)
|
||||||
|
|
||||||
storage := CreateInMemoryTexturesStorage()
|
storage := CreateInMemoryTexturesStorage()
|
||||||
storage.StoreTextures(texturesWithSkin)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithSkin)
|
||||||
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
|
|
||||||
assert.Equal(texturesWithSkin, result)
|
assert.Equal(texturesWithSkin, result)
|
||||||
@ -101,8 +101,8 @@ func TestInMemoryTexturesStorage_StoreTextures(t *testing.T) {
|
|||||||
assert := testify.New(t)
|
assert := testify.New(t)
|
||||||
|
|
||||||
storage := CreateInMemoryTexturesStorage()
|
storage := CreateInMemoryTexturesStorage()
|
||||||
storage.StoreTextures(texturesWithoutSkin)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithoutSkin)
|
||||||
storage.StoreTextures(texturesWithSkin)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithSkin)
|
||||||
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
|
|
||||||
assert.NotEqual(texturesWithoutSkin, result)
|
assert.NotEqual(texturesWithoutSkin, result)
|
||||||
@ -110,6 +110,17 @@ func TestInMemoryTexturesStorage_StoreTextures(t *testing.T) {
|
|||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("store nil textures", func(t *testing.T) {
|
||||||
|
assert := testify.New(t)
|
||||||
|
|
||||||
|
storage := CreateInMemoryTexturesStorage()
|
||||||
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", nil)
|
||||||
|
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
|
|
||||||
|
assert.Nil(result)
|
||||||
|
assert.Nil(err)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("should panic if textures prop is not decoded", func(t *testing.T) {
|
t.Run("should panic if textures prop is not decoded", func(t *testing.T) {
|
||||||
assert := testify.New(t)
|
assert := testify.New(t)
|
||||||
|
|
||||||
@ -121,7 +132,7 @@ func TestInMemoryTexturesStorage_StoreTextures(t *testing.T) {
|
|||||||
|
|
||||||
assert.PanicsWithValue("unable to decode textures", func() {
|
assert.PanicsWithValue("unable to decode textures", func() {
|
||||||
storage := CreateInMemoryTexturesStorage()
|
storage := CreateInMemoryTexturesStorage()
|
||||||
storage.StoreTextures(toStore)
|
storage.StoreTextures("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", toStore)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -164,8 +175,8 @@ func TestInMemoryTexturesStorage_GarbageCollection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
storage := CreateInMemoryTexturesStorage()
|
storage := CreateInMemoryTexturesStorage()
|
||||||
storage.StoreTextures(textures1)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", textures1)
|
||||||
storage.StoreTextures(textures2)
|
storage.StoreTextures("b5d58475007d4f9e9ddd1403e2497579", textures2)
|
||||||
|
|
||||||
storage.Start()
|
storage.Start()
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package queue
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -15,7 +16,7 @@ import (
|
|||||||
|
|
||||||
var usernamesToUuids = mojang.UsernamesToUuids
|
var usernamesToUuids = mojang.UsernamesToUuids
|
||||||
var uuidToTextures = mojang.UuidToTextures
|
var uuidToTextures = mojang.UuidToTextures
|
||||||
var uuidsQueuePeriod = time.Second
|
var uuidsQueueIterationDelay = time.Second
|
||||||
var forever = func() bool {
|
var forever = func() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -96,13 +97,13 @@ func (ctx *JobsQueue) GetTexturesForUsername(username string) chan *mojang.Signe
|
|||||||
|
|
||||||
func (ctx *JobsQueue) startQueue() {
|
func (ctx *JobsQueue) startQueue() {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(uuidsQueuePeriod)
|
time.Sleep(uuidsQueueIterationDelay)
|
||||||
for forever() {
|
for forever() {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
ctx.queueRound()
|
ctx.queueRound()
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
ctx.Logger.RecordTimer("mojang_textures.usernames.round_time", elapsed)
|
ctx.Logger.RecordTimer("mojang_textures.usernames.round_time", elapsed)
|
||||||
time.Sleep(uuidsQueuePeriod - elapsed)
|
time.Sleep(uuidsQueueIterationDelay)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@ -123,7 +124,7 @@ func (ctx *JobsQueue) queueRound() {
|
|||||||
|
|
||||||
profiles, err := usernamesToUuids(usernames)
|
profiles, err := usernamesToUuids(usernames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.handleResponseError(err)
|
ctx.handleResponseError(err, "usernames")
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
job.RespondTo <- nil
|
job.RespondTo <- nil
|
||||||
}
|
}
|
||||||
@ -134,7 +135,7 @@ func (ctx *JobsQueue) queueRound() {
|
|||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
go func(job *jobItem) {
|
go func(job *jobItem) {
|
||||||
var uuid string
|
var uuid string
|
||||||
// Profiles in response not ordered, so we must search each username over full array
|
// The profiles in the response are not ordered, so we must search each username over full array
|
||||||
for _, profile := range profiles {
|
for _, profile := range profiles {
|
||||||
if strings.EqualFold(job.Username, profile.Name) {
|
if strings.EqualFold(job.Username, profile.Name) {
|
||||||
uuid = profile.Id
|
uuid = profile.Id
|
||||||
@ -166,26 +167,23 @@ func (ctx *JobsQueue) getTextures(uuid string) *mojang.SignedTexturesResponse {
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
result, err := uuidToTextures(uuid, true)
|
result, err := uuidToTextures(uuid, true)
|
||||||
ctx.Logger.RecordTimer("mojang_textures.textures.request_time", time.Since(start))
|
ctx.Logger.RecordTimer("mojang_textures.textures.request_time", time.Since(start))
|
||||||
shouldCache := true
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.handleResponseError(err)
|
ctx.handleResponseError(err, "textures")
|
||||||
shouldCache = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldCache && result != nil {
|
// Mojang can respond with an error, but count it as a hit, so store result even if the textures is nil
|
||||||
ctx.Storage.StoreTextures(result)
|
ctx.Storage.StoreTextures(uuid, result)
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *JobsQueue) handleResponseError(err error) {
|
func (ctx *JobsQueue) handleResponseError(err error, threadName string) {
|
||||||
ctx.Logger.Debug("Got response error :err", wd.ErrParam(err))
|
ctx.Logger.Debug(":name: Got response error :err", wd.NameParam(threadName), wd.ErrParam(err))
|
||||||
|
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case mojang.ResponseError:
|
case mojang.ResponseError:
|
||||||
if _, ok := err.(*mojang.TooManyRequestsError); ok {
|
if _, ok := err.(*mojang.TooManyRequestsError); ok {
|
||||||
ctx.Logger.Warning("Got 429 Too Many Requests :err", wd.ErrParam(err))
|
ctx.Logger.Warning(":name: Got 429 Too Many Requests :err", wd.NameParam(threadName), wd.ErrParam(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -194,6 +192,10 @@ func (ctx *JobsQueue) handleResponseError(err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := err.(*url.Error); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if opErr, ok := err.(*net.OpError); ok && (opErr.Op == "dial" || opErr.Op == "read") {
|
if opErr, ok := err.(*net.OpError); ok && (opErr.Op == "dial" || opErr.Op == "read") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -203,5 +205,5 @@ func (ctx *JobsQueue) handleResponseError(err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Logger.Emergency("Unknown Mojang response error: :err", wd.ErrParam(err))
|
ctx.Logger.Emergency(":name: Unknown Mojang response error: :err", wd.NameParam(threadName), wd.ErrParam(err))
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,19 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/elyby/chrly/api/mojang"
|
"github.com/elyby/chrly/api/mojang"
|
||||||
|
|
||||||
mocks "github.com/elyby/chrly/tests"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"testing"
|
|
||||||
|
mocks "github.com/elyby/chrly/tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mojangApiMocks struct {
|
type mojangApiMocks struct {
|
||||||
@ -64,8 +67,8 @@ func (m *mockStorage) GetTextures(uuid string) (*mojang.SignedTexturesResponse,
|
|||||||
return result, args.Error(1)
|
return result, args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockStorage) StoreTextures(textures *mojang.SignedTexturesResponse) {
|
func (m *mockStorage) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
|
||||||
m.Called(textures)
|
m.Called(uuid, textures)
|
||||||
}
|
}
|
||||||
|
|
||||||
type queueTestSuite struct {
|
type queueTestSuite struct {
|
||||||
@ -81,7 +84,7 @@ type queueTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *queueTestSuite) SetupSuite() {
|
func (suite *queueTestSuite) SetupSuite() {
|
||||||
uuidsQueuePeriod = 0
|
uuidsQueueIterationDelay = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *queueTestSuite) SetupTest() {
|
func (suite *queueTestSuite) SetupTest() {
|
||||||
@ -132,7 +135,7 @@ func (suite *queueTestSuite) TestReceiveTexturesForOneUsernameWithoutAnyCache()
|
|||||||
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{})
|
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{})
|
||||||
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
|
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
|
||||||
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
||||||
suite.Storage.On("StoreTextures", expectedResult).Once()
|
suite.Storage.On("StoreTextures", "0d252b7218b648bfb86c2ae476954d32", expectedResult).Once()
|
||||||
|
|
||||||
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
||||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||||
@ -167,8 +170,8 @@ func (suite *queueTestSuite) TestReceiveTexturesForFewUsernamesWithoutAnyCache()
|
|||||||
suite.Storage.On("StoreUuid", "Thinkofdeath", "4566e69fc90748ee8d71d7ba5aa00d20").Once()
|
suite.Storage.On("StoreUuid", "Thinkofdeath", "4566e69fc90748ee8d71d7ba5aa00d20").Once()
|
||||||
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
||||||
suite.Storage.On("GetTextures", "4566e69fc90748ee8d71d7ba5aa00d20").Once().Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "4566e69fc90748ee8d71d7ba5aa00d20").Once().Return(nil, &ValueNotFound{})
|
||||||
suite.Storage.On("StoreTextures", expectedResult1).Once()
|
suite.Storage.On("StoreTextures", "0d252b7218b648bfb86c2ae476954d32", expectedResult1).Once()
|
||||||
suite.Storage.On("StoreTextures", expectedResult2).Once()
|
suite.Storage.On("StoreTextures", "4566e69fc90748ee8d71d7ba5aa00d20", expectedResult2).Once()
|
||||||
|
|
||||||
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb", "Thinkofdeath"}).Once().Return([]*mojang.ProfileInfo{
|
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb", "Thinkofdeath"}).Once().Return([]*mojang.ProfileInfo{
|
||||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||||
@ -198,7 +201,7 @@ func (suite *queueTestSuite) TestReceiveTexturesForUsernameWithCachedUuid() {
|
|||||||
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("0d252b7218b648bfb86c2ae476954d32", nil)
|
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("0d252b7218b648bfb86c2ae476954d32", nil)
|
||||||
// Storage.StoreUuid shouldn't be called
|
// Storage.StoreUuid shouldn't be called
|
||||||
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
||||||
suite.Storage.On("StoreTextures", expectedResult).Once()
|
suite.Storage.On("StoreTextures", "0d252b7218b648bfb86c2ae476954d32", expectedResult).Once()
|
||||||
|
|
||||||
// MojangApi.UsernamesToUuids shouldn't be called
|
// MojangApi.UsernamesToUuids shouldn't be called
|
||||||
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(expectedResult, nil)
|
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(expectedResult, nil)
|
||||||
@ -307,7 +310,7 @@ func (suite *queueTestSuite) TestReceiveTexturesForTheSameUsernames() {
|
|||||||
suite.Storage.On("GetUuid", "maksimkurb").Twice().Return("", &ValueNotFound{})
|
suite.Storage.On("GetUuid", "maksimkurb").Twice().Return("", &ValueNotFound{})
|
||||||
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
|
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
|
||||||
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
||||||
suite.Storage.On("StoreTextures", expectedResult).Once()
|
suite.Storage.On("StoreTextures", "0d252b7218b648bfb86c2ae476954d32", expectedResult).Once()
|
||||||
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
||||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||||
}, nil)
|
}, nil)
|
||||||
@ -339,7 +342,7 @@ func (suite *queueTestSuite) TestReceiveTexturesForUsernameThatAlreadyProcessing
|
|||||||
suite.Storage.On("GetUuid", "maksimkurb").Twice().Return("", &ValueNotFound{})
|
suite.Storage.On("GetUuid", "maksimkurb").Twice().Return("", &ValueNotFound{})
|
||||||
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
|
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
|
||||||
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
|
||||||
suite.Storage.On("StoreTextures", expectedResult).Once()
|
suite.Storage.On("StoreTextures", "0d252b7218b648bfb86c2ae476954d32", expectedResult).Once()
|
||||||
|
|
||||||
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
||||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||||
@ -402,6 +405,7 @@ var expectedErrors = []error{
|
|||||||
&mojang.TooManyRequestsError{},
|
&mojang.TooManyRequestsError{},
|
||||||
&mojang.ServerError{},
|
&mojang.ServerError{},
|
||||||
&timeoutError{},
|
&timeoutError{},
|
||||||
|
&url.Error{Op: "GET", URL: "http://localhost"},
|
||||||
&net.OpError{Op: "read"},
|
&net.OpError{Op: "read"},
|
||||||
&net.OpError{Op: "dial"},
|
&net.OpError{Op: "dial"},
|
||||||
syscall.ECONNREFUSED,
|
syscall.ECONNREFUSED,
|
||||||
@ -411,8 +415,8 @@ func (suite *queueTestSuite) TestShouldNotLogErrorWhenExpectedErrorReturnedFromU
|
|||||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("Debug", "Got response error :err", mock.Anything).Times(len(expectedErrors))
|
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Times(len(expectedErrors))
|
||||||
suite.Logger.On("Warning", "Got 429 Too Many Requests :err", mock.Anything).Once()
|
suite.Logger.On("Warning", ":name: Got 429 Too Many Requests :err", mock.Anything, mock.Anything).Once()
|
||||||
|
|
||||||
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
||||||
|
|
||||||
@ -431,8 +435,8 @@ func (suite *queueTestSuite) TestShouldLogEmergencyOnUnexpectedErrorReturnedFrom
|
|||||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("Debug", "Got response error :err", mock.Anything).Once()
|
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Once()
|
||||||
suite.Logger.On("Emergency", "Unknown Mojang response error: :err", mock.Anything).Once()
|
suite.Logger.On("Emergency", ":name: Unknown Mojang response error: :err", mock.Anything, mock.Anything).Once()
|
||||||
|
|
||||||
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
||||||
|
|
||||||
@ -447,13 +451,13 @@ func (suite *queueTestSuite) TestShouldNotLogErrorWhenExpectedErrorReturnedFromU
|
|||||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("Debug", "Got response error :err", mock.Anything).Times(len(expectedErrors))
|
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Times(len(expectedErrors))
|
||||||
suite.Logger.On("Warning", "Got 429 Too Many Requests :err", mock.Anything).Once()
|
suite.Logger.On("Warning", ":name: Got 429 Too Many Requests :err", mock.Anything, mock.Anything).Once()
|
||||||
|
|
||||||
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
||||||
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32")
|
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32")
|
||||||
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Return(nil, &ValueNotFound{})
|
||||||
// Storage.StoreTextures shouldn't be called
|
suite.Storage.On("StoreTextures", "0d252b7218b648bfb86c2ae476954d32", (*mojang.SignedTexturesResponse)(nil))
|
||||||
|
|
||||||
for _, err := range expectedErrors {
|
for _, err := range expectedErrors {
|
||||||
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
||||||
@ -473,13 +477,13 @@ func (suite *queueTestSuite) TestShouldLogEmergencyOnUnexpectedErrorReturnedFrom
|
|||||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
suite.Logger.On("UpdateGauge", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||||
suite.Logger.On("Debug", "Got response error :err", mock.Anything).Once()
|
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Once()
|
||||||
suite.Logger.On("Emergency", "Unknown Mojang response error: :err", mock.Anything).Once()
|
suite.Logger.On("Emergency", ":name: Unknown Mojang response error: :err", mock.Anything, mock.Anything).Once()
|
||||||
|
|
||||||
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
suite.Storage.On("GetUuid", "maksimkurb").Return("", &ValueNotFound{})
|
||||||
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32")
|
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32")
|
||||||
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Return(nil, &ValueNotFound{})
|
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Return(nil, &ValueNotFound{})
|
||||||
// Storage.StoreTextures shouldn't be called
|
suite.Storage.On("StoreTextures", "0d252b7218b648bfb86c2ae476954d32", (*mojang.SignedTexturesResponse)(nil))
|
||||||
|
|
||||||
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
suite.MojangApi.On("UsernamesToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
|
||||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||||
|
@ -7,12 +7,12 @@ type UuidsStorage interface {
|
|||||||
StoreUuid(username string, uuid string)
|
StoreUuid(username string, uuid string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nil value can be passed to the storage to indicate that there is no textures
|
||||||
|
// for uuid and we know about it. Return err only in case, when storage completely
|
||||||
|
// unable to load any information about textures
|
||||||
type TexturesStorage interface {
|
type TexturesStorage interface {
|
||||||
// nil can be returned to indicate that there is no textures for uuid
|
|
||||||
// and we know about it. Return err only in case, when storage completely
|
|
||||||
// don't know anything about uuid
|
|
||||||
GetTextures(uuid string) (*mojang.SignedTexturesResponse, error)
|
GetTextures(uuid string) (*mojang.SignedTexturesResponse, error)
|
||||||
StoreTextures(textures *mojang.SignedTexturesResponse)
|
StoreTextures(uuid string, textures *mojang.SignedTexturesResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
@ -39,8 +39,8 @@ func (s *SplittedStorage) GetTextures(uuid string) (*mojang.SignedTexturesRespon
|
|||||||
return s.TexturesStorage.GetTextures(uuid)
|
return s.TexturesStorage.GetTextures(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SplittedStorage) StoreTextures(textures *mojang.SignedTexturesResponse) {
|
func (s *SplittedStorage) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
|
||||||
s.TexturesStorage.StoreTextures(textures)
|
s.TexturesStorage.StoreTextures(uuid, textures)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This error can be used to indicate, that requested
|
// This error can be used to indicate, that requested
|
||||||
|
@ -35,8 +35,8 @@ func (m *texturesStorageMock) GetTextures(uuid string) (*mojang.SignedTexturesRe
|
|||||||
return result, args.Error(1)
|
return result, args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *texturesStorageMock) StoreTextures(textures *mojang.SignedTexturesResponse) {
|
func (m *texturesStorageMock) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
|
||||||
m.Called(textures)
|
m.Called(uuid, textures)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSplittedStorage(t *testing.T) {
|
func TestSplittedStorage(t *testing.T) {
|
||||||
@ -74,10 +74,10 @@ func TestSplittedStorage(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("StoreTextures", func(t *testing.T) {
|
t.Run("StoreTextures", func(t *testing.T) {
|
||||||
toStore := &mojang.SignedTexturesResponse{Id: "mock id"}
|
toStore := &mojang.SignedTexturesResponse{}
|
||||||
storage, _, texturesMock := createMockedStorage()
|
storage, _, texturesMock := createMockedStorage()
|
||||||
texturesMock.On("StoreTextures", toStore).Once()
|
texturesMock.On("StoreTextures", "mock id", toStore).Once()
|
||||||
storage.StoreTextures(toStore)
|
storage.StoreTextures("mock id", toStore)
|
||||||
texturesMock.AssertExpectations(t)
|
texturesMock.AssertExpectations(t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user