86 lines
1.8 KiB
Go
86 lines
1.8 KiB
Go
package web
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type dashboardCacheEntry struct {
|
|
value map[string]any
|
|
expiresAt time.Time
|
|
building chan struct{}
|
|
}
|
|
|
|
type dashboardSnapshotCache struct {
|
|
mu sync.Mutex
|
|
ttl time.Duration
|
|
generation uint64
|
|
entries map[string]*dashboardCacheEntry
|
|
}
|
|
|
|
func newDashboardSnapshotCache(ttl time.Duration) *dashboardSnapshotCache {
|
|
if ttl <= 0 {
|
|
ttl = 5 * time.Second
|
|
}
|
|
return &dashboardSnapshotCache{ttl: ttl, entries: map[string]*dashboardCacheEntry{}}
|
|
}
|
|
|
|
func (c *dashboardSnapshotCache) Get(key string, build func() (map[string]any, error)) (map[string]any, bool, error) {
|
|
for {
|
|
now := time.Now()
|
|
c.mu.Lock()
|
|
entry := c.entries[key]
|
|
if entry != nil && entry.value != nil && now.Before(entry.expiresAt) {
|
|
value := entry.value
|
|
c.mu.Unlock()
|
|
return value, true, nil
|
|
}
|
|
if entry != nil && entry.building != nil {
|
|
ready := entry.building
|
|
c.mu.Unlock()
|
|
<-ready
|
|
continue
|
|
}
|
|
generation := c.generation
|
|
ready := make(chan struct{})
|
|
c.entries[key] = &dashboardCacheEntry{building: ready}
|
|
c.mu.Unlock()
|
|
|
|
value, err := build()
|
|
|
|
c.mu.Lock()
|
|
if generation != c.generation {
|
|
if current := c.entries[key]; current != nil && current.building == ready {
|
|
delete(c.entries, key)
|
|
}
|
|
close(ready)
|
|
c.mu.Unlock()
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
continue
|
|
}
|
|
if err != nil {
|
|
delete(c.entries, key)
|
|
close(ready)
|
|
c.mu.Unlock()
|
|
return nil, false, err
|
|
}
|
|
c.entries[key] = &dashboardCacheEntry{value: value, expiresAt: time.Now().Add(c.ttl)}
|
|
close(ready)
|
|
c.mu.Unlock()
|
|
return value, false, nil
|
|
}
|
|
}
|
|
|
|
func (c *dashboardSnapshotCache) Invalidate() {
|
|
c.mu.Lock()
|
|
c.generation++
|
|
for key, entry := range c.entries {
|
|
if entry.building == nil {
|
|
delete(c.entries, key)
|
|
}
|
|
}
|
|
c.mu.Unlock()
|
|
}
|