Skip to content

Commit fdfc784

Browse files
Reworks the worker pool to be more configurable (#5764)
**What's the problem this PR addresses?** This PR extends @anuragkalia's work in #5636 (it starts from the same commits and builds upon them). - The original implementation was keeping a singleton worker pool - I felt this was problematic since we may enter situations (even if only in tests) where multiple calls to `convertToZip` would want to use different pools. - Converting an archive without using a worker was only possible when the concurrency was completely disabled. This was my idea, but retrospectively it felt better to me to have two settings: one for the concurrency, and another to define whether the concurrency is enabled or not. - Passing all those settings from the various fetchers would have been unwieldly. **How did you fix it?** - I created a `TaskPool` interface; `WorkerPool` implements it (using workers, same implementation as before), but so does `AsyncPool` (a new implementation that simply forwards the call to the `pLimit` limiter). - I feel like this also addresses @merceyz's comment regarding the setting name - it's now called `taskPoolConcurrency`, which doesn't directly refers to workers. - A `getConfigurationWorker` function returns the proper task pool for the given configuration object. To make that possible, WeakMap instances can now be used as storage argument for the `get*WithDefault` family of functions. **Checklist** <!--- Don't worry if you miss something, chores are automatically tested. --> <!--- This checklist exists to help you remember doing the chores when you submit a PR. --> <!--- Put an `x` in all the boxes that apply. --> - [x] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). <!-- See https://yarnpkg.com/advanced/contributing#preparing-your-pr-to-be-released for more details. --> <!-- Check with `yarn version check` and fix with `yarn version check -i` --> - [x] I have set the packages that need to be released for my changes to be effective. <!-- The "Testing chores" workflow validates that your PR follows our guidelines. --> <!-- If it doesn't pass, click on it to see details as to what your PR might be missing. --> - [x] I will check that all automated PR checks pass before the PR gets reviewed. --------- Co-authored-by: Anurag Kalia <[email protected]>
1 parent ade06c5 commit fdfc784

File tree

16 files changed

+214
-50
lines changed

16 files changed

+214
-50
lines changed

.yarn/versions/85334687.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
releases:
2+
"@yarnpkg/cli": minor
3+
"@yarnpkg/core": minor
4+
"@yarnpkg/plugin-file": minor
5+
"@yarnpkg/plugin-git": minor
6+
"@yarnpkg/plugin-github": minor
7+
"@yarnpkg/plugin-http": minor
8+
"@yarnpkg/plugin-npm": minor
9+
10+
declined:
11+
- "@yarnpkg/plugin-compat"
12+
- "@yarnpkg/plugin-constraints"
13+
- "@yarnpkg/plugin-dlx"
14+
- "@yarnpkg/plugin-essentials"
15+
- "@yarnpkg/plugin-exec"
16+
- "@yarnpkg/plugin-init"
17+
- "@yarnpkg/plugin-interactive-tools"
18+
- "@yarnpkg/plugin-link"
19+
- "@yarnpkg/plugin-nm"
20+
- "@yarnpkg/plugin-npm-cli"
21+
- "@yarnpkg/plugin-pack"
22+
- "@yarnpkg/plugin-patch"
23+
- "@yarnpkg/plugin-pnp"
24+
- "@yarnpkg/plugin-pnpm"
25+
- "@yarnpkg/plugin-stage"
26+
- "@yarnpkg/plugin-typescript"
27+
- "@yarnpkg/plugin-version"
28+
- "@yarnpkg/plugin-workspace-tools"
29+
- "@yarnpkg/builder"
30+
- "@yarnpkg/doctor"
31+
- "@yarnpkg/extensions"
32+
- "@yarnpkg/nm"
33+
- "@yarnpkg/pnpify"
34+
- "@yarnpkg/sdks"

packages/docusaurus/static/configuration/yarnrc.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,21 @@
777777
}
778778
}
779779
},
780+
"taskPoolConcurrency": {
781+
"_package": "@yarnpkg/core",
782+
"title": "Maximal amount of concurrent heavy task processing.",
783+
"description": "We default to the platform parallelism, but for some CI, `os.cpus` may not report accurate values and may overwhelm their containers.",
784+
"type": "number",
785+
"default": "os.availableParallelism()"
786+
},
787+
"workerPoolMode": {
788+
"_package": "@yarnpkg/core",
789+
"title": "Execution strategy for heavy tasks.",
790+
"description": "By default will use workers when performing heavy tasks, such as converting tgz files to zip. This setting can be used to disable workers and use a regular in-thread async processing.",
791+
"type": "string",
792+
"enum": ["async", "workers"],
793+
"default": "workers"
794+
},
780795
"telemetryInterval": {
781796
"_package": "@yarnpkg/core",
782797
"title": "Define the minimal amount of time between two telemetry events, in days.",

packages/plugin-file/sources/TarballFileFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class TarballFileFetcher implements Fetcher {
4242
const sourceBuffer = await fileUtils.fetchArchiveFromLocator(locator, opts);
4343

4444
return await tgzUtils.convertToZip(sourceBuffer, {
45-
compressionLevel: opts.project.configuration.get(`compressionLevel`),
45+
configuration: opts.project.configuration,
4646
prefixPath: structUtils.getIdentVendorPath(locator),
4747
stripComponents: 1,
4848
});

packages/plugin-git/sources/GitFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class GitFetcher implements Fetcher {
6464

6565
return await miscUtils.releaseAfterUseAsync(async () => {
6666
return await tgzUtils.convertToZip(sourceBuffer, {
67-
compressionLevel: opts.project.configuration.get(`compressionLevel`),
67+
configuration: opts.project.configuration,
6868
prefixPath: structUtils.getIdentVendorPath(locator),
6969
stripComponents: 1,
7070
});

packages/plugin-github/sources/GithubFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class GithubFetcher implements Fetcher {
6161
const packedBuffer = await xfs.readFilePromise(packagePath);
6262

6363
return await tgzUtils.convertToZip(packedBuffer, {
64-
compressionLevel: opts.project.configuration.get(`compressionLevel`),
64+
configuration: opts.project.configuration,
6565
prefixPath: structUtils.getIdentVendorPath(locator),
6666
stripComponents: 1,
6767
});

packages/plugin-http/sources/TarballHttpFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class TarballHttpFetcher implements Fetcher {
4343
});
4444

4545
return await tgzUtils.convertToZip(sourceBuffer, {
46-
compressionLevel: opts.project.configuration.get(`compressionLevel`),
46+
configuration: opts.project.configuration,
4747
prefixPath: structUtils.getIdentVendorPath(locator),
4848
stripComponents: 1,
4949
});

packages/plugin-npm/sources/NpmHttpFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class NpmHttpFetcher implements Fetcher {
5555
});
5656

5757
return await tgzUtils.convertToZip(sourceBuffer, {
58-
compressionLevel: opts.project.configuration.get(`compressionLevel`),
58+
configuration: opts.project.configuration,
5959
prefixPath: structUtils.getIdentVendorPath(locator),
6060
stripComponents: 1,
6161
});

packages/plugin-npm/sources/NpmSemverFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class NpmSemverFetcher implements Fetcher {
6565
}
6666

6767
return await tgzUtils.convertToZip(sourceBuffer, {
68-
compressionLevel: opts.project.configuration.get(`compressionLevel`),
68+
configuration: opts.project.configuration,
6969
prefixPath: structUtils.getIdentVendorPath(locator),
7070
stripComponents: 1,
7171
});

packages/yarnpkg-core/sources/Configuration.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,17 @@ export const coreDefinitions: {[coreSettingName: string]: SettingsDefinition} =
401401
type: SettingsType.NUMBER,
402402
default: 50,
403403
},
404+
taskPoolConcurrency: {
405+
description: `Maximal amount of concurrent heavy task processing`,
406+
type: SettingsType.NUMBER,
407+
default: nodeUtils.availableParallelism(),
408+
},
409+
taskPoolMode: {
410+
description: `Execution strategy for heavy tasks`,
411+
type: SettingsType.STRING,
412+
values: [`async`, `workers`],
413+
default: `workers`,
414+
},
404415
networkSettings: {
405416
description: `Network settings per hostname (glob patterns are supported)`,
406417
type: SettingsType.MAP,
@@ -643,6 +654,8 @@ export interface ConfigurationValueMap {
643654
httpsKeyFilePath: PortablePath | null;
644655
httpsCertFilePath: PortablePath | null;
645656
enableStrictSsl: boolean;
657+
taskPoolConcurrency: number;
658+
taskPoolMode: string;
646659

647660
logFilters: Array<miscUtils.ToMapValue<{code?: string, text?: string, pattern?: string, level?: formatUtils.LogLevel | null}>>;
648661

packages/yarnpkg-core/sources/WorkerPool.ts renamed to packages/yarnpkg-core/sources/TaskPool.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
1-
import PLimit from 'p-limit';
2-
import {Worker} from 'worker_threads';
3-
4-
import * as nodeUtils from './nodeUtils';
1+
import pLimit, {Limit} from 'p-limit';
2+
import {Worker} from 'worker_threads';
53

64
const kTaskInfo = Symbol(`kTaskInfo`);
75

86
type PoolWorker<TOut> = Worker & {
97
[kTaskInfo]: null | { resolve: (value: TOut) => void, reject: (reason?: any) => void };
108
};
119

12-
export class WorkerPool<TIn, TOut> {
10+
export interface TaskPool<TIn, TOut> {
11+
run(data: TIn): Promise<TOut>;
12+
}
13+
14+
export class AsyncPool<TIn, TOut> implements TaskPool<TIn, TOut> {
15+
private limit: Limit;
16+
17+
constructor(private fn: (data: TIn) => Promise<TOut>, opts: {poolSize: number}) {
18+
this.limit = pLimit(opts.poolSize);
19+
}
20+
21+
run(data: TIn) {
22+
return this.limit(() => this.fn(data));
23+
}
24+
}
25+
26+
export class WorkerPool<TIn, TOut> implements TaskPool<TIn, TOut> {
1327
private workers: Array<PoolWorker<TOut>> = [];
14-
private limit = PLimit(nodeUtils.availableParallelism());
28+
1529
private cleanupInterval: ReturnType<typeof setInterval>;
1630

17-
constructor(private source: string) {
31+
private limit: Limit;
32+
33+
constructor(private source: string, opts: {poolSize: number}) {
34+
this.limit = pLimit(opts.poolSize);
35+
1836
this.cleanupInterval = setInterval(() => {
1937
if (this.limit.pendingCount === 0 && this.limit.activeCount === 0) {
2038
// Start terminating one worker at a time when there are no tasks left.

0 commit comments

Comments
 (0)