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

Tagfetcherratelimit #9

Conversation

daschuer
Copy link

This is the second part of the PR taken from mixxxdj#10822

It calculates the time from the start of the request instead of the time in between.
It also cleans up the state chart and fixes the stalled Window on case of client side timeout.

Idle,
Initial,
Copy link
Owner

Choose a reason for hiding this comment

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

IMO the renaming only makes the state machine more difficult to understand, but I'm not gonna start bikeshedding over a name.

Comment on lines -173 to +194
slotStart(m_parentTimeoutMillis, kRateLimitMillis);
auto timerSinceLastRequest = m_measurementTimer.elapsed(true);
int timerSinceLastRequestMillis = timerSinceLastRequest.toIntegerMillis();
qDebug() << "Task took:" << timerSinceLastRequestMillis;
int delayMillis = kMinTimeBetweenMbRequests - timerSinceLastRequestMillis;
if (delayMillis <= 0) {
qDebug() << "Task took more than a second, slot is calling now.";
slotStart(m_parentTimeoutMillis, 0);
} else {
qDebug() << "Task took less than a second, slot is going to be called in:" << delayMillis;
slotStart(m_parentTimeoutMillis, delayMillis);
}
Copy link
Owner

Choose a reason for hiding this comment

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

is this extra complexity really required? What does advantage does it have?
from what I can tell, delayMillis will always be positive because each task will take longer than a second. Also calling slotStart with 0 delay will cause the request to be fired shortly after the first one, resulting in more than one request per second.

Copy link
Author

Choose a reason for hiding this comment

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

Imagine the a request itself takes 500 ms. Without it, we will have one request each 1500 ms and not as desired one on 1000 ms. The timer is started right after the wait time is over, just before the actual request is done in doStartNetworkRequest();

Copy link
Owner

Choose a reason for hiding this comment

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

Well how strict the the musicbrainz rate-limiting? If request A arrived at MB after 550ms (bad network routing, flaky connection, whatever) but then comes back after 50ms and request B only took 50ms per direction, the time between the two requests arriving at MB would only be 500ms.

Copy link
Author

Choose a reason for hiding this comment

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

Two consecutive request do not triggered the rate penalty. My guess is actually for this reason.
@fatihemreyildiz can you confirm that?

Choose a reason for hiding this comment

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

Yes, I can confirm that @daschuer 👍 .

I have tried it many times with the different tracks. After this fix, I've only hit the rate limit once (I think that is because of the global rate limit (300 requests per second) stated on the MB API doc ) besides that I've never hit the rate limit. If this happens, it can be easily fixed by a retry button replaced on the new GUI.

Since I played with the tag fetcher a lot. I also want to add few things that I noticed. If a track has about 8-9 recording IDs they might hit the rate limit time to time in my case (If someone has a way better connection than I have, this amount of recording IDs might be lower). After 10 recording IDs, I always hit the rate limit, as I said this can be different in your case.

One of my rough test result was like this:

  • Abba - Super Trouper -> Has 5 recording IDs normally took 4.78 seconds - after fix took 6.40 seconds
  • Axwell Ingrosso - MTYK -> Has 6 recording IDs normally took 4.30 seconds - after fix took 6.81 seconds
  • DVBBS - Tsunami -> Has 12 recording IDs normally failed in 8:36 seconds - after fix took 18.15 seconds.

In this case with the delayMillis the difference is not too much for the tracks which has less recording IDs. If we delay the every request by 1000ms that would be easily noticeable for popular tracks.

Copy link
Owner

Choose a reason for hiding this comment

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

fine. If this avoids a failure we can keep it.

@Swiftb0y
Copy link
Owner

Swiftb0y commented Aug 30, 2022

Also, alternatively we could ask the musicbrainz folks if they can make a special rule for us (just as they do for beets for example). Having to do requests in serial which we could technically do in parallel results in much worse UX...

@daschuer
Copy link
Author

We already request the releases in chunks of ~5. The wait time happens only if you have a recording with many releases like Abba or other popular tracks like that. So it is effordable.

Before we request an extra rule, it is a good idea to not ignore the current rule.

@fatihemreyildiz
Copy link

Before we request an extra rule, it is a good idea to not ignore the current rule.

I totally agree with that, I was thinking to send a detailed message about our situation. Then I wanted to mention that delaying our requests might be bad in user experience. For that I was waiting fix PR to be merged.

Comment on lines -144 to -145
if (m_state == State::Aborted) {
emitAborted(responseWithContent.requestUrl());
Copy link
Owner

Choose a reason for hiding this comment

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

The state is still aborted, why remove the emitAborted signal?

Copy link
Author

Choose a reason for hiding this comment

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

This condition can no longer be true because we have removed m_state = State::Aborted; above.

The original issue was that emitAborted() is designed to be the responds to manual user Abort(). There is no change in the GUI. Now we emitNetworkError() in the client timeout case as well.

Copy link
Owner

Choose a reason for hiding this comment

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

So does the Abort state have any purpose then? Can we remove it?

Copy link
Author

Choose a reason for hiding this comment

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

We need it to change the state from any other state when calling slotAbort()

However we have some other places where State::Abort is used in responds of an error. I will do some checks if this even works as desired.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

@Swiftb0y Swiftb0y merged commit 95d9d3e into Swiftb0y:uklotzde-musicbrainz-ratelimit-patch Sep 5, 2022
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.

3 participants