From f34056f241c744d20595e4eb2a32235a0399e7e5 Mon Sep 17 00:00:00 2001 From: Adora Laura Kalb Date: Fri, 19 Apr 2024 09:02:33 +0200 Subject: [PATCH] add cache file and a unit test --- cache/cache.go | 92 +++++++++++++++++++++++++++++++++++++++++++++ cache/cache_test.go | 34 +++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 cache/cache.go create mode 100644 cache/cache_test.go diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..96cde8b --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,92 @@ +/* +Copyright 2024 Adora Laura Kalb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "net/netip" + "sync" + "time" +) + +var ( + GlobalScoreCache *ScoreCache + cacheExpiredAfter = 5 * time.Minute +) + +type ServerScore struct { + expiresAt time.Time + Score float64 +} + +type ScoreCache struct { + stop chan struct{} + + mu sync.RWMutex + + scores map[netip.Addr]ServerScore +} + +type CacheMissError struct{} + +func (m *CacheMissError) Error() string { + return "User is not in cache!" +} + +func newCacheMissError() *CacheMissError { + return &CacheMissError{} +} + +func NewLocalCache() *ScoreCache { + lc := &ScoreCache{ + scores: make(map[netip.Addr]ServerScore), + stop: make(chan struct{}), + } + + return lc +} + +func (sc *ScoreCache) Add(score float64, ip netip.Addr, ts time.Time) { + ssc := ServerScore{Score: score, expiresAt: ts.Add(cacheExpiredAfter)} + sc.mu.Lock() + sc.scores[ip] = ssc + sc.mu.Unlock() +} + +func (lc *ScoreCache) Get(ip netip.Addr) (ServerScore, error) { + now := time.Now() + lc.mu.RLock() + defer lc.mu.RUnlock() + + cachedScore, ok := lc.scores[ip] + if !ok { + return ServerScore{}, newCacheMissError() + } + + if now.After(cachedScore.expiresAt) { + lc.delete(ip) + return ServerScore{}, newCacheMissError() + } + + return cachedScore, nil +} + +func (lc *ScoreCache) delete(ip netip.Addr) { + lc.mu.Lock() + + delete(lc.scores, ip) + lc.mu.Unlock() +} diff --git a/cache/cache_test.go b/cache/cache_test.go new file mode 100644 index 0000000..38be9a9 --- /dev/null +++ b/cache/cache_test.go @@ -0,0 +1,34 @@ +/* +Copyright 2024 Adora Laura Kalb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "net/netip" + "testing" + "time" +) + +func TestCacheGet(t *testing.T) { + const testFloat = 1.23456 + cache := NewLocalCache() + cache.Add(testFloat, netip.MustParseAddr("1.2.3.4"), time.Now().Add(5*time.Minute)) + + score, _ := cache.Get(netip.MustParseAddr("1.2.3.4")) + if score.Score != testFloat { + t.Fatalf("cache.Get(\"1.2.3.4\") = %f, want %f", score.Score, testFloat) + } +}