From 0a937743bc7f8aff27b735752b421e24d8cb62e1 Mon Sep 17 00:00:00 2001
From: mr0x50 <24775431+mroxso@users.noreply.github.com>
Date: Sun, 23 Feb 2025 22:38:04 +0100
Subject: [PATCH] implement caching

---
 relay/cache/cache.go    | 64 +++++++++++++++++++++++++++++++++++++++++
 relay/trending/kinds.go | 12 ++++++++
 2 files changed, 76 insertions(+)
 create mode 100644 relay/cache/cache.go

diff --git a/relay/cache/cache.go b/relay/cache/cache.go
new file mode 100644
index 0000000..90bc653
--- /dev/null
+++ b/relay/cache/cache.go
@@ -0,0 +1,64 @@
+package cache
+
+import (
+	"sync"
+	"time"
+)
+
+type item struct {
+	value      interface{}
+	expiration int64
+}
+
+type Cache struct {
+	items map[string]item
+	mu    sync.RWMutex
+}
+
+func New() *Cache {
+	cache := &Cache{
+		items: make(map[string]item),
+	}
+	go cache.startCleanup()
+	return cache
+}
+
+func (c *Cache) Set(key string, value interface{}, duration time.Duration) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	c.items[key] = item{
+		value:      value,
+		expiration: time.Now().Add(duration).UnixNano(),
+	}
+}
+
+func (c *Cache) Get(key string) (interface{}, bool) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+
+	item, exists := c.items[key]
+	if !exists {
+		return nil, false
+	}
+
+	if time.Now().UnixNano() > item.expiration {
+		return nil, false
+	}
+
+	return item.value, true
+}
+
+func (c *Cache) startCleanup() {
+	ticker := time.NewTicker(time.Minute)
+	for range ticker.C {
+		c.mu.Lock()
+		now := time.Now().UnixNano()
+		for k, v := range c.items {
+			if now > v.expiration {
+				delete(c.items, k)
+			}
+		}
+		c.mu.Unlock()
+	}
+}
diff --git a/relay/trending/kinds.go b/relay/trending/kinds.go
index dbbefe9..ca394f9 100644
--- a/relay/trending/kinds.go
+++ b/relay/trending/kinds.go
@@ -4,6 +4,8 @@ import (
 	"database/sql"
 	"encoding/json"
 	"time"
+
+	"git.highperfocused.tech/highperfocused/lumina-relay/relay/cache"
 )
 
 type Post struct {
@@ -16,8 +18,17 @@ type Post struct {
 	ReactionCount int        `json:"reaction_count"`
 }
 
+var (
+	trendingCache = cache.New()
+	cacheDuration = 5 * time.Minute
+)
+
 // GetTrendingKind20 returns the top 20 trending posts of kind 20 from the last 24 hours
 func GetTrendingKind20(db *sql.DB) ([]Post, error) {
+	if cached, ok := trendingCache.Get("trending_kind_20"); ok {
+		return cached.([]Post), nil
+	}
+
 	query := `
 		WITH reactions AS (
 			SELECT 
@@ -65,5 +76,6 @@ func GetTrendingKind20(db *sql.DB) ([]Post, error) {
 		trendingPosts = append(trendingPosts, post)
 	}
 
+	trendingCache.Set("trending_kind_20", trendingPosts, cacheDuration)
 	return trendingPosts, nil
 }