diff --git a/controller/controller.go b/controller/controller.go index 81b77b6..fa754d8 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -320,13 +320,13 @@ func GetForecast( } } -func GetMoon(res http.ResponseWriter, req *http.Request, cache *types.CacheEntity[types.Moon], vars *types.Variables) { +func GetMoon(res http.ResponseWriter, req *http.Request, cache *types.Cache[types.Moon], vars *types.Variables) { if req.Method != http.MethodGet { jsonError(res, "error", "method not allowed", http.StatusMethodNotAllowed) return } - cachedValue, found := cache.GetEntry(vars.TimeToLive) + cachedValue, found := cache.GetEntry(fmtKey("moon"), vars.TimeToLive) if found { // Format moon object and then return it cachedValue.Percentage = fmt.Sprintf("%s%%", cachedValue.Percentage) @@ -341,7 +341,7 @@ func GetMoon(res http.ResponseWriter, req *http.Request, cache *types.CacheEntit } // Add result to cache - cache.AddEntry(moon) + cache.AddEntry(moon, fmtKey("moon")) // Format moon object and then return it moon.Percentage = fmt.Sprintf("%s%%", moon.Percentage) diff --git a/types/cache.go b/types/cache.go index 8929707..fb0392f 100644 --- a/types/cache.go +++ b/types/cache.go @@ -2,6 +2,7 @@ package types import ( "strings" + "sync" "time" ) @@ -18,6 +19,7 @@ type CacheEntity[T cacheType] struct { // Cache, representing a mapping between a key(str) and a CacheEntity type Cache[T cacheType] struct { + mu sync.RWMutex Data map[string]CacheEntity[T] } @@ -28,7 +30,7 @@ type Caches struct { WindCache Cache[Wind] DailyForecastCache Cache[DailyForecast] HourlyForecastCache Cache[HourlyForecast] - MoonCache CacheEntity[Moon] + MoonCache Cache[Moon] } func InitCache() *Caches { @@ -38,11 +40,14 @@ func InitCache() *Caches { WindCache: Cache[Wind]{Data: make(map[string]CacheEntity[Wind])}, DailyForecastCache: Cache[DailyForecast]{Data: make(map[string]CacheEntity[DailyForecast])}, HourlyForecastCache: Cache[HourlyForecast]{Data: make(map[string]CacheEntity[HourlyForecast])}, - MoonCache: CacheEntity[Moon]{element: Moon{}, timestamp: time.Time{}}, + MoonCache: Cache[Moon]{Data: make(map[string]CacheEntity[Moon])}, } } func (cache *Cache[T]) GetEntry(cityName string, ttl int8) (T, bool) { + cache.mu.RLock() + defer cache.mu.RUnlock() + val, isPresent := cache.Data[strings.ToUpper(cityName)] // If key is not present, return a zero value @@ -61,6 +66,9 @@ func (cache *Cache[T]) GetEntry(cityName string, ttl int8) (T, bool) { } func (cache *Cache[T]) AddEntry(entry T, cityName string) { + cache.mu.Lock() + defer cache.mu.Unlock() + currentTime := time.Now() cache.Data[strings.ToUpper(cityName)] = CacheEntity[T]{ @@ -68,26 +76,3 @@ func (cache *Cache[T]) AddEntry(entry T, cityName string) { timestamp: currentTime, } } - -func (moon *CacheEntity[Moon]) GetEntry(ttl int8) (Moon, bool) { - var zeroMoon Moon - - // If moon data is not present, return a zero value - if moon == nil { - return zeroMoon, false - } - - // Otherwise check whether the element is expired - currentTime := time.Now() - expired := currentTime.Sub(moon.timestamp) > (time.Duration(ttl) * time.Hour) - if expired { - return zeroMoon, false - } - - return moon.element, true -} - -func (cache *CacheEntity[Moon]) AddEntry(entry Moon) { - cache.element = entry - cache.timestamp = time.Now() -} diff --git a/types/statDB.go b/types/statDB.go index f05ebd7..9b69ee7 100644 --- a/types/statDB.go +++ b/types/statDB.go @@ -3,11 +3,13 @@ package types import ( "fmt" "strings" + "sync" "time" ) // StatDB data type, representing a mapping between a location and its weather type StatDB struct { + mu sync.RWMutex db map[string]Weather } @@ -18,6 +20,9 @@ func InitDB() *StatDB { } func (statDB *StatDB) AddStatistic(cityName string, weather Weather) { + statDB.mu.Lock() + defer statDB.mu.Unlock() + key := fmt.Sprintf("%s@%s", weather.Date.Date.Format("2006-01-02"), cityName) // Insert weather statistic into the database only if it isn't present @@ -29,6 +34,9 @@ func (statDB *StatDB) AddStatistic(cityName string, weather Weather) { } func (statDB *StatDB) IsKeyInvalid(key string) bool { + statDB.mu.RLock() + defer statDB.mu.RUnlock() + // A key is invalid if it has less than 2 entries within the last 2 days threshold := time.Now().AddDate(0, 0, -2) @@ -52,6 +60,9 @@ func (statDB *StatDB) IsKeyInvalid(key string) bool { } func (statDB *StatDB) GetCityStatistics(cityName string) []Weather { + statDB.mu.RLock() + defer statDB.mu.RUnlock() + result := make([]Weather, 0) for key, record := range statDB.db {