Skip to content

Commit

Permalink
feat: read and write from cache
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Gleich <[email protected]>
  • Loading branch information
gleich committed Jul 27, 2024
1 parent c98781a commit cf97406
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 8 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func main() {
stravaActivities := strava.FetchActivities(*minioClient, stravaTokens)
stravaCache := cache.New("strava", stravaActivities)
r.Get("/strava/cache", stravaCache.Route())
r.Post("/strava/event", strava.EventRoute(&stravaCache, *minioClient, stravaTokens))
r.Post("/strava/event", strava.EventRoute(stravaCache, *minioClient, stravaTokens))
r.Get("/strava/event", strava.ChallengeRoute)
lumber.Success("init strava cache")

Expand Down
20 changes: 13 additions & 7 deletions pkg/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"path/filepath"
"reflect"
"sync"
"time"
Expand All @@ -22,13 +23,12 @@ type Cache[T any] struct {
updated time.Time
updateCounter prometheus.Counter
requestCounter prometheus.Counter
filePath string
}

func New[T any](name string, data T) Cache[T] {
return Cache[T]{
Name: name,
data: data,
updated: time.Now(),
func New[T any](name string, data T) *Cache[T] {
cache := Cache[T]{
Name: name,
updateCounter: promauto.NewCounter(prometheus.CounterOpts{
Name: fmt.Sprintf("cache_%s_updates", name),
Help: fmt.Sprintf(`The total number of times the cache "%s" has been updated`, name),
Expand All @@ -37,10 +37,15 @@ func New[T any](name string, data T) Cache[T] {
Name: fmt.Sprintf("cache_%s_requests", name),
Help: fmt.Sprintf(`The total number of times the cache "%s" has been requested`, name),
}),
filePath: filepath.Join(cacheFolder, fmt.Sprintf("%s.json", name)),
}
cache.seedFromFile()
cache.Update(data)
cache.writeToFile()
return &cache
}

type response[T any] struct {
type cacheData[T any] struct {
Data T `json:"data"`
Updated time.Time `json:"updated"`
}
Expand All @@ -54,7 +59,7 @@ func (c *Cache[T]) Route() http.HandlerFunc {
}
c.mutex.RLock()
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(response[T]{Data: c.data, Updated: c.updated})
err := json.NewEncoder(w).Encode(cacheData[T]{Data: c.data, Updated: c.updated})
c.mutex.RUnlock()
c.requestCounter.Inc()
if err != nil {
Expand All @@ -75,6 +80,7 @@ func (c *Cache[T]) Update(data T) {
c.mutex.Unlock()
c.updateCounter.Inc()
metrics.CacheUpdates.Inc()
c.writeToFile()
if updated {
lumber.Success(c.Name, "updated")
}
Expand Down
67 changes: 67 additions & 0 deletions pkg/cache/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package cache

import (
"encoding/json"
"os"
"path/filepath"

"github.com/gleich/lumber/v2"
)

const cacheFolder = "/caches/"

func (c *Cache[T]) writeToFile() {
var file *os.File
if _, err := os.Stat(c.filePath); os.IsNotExist(err) {
folder := filepath.Dir(c.filePath)
err := os.MkdirAll(folder, 0700)
if err != nil {
lumber.Error(err, "failed to create folder at path:", folder)
return
}
file, err = os.Create(c.filePath)
if err != nil {
lumber.Error(err, "failed to create file at path:", c.filePath)
return
}
} else {
file, err = os.OpenFile(c.filePath, os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
lumber.Error(err, "failed to read file at path:", c.filePath)
return
}
}
defer file.Close()

b, err := json.Marshal(c.data)
if err != nil {
lumber.Error(err, "encoding data to json failed")
return
}
_, err = file.Write(b)
if err != nil {
lumber.Error(err, "writing data to json failed")
}
}

// Seed the cache from the persistent cache file
// returns if the cache was able to be seeded or not
func (c *Cache[T]) seedFromFile() {
if _, err := os.Stat(c.filePath); !os.IsNotExist(err) {
b, err := os.ReadFile(c.filePath)
if err != nil {
lumber.Error(err, "reading from cache file from", c.filePath, "failed")
return
}

var data cacheData[T]
err = json.Unmarshal(b, &data)
if err != nil {
lumber.Error(err, "unmarshal json data failed from:", string(b))
return
}

c.data = data.Data
c.updated = data.Updated
}
}

0 comments on commit cf97406

Please sign in to comment.