Pace IGDB requests; rewrite the new help text
IGDB allows 4 requests a second, and an import searching three entries at once (plus detail lookups) went over it. Every IGDB request now waits at least 300 ms after the last one, and a 429 is retried after 1, 2 and 4 seconds before giving up. The import dialog, ranking editor and their error messages are reworded in plainer language, and talk about "spots" instead of "places". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
62fcf6b45d
commit
1f3fa4fb2b
7 files changed
+60
-37
No files matched your search
@@ -99,11 +99,15 @@ func (g *igdb) query(ctx context.Context, body string) ([]Result, error) {
|
||||
Name string `json:"name"`
|
||||
} `json:"platforms"`
|
||||
}
|
||||
for attempt := 0; ; attempt++ {
|
||||
tok, err := g.token(ctx, attempt > 0)
|
||||
refreshed, backoff := false, time.Second
|
||||
for {
|
||||
tok, err := g.token(ctx, refreshed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := g.r.igdbPace.wait(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.igdb.com/v4/games", strings.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -112,7 +116,19 @@ func (g *igdb) query(ctx context.Context, body string) ([]Result, error) {
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
err = g.r.do(req, &games)
|
||||
if isStatus(err, 401) && attempt == 0 {
|
||||
if isStatus(err, 401) && !refreshed {
|
||||
refreshed = true
|
||||
continue
|
||||
}
|
||||
// Rate limited anyway (another app on the same key, say): back off
|
||||
// and try again, up to about 7 seconds in all.
|
||||
if isStatus(err, 429) && backoff <= 4*time.Second {
|
||||
select {
|
||||
case <-time.After(backoff):
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
backoff *= 2
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -74,15 +74,19 @@ type Registry struct {
|
||||
tvdb *tvdbTokens
|
||||
igdb *igdbTokens
|
||||
mb *throttle
|
||||
// IGDB allows 4 requests a second; stay under it so an import's
|
||||
// searches and detail lookups don't get answered with 429.
|
||||
igdbPace *throttle
|
||||
}
|
||||
|
||||
func NewRegistry(creds func(context.Context) Credentials) *Registry {
|
||||
return &Registry{
|
||||
creds: creds,
|
||||
client: &http.Client{Timeout: 20 * time.Second},
|
||||
tvdb: &tvdbTokens{},
|
||||
igdb: &igdbTokens{},
|
||||
mb: &throttle{gap: 1100 * time.Millisecond},
|
||||
creds: creds,
|
||||
client: &http.Client{Timeout: 20 * time.Second},
|
||||
tvdb: &tvdbTokens{},
|
||||
igdb: &igdbTokens{},
|
||||
mb: &throttle{gap: 1100 * time.Millisecond},
|
||||
igdbPace: &throttle{gap: 300 * time.Millisecond},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ func (s *Store) UpdateList(ctx context.Context, l List) (List, error) {
|
||||
if last, err := s.MaxRank(ctx, l.ID); err != nil {
|
||||
return l, err
|
||||
} else if l.MaxItems < last {
|
||||
return l, invalid("An entry sits at #%d; move it up or close the gaps before shrinking the list to %d", last, l.MaxItems)
|
||||
return l, invalid("Something's at #%d, so the list can't be smaller than that. Move it up or close the gaps first.", last)
|
||||
}
|
||||
if l.Category != cur.Category && cur.ItemCount > 0 {
|
||||
return l, invalid("Can't change the category of a list that has items")
|
||||
|
||||
@@ -67,7 +67,7 @@ func (p places) makeRoom(at, limit int) error {
|
||||
gap++
|
||||
}
|
||||
if gap > limit {
|
||||
return invalid("No room: this list ends at #%d. Raise its size in List settings, or remove an empty place first.", limit)
|
||||
return invalid("There's no room left. This list stops at #%d; make it bigger in List settings or close up an open spot.", limit)
|
||||
}
|
||||
p.shift(at, gap-1, +1)
|
||||
return nil
|
||||
@@ -187,7 +187,7 @@ func (s *Store) beginPlaces(ctx context.Context, listID int64) (*placeTx, error)
|
||||
// write saves the places that changed, without committing.
|
||||
func (pt *placeTx) write(ctx context.Context) error {
|
||||
if last := pt.p.last(); last > pt.limit {
|
||||
return invalid("No room: this list ends at #%d. Raise its size in List settings.", pt.limit)
|
||||
return invalid("There's no room left. This list stops at #%d; make it bigger in List settings.", pt.limit)
|
||||
}
|
||||
stmt, err := pt.tx.PrepareContext(ctx, `UPDATE items SET rank = ? WHERE id = ?`)
|
||||
if err != nil {
|
||||
@@ -241,7 +241,7 @@ func (pt *placeTx) list() []Place {
|
||||
|
||||
func (pt *placeTx) checkPlace(at int) error {
|
||||
if at < 1 || at > pt.limit {
|
||||
return invalid("Pick a place from #1 to #%d.", pt.limit)
|
||||
return invalid("Pick a number from 1 to %d.", pt.limit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -310,7 +310,7 @@ func (s *Store) RemovePlace(ctx context.Context, listID int64, at, count int) ([
|
||||
count = max(count, 1)
|
||||
for r := at; r < at+count; r++ {
|
||||
if _, taken := pt.p[r]; taken {
|
||||
return nil, invalid("#%d isn't empty.", r)
|
||||
return nil, invalid("#%d isn't open.", r)
|
||||
}
|
||||
}
|
||||
for range count {
|
||||
|
||||
Reference in new issue
Block a user