Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Spotify 429 (too many requests) API error handling #4370

Merged
merged 12 commits into from
Jun 19, 2022

Conversation

arsaboo
Copy link
Contributor

@arsaboo arsaboo commented Jun 12, 2022

Description

Added ability to handle API error (too many requests).

Fixes #4369

To Do

  • Documentation. (If you've add a new command-line flag, for example, find the appropriate page under docs/ to describe it.)
  • Changelog. (Add an entry to docs/changelog.rst near the top of the document.)
  • Tests. (Encouraged but not strictly required.)

beetsplug/spotify.py Outdated Show resolved Hide resolved
beetsplug/spotify.py Outdated Show resolved Hide resolved
Copy link
Member

@sampsyo sampsyo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely a step in the right direction! Handling the error and retrying seems much better than crashing when we exceed the rate-limit.

Can we remove the other time.sleep call here with this in place?

time.sleep(.5)

As I mentioned in #4369 (comment), however, I think it's possible to do even better (perhaps in a subsequent PR?). The rough idea would be to explicitly account for Spotify's 30-second rolling window on the client side by counting up the number of requests we've made recently and delaying only ones that exceed the limit. But like I said above, I this reactive-rather-than-proactive approach in this PR is a great 80% solution.

elif response.status_code == 429:
seconds = response.headers['Retry-After']
time.sleep(int(seconds))
self._log.info('Too many API requests. Retrying after {} \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think log messages about retrying, etc., should be debug-level rather than info-level messages: they are not "actionable" from the user's perspective and are kind of an internal implementation detail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought was to keep the user informed about the delay. It will also be nice to get those reports if users see those messages so that we can adjust the timeout. In any case, I have changed it to debug.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this changed back to info?

@arsaboo
Copy link
Contributor Author

arsaboo commented Jun 13, 2022

Can we remove the other time.sleep call here with this in place?

I think we can, but it may generate frequent 429 errors. We can reduce the sleep to 0.1 from 0.5 for now. I can test things out after this PR is merged and then maybe we can remove it entirely. Let me know what you think.

@sampsyo
Copy link
Member

sampsyo commented Jun 16, 2022

Looking better and better! But I guess I would like to ask a question about this:

I think we can, but it may generate frequent 429 errors.

Have you observed that this happens? It might be a really useful experiment to run: what does the plugin behave like (how many errors does it see) if there is no extra sleep?

I ask for two reasons: first, intuitively, it seems like the 429 error's "please back off by N seconds" response could, in fact, work to avoid repeating the error too many times. That is, we'd inevitably get one such error after doing many tracks, but then things would settle down after the error tells us how long to sleep for. Second, reading that aforelinked Spotify API documentation page made it sound like following the 429 errors was the recommended way to do rate limiting. It doesn't even say what the rate limits are, which would be critical to avoiding 429 errors! So maybe this the technique we're supposed to use?

@arsaboo
Copy link
Contributor Author

arsaboo commented Jun 16, 2022

I did receive 429 with the other (now closed) PR (#4102 ). While Spotify does not explicitly specify the limits, I read that it should be able to handle 2000 calls per hour (so slightly less than 1 call per second).

However, as you suggested, using the retry-after should be able to handle those cases. Thus, I have removed the sleep for now. We can add it back later if required. I also changed the log back to info from debug. Otherwise, users may not know why the process is not updating - it will be stuck at:

spotify: Processing 1/10 tracks - Amitabh Bachchan - 102 Not out - Waqt Ne Kiya Kya Haseen Sitam

@sampsyo
Copy link
Member

sampsyo commented Jun 16, 2022

Great!! Just out of curiosity, have you gotten a chance to try it out on a large-ish number of files to see how well/poorly it performs?

@arsaboo
Copy link
Contributor Author

arsaboo commented Jun 16, 2022

So far, I have not hit 429 (but that may be because I don't have my entire library on my test setup). Once we have this merged and hopefully released, I can test it out extensively. I have other features in mind for Spotify and Plex integration, so I will be sure to fix any issues that may come up :)

Copy link
Member

@sampsyo sampsyo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great!! Just one last suggestion within.

elif response.status_code == 429:
seconds = response.headers['Retry-After']
time.sleep(int(seconds))
self._log.info('Too many API requests. Retrying after {} \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this changed back to info?

Comment on lines 35 to 36
* Added Spotify 429 (too many requests) API error handling
:bug:`4370`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should also say the bottom line for what the user can expect here: namely, we now respect the Spotify API's rate limiting, which avoids crashing when the API reports code 429 (too many requests).

Copy link
Contributor Author

@arsaboo arsaboo Jun 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it back to info so that the users are not guessing. See my argument here.

Copy link
Contributor Author

@arsaboo arsaboo Jun 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed logging back to debug.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!!! I do think this is the right thing. The philosophy is that now, with this new strategy, 429 responses are part of the normal operation of the command—it's how we're enforcing the rate limit logic. So there's nothing actionable for the user to interpret when these errors occur.

Copy link
Member

@sampsyo sampsyo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wahoo! Thank you for working on this!!

@sampsyo sampsyo merged commit b1ceee6 into beetbox:master Jun 19, 2022
@arsaboo arsaboo deleted the rate_limit branch June 20, 2022 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add the ability to handle error 429 (too many requests) in Spotify plugin
3 participants