Cap the post TTL cache at 10000 entries; once full, evict the entry with the earliest expiry instead of growing without bound.
88 lines
1.7 KiB
Go
88 lines
1.7 KiB
Go
package server
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const postCacheTTL = 5 * time.Minute
|
|
const postCacheMaxItems = 10000
|
|
|
|
type ttlCacheEntry struct {
|
|
value any
|
|
exp time.Time
|
|
}
|
|
|
|
type ttlCache struct {
|
|
mu sync.Mutex
|
|
items map[string]ttlCacheEntry
|
|
ttl time.Duration
|
|
maxItems int
|
|
}
|
|
|
|
func newTTLCache(ttl time.Duration) *ttlCache {
|
|
return &ttlCache{
|
|
items: make(map[string]ttlCacheEntry),
|
|
ttl: ttl,
|
|
maxItems: postCacheMaxItems,
|
|
}
|
|
}
|
|
|
|
func (c *ttlCache) get(key string) (any, bool) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
e, ok := c.items[key]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
if time.Now().After(e.exp) {
|
|
delete(c.items, key)
|
|
return nil, false
|
|
}
|
|
return e.value, true
|
|
}
|
|
|
|
func (c *ttlCache) set(key string, value any) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if len(c.items) >= c.maxItems {
|
|
// Evict the oldest expired entry, or if none expired, the first one.
|
|
var oldestKey string
|
|
var oldestTime time.Time
|
|
first := true
|
|
for k, e := range c.items {
|
|
if first || e.exp.Before(oldestTime) {
|
|
oldestKey = k
|
|
oldestTime = e.exp
|
|
first = false
|
|
}
|
|
}
|
|
if oldestKey != "" {
|
|
delete(c.items, oldestKey)
|
|
}
|
|
}
|
|
c.items[key] = ttlCacheEntry{value: value, exp: time.Now().Add(c.ttl)}
|
|
}
|
|
|
|
// cachedFetch returns the cached value for key or calls fetch to obtain it,
|
|
// storing the result in the cache before returning. The zero value is returned
|
|
// on fetch error and not cached.
|
|
func cachedFetch[T any](c *ttlCache, key string, fetch func() (T, error)) (T, error) {
|
|
if v, ok := c.get(key); ok {
|
|
if typed, ok := v.(T); ok {
|
|
return typed, nil
|
|
}
|
|
}
|
|
val, err := fetch()
|
|
if err != nil {
|
|
var zero T
|
|
return zero, err
|
|
}
|
|
c.set(key, val)
|
|
return val, nil
|
|
}
|
|
|
|
type latestCacheValue struct {
|
|
posts []*GetPostResponsePost
|
|
aid string
|
|
}
|