diff --git a/internal/cache/expire.go b/internal/cache/expire.go new file mode 100644 index 00000000..7a2bda93 --- /dev/null +++ b/internal/cache/expire.go @@ -0,0 +1,46 @@ +package cache + +import ( + "time" + + log "github.com/sirupsen/logrus" +) + +type Cache[T any] struct { + f func() (T, error) + Timestamp time.Time + Duration time.Duration + Data T +} + +func New[T any](f func() (T, error), duration time.Duration) *Cache[T] { + return &Cache[T]{ + f: f, + Duration: duration, + } +} + +func (c *Cache[T]) GetWithHit() (T, error, bool) { + hit := true + if c.Timestamp.IsZero() || time.Since(c.Timestamp) > c.Duration { + hit = false + + var err error + c.Data, err = c.f() + if err != nil { + return c.Data, err, hit + } + c.Timestamp = time.Now() + } + if hit { + log.Debugf("Cache hit for %T", c.Data) + } else { + log.Debugf("Cache miss for %T", c.Data) + } + return c.Data, nil, hit +} + +func (c *Cache[T]) Get() (T, error) { + data, err, _ := c.GetWithHit() + return data, err +} diff --git a/internal/web/releases.go b/internal/web/releases.go index 0659a193..2871ac6c 100644 --- a/internal/web/releases.go +++ b/internal/web/releases.go @@ -3,13 +3,22 @@ package web import ( "encoding/json" "net/http" + "time" + "github.com/amir20/dozzle/internal/cache" "github.com/amir20/dozzle/internal/releases" log "github.com/sirupsen/logrus" ) +var cachedReleases *cache.Cache[[]releases.Release] + func (h *handler) releases(w http.ResponseWriter, r *http.Request) { - releases, err := releases.Fetch(h.config.Version) + if cachedReleases == nil { + cachedReleases = cache.New(func() ([]releases.Release, error) { + return releases.Fetch(h.config.Version) + }, time.Hour) + } + releases, err, hit := cachedReleases.GetWithHit() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -17,8 +26,10 @@ func (h *handler) releases(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Cache-Control", "max-age=3600") w.Header().Set("Content-Type", "application/json") + if hit { + w.Header().Set("X-Cache", "HIT") + } if err := json.NewEncoder(w).Encode(releases); err != nil { log.Errorf("json encoding error while streaming %v", err.Error())