perf: cache generated image thumbnails

This commit is contained in:
OpenClaw Agent
2026-09-22 05:51:06 +08:00
parent f799104fb2
commit e6806de5f6
4 changed files with 225 additions and 27 deletions
+103
View File
@@ -0,0 +1,103 @@
package api
import (
"container/list"
"sync"
)
type thumbnailCacheKey struct {
Path string
Size int64
ModTime int64
}
type thumbnailCacheEntry struct {
key thumbnailCacheKey
data []byte
}
type thumbnailFlight struct {
done chan struct{}
data []byte
err error
}
type thumbnailCache struct {
mu sync.Mutex
items map[thumbnailCacheKey]*list.Element
order *list.List
flights map[thumbnailCacheKey]*thumbnailFlight
bytes int64
maxBytes int64
maxItems int
}
func newThumbnailCache() *thumbnailCache {
return &thumbnailCache{
items: make(map[thumbnailCacheKey]*list.Element),
order: list.New(),
flights: make(map[thumbnailCacheKey]*thumbnailFlight),
maxBytes: 32 << 20,
maxItems: 64,
}
}
func (c *thumbnailCache) Do(key thumbnailCacheKey, fn func() ([]byte, error)) ([]byte, error) {
c.mu.Lock()
if e, ok := c.items[key]; ok {
c.order.MoveToFront(e)
data := append([]byte(nil), e.Value.(thumbnailCacheEntry).data...)
c.mu.Unlock()
return data, nil
}
if f, ok := c.flights[key]; ok {
c.mu.Unlock()
<-f.done
return append([]byte(nil), f.data...), f.err
}
f := &thumbnailFlight{done: make(chan struct{})}
c.flights[key] = f
c.mu.Unlock()
data, err := fn()
if err == nil && len(data) > 0 && int64(len(data)) <= c.maxBytes {
c.mu.Lock()
c.putLocked(key, data)
f.data = append([]byte(nil), data...)
f.err = err
delete(c.flights, key)
close(f.done)
c.mu.Unlock()
return data, nil
}
c.mu.Lock()
f.err = err
delete(c.flights, key)
close(f.done)
c.mu.Unlock()
return data, err
}
func (c *thumbnailCache) putLocked(key thumbnailCacheKey, data []byte) {
if e, ok := c.items[key]; ok {
old := e.Value.(thumbnailCacheEntry)
c.bytes -= int64(len(old.data))
e.Value = thumbnailCacheEntry{key: key, data: append([]byte(nil), data...)}
c.bytes += int64(len(data))
c.order.MoveToFront(e)
} else {
e := c.order.PushFront(thumbnailCacheEntry{key: key, data: append([]byte(nil), data...)})
c.items[key] = e
c.bytes += int64(len(data))
}
for c.bytes > c.maxBytes || c.order.Len() > c.maxItems {
e := c.order.Back()
if e == nil {
break
}
entry := e.Value.(thumbnailCacheEntry)
delete(c.items, entry.key)
c.bytes -= int64(len(entry.data))
c.order.Remove(e)
}
}