Skip to content
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

Add concurrency support for more than one thread #681

Merged
merged 30 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
28f57a6
Update compile.js
vicary Jul 3, 2020
c96f31f
Update README.md
vicary Jul 3, 2020
c3c3134
Rename to concurrency
vicary Jul 3, 2020
0e45639
Update Configuration.test.js
vicary Jul 3, 2020
b779c2d
Update validate.js
vicary Jul 3, 2020
b26c27e
Update compile.test.js
vicary Jul 3, 2020
159388d
bluebird doesn't like undefined concurrency
vicary Jul 3, 2020
ae9794c
Support icloud drive
Oct 4, 2020
17ac840
Serialized compile to address #299
asprouse Jun 14, 2019
e3b1a0e
Add serializedCompile documentation
asprouse Jun 14, 2019
13365aa
compatible with serialized compile
vicary Oct 4, 2020
e2b86d1
fix tests
vicary Oct 4, 2020
2e65368
Option to override concurrency setting via serverless CLI
coyoteecd Jul 6, 2020
2d5a9c4
Remove compile-concurrency option, since the same can be achieved via…
coyoteecd Jul 6, 2020
6f17d49
Move the parsing and default value for concurrency option in Configur…
coyoteecd Oct 14, 2020
4d8ff23
Upgrade deps
j0k3r Sep 3, 2020
33e3a97
Skip compile & packaging if --no-build is set (#560)
jamesmbourne May 7, 2020
b580a0e
Update README.md
miguel-a-calles-mba Jul 12, 2020
d55d96b
Add some tests & fix linter
j0k3r Jul 24, 2020
eecb07b
Serialized compile to address #299
asprouse Jun 14, 2019
3e31d7c
compatible with serialized compile
vicary Oct 4, 2020
0ec8200
Option to override concurrency setting via serverless CLI
coyoteecd Jul 6, 2020
7164e8c
Move the parsing and default value for concurrency option in Configur…
coyoteecd Oct 14, 2020
04f02aa
fix rebase artifact
vicary Jan 13, 2021
9f6e3d2
fix rebase artifacts
vicary Jan 13, 2021
4bae141
fix test coverage
vicary Mar 1, 2021
1cf851d
remove rebase artifacts
vicary Mar 1, 2021
a5e620a
defaults to number of cores
vicary Mar 3, 2021
9f4fcfe
change test concurrency to reduce ambiguity
vicary Mar 4, 2021
b583a8d
remove multiCompile and simplify concurrent log
vicary Mar 4, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
node_modules.nosync
dist
.webpack
.serverless
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -541,12 +541,13 @@ if you are trying to override the entry in webpack.config.js with other unsuppor
The individual packaging needs more time at the packaging phase, but you'll
get that paid back twice at runtime.

#### Individual packaging serializedCompile
#### Individual packaging concurrency
```yaml
# serverless.yml
custom:
webpack:
serializedCompile: true
concurrency: 5 # desired concurrency, defaults to the number of available cores
serializedCompile: true # backward compatible, this translates to concurrency: 1
```
Will run each webpack build one at a time which helps reduce memory usage and in some cases impoves overall build performance.

Expand Down
24 changes: 20 additions & 4 deletions lib/Configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

const _ = require('lodash');
const os = require('os');

/**
* Plugin defaults
Expand All @@ -15,7 +16,7 @@ const DefaultConfig = {
packagerOptions: {},
keepOutputDirectory: false,
config: null,
serializedCompile: false
concurrency: os.cpus().length
};

class Configuration {
Expand All @@ -38,6 +39,21 @@ class Configuration {
}
}

// Concurrency may be passed via CLI, e.g.
// custom:
// webpack:
// concurrency: ${opt:compile-concurrency, 7}
// In this case it is typed as a string and we have to validate it
if (this._config.concurrency !== undefined) {
this._config.concurrency = Number(this._config.concurrency);
if (isNaN(this._config.concurrency) || this._config.concurrency < 1) {
throw new Error('concurrency option must be a positive number');
}
} else if (this._config.serializedCompile === true) {
// Backwards compatibility with serializedCompile setting
this._config.concurrency = 1;
}

// Set defaults for all missing properties
_.defaults(this._config, DefaultConfig);
}
Expand All @@ -53,7 +69,7 @@ class Configuration {
get excludeFiles() {
return this._config.excludeFiles;
}

get excludeRegex() {
return this._config.excludeRegex;
}
Expand All @@ -78,8 +94,8 @@ class Configuration {
return this._config.keepOutputDirectory;
}

get serializedCompile() {
return this._config.serializedCompile;
get concurrency() {
return this._config.concurrency;
}

toJSON() {
Expand Down
53 changes: 49 additions & 4 deletions lib/Configuration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Unit tests for Configuration.
*/

const os = require('os');
const chai = require('chai');
const Configuration = require('./Configuration');

Expand All @@ -20,7 +21,7 @@ describe('Configuration', () => {
packagerOptions: {},
keepOutputDirectory: false,
config: null,
serializedCompile: false
concurrency: os.cpus().length
};
});

