Skip to content

Download missing dist archives in parallel - #1233

Open
andreaskienast wants to merge 1 commit into
composer:mainfrom
andreaskienast:parallel-dist-downloads
Open

andreaskienast wants to merge 1 commit into
composer:mainfrom
andreaskienast:parallel-dist-downloads

Conversation

@andreaskienast

@andreaskienast andreaskienast commented Oct 28, 2026 •

Copy link
Copy Markdown

With rearchive disabled, dist archives of type zip or tar are downloaded as is. Each download was awaited before the next one started, so a build creating thousands of archives spent most of its time waiting for single HTTP requests.

ArchiveBuilder::dump() now starts the downloads of all missing dist archives up front and waits for them together, letting Composer's HttpDownloader run them concurrently (12 by default, configurable via COMPOSER_MAX_PARALLEL_HTTP). The archive loop then finds the files in place. Failed downloads are ignored at this stage and retried by the loop, so error handling and --skip-errors behave as before.

The path and download logic of archive() is extracted into helper methods shared by both steps.

ArchiveBuilder uses react/promise natively, therefore it is added as a direct dependency.

The benchmark was executed on Apple M3 Pro, 36 GB RAM, SSD with APFS, and PHP 8.5.12 installed. Each run downloaded 700 packages.

┌───────────────────────┬─────────┬─────────┬───────────────┬────────────────────────────┬─────────┐
│          Run          │  Total  │  Scan   │ Archive phase │ Of which parallel download │ Max RSS │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ Without patch, run 1  │ 798.2 s │ 187.0 s │       611.1 s │                          – │  241 MB │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ Without patch, run 2  │ 746.0 s │ 183.6 s │       562.3 s │                          – │  250 MB │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ Without patch, run 3  │ 723.5 s │ 181.2 s │       542.1 s │                          – │  250 MB │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ With patch, run 1     │ 256.7 s │ 182.8 s │        73.7 s │                     48.3 s │  247 MB │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ With patch, run 2     │ 255.7 s │ 183.2 s │        72.4 s │                     53.5 s │  264 MB │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ With patch, run 3     │ 245.6 s │ 181.8 s │        63.6 s │                     43.4 s │  264 MB │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ Average without patch │ 755.9 s │ 183.9 s │       571.8 s │                          – │  247 MB │
├───────────────────────┼─────────┼─────────┼───────────────┼────────────────────────────┼─────────┤
│ Average with patch    │ 252.7 s │ 182.6 s │        69.9 s │                     48.4 s │  258 MB │
└───────────────────────┴─────────┴─────────┴───────────────┴────────────────────────────┴─────────┘

With `rearchive` disabled, dist archives of type zip or tar are
downloaded as is. Each download was awaited before the next one
started, so a build creating thousands of archives spent most of its
time waiting for single HTTP requests.

`ArchiveBuilder::dump()` now starts the downloads of all missing dist
archives up front and waits for them together, letting Composer's
`HttpDownloader` run them concurrently (12 by default, configurable via
`COMPOSER_MAX_PARALLEL_HTTP`). The archive loop then finds the files in
place. Failed downloads are ignored at this stage and retried by the
loop, so error handling and `--skip-errors` behave as before.

The path and download logic of `archive()` is extracted into helper
methods shared by both steps.

`ArchiveBuilder` uses `react/promise` natively, therefore it is added as
a direct dependency.
@andreaskienast
andreaskienast force-pushed the parallel-dist-downloads branch from 438c8e5 to 9b41b2f Compare October 28, 2026 08:36

This branch has not been deployed

No deployments
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.

1 participant