You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This PR adds an experimental, opt-in ThroughputAutoscaledPool that looks for the concurrency at which the crawl finishes the most tasks, and an autoscaled_pool_class option on every crawler to select it. The default stays AutoscaledPool, unchanged.
The pool helps whenever tasks slow down under load for a reason outside the machine. The target host is the usual one, but the network, proxies, and the crawler's own parts, such as storage speed, slow tasks down the same way.
How the pool decides:
It reads throughput, finished tasks per second by Little's law, from windows of at least 10 tasks once 80% of a window has finished.
It doubles the concurrency while each doubling delivers at least 25% more. Then it measures a level above and a level below the current concurrency and moves toward the one that delivers at least 10% more. When the two tie at the narrowest spread, it holds for six windows and then measures again.
A window is given up early only when even its best possible reading already loses.
All CPU, memory and event loop checks of AutoscaledPool still apply. If the machine reports an overload right after a raise, before the raised level has been measured, the raise is undone.
It learns the peak from finished tasks, so it pays off best on long-running crawls.
Known limitations:
Each step waits for its tasks to finish, so on slow pages it ramps up more slowly than AutoscaledPool.
Every finished task counts as work, so a site that answers fast with errors such as HTTP 429 looks like it can take more.
It looks for one concurrency for the whole crawl, which is unstable across many hosts with different peaks.
With max_tasks_per_minute it settles at whatever rate the limit lets through, so a fixed rate belongs with the default pool.
Hi @Mantisus, thanks for looking into this. On the JS side, we decided to extract the IConcurrencySystem from the AutoscaledPool and make it "pluggable" into your crawler. Allowing passing in autoscaled_pool_class introduces a parity gap.
Could you please look into IConcurrencySystem and see if you can bend the design of your PR to be in line with that?
Could you please look into IConcurrencySystem and see if you can bend the design of your PR to be in line with that?
I've created an issue for a small change to the IConcurrencySystem interface: apify/crawlee#4127
As for this PR, I agree that it doesn't make sense to add autoscaled_pool_class as a temporary option before refactoring AutoscaledPool. One question, though: can we do that refactoring in v1, since _autoscaling is a private module (even though AutoscaledPool shows up in the API docs), or should we postpone it, together with this PR, until v2? @vdusek, what do you think?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
ThroughputAutoscaledPoolthat looks for the concurrency at which the crawl finishes the most tasks, and anautoscaled_pool_classoption on every crawler to select it. The default staysAutoscaledPool, unchanged.How the pool decides:
AutoscaledPoolstill apply. If the machine reports an overload right after a raise, before the raised level has been measured, the raise is undone.Known limitations:
AutoscaledPool.max_tasks_per_minuteit settles at whatever rate the limit lets through, so a fixed rate belongs with the default pool.Issues
AutoscaledPoolcontroller does not converge to optimaldesired_concurency#1224Testing
_autoscale()and_worker_task()with a fake clock and assert ondesired_concurrency.✍️ Drafted by Claude Code