feat(assets): throttle concurrent asset requests#8929
Conversation
Public API reportThis PR changes the public API surface (+2 / −0), per the docs' rules (@ignore / @Private / undocumented are excluded). Show API diff+ResourceLoader.get maxConcurrentRequests(): number
+ResourceLoader.set maxConcurrentRequests(value: number)Informational only — this never fails the build. |
Build size reportThis PR changes the size of the minified bundles.
|
There was a problem hiding this comment.
Pull request overview
This PR introduces a concurrency throttle for XHR-based asset/network requests to prevent browsers from rejecting large bursts of simultaneous requests (e.g. net::ERR_INSUFFICIENT_RESOURCES). It implements throttling at the shared HTTP layer and exposes configuration via app.loader.maxConcurrentRequests and an application property.
Changes:
- Add a max-concurrency request throttle in
Http, queuing excess requests and dispatching them as in-flight requests complete. - Expose the throttle as
ResourceLoader#maxConcurrentRequests, and allow configuration viaapplication_properties.maxConcurrentRequests. - Add unit tests covering queuing/dispatch behavior and the “0 = unlimited” mode.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/platform/net/http.js |
Implements request slot accounting, queueing, and pumping logic for concurrent XHR throttling. |
src/framework/handlers/loader.js |
Adds app.loader.maxConcurrentRequests getter/setter to control the shared HTTP throttle. |
src/framework/app-base.js |
Parses maxConcurrentRequests from application properties and applies it to the loader. |
test/platform/net/http.test.mjs |
Adds tests validating request throttling, unlimited mode, and dynamic limit changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
2de0f29 to
dd725da
Compare
Browsers reject requests with net::ERR_INSUFFICIENT_RESOURCES once too many are in flight at once. The engine previously fired every asset request immediately with no cap, so large preloads could fail. Add a concurrency limit at the HTTP layer - the single choke point all resource handlers go through. Requests beyond the limit are queued and dispatched as earlier ones complete. The XHR is still created and returned synchronously; only the .send() call is deferred, so progress/abort behaviour is unchanged. - Http.maxConcurrentRequests (internal): default 128, 0 = unlimited - ResourceLoader#maxConcurrentRequests: public getter/setter (app.loader) - maxConcurrentRequests application property parsed in app-base, mirroring the existing maxAssetRetries
dd725da to
50f2efc
Compare
|
Thanks for the review — addressed all five points in
Full test suite passes (1850) including the new per-request slot-tracking test. @copilot review |
All five fixes look good. The tests pass (4/4) and lint is clean. A few minor observations on the current state:
|
| // start each test from a known, clean throttle state on the shared singleton | ||
| http.maxConcurrentRequests = 128; | ||
| http._activeRequests = 0; | ||
| http._sendQueue.length = 0; |
| // restore defaults so state doesn't leak into other test files | ||
| http.maxConcurrentRequests = 128; | ||
| http._activeRequests = 0; | ||
| http._sendQueue.length = 0; |
| if (!throttled) { | ||
| // unthrottled (0 or Infinity): send everything immediately, with no slot accounting | ||
| while (this._sendQueueHead < this._sendQueue.length) { | ||
| this._sendQueue[this._sendQueueHead++].send(); | ||
| } | ||
| } else { | ||
| // throttled: keep the number of in-flight requests under the limit | ||
| while (this._sendQueueHead < this._sendQueue.length && this._activeRequests < limit) { | ||
| const { xhr, send } = this._sendQueue[this._sendQueueHead++]; | ||
| this._activeRequests++; | ||
| xhr._slotHeld = true; | ||
| send(); | ||
| } | ||
| } |
Adds a limit on the number of concurrent asset requests, so large preloads no longer fail with
net::ERR_INSUFFICIENT_RESOURCES— browsers reject requests once too many are in flight at once (empirically reproducible well below the asset counts some projects preload). Fixes #5258.Changes:
XMLHttpRequestis still created and returned synchronously; only the.send()call is deferred, so progress/abort behaviour is unchanged.0disables throttling (previous behaviour). It is a process-global limit, matching the browser's per-process resource limit, and covers all XHR-based loads (the large majority of assets; the<img>-element texture fallback,<audio>,<script>and bundlefetchpaths are browser-managed and not affected).maxConcurrentRequestsapplication property, parsed alongside the existingmaxAssetRetries.API Changes:
app.loader.maxConcurrentRequests(getter/setter) to read or override the limit at runtime:Related: