fix(photos): faster large-album loading and album cover lifecycle
Large albums opened slowly and flickered; the default album cover was also lost in the media-objects refactor. Large-album loading: - quantize signed object URLs within a window and send Cache-Control so previews are reused instead of re-fetched - publish photo.updated (not photo.created) when variants are ready, so clients update in place without reordering the grid - worker concurrency + newest-first variant priority; raise default batch Album cover: - first uploaded photo becomes the cover when none is set - clear the cover when the cover photo is trashed or moved out - "set as album cover" in the photo context menu and actions panel OpenSpec changes: fix-large-album-load, fix-album-cover
This commit is contained in:
@@ -40,9 +40,21 @@ func runWorkerFlow(t *testing.T, pool *pgxpool.Pool, svc *Service, cl storage.Ba
|
||||
}
|
||||
t.Cleanup(func() { cleanupPhoto(t, ctx, pool, cl, p.ID) })
|
||||
|
||||
w := NewWorker(svc, time.Second, 10, time.Minute)
|
||||
sub := svc.hub.SubscribeAlbum(album)
|
||||
defer sub.Close()
|
||||
|
||||
w := NewWorker(svc, time.Second, 10, time.Minute, 2)
|
||||
w.processVariantJobs(ctx)
|
||||
|
||||
select {
|
||||
case ev := <-sub.Events():
|
||||
if ev.Type != "photo.updated" {
|
||||
t.Fatalf("variant-ready event = %q, want photo.updated", ev.Type)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("no variant-ready event published")
|
||||
}
|
||||
|
||||
refreshed, err := svc.store.GetByID(ctx, p.ID, "")
|
||||
if err != nil {
|
||||
t.Fatalf("get refreshed: %v", err)
|
||||
@@ -82,6 +94,52 @@ func runWorkerFlow(t *testing.T, pool *pgxpool.Pool, svc *Service, cl storage.Ba
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerConcurrencyPool(t *testing.T) {
|
||||
w := &Worker{concurrency: 3}
|
||||
ids := []string{"a", "b", "c", "d", "e"}
|
||||
started := make(chan struct{}, len(ids))
|
||||
release := make(chan struct{})
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
w.forEachConcurrent(ids, func(string) {
|
||||
started <- struct{}{}
|
||||
<-release
|
||||
})
|
||||
close(done)
|
||||
}()
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
select {
|
||||
case <-started:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatalf("only %d tasks started concurrently, want 3", i)
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-started:
|
||||
t.Fatalf("more than concurrency tasks started concurrently")
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
|
||||
close(release)
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatalf("pool did not finish")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerConcurrencySequentialAtOne(t *testing.T) {
|
||||
w := &Worker{concurrency: 1}
|
||||
var order []string
|
||||
w.forEachConcurrent([]string{"a", "b", "c"}, func(id string) {
|
||||
order = append(order, id)
|
||||
})
|
||||
if len(order) != 3 || order[0] != "a" || order[1] != "b" || order[2] != "c" {
|
||||
t.Fatalf("sequential order = %v, want [a b c]", order)
|
||||
}
|
||||
}
|
||||
|
||||
func countObjects(t *testing.T, ctx context.Context, pool *pgxpool.Pool, ids []string) int {
|
||||
t.Helper()
|
||||
var n int
|
||||
|
||||
Reference in New Issue
Block a user