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

store cache after large changes #13649

Merged
merged 1 commit into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions declarations/WebpackOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -916,11 +916,15 @@ export interface FileCacheOptions {
*/
hashAlgorithm?: string;
/**
* Time in ms after which idle period the cache storing should happen (only for store: 'pack').
* Time in ms after which idle period the cache storing should happen.
*/
idleTimeout?: number;
/**
* Time in ms after which idle period the initial cache storing should happen (only for store: 'pack').
* Time in ms after which idle period the cache storing should happen when larger changes has been detected (cumulative build time > 2 x avg cache store time).
*/
idleTimeoutAfterLargeChanges?: number;
/**
* Time in ms after which idle period the initial cache storing should happen.
*/
idleTimeoutForInitialStore?: number;
/**
Expand Down
3 changes: 2 additions & 1 deletion lib/Watching.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class Watching {

_go(fileTimeInfoEntries, contextTimeInfoEntries, changedFiles, removedFiles) {
this._initial = false;
this.startTime = Date.now();
if (this.startTime === null) this.startTime = Date.now();
this.running = true;
if (this.watcher) {
this.pausedWatcher = this.watcher;
Expand Down Expand Up @@ -252,6 +252,7 @@ class Watching {
compilation.endTime = Date.now();
stats = new Stats(compilation);
}
this.startTime = null;
if (err) return handleError(err);

const cbs = this.callbacks;
Expand Down
3 changes: 2 additions & 1 deletion lib/WebpackOptionsApply.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,8 @@ class WebpackOptionsApply extends OptionsApply {
allowCollectingMemory: cacheOptions.allowCollectingMemory
}),
cacheOptions.idleTimeout,
cacheOptions.idleTimeoutForInitialStore
cacheOptions.idleTimeoutForInitialStore,
cacheOptions.idleTimeoutAfterLargeChanges
).apply(compiler);
break;
}
Expand Down
73 changes: 60 additions & 13 deletions lib/cache/IdleFileCachePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ class IdleFileCachePlugin {
* @param {TODO} strategy cache strategy
* @param {number} idleTimeout timeout
* @param {number} idleTimeoutForInitialStore initial timeout
* @param {number} idleTimeoutAfterLargeChanges timeout after changes
*/
constructor(strategy, idleTimeout, idleTimeoutForInitialStore) {
constructor(
strategy,
idleTimeout,
idleTimeoutForInitialStore,
idleTimeoutAfterLargeChanges
) {
this.strategy = strategy;
this.idleTimeout = idleTimeout;
this.idleTimeoutForInitialStore = idleTimeoutForInitialStore;
this.idleTimeoutAfterLargeChanges = idleTimeoutAfterLargeChanges;
}

/**
Expand All @@ -36,9 +43,13 @@ class IdleFileCachePlugin {
idleTimeout,
this.idleTimeoutForInitialStore
);

const idleTimeoutAfterLargeChanges = this.idleTimeoutAfterLargeChanges;
const resolvedPromise = Promise.resolve();

let timeSpendInBuild = 0;
let timeSpendInStore = 0;
let avgTimeSpendInStore = 0;

/** @type {Map<string | typeof BUILD_DEPENDENCIES_KEY, () => Promise>} */
const pendingIdleTasks = new Map();

Expand Down Expand Up @@ -121,9 +132,10 @@ class IdleFileCachePlugin {
let isInitialStore = true;
const processIdleTasks = () => {
if (isIdle) {
const startTime = Date.now();
if (pendingIdleTasks.size > 0) {
const promises = [currentIdlePromise];
const maxTime = Date.now() + 100;
const maxTime = startTime + 100;
let maxCount = 100;
for (const [filename, factory] of pendingIdleTasks) {
pendingIdleTasks.delete(filename);
Expand All @@ -132,13 +144,23 @@ class IdleFileCachePlugin {
}
currentIdlePromise = Promise.all(promises);
currentIdlePromise.then(() => {
timeSpendInStore += Date.now() - startTime;
// Allow to exit the process between
setTimeout(processIdleTasks, 0).unref();
idleTimer = setTimeout(processIdleTasks, 0);
idleTimer.unref();
});
return;
}
currentIdlePromise = currentIdlePromise
.then(() => strategy.afterAllStored())
.then(async () => {
await strategy.afterAllStored();
timeSpendInStore += Date.now() - startTime;
avgTimeSpendInStore =
Math.max(avgTimeSpendInStore, timeSpendInStore) * 0.9 +
timeSpendInStore * 0.1;
timeSpendInStore = 0;
timeSpendInBuild = 0;
})
.catch(err => {
const logger = compiler.getInfrastructureLogger(
"IdleFileCachePlugin"
Expand All @@ -153,14 +175,34 @@ class IdleFileCachePlugin {
compiler.cache.hooks.beginIdle.tap(
{ name: "IdleFileCachePlugin", stage: Cache.STAGE_DISK },
() => {
idleTimer = setTimeout(
() => {
idleTimer = undefined;
isIdle = true;
resolvedPromise.then(processIdleTasks);
},
isInitialStore ? idleTimeoutForInitialStore : idleTimeout
);
const isLargeChange = timeSpendInBuild > avgTimeSpendInStore * 2;
if (isInitialStore && idleTimeoutForInitialStore < idleTimeout) {
compiler
.getInfrastructureLogger("IdleFileCachePlugin")
.log(
`Initial cache was generated and cache will be persisted in ${
idleTimeoutForInitialStore / 1000
}s.`
);
} else if (
isLargeChange &&
idleTimeoutAfterLargeChanges < idleTimeout
) {
compiler
.getInfrastructureLogger("IdleFileCachePlugin")
.log(
`Spend ${Math.round(timeSpendInBuild) / 1000}s in build and ${
Math.round(avgTimeSpendInStore) / 1000
}s in average in cache store. This is considered as large change and cache will be persisted in ${
idleTimeoutAfterLargeChanges / 1000
}s.`
);
}
idleTimer = setTimeout(() => {
idleTimer = undefined;
isIdle = true;
resolvedPromise.then(processIdleTasks);
}, Math.min(isInitialStore ? idleTimeoutForInitialStore : Infinity, isLargeChange ? idleTimeoutAfterLargeChanges : Infinity, idleTimeout));
idleTimer.unref();
}
);
Expand All @@ -174,6 +216,11 @@ class IdleFileCachePlugin {
isIdle = false;
}
);
compiler.hooks.done.tap("IdleFileCachePlugin", stats => {
// 10% build overhead is ignored, as it's not cacheable
timeSpendInBuild *= 0.9;
timeSpendInBuild += stats.endTime - stats.startTime;
});
}
}

Expand Down
3 changes: 2 additions & 1 deletion lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ const applyCacheDefaults = (cache, { name, mode, development }) => {
D(cache, "store", "pack");
D(cache, "profile", false);
D(cache, "idleTimeout", 60000);
D(cache, "idleTimeoutForInitialStore", 0);
D(cache, "idleTimeoutForInitialStore", 5000);
D(cache, "idleTimeoutAfterLargeChanges", 1000);
D(cache, "maxMemoryGenerations", development ? 5 : Infinity);
D(cache, "maxAge", 1000 * 60 * 60 * 24 * 60); // 1 month
D(cache, "allowCollectingMemory", development);
Expand Down
1 change: 1 addition & 0 deletions lib/config/normalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const getNormalizedWebpackOptions = config => {
hashAlgorithm: cache.hashAlgorithm,
idleTimeout: cache.idleTimeout,
idleTimeoutForInitialStore: cache.idleTimeoutForInitialStore,
idleTimeoutAfterLargeChanges: cache.idleTimeoutAfterLargeChanges,
name: cache.name,
store: cache.store,
version: cache.version
Expand Down
2 changes: 1 addition & 1 deletion schemas/WebpackOptions.check.js

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions schemas/WebpackOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -956,12 +956,17 @@
"type": "string"
},
"idleTimeout": {
"description": "Time in ms after which idle period the cache storing should happen (only for store: 'pack').",
"description": "Time in ms after which idle period the cache storing should happen.",
"type": "number",
"minimum": 0
},
"idleTimeoutAfterLargeChanges": {
"description": "Time in ms after which idle period the cache storing should happen when larger changes has been detected (cumulative build time > 2 x avg cache store time).",
"type": "number",
"minimum": 0
},
"idleTimeoutForInitialStore": {
"description": "Time in ms after which idle period the initial cache storing should happen (only for store: 'pack').",
"description": "Time in ms after which idle period the initial cache storing should happen.",
"type": "number",
"minimum": 0
},
Expand Down
9 changes: 6 additions & 3 deletions test/Defaults.unittest.js
Original file line number Diff line number Diff line change
Expand Up @@ -1502,7 +1502,8 @@ describe("Defaults", () => {
+ "cacheLocation": "<cwd>/node_modules/.cache/webpack/default-none",
+ "hashAlgorithm": "md4",
+ "idleTimeout": 60000,
+ "idleTimeoutForInitialStore": 0,
+ "idleTimeoutAfterLargeChanges": 1000,
+ "idleTimeoutForInitialStore": 5000,
+ "maxAge": 5184000000,
+ "maxMemoryGenerations": Infinity,
+ "name": "default-none",
Expand Down Expand Up @@ -1543,7 +1544,8 @@ describe("Defaults", () => {
+ "cacheLocation": "<cwd>/node_modules/.cache/webpack/default-development",
+ "hashAlgorithm": "md4",
+ "idleTimeout": 60000,
+ "idleTimeoutForInitialStore": 0,
+ "idleTimeoutAfterLargeChanges": 1000,
+ "idleTimeoutForInitialStore": 5000,
+ "maxAge": 5184000000,
+ "maxMemoryGenerations": 5,
+ "name": "default-development",
Expand Down Expand Up @@ -1789,7 +1791,8 @@ describe("Defaults", () => {
+ "cacheLocation": "<cwd>/node_modules/.cache/webpack/default-none",
+ "hashAlgorithm": "md4",
+ "idleTimeout": 60000,
+ "idleTimeoutForInitialStore": 0,
+ "idleTimeoutAfterLargeChanges": 1000,
+ "idleTimeoutForInitialStore": 5000,
+ "maxAge": 5184000000,
+ "maxMemoryGenerations": Infinity,
+ "name": "default-none",
Expand Down
21 changes: 17 additions & 4 deletions test/__snapshots__/Cli.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -111,26 +111,39 @@ Object {
"cache-idle-timeout": Object {
"configs": Array [
Object {
"description": "Time in ms after which idle period the cache storing should happen (only for store: 'pack').",
"description": "Time in ms after which idle period the cache storing should happen.",
"multiple": false,
"path": "cache.idleTimeout",
"type": "number",
},
],
"description": "Time in ms after which idle period the cache storing should happen (only for store: 'pack').",
"description": "Time in ms after which idle period the cache storing should happen.",
"multiple": false,
"simpleType": "number",
},
"cache-idle-timeout-after-large-changes": Object {
"configs": Array [
Object {
"description": "Time in ms after which idle period the cache storing should happen when larger changes has been detected (cumulative build time > 2 x avg cache store time).",
"multiple": false,
"path": "cache.idleTimeoutAfterLargeChanges",
"type": "number",
},
],
"description": "Time in ms after which idle period the cache storing should happen when larger changes has been detected (cumulative build time > 2 x avg cache store time).",
"multiple": false,
"simpleType": "number",
},
"cache-idle-timeout-for-initial-store": Object {
"configs": Array [
Object {
"description": "Time in ms after which idle period the initial cache storing should happen (only for store: 'pack').",
"description": "Time in ms after which idle period the initial cache storing should happen.",
"multiple": false,
"path": "cache.idleTimeoutForInitialStore",
"type": "number",
},
],
"description": "Time in ms after which idle period the initial cache storing should happen (only for store: 'pack').",
"description": "Time in ms after which idle period the initial cache storing should happen.",
"multiple": false,
"simpleType": "number",
},
Expand Down
9 changes: 7 additions & 2 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3785,12 +3785,17 @@ declare interface FileCacheOptions {
hashAlgorithm?: string;

/**
* Time in ms after which idle period the cache storing should happen (only for store: 'pack').
* Time in ms after which idle period the cache storing should happen.
*/
idleTimeout?: number;

/**
* Time in ms after which idle period the initial cache storing should happen (only for store: 'pack').
* Time in ms after which idle period the cache storing should happen when larger changes has been detected (cumulative build time > 2 x avg cache store time).
*/
idleTimeoutAfterLargeChanges?: number;

/**
* Time in ms after which idle period the initial cache storing should happen.
*/
idleTimeoutForInitialStore?: number;

Expand Down