fix: cap slow-path search at 3s to prevent 20s+ searches
The cross-referencing phase browses 3 artist discographies via MB API. When the background indexer is also making MB requests, 429 retries with exponential backoff can stack up to 20+ seconds. Added a 3s context.WithTimeout covering both LB popularity and cross-referencing. If LB popularity exhausts the budget, cross-ref is skipped entirely. If cross-ref is running when the deadline hits, the BrowseReleaseGroups calls are cancelled mid-flight. Worst case search time is now ~7s (4s MB search + 3s slow path) instead of unbounded.
This commit is contained in:
@@ -424,14 +424,23 @@ func (e *Service) Search(query string) (*MBSearchResult, error) {
|
||||
e.boostWithIndexPopularity(&result)
|
||||
} else {
|
||||
// Phase 2: LB popularity lookups (3 POST calls, rate-limited).
|
||||
// Use a tight deadline so a slow LB/MB doesn't stall the search.
|
||||
slowCtx, slowCancel := context.WithTimeout(e.ctx, searchSlowPathTimeout)
|
||||
|
||||
lbStart := time.Now()
|
||||
e.boostWithPopularity(&result)
|
||||
lbDur := time.Since(lbStart)
|
||||
|
||||
// Phase 3: cross-reference artist discographies.
|
||||
// Skip if the slow-path budget is already exhausted.
|
||||
xrefStart := time.Now()
|
||||
e.crossReferenceAlbums(query, &result)
|
||||
|
||||
if slowCtx.Err() == nil {
|
||||
e.crossReferenceAlbums(slowCtx, query, &result)
|
||||
}
|
||||
|
||||
xrefDur := time.Since(xrefStart)
|
||||
slowCancel()
|
||||
|
||||
e.logger.Info("search slow path breakdown",
|
||||
"query", query,
|
||||
@@ -489,7 +498,7 @@ const (
|
||||
// Matched albums not already in result.ReleaseGroups are injected
|
||||
// at the front. This handles queries like "for you tatsuro"
|
||||
// where MB text search can't associate the title with the artist.
|
||||
func (e *Service) crossReferenceAlbums(query string, result *MBSearchResult) {
|
||||
func (e *Service) crossReferenceAlbums(ctx context.Context, query string, result *MBSearchResult) {
|
||||
if len(result.Artists) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -526,7 +535,7 @@ func (e *Service) crossReferenceAlbums(query string, result *MBSearchResult) {
|
||||
go func(a MBArtist) {
|
||||
defer wg.Done()
|
||||
|
||||
rgs, err := e.mb.BrowseReleaseGroups(e.ctx, a.MBID)
|
||||
rgs, err := e.mb.BrowseReleaseGroups(ctx, a.MBID)
|
||||
if err != nil {
|
||||
e.logger.Warn("cross-reference browse failed",
|
||||
"artist", a.Name,
|
||||
@@ -830,6 +839,14 @@ const (
|
||||
// results degrade to index-only rather than blocking the user.
|
||||
searchMBTimeout = 4 * time.Second
|
||||
|
||||
// searchSlowPathTimeout caps the total time spent on the slow
|
||||
// path (LB popularity + cross-referencing). When the index
|
||||
// isn't ready, these API calls can stack up — especially
|
||||
// cross-referencing, which browses 3 artist discographies via
|
||||
// MB and can hit 429 retries. The timeout ensures search
|
||||
// returns within a reasonable window.
|
||||
searchSlowPathTimeout = 3 * time.Second
|
||||
|
||||
// maxResults caps each entity slice after filtering.
|
||||
maxResults = 15
|
||||
|
||||
|
||||
Reference in New Issue
Block a user