perf: cache generated image thumbnails
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestThumbnailCacheDeduplicatesConcurrentBuilds(t *testing.T) {
|
||||
c := newThumbnailCache()
|
||||
key := thumbnailCacheKey{Path: "photo.jpg", Size: 10, ModTime: 20}
|
||||
var builds atomic.Int32
|
||||
const workers = 24
|
||||
var wg sync.WaitGroup
|
||||
results := make([][]byte, workers)
|
||||
errs := make([]error, workers)
|
||||
wg.Add(workers)
|
||||
for i := 0; i < workers; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
results[i], errs[i] = c.Do(key, func() ([]byte, error) {
|
||||
builds.Add(1)
|
||||
return []byte("thumbnail"), nil
|
||||
})
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if builds.Load() != 1 {
|
||||
t.Fatalf("thumbnail builder ran %d times, want 1", builds.Load())
|
||||
}
|
||||
for i := range results {
|
||||
if errs[i] != nil || string(results[i]) != "thumbnail" {
|
||||
t.Fatalf("worker %d got result %q, err=%v", i, results[i], errs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestThumbnailCacheDoesNotCacheErrors(t *testing.T) {
|
||||
c := newThumbnailCache()
|
||||
key := thumbnailCacheKey{Path: "broken.jpg"}
|
||||
wantErr := errors.New("decode failed")
|
||||
var builds atomic.Int32
|
||||
for i := 0; i < 2; i++ {
|
||||
_, err := c.Do(key, func() ([]byte, error) {
|
||||
builds.Add(1)
|
||||
return nil, wantErr
|
||||
})
|
||||
if !errors.Is(err, wantErr) {
|
||||
t.Fatalf("got err %v, want %v", err, wantErr)
|
||||
}
|
||||
}
|
||||
if builds.Load() != 2 {
|
||||
t.Fatalf("error result was cached; builder ran %d times", builds.Load())
|
||||
}
|
||||
}
|
||||
|
||||
func TestThumbnailCacheEvictsByBound(t *testing.T) {
|
||||
c := newThumbnailCache()
|
||||
c.maxItems = 2
|
||||
for i := 0; i < 3; i++ {
|
||||
key := thumbnailCacheKey{Path: string(rune('a' + i))}
|
||||
if _, err := c.Do(key, func() ([]byte, error) { return []byte("x"), nil }); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if len(c.items) != 2 || c.order.Len() != 2 {
|
||||
t.Fatalf("cache size=%d/list=%d, want 2/2", len(c.items), c.order.Len())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user