mirror of
				https://github.com/elyby/chrly.git
				synced 2025-05-31 14:11:51 +05:30 
			
		
		
		
	Rework in_memory_textures_storage. Handle empty properties correctly
This commit is contained in:
		
							
								
								
									
										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") | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user