perf(web): probe datastore hosts concurrently in SeedExtraDevices (refs #451)

SeedExtraDevices probed each datastore host serially via AddDeviceByHost,
whose /info call blocks up to its 10 s timeout for an unknown host. With
offline speakers in the datastore, a re-sync (e.g. the admin page's
discovery sweep on load, or the periodic discovery) stalled for 10 s per
offline device, one after another.

Fan the per-host probes out across goroutines and wait for all of them,
so the seed costs roughly a single timeout regardless of how many devices
are offline. AddDeviceByHost is already registry-safe under concurrency
(covered by TestRegistryConcurrent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tobias Gesellchen
2026-06-07 15:08:26 +02:00
co-authored by Claude Opus 4.8
parent 86878cd23b
commit 09113afd87
+18 -1
View File
@@ -3,6 +3,7 @@ package soundtouchweb
import (
"context"
"log"
"sync"
"time"
"github.com/gesellix/bose-soundtouch/pkg/client"
@@ -95,18 +96,34 @@ func (app *WebApp) AddDeviceByHost(host string, port int, source string) {
// (if set) via AddDeviceByHost. Idempotent: already-known hosts are skipped.
// Used by the embedded build to surface the service datastore's devices even
// when network discovery is disabled; a no-op for standalone soundtouch-web.
//
// Hosts are probed concurrently: AddDeviceByHost makes a blocking /info call
// (up to its 10 s timeout) for each unknown host, so an offline speaker in the
// datastore would otherwise stall the whole seed for 10 s, serially. Fanning
// out bounds the cost to roughly a single timeout regardless of how many
// devices are offline. AddDeviceByHost is registry-safe under concurrency.
func (app *WebApp) SeedExtraDevices() {
if app.ExtraDeviceHosts == nil {
return
}
var wg sync.WaitGroup
for _, host := range app.ExtraDeviceHosts() {
if host == "" {
continue
}
app.AddDeviceByHost(host, 8090, "service-store")
wg.Add(1)
go func(h string) {
defer wg.Done()
app.AddDeviceByHost(h, 8090, "service-store")
}(host)
}
wg.Wait()
}
// DiscoverDevices refreshes the device registry. When TriggerDiscovery is set