Expand Down Expand Up @@ -70,7 +71,7 @@ describe('Configuration', () => {
packagerOptions: {},
keepOutputDirectory: false,
config: null,
serializedCompile: false
concurrency: os.cpus().length
});
});
});
Expand All @@ -91,7 +92,7 @@ describe('Configuration', () => {
packagerOptions: {},
keepOutputDirectory: false,
config: null,
serializedCompile: false
concurrency: os.cpus().length
});
});

Expand All @@ -111,8 +112,52 @@ describe('Configuration', () => {
packagerOptions: {},
keepOutputDirectory: false,
config: null,
serializedCompile: false
concurrency: os.cpus().length
});
});

it('should accept a numeric string as concurrency value', () => {
const testCustom = {
webpack: {
includeModules: { forceInclude: ['mod1'] },
webpackConfig: 'myWebpackFile.js',
concurrency: '3'
}
};
const config = new Configuration(testCustom);
expect(config.concurrency).to.equal(3);
});

it('should not accept an invalid string as concurrency value', () => {
const testCustom = {
webpack: {
includeModules: { forceInclude: ['mod1'] },
webpackConfig: 'myWebpackFile.js',
concurrency: '3abc'
}
};
expect(() => new Configuration(testCustom)).throws();
});

it('should not accept a non-positive number as concurrency value', () => {
const testCustom = {
webpack: {
includeModules: { forceInclude: ['mod1'] },
webpackConfig: 'myWebpackFile.js',
concurrency: 0
}
};
expect(() => new Configuration(testCustom)).throws();
});

it('should be backward compatible with serializedCompile', () => {
const testCustom = {
webpack: {
serializedCompile: true
}
};
const config = new Configuration(testCustom);
expect(config.concurrency).to.equal(1);
});
});
});
48 changes: 25 additions & 23 deletions lib/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,25 @@ function getStatsLogger(statsConfig, consoleLog) {
}

function webpackCompile(config, logStats) {
return BbPromise
.fromCallback(cb => webpack(config).run(cb))
.then(stats => {
// ensure stats in any array in the case of multiCompile
stats = stats.stats ? stats.stats : [stats];

_.forEach(stats, compileStats => {
logStats(compileStats);
if (compileStats.hasErrors()) {
throw new Error('Webpack compilation error, see stats above');
}
});

return stats;
return BbPromise.fromCallback(cb => webpack(config).run(cb)).then(stats => {
// ensure stats in any array in the case of concurrent build.
stats = stats.stats ? stats.stats : [stats];

_.forEach(stats, compileStats => {
logStats(compileStats);
if (compileStats.hasErrors()) {
throw new Error('Webpack compilation error, see stats above');
}
});

return stats;
});
}

function webpackCompileSerial(configs, logStats) {
return BbPromise
.mapSeries(configs, config => webpackCompile(config, logStats))
.then(stats => _.flatten(stats));
function webpackConcurrentCompile(configs, logStats, concurrency) {
return BbPromise.map(configs, config => webpackCompile(config, logStats), { concurrency }).then(stats =>
_.flatten(stats)
);
}

module.exports = {
Expand All @@ -52,10 +50,14 @@ module.exports = {
const configs = ensureArray(this.webpackConfig);
const logStats = getStatsLogger(configs[0].stats, this.serverless.cli.consoleLog);

return (this.serializedCompile ? webpackCompileSerial : webpackCompile)(configs, logStats)
.then(stats => {
this.compileStats = { stats };
return BbPromise.resolve();
});
if (!this.configuration) {
return BbPromise.reject('Missing plugin configuration');
}
const concurrency = this.configuration.concurrency;

return webpackConcurrentCompile(configs, logStats, concurrency).then(stats => {
this.compileStats = { stats };
return BbPromise.resolve();
});
}
};
4 changes: 1 addition & 3 deletions lib/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,9 @@ module.exports = {

// In case of individual packaging we have to create a separate config for each function
if (_.has(this.serverless, 'service.package') && this.serverless.service.package.individually) {
this.multiCompile = true;
this.serializedCompile = this.configuration.serializedCompile;
this.options.verbose &&
this.serverless.cli.log(
`Using ${this.serializedCompile ? 'serialized' : 'multi'}-compile (individual packaging)`
`Individually packaging with concurrency at ${this.configuration.concurrency} entries a time.`
);

if (this.webpackConfig.entry && !_.isEqual(this.webpackConfig.entry, entries)) {
Expand Down
19 changes: 8 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading