mirror of
https://github.com/elyby/chrly.git
synced 2024-11-30 02:32:19 +05:30
Rework in_memory_textures_storage. Handle empty properties correctly
This commit is contained in:
parent
05c68c6ba6
commit
c4566a337b
9
Gopkg.lock
generated
9
Gopkg.lock
generated
@ -277,14 +277,6 @@
|
|||||||
revision = "3ebf1ddaeb260c4b1ae502a01c7844fa8c1fa0e9"
|
revision = "3ebf1ddaeb260c4b1ae502a01c7844fa8c1fa0e9"
|
||||||
version = "v1.5.1"
|
version = "v1.5.1"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:86e6712cfd4070a2120c03fcec41cfcbbc51813504a74e28d74479edfaf669ee"
|
|
||||||
name = "github.com/tevino/abool"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "9b9efcf221b50905aab9bbabd3daed56dc10f339"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:061754b9de261d8e1cf804970dff7b3e105d1cb4883ef446dbe911489ba8e9eb"
|
digest = "1:061754b9de261d8e1cf804970dff7b3e105d1cb4883ef446dbe911489ba8e9eb"
|
||||||
name = "github.com/thedevsaddam/govalidator"
|
name = "github.com/thedevsaddam/govalidator"
|
||||||
@ -352,7 +344,6 @@
|
|||||||
"github.com/stretchr/testify/mock",
|
"github.com/stretchr/testify/mock",
|
||||||
"github.com/stretchr/testify/require",
|
"github.com/stretchr/testify/require",
|
||||||
"github.com/stretchr/testify/suite",
|
"github.com/stretchr/testify/suite",
|
||||||
"github.com/tevino/abool",
|
|
||||||
"github.com/thedevsaddam/govalidator",
|
"github.com/thedevsaddam/govalidator",
|
||||||
]
|
]
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
|
@ -32,10 +32,6 @@ ignored = ["github.com/elyby/chrly"]
|
|||||||
name = "github.com/thedevsaddam/govalidator"
|
name = "github.com/thedevsaddam/govalidator"
|
||||||
version = "^1.9.6"
|
version = "^1.9.6"
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/tevino/abool"
|
|
||||||
branch = "master"
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/asaskevich/EventBus"
|
name = "github.com/asaskevich/EventBus"
|
||||||
source = "https://github.com/erickskrauch/EventBus.git"
|
source = "https://github.com/erickskrauch/EventBus.git"
|
||||||
|
5
di/db.go
5
di/db.go
@ -66,8 +66,5 @@ func newFSFactory(config *viper.Viper) (*fs.Filesystem, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newMojangSignedTexturesStorage() mojangtextures.TexturesStorage {
|
func newMojangSignedTexturesStorage() mojangtextures.TexturesStorage {
|
||||||
texturesStorage := mojangtextures.NewInMemoryTexturesStorage()
|
return mojangtextures.NewInMemoryTexturesStorage()
|
||||||
texturesStorage.Start()
|
|
||||||
|
|
||||||
return texturesStorage
|
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/elyby/chrly/api/mojang"
|
"github.com/elyby/chrly/api/mojang"
|
||||||
|
|
||||||
"github.com/tevino/abool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var now = time.Now
|
|
||||||
|
|
||||||
type inMemoryItem struct {
|
type inMemoryItem struct {
|
||||||
textures *mojang.SignedTexturesResponse
|
textures *mojang.SignedTexturesResponse
|
||||||
timestamp int64
|
timestamp int64
|
||||||
@ -20,9 +16,10 @@ type InMemoryTexturesStorage struct {
|
|||||||
GCPeriod time.Duration
|
GCPeriod time.Duration
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
|
|
||||||
lock sync.RWMutex
|
once sync.Once
|
||||||
data map[string]*inMemoryItem
|
lock sync.RWMutex
|
||||||
working *abool.AtomicBool
|
data map[string]*inMemoryItem
|
||||||
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInMemoryTexturesStorage() *InMemoryTexturesStorage {
|
func NewInMemoryTexturesStorage() *InMemoryTexturesStorage {
|
||||||
@ -35,30 +32,6 @@ func NewInMemoryTexturesStorage() *InMemoryTexturesStorage {
|
|||||||
return storage
|
return storage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InMemoryTexturesStorage) Start() {
|
|
||||||
if s.working == nil {
|
|
||||||
s.working = abool.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.working.IsSet() {
|
|
||||||
go func() {
|
|
||||||
time.Sleep(s.GCPeriod)
|
|
||||||
// TODO: this can be reimplemented in future with channels, but right now I have no idea how to make it right
|
|
||||||
for s.working.IsSet() {
|
|
||||||
start := time.Now()
|
|
||||||
s.gc()
|
|
||||||
time.Sleep(s.GCPeriod - time.Since(start))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
s.working.Set()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *InMemoryTexturesStorage) Stop() {
|
|
||||||
s.working.UnSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *InMemoryTexturesStorage) GetTextures(uuid string) (*mojang.SignedTexturesResponse, error) {
|
func (s *InMemoryTexturesStorage) GetTextures(uuid string) (*mojang.SignedTexturesResponse, error) {
|
||||||
s.lock.RLock()
|
s.lock.RLock()
|
||||||
defer s.lock.RUnlock()
|
defer s.lock.RUnlock()
|
||||||
@ -73,27 +46,36 @@ func (s *InMemoryTexturesStorage) GetTextures(uuid string) (*mojang.SignedTextur
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *InMemoryTexturesStorage) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
|
func (s *InMemoryTexturesStorage) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
|
||||||
var timestamp int64
|
s.once.Do(s.start)
|
||||||
if textures != nil {
|
|
||||||
decoded, err := textures.DecodeTextures()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp = decoded.Timestamp
|
|
||||||
} else {
|
|
||||||
timestamp = unixNanoToUnixMicro(now().UnixNano())
|
|
||||||
}
|
|
||||||
|
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
s.data[uuid] = &inMemoryItem{
|
s.data[uuid] = &inMemoryItem{
|
||||||
textures: textures,
|
textures: textures,
|
||||||
timestamp: timestamp,
|
timestamp: unixNanoToUnixMicro(time.Now().UnixNano()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *InMemoryTexturesStorage) start() {
|
||||||
|
s.done = make(chan struct{})
|
||||||
|
ticker := time.NewTicker(s.GCPeriod)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.done:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
s.gc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *InMemoryTexturesStorage) Stop() {
|
||||||
|
close(s.done)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *InMemoryTexturesStorage) gc() {
|
func (s *InMemoryTexturesStorage) gc() {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
@ -107,7 +89,7 @@ func (s *InMemoryTexturesStorage) gc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *InMemoryTexturesStorage) getMinimalNotExpiredTimestamp() int64 {
|
func (s *InMemoryTexturesStorage) getMinimalNotExpiredTimestamp() int64 {
|
||||||
return unixNanoToUnixMicro(now().Add(s.Duration * time.Duration(-1)).UnixNano())
|
return unixNanoToUnixMicro(time.Now().Add(s.Duration * time.Duration(-1)).UnixNano())
|
||||||
}
|
}
|
||||||
|
|
||||||
func unixNanoToUnixMicro(unixNano int64) int64 {
|
func unixNanoToUnixMicro(unixNano int64) int64 {
|
||||||
|
@ -64,18 +64,16 @@ func TestInMemoryTexturesStorage_GetTextures(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("should return nil, nil when textures are exists, but cache duration is expired", func(t *testing.T) {
|
t.Run("should return nil, nil when textures are exists, but cache duration is expired", func(t *testing.T) {
|
||||||
storage := NewInMemoryTexturesStorage()
|
storage := NewInMemoryTexturesStorage()
|
||||||
|
storage.Duration = 10 * time.Millisecond
|
||||||
|
storage.GCPeriod = time.Minute
|
||||||
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithSkin)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithSkin)
|
||||||
|
|
||||||
now = func() time.Time {
|
time.Sleep(storage.Duration * 2)
|
||||||
return time.Now().Add(time.Minute * 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
|
|
||||||
assert.Nil(t, result)
|
assert.Nil(t, result)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
now = time.Now
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +98,21 @@ func TestInMemoryTexturesStorage_StoreTextures(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("store textures with empty properties", func(t *testing.T) {
|
||||||
|
texturesWithEmptyProps := &mojang.SignedTexturesResponse{
|
||||||
|
Id: "dead24f9a4fa4877b7b04c8c6c72bb46",
|
||||||
|
Name: "mock",
|
||||||
|
Props: []*mojang.Property{},
|
||||||
|
}
|
||||||
|
|
||||||
|
storage := NewInMemoryTexturesStorage()
|
||||||
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", texturesWithEmptyProps)
|
||||||
|
result, err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
|
|
||||||
|
assert.Exactly(t, texturesWithEmptyProps, result)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("store nil textures", func(t *testing.T) {
|
t.Run("store nil textures", func(t *testing.T) {
|
||||||
storage := NewInMemoryTexturesStorage()
|
storage := NewInMemoryTexturesStorage()
|
||||||
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", nil)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", nil)
|
||||||
@ -108,31 +121,13 @@ func TestInMemoryTexturesStorage_StoreTextures(t *testing.T) {
|
|||||||
assert.Nil(t, result)
|
assert.Nil(t, result)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should panic if textures prop is not decoded", func(t *testing.T) {
|
|
||||||
toStore := &mojang.SignedTexturesResponse{
|
|
||||||
Id: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
||||||
Name: "mock",
|
|
||||||
Props: []*mojang.Property{
|
|
||||||
{
|
|
||||||
Name: "textures",
|
|
||||||
Value: "totally not base64 encoded json",
|
|
||||||
Signature: "totally not base64 encoded signature",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
storage := NewInMemoryTexturesStorage()
|
|
||||||
storage.StoreTextures("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", toStore)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInMemoryTexturesStorage_GarbageCollection(t *testing.T) {
|
func TestInMemoryTexturesStorage_GarbageCollection(t *testing.T) {
|
||||||
storage := NewInMemoryTexturesStorage()
|
storage := NewInMemoryTexturesStorage()
|
||||||
storage.GCPeriod = 10 * time.Millisecond
|
defer storage.Stop()
|
||||||
storage.Duration = 10 * time.Millisecond
|
storage.GCPeriod = 40 * time.Millisecond
|
||||||
|
storage.Duration = 40 * time.Millisecond
|
||||||
|
|
||||||
textures1 := &mojang.SignedTexturesResponse{
|
textures1 := &mojang.SignedTexturesResponse{
|
||||||
Id: "dead24f9a4fa4877b7b04c8c6c72bb46",
|
Id: "dead24f9a4fa4877b7b04c8c6c72bb46",
|
||||||
@ -141,7 +136,7 @@ func TestInMemoryTexturesStorage_GarbageCollection(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "textures",
|
Name: "textures",
|
||||||
Value: mojang.EncodeTextures(&mojang.TexturesProp{
|
Value: mojang.EncodeTextures(&mojang.TexturesProp{
|
||||||
Timestamp: time.Now().Add(storage.GCPeriod-time.Millisecond*time.Duration(5)).UnixNano() / 10e5,
|
Timestamp: time.Now().UnixNano() / 10e5,
|
||||||
ProfileID: "dead24f9a4fa4877b7b04c8c6c72bb46",
|
ProfileID: "dead24f9a4fa4877b7b04c8c6c72bb46",
|
||||||
ProfileName: "mock1",
|
ProfileName: "mock1",
|
||||||
Textures: &mojang.TexturesResponse{},
|
Textures: &mojang.TexturesResponse{},
|
||||||
@ -149,6 +144,7 @@ func TestInMemoryTexturesStorage_GarbageCollection(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
textures2 := &mojang.SignedTexturesResponse{
|
textures2 := &mojang.SignedTexturesResponse{
|
||||||
Id: "b5d58475007d4f9e9ddd1403e2497579",
|
Id: "b5d58475007d4f9e9ddd1403e2497579",
|
||||||
Name: "mock2",
|
Name: "mock2",
|
||||||
@ -156,7 +152,7 @@ func TestInMemoryTexturesStorage_GarbageCollection(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "textures",
|
Name: "textures",
|
||||||
Value: mojang.EncodeTextures(&mojang.TexturesProp{
|
Value: mojang.EncodeTextures(&mojang.TexturesProp{
|
||||||
Timestamp: time.Now().Add(storage.GCPeriod-time.Millisecond*time.Duration(15)).UnixNano() / 10e5,
|
Timestamp: time.Now().UnixNano() / 10e5,
|
||||||
ProfileID: "b5d58475007d4f9e9ddd1403e2497579",
|
ProfileID: "b5d58475007d4f9e9ddd1403e2497579",
|
||||||
ProfileName: "mock2",
|
ProfileName: "mock2",
|
||||||
Textures: &mojang.TexturesResponse{},
|
Textures: &mojang.TexturesResponse{},
|
||||||
@ -166,22 +162,20 @@ func TestInMemoryTexturesStorage_GarbageCollection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", textures1)
|
storage.StoreTextures("dead24f9a4fa4877b7b04c8c6c72bb46", textures1)
|
||||||
|
time.Sleep(storage.GCPeriod / 4) // Store second texture a bit later to avoid it removing by gc
|
||||||
storage.StoreTextures("b5d58475007d4f9e9ddd1403e2497579", textures2)
|
storage.StoreTextures("b5d58475007d4f9e9ddd1403e2497579", textures2)
|
||||||
|
|
||||||
storage.Start()
|
time.Sleep(storage.GCPeriod) // Let it start first iteration
|
||||||
defer storage.Stop()
|
|
||||||
|
|
||||||
time.Sleep(storage.GCPeriod + time.Millisecond) // Let it start first iteration
|
|
||||||
|
|
||||||
texturesFromStorage1, textures1Err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
texturesFromStorage1, textures1Err := storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
texturesFromStorage2, textures2Err := storage.GetTextures("b5d58475007d4f9e9ddd1403e2497579")
|
texturesFromStorage2, textures2Err := storage.GetTextures("b5d58475007d4f9e9ddd1403e2497579")
|
||||||
|
|
||||||
assert.NotNil(t, texturesFromStorage1)
|
assert.Nil(t, texturesFromStorage1)
|
||||||
assert.Nil(t, textures1Err)
|
assert.Nil(t, textures1Err)
|
||||||
assert.Nil(t, texturesFromStorage2)
|
assert.NotNil(t, texturesFromStorage2)
|
||||||
assert.Nil(t, textures2Err)
|
assert.Nil(t, textures2Err)
|
||||||
|
|
||||||
time.Sleep(storage.GCPeriod + time.Millisecond) // Let another iteration happen
|
time.Sleep(storage.GCPeriod) // Let another iteration happen
|
||||||
|
|
||||||
texturesFromStorage1, textures1Err = storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
texturesFromStorage1, textures1Err = storage.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46")
|
||||||
texturesFromStorage2, textures2Err = storage.GetTextures("b5d58475007d4f9e9ddd1403e2497579")
|
texturesFromStorage2, textures2Err = storage.GetTextures("b5d58475007d4f9e9ddd1403e2497579")
|
||||||
|
Loading…
Reference in New Issue
Block a user