-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Fix: Rate limit exceeds for Musicbrainztask. #10822
Conversation
src/network/webtask.h
Outdated
@@ -150,6 +150,8 @@ class WebTask : public NetworkTask { | |||
Idle, | |||
// Pending state | |||
Pending, | |||
// Looping state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new state does not belong to the low-level network task. WebTask
has no notion of retries and doesn't need this state for implementing its internal state machine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does NOT control retries and the state IS used in WebTask itself.
It controls the new state where the WebTask is in the idle loop between two requests. Before there was no need for this state because the new request was issued just after the old one. So the solution looks like a valid one.
I am not saying this is the only possible solution, so if you like to see another solution for this Bugfix. Please give some hints how.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A task models the lifecycle of a single request. It could be restarted while not pending. Retries have to be controlled by the calling code.
Introducing a Trojan Horse into the base class just because some derived class wants to do implicit retries is the wrong approach.
I strongly recommend not to merge this PR. It introduces unneeded complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A task models the lifecycle of a single request.
Sorry, that is not correct. You may have designed it like that original, but since MusicBrainzRecordingsTask that inherits from WebTask works with a couple of request this no longer the case. See: MusicBrainzRecordingsTask::continueWithNextRequest();
Retries have to be controlled by the calling code.
Again, this change has nothing to do with reties.
This PR is a valid solution for a real issue, maybe not perfect. So it would be helpful to suggest improvements instead of to title it with striking words.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I overlooked that. Long ago. I was confused by the wording "Looping" which is used out of context and has no meaning in the context of WebTask
. This misunderstanding also reveals why adding this logic to WebTask
would be wrong.
continueWithNextRequest()
is undefined, probably a left over, please delete it.
I suggest to use a local timer in MusicBrainzRecordingsTask and invoke the next slotStart() with a delay. You could even override slotStart()/slotAbort() locally to detect a pending timer if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still disagree. It's a move in the wrong direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which code duplication? It would just be a shallow wrapper around the base class slots that handles the pending timer. It is actually an supervision or overlay state and reflects the target design for a future refactoring, i.e. when extracting the looping into an orchestration task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you really want to implement a throttling between subsequent requests then you could implement it entirely in the base class by adding a configuration parameter. But I don't recommend it, because the base class with its state machine is already complex.
The distributed approach introduced by this PR is hard to maintain and not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which code duplication?
slotStart() and slotAbort()
It is actually an supervision or overlay state and reflects the target design for a future refactoring
The additional state is a much more clean and maintainable solution. Less code duplication and a clear distinguished state from the other states that actually exists. I am pretty confident with the current solution and do not plan a future refactoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish you luck.
} | ||
|
||
// Continue with next recording id | ||
DEBUG_ASSERT(!m_queuedRecordingIds.isEmpty()); | ||
auto inhibitTimerElapsed = m_measurementTimer.elapsed(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could create multiple tasks performing concurrent requests. Adding an isolated and independent inhibit timer for each instance doesn't make any sense at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must not perform concurrent requests. Only one per second is allowed. Using multiple tasks for this is out of scope of this bugfix PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...then implement the throttling locally in the convoluted MusicBrainzRecordingsTask class and keep the base and all other classes clean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sound like a giant scope creep. No interest for this simple bugfix.
@daschuer There is a cleaner, more robust and more flexible solution for what this PR tries to achieve. One of my proposals, a thoughtful and careful extension of the API and state machine that doesn't affect any other, existing classes. q.e.d. But since decisions are now made behind the scenes... let's forget about that. Your project, your choice. Copy that. |
@uklotzde Without outlining the solution you have in mind, I am not able to compare that to the current solution.
What is you proposal? Our proposal is here, proved working and with a scope suitable for a bug-fix release. You have even pointed out issues explicit.
Why do you think that. All review comments are public. There is now hidden discussion in this case. |
@daschuer You have clearly communicated that you already made decisions (not hidden, but not in the main repo and out of sight), that you are not interested in any alternative proposals, or you rejected them with unjustified arguments.
I could easily debunk those statements with working code. |
@fatihemreyildiz I have verified whether the states are reasonable and came up with this drawing: It turns out that there are indeed some issue with the state changes. Can you fix them? We must not go into the Idle state in slotStart() here, because we start and go not to idle. If it fails an error state is appropriated. We must not go to state Pending if we have no network manager here: Line 189 in 669d451
I think we can immediately move to the failed state here. The assertion in onNetworkError() needs to be adjusted accordingly. The idle state need to be renamed to Inital. Because there is no way to get into the idle state again. Once the Task has been started. |
@daschuer I can't face this any longer. This is how a proper solution would look like: Update: Renaming the |
Can you please create a PR for this? This way we can use the normal review process. I am open to merge your PR as long as the big is fixed finally and we get out of the deadlock situation here. |
This patch is a gift by me to the Mixxx project. I have deleted my forked repo for a reason and don't plan to revive it under the current circumstances. |
I've compared both approaches (this PR and the patch offered by @uklotzde) and I conclude that the latter one is simpler and cleaner. As pointed out previously this PR pollutes WebTask with implicit retry logic which is only used to by MusicBrainz really. Instead of making this a hidden feature, the better solution is defer a task during its creation. Here's the patch in PR form: #10836 |
I think this one is superseded by #10875 |
You are welcome. I am thankful for all the guidance along with the help you've did for me to trying to solve this bug and also the 404 issue. I've learned a lot meanwhile such as the Activity Diagram 'Swimlane' and UML State charts. No need to be sorry to be honest. I'm happy that the bug is fixed. And that is all it matters IMHO 🐙 |
This PR aims to fix bug #10795 Rate Limit exceeds for some songs while Importing Metadata From Musicbrainz.