make each memory store struct have its own internal map

effectively removing the global state
This commit is contained in:
Chris Heppell 2020-12-31 20:37:11 +00:00
parent 029c87df89
commit 8ca9fd7db5
2 changed files with 26 additions and 28 deletions

View File

@ -7,28 +7,26 @@ import (
"github.com/TwinProduction/gatus/core" "github.com/TwinProduction/gatus/core"
) )
var (
serviceStatuses = make(map[string]*core.ServiceStatus)
// serviceResultsMutex is used to prevent concurrent map access
serviceResultsMutex sync.RWMutex
)
// InMemoryStore implements an in-memory store // InMemoryStore implements an in-memory store
type InMemoryStore struct{} type InMemoryStore struct {
serviceStatuses map[string]*core.ServiceStatus
serviceResultsMutex sync.RWMutex
}
// NewInMemoryStore returns an in-memory store. Note that the store acts as a singleton, so although new-ing // NewInMemoryStore returns an in-memory store. Note that the store acts as a singleton, so although new-ing
// up in-memory stores will give you a unique reference to a struct each time, all structs returned // up in-memory stores will give you a unique reference to a struct each time, all structs returned
// by this function will act on the same in-memory store. // by this function will act on the same in-memory store.
func NewInMemoryStore() InMemoryStore { func NewInMemoryStore() InMemoryStore {
return InMemoryStore{} return InMemoryStore{
serviceStatuses: make(map[string]*core.ServiceStatus),
}
} }
// GetAll returns all the observed results for all services from the in memory store // GetAll returns all the observed results for all services from the in memory store
func (ims *InMemoryStore) GetAll() map[string]*core.ServiceStatus { func (ims *InMemoryStore) GetAll() map[string]*core.ServiceStatus {
results := make(map[string]*core.ServiceStatus) results := make(map[string]*core.ServiceStatus)
serviceResultsMutex.RLock() ims.serviceResultsMutex.RLock()
for key, svcStatus := range serviceStatuses { for key, svcStatus := range ims.serviceStatuses {
copiedResults := copyResults(svcStatus.Results) copiedResults := copyResults(svcStatus.Results)
results[key] = &core.ServiceStatus{ results[key] = &core.ServiceStatus{
Name: svcStatus.Name, Name: svcStatus.Name,
@ -36,7 +34,7 @@ func (ims *InMemoryStore) GetAll() map[string]*core.ServiceStatus {
Results: copiedResults, Results: copiedResults,
} }
} }
serviceResultsMutex.RUnlock() ims.serviceResultsMutex.RUnlock()
return results return results
} }
@ -44,9 +42,9 @@ func (ims *InMemoryStore) GetAll() map[string]*core.ServiceStatus {
// GetServiceStatus returns the service status for a given service name in the given group // GetServiceStatus returns the service status for a given service name in the given group
func (ims *InMemoryStore) GetServiceStatus(group, name string) *core.ServiceStatus { func (ims *InMemoryStore) GetServiceStatus(group, name string) *core.ServiceStatus {
key := fmt.Sprintf("%s_%s", group, name) key := fmt.Sprintf("%s_%s", group, name)
serviceResultsMutex.RLock() ims.serviceResultsMutex.RLock()
serviceStatus, exists := serviceStatuses[key] serviceStatus, exists := ims.serviceStatuses[key]
serviceResultsMutex.RUnlock() ims.serviceResultsMutex.RUnlock()
if !exists { if !exists {
return nil return nil
} }
@ -56,14 +54,14 @@ func (ims *InMemoryStore) GetServiceStatus(group, name string) *core.ServiceStat
// Insert inserts the observed result for the specified service into the in memory store // Insert inserts the observed result for the specified service into the in memory store
func (ims *InMemoryStore) Insert(service *core.Service, result *core.Result) { func (ims *InMemoryStore) Insert(service *core.Service, result *core.Result) {
key := fmt.Sprintf("%s_%s", service.Group, service.Name) key := fmt.Sprintf("%s_%s", service.Group, service.Name)
serviceResultsMutex.Lock() ims.serviceResultsMutex.Lock()
serviceStatus, exists := serviceStatuses[key] serviceStatus, exists := ims.serviceStatuses[key]
if !exists { if !exists {
serviceStatus = core.NewServiceStatus(service) serviceStatus = core.NewServiceStatus(service)
serviceStatuses[key] = serviceStatus ims.serviceStatuses[key] = serviceStatus
} }
serviceStatus.AddResult(result) serviceStatus.AddResult(result)
serviceResultsMutex.Unlock() ims.serviceResultsMutex.Unlock()
} }
func copyResults(results []*core.Result) []*core.Result { func copyResults(results []*core.Result) []*core.Result {
@ -112,7 +110,7 @@ func copyErrors(errors []string) []string {
// Clear will empty all the results from the in memory store // Clear will empty all the results from the in memory store
func (ims *InMemoryStore) Clear() { func (ims *InMemoryStore) Clear() {
serviceResultsMutex.Lock() ims.serviceResultsMutex.Lock()
serviceStatuses = make(map[string]*core.ServiceStatus) ims.serviceStatuses = make(map[string]*core.ServiceStatus)
serviceResultsMutex.Unlock() ims.serviceResultsMutex.Unlock()
} }

View File

@ -360,7 +360,7 @@ func TestStorage_InsertResultForServiceWithConditionResultsIntoEmptyMemoryStore_
} }
} }
func TestStorage_MultipleMemoryStoreInstancesReferToSameMemoryMap(t *testing.T) { func TestStorage_MultipleMemoryStoreInstancesReferToDifferentInternalMaps(t *testing.T) {
memoryStore.Clear() memoryStore.Clear()
currentMap := memoryStore.GetAll() currentMap := memoryStore.GetAll()
@ -368,23 +368,23 @@ func TestStorage_MultipleMemoryStoreInstancesReferToSameMemoryMap(t *testing.T)
otherMemoryStoresMap := otherMemoryStore.GetAll() otherMemoryStoresMap := otherMemoryStore.GetAll()
if len(currentMap) != len(otherMemoryStoresMap) { if len(currentMap) != len(otherMemoryStoresMap) {
t.Errorf("Multiple memory stores should refer to the same internal map, but 'memoryStore' returned %d results, and 'otherMemoryStore' returned %d results", len(currentMap), len(otherMemoryStoresMap)) t.Errorf("Multiple memory stores should refer to the different internal maps, but 'memoryStore' returned %d results, and 'otherMemoryStore' returned %d results", len(currentMap), len(otherMemoryStoresMap))
} }
memoryStore.Insert(&testService, &core.Result{}) memoryStore.Insert(&testService, &core.Result{})
currentMap = memoryStore.GetAll() currentMap = memoryStore.GetAll()
otherMemoryStoresMap = otherMemoryStore.GetAll() otherMemoryStoresMap = otherMemoryStore.GetAll()
if len(currentMap) != len(otherMemoryStoresMap) { if len(currentMap) == len(otherMemoryStoresMap) {
t.Errorf("Multiple memory stores should refer to the same internal map, but 'memoryStore' returned %d results after inserting, and 'otherMemoryStore' returned %d results after inserting", len(currentMap), len(otherMemoryStoresMap)) t.Errorf("Multiple memory stores should refer to different internal maps, but 'memoryStore' returned %d results after inserting, and 'otherMemoryStore' returned %d results after inserting", len(currentMap), len(otherMemoryStoresMap))
} }
otherMemoryStore.Clear() otherMemoryStore.Clear()
currentMap = memoryStore.GetAll() currentMap = memoryStore.GetAll()
otherMemoryStoresMap = otherMemoryStore.GetAll() otherMemoryStoresMap = otherMemoryStore.GetAll()
if len(currentMap) != len(otherMemoryStoresMap) { if len(currentMap) == len(otherMemoryStoresMap) {
t.Errorf("Multiple memory stores should refer to the same internal map, but 'memoryStore' returned %d results after clearing, and 'otherMemoryStore' returned %d results after clearing", len(currentMap), len(otherMemoryStoresMap)) t.Errorf("Multiple memory stores should refer to different internal maps, but 'memoryStore' returned %d results after clearing, and 'otherMemoryStore' returned %d results after clearing", len(currentMap), len(otherMemoryStoresMap))
} }
} }