@atlaspack/core 2.14.1-dev.144 → 2.14.1-dev.146
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +201 -0
- package/lib/AssetGraph.js +17 -6
- package/lib/Atlaspack.js +24 -5
- package/lib/BundleGraph.js +6 -5
- package/lib/Dependency.js +6 -2
- package/lib/Environment.js +4 -3
- package/lib/EnvironmentManager.js +137 -0
- package/lib/InternalConfig.js +3 -2
- package/lib/PackagerRunner.js +52 -15
- package/lib/RequestTracker.js +337 -82
- package/lib/UncommittedAsset.js +20 -2
- package/lib/applyRuntimes.js +2 -1
- package/lib/assetUtils.js +2 -1
- package/lib/atlaspack-v3/worker/worker.js +8 -0
- package/lib/public/Asset.js +3 -2
- package/lib/public/Bundle.js +2 -1
- package/lib/public/BundleGraph.js +21 -5
- package/lib/public/Config.js +98 -3
- package/lib/public/Dependency.js +2 -1
- package/lib/public/MutableBundleGraph.js +2 -1
- package/lib/public/Target.js +2 -1
- package/lib/requests/AssetGraphRequest.js +13 -1
- package/lib/requests/AssetRequest.js +2 -1
- package/lib/requests/BundleGraphRequest.js +13 -1
- package/lib/requests/ConfigRequest.js +27 -4
- package/lib/requests/TargetRequest.js +18 -16
- package/lib/requests/WriteBundleRequest.js +15 -3
- package/lib/requests/WriteBundlesRequest.js +1 -0
- package/lib/resolveOptions.js +5 -6
- package/package.json +22 -18
- package/src/AssetGraph.js +12 -6
- package/src/Atlaspack.js +29 -13
- package/src/BundleGraph.js +13 -8
- package/src/Dependency.js +13 -5
- package/src/Environment.js +8 -5
- package/src/EnvironmentManager.js +145 -0
- package/src/InternalConfig.js +6 -5
- package/src/PackagerRunner.js +72 -20
- package/src/RequestTracker.js +567 -131
- package/src/UncommittedAsset.js +23 -3
- package/src/applyRuntimes.js +6 -1
- package/src/assetUtils.js +4 -3
- package/src/atlaspack-v3/worker/compat/plugin-config.js +9 -5
- package/src/atlaspack-v3/worker/worker.js +7 -0
- package/src/public/Asset.js +9 -2
- package/src/public/Bundle.js +2 -1
- package/src/public/BundleGraph.js +22 -5
- package/src/public/Config.js +129 -14
- package/src/public/Dependency.js +2 -1
- package/src/public/MutableBundleGraph.js +2 -1
- package/src/public/Target.js +2 -1
- package/src/requests/AssetGraphRequest.js +13 -3
- package/src/requests/AssetRequest.js +2 -1
- package/src/requests/BundleGraphRequest.js +13 -3
- package/src/requests/ConfigRequest.js +33 -9
- package/src/requests/TargetRequest.js +19 -25
- package/src/requests/WriteBundleRequest.js +14 -8
- package/src/requests/WriteBundlesRequest.js +1 -0
- package/src/resolveOptions.js +6 -8
- package/src/types.js +9 -7
- package/test/Environment.test.js +43 -34
- package/test/EnvironmentManager.test.js +192 -0
- package/test/PublicEnvironment.test.js +10 -7
- package/test/RequestTracker.test.js +115 -3
- package/test/public/Config.test.js +108 -0
- package/test/requests/ConfigRequest.test.js +187 -3
- package/test/test-utils.js +4 -9
package/lib/RequestTracker.js
CHANGED
|
@@ -6,9 +6,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.RequestGraph = void 0;
|
|
7
7
|
exports.cleanUpOrphans = cleanUpOrphans;
|
|
8
8
|
exports.default = void 0;
|
|
9
|
+
exports.getBiggestFSEventsInvalidations = getBiggestFSEventsInvalidations;
|
|
9
10
|
exports.getWatcherOptions = getWatcherOptions;
|
|
11
|
+
exports.invalidateRequestGraph = invalidateRequestGraph;
|
|
12
|
+
exports.invalidateRequestGraphFSEvents = invalidateRequestGraphFSEvents;
|
|
10
13
|
exports.readAndDeserializeRequestGraph = readAndDeserializeRequestGraph;
|
|
11
14
|
exports.requestTypes = exports.requestGraphEdgeTypes = void 0;
|
|
15
|
+
exports.runInvalidation = runInvalidation;
|
|
12
16
|
function _assert() {
|
|
13
17
|
const data = _interopRequireWildcard(require("assert"));
|
|
14
18
|
_assert = function () {
|
|
@@ -30,6 +34,13 @@ function _buildCache() {
|
|
|
30
34
|
};
|
|
31
35
|
return data;
|
|
32
36
|
}
|
|
37
|
+
function _cache() {
|
|
38
|
+
const data = require("@atlaspack/cache");
|
|
39
|
+
_cache = function () {
|
|
40
|
+
return data;
|
|
41
|
+
};
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
33
44
|
function _featureFlags() {
|
|
34
45
|
const data = require("@atlaspack/feature-flags");
|
|
35
46
|
_featureFlags = function () {
|
|
@@ -45,7 +56,7 @@ function _graph() {
|
|
|
45
56
|
return data;
|
|
46
57
|
}
|
|
47
58
|
function _logger() {
|
|
48
|
-
const data =
|
|
59
|
+
const data = _interopRequireWildcard(require("@atlaspack/logger"));
|
|
49
60
|
_logger = function () {
|
|
50
61
|
return data;
|
|
51
62
|
};
|
|
@@ -77,6 +88,14 @@ var _projectPath = require("./projectPath");
|
|
|
77
88
|
var _ReporterRunner = require("./ReporterRunner");
|
|
78
89
|
var _ConfigRequest = require("./requests/ConfigRequest");
|
|
79
90
|
var _utils2 = require("./utils");
|
|
91
|
+
function _perf_hooks() {
|
|
92
|
+
const data = require("perf_hooks");
|
|
93
|
+
_perf_hooks = function () {
|
|
94
|
+
return data;
|
|
95
|
+
};
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
var _EnvironmentManager = require("./EnvironmentManager");
|
|
80
99
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
81
100
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
82
101
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -144,7 +163,7 @@ const nodeFromOption = (option, value) => ({
|
|
|
144
163
|
hash: (0, _utils2.hashFromOption)(value)
|
|
145
164
|
});
|
|
146
165
|
const nodeFromConfigKey = (fileName, configKey, contentHash) => ({
|
|
147
|
-
id: `config_key:${(0, _projectPath.fromProjectPathRelative)(fileName)}:${configKey}`,
|
|
166
|
+
id: `config_key:${(0, _projectPath.fromProjectPathRelative)(fileName)}:${JSON.stringify(configKey)}`,
|
|
148
167
|
type: CONFIG_KEY,
|
|
149
168
|
configKey,
|
|
150
169
|
contentHash
|
|
@@ -272,6 +291,12 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
272
291
|
// If the node is invalidated, the cached request chunk on disk needs to be re-written
|
|
273
292
|
this.removeCachedRequestChunkForNode(nodeId);
|
|
274
293
|
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Nodes that are invalidated on start-up, such as JavaScript babel configuration files which are
|
|
297
|
+
* imported when the build kicks-off and might doing arbitrary work such as reading from the file
|
|
298
|
+
* system.
|
|
299
|
+
*/
|
|
275
300
|
invalidateUnpredictableNodes() {
|
|
276
301
|
for (let nodeId of this.unpredicatableNodeIds) {
|
|
277
302
|
let node = (0, _nullthrows().default)(this.getNode(nodeId));
|
|
@@ -279,6 +304,10 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
279
304
|
this.invalidateNode(nodeId, _constants.STARTUP);
|
|
280
305
|
}
|
|
281
306
|
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Effectively uncacheable nodes.
|
|
310
|
+
*/
|
|
282
311
|
invalidateOnBuildNodes() {
|
|
283
312
|
for (let nodeId of this.invalidateOnBuildNodeIds) {
|
|
284
313
|
let node = (0, _nullthrows().default)(this.getNode(nodeId));
|
|
@@ -286,29 +315,45 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
286
315
|
this.invalidateNode(nodeId, _constants.STARTUP);
|
|
287
316
|
}
|
|
288
317
|
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Nodes invalidated by environment changes, corresponds to `env: ...` inputs.
|
|
321
|
+
*/
|
|
289
322
|
invalidateEnvNodes(env) {
|
|
323
|
+
const invalidatedKeys = [];
|
|
290
324
|
for (let nodeId of this.envNodeIds) {
|
|
291
325
|
let node = (0, _nullthrows().default)(this.getNode(nodeId));
|
|
292
326
|
(0, _assert().default)(node.type === ENV);
|
|
293
|
-
|
|
327
|
+
const key = keyFromEnvContentKey(node.id);
|
|
328
|
+
if (env[key] !== node.value) {
|
|
329
|
+
invalidatedKeys.push(key);
|
|
294
330
|
let parentNodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update);
|
|
295
331
|
for (let parentNode of parentNodes) {
|
|
296
332
|
this.invalidateNode(parentNode, _constants.ENV_CHANGE);
|
|
297
333
|
}
|
|
298
334
|
}
|
|
299
335
|
}
|
|
336
|
+
return invalidatedKeys;
|
|
300
337
|
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Nodes invalidated by option changes.
|
|
341
|
+
*/
|
|
301
342
|
invalidateOptionNodes(options) {
|
|
343
|
+
const invalidatedKeys = [];
|
|
302
344
|
for (let nodeId of this.optionNodeIds) {
|
|
303
345
|
let node = (0, _nullthrows().default)(this.getNode(nodeId));
|
|
304
346
|
(0, _assert().default)(node.type === OPTION);
|
|
305
|
-
|
|
347
|
+
const key = keyFromOptionContentKey(node.id);
|
|
348
|
+
if ((0, _utils2.hashFromOption)(options[key]) !== node.hash) {
|
|
349
|
+
invalidatedKeys.push(key);
|
|
306
350
|
let parentNodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update);
|
|
307
351
|
for (let parentNode of parentNodes) {
|
|
308
352
|
this.invalidateNode(parentNode, _constants.OPTION_CHANGE);
|
|
309
353
|
}
|
|
310
354
|
}
|
|
311
355
|
}
|
|
356
|
+
return invalidatedKeys;
|
|
312
357
|
}
|
|
313
358
|
invalidateOnConfigKeyChange(requestNodeId, filePath, configKey, contentHash) {
|
|
314
359
|
let configKeyNodeId = this.addNode(nodeFromConfigKey(filePath, configKey, contentHash));
|
|
@@ -401,8 +446,8 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
401
446
|
this.invalidateOnBuildNodeIds.add(requestNodeId);
|
|
402
447
|
}
|
|
403
448
|
invalidateOnEnvChange(requestNodeId, env, value) {
|
|
404
|
-
|
|
405
|
-
|
|
449
|
+
const envNode = nodeFromEnv(env, value);
|
|
450
|
+
const envNodeId = this.addNode(envNode);
|
|
406
451
|
if (!this.hasEdge(requestNodeId, envNodeId, requestGraphEdgeTypes.invalidated_by_update)) {
|
|
407
452
|
this.addEdge(requestNodeId, envNodeId, requestGraphEdgeTypes.invalidated_by_update);
|
|
408
453
|
}
|
|
@@ -549,10 +594,12 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
549
594
|
aboveCache.set(fileNameNodeId, above);
|
|
550
595
|
return above;
|
|
551
596
|
};
|
|
597
|
+
const invalidationsByPath = new Map();
|
|
552
598
|
for (let {
|
|
553
599
|
path: _path,
|
|
554
600
|
type
|
|
555
601
|
} of events) {
|
|
602
|
+
const invalidationsBefore = this.getInvalidNodeCount();
|
|
556
603
|
if (!enableOptimization && process.env.ATLASPACK_DISABLE_CACHE_TIMEOUT !== 'true' && ++count === 256) {
|
|
557
604
|
let duration = Date.now() - startTime;
|
|
558
605
|
predictedTime = duration * (events.length >> 8);
|
|
@@ -589,7 +636,10 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
589
636
|
this.invalidNodeIds.add(id);
|
|
590
637
|
}
|
|
591
638
|
}
|
|
592
|
-
return
|
|
639
|
+
return {
|
|
640
|
+
didInvalidate: true,
|
|
641
|
+
invalidationsByPath: new Map()
|
|
642
|
+
};
|
|
593
643
|
}
|
|
594
644
|
|
|
595
645
|
// sometimes mac os reports update events as create events.
|
|
@@ -646,10 +696,18 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
646
696
|
this.removeNode(nodeId, removeOrphans);
|
|
647
697
|
}
|
|
648
698
|
let configKeyNodes = this.configKeyNodes.get(_filePath);
|
|
649
|
-
|
|
699
|
+
|
|
700
|
+
// With granular invalidations we will always run this block,
|
|
701
|
+
// so even if we get a create event (for whatever reason), we will still
|
|
702
|
+
// try to limit invalidations from config key changes through hashing.
|
|
703
|
+
//
|
|
704
|
+
// Currently create events can invalidate a large number of nodes due to
|
|
705
|
+
// "create above" invalidations.
|
|
706
|
+
const isConfigKeyChange = (0, _featureFlags().getFeatureFlag)('granularTsConfigInvalidation') || type === 'delete' || type === 'update';
|
|
707
|
+
if (configKeyNodes && isConfigKeyChange) {
|
|
650
708
|
for (let nodeId of configKeyNodes) {
|
|
651
709
|
let isInvalid = type === 'delete';
|
|
652
|
-
if (type
|
|
710
|
+
if (type !== 'delete') {
|
|
653
711
|
let node = this.getNode(nodeId);
|
|
654
712
|
(0, _assert().default)(node && node.type === CONFIG_KEY);
|
|
655
713
|
let contentHash = await (0, _ConfigRequest.getConfigKeyContentHash)(_filePath, node.configKey, options);
|
|
@@ -664,6 +722,8 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
664
722
|
}
|
|
665
723
|
}
|
|
666
724
|
}
|
|
725
|
+
const invalidationsAfter = this.getInvalidNodeCount();
|
|
726
|
+
invalidationsByPath.set(_path, (invalidationsByPath.get(_path) ?? 0) + (invalidationsAfter - invalidationsBefore));
|
|
667
727
|
}
|
|
668
728
|
if ((0, _featureFlags().getFeatureFlag)('fixQuadraticCacheInvalidation')) {
|
|
669
729
|
cleanUpOrphans(this);
|
|
@@ -681,7 +741,10 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
681
741
|
numberOfInvalidatedNodes: invalidatedNodes.size
|
|
682
742
|
}
|
|
683
743
|
});
|
|
684
|
-
return
|
|
744
|
+
return {
|
|
745
|
+
didInvalidate,
|
|
746
|
+
invalidationsByPath
|
|
747
|
+
};
|
|
685
748
|
}
|
|
686
749
|
hasCachedRequestChunk(index) {
|
|
687
750
|
return this.cachedRequestChunks.has(index);
|
|
@@ -692,6 +755,13 @@ class RequestGraph extends _graph().ContentGraph {
|
|
|
692
755
|
removeCachedRequestChunkForNode(nodeId) {
|
|
693
756
|
this.cachedRequestChunks.delete(Math.floor(nodeId / this.nodesPerBlob));
|
|
694
757
|
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Returns the number of invalidated nodes in the graph.
|
|
761
|
+
*/
|
|
762
|
+
getInvalidNodeCount() {
|
|
763
|
+
return this.invalidNodeIds.size;
|
|
764
|
+
}
|
|
695
765
|
}
|
|
696
766
|
exports.RequestGraph = RequestGraph;
|
|
697
767
|
class RequestTracker {
|
|
@@ -759,8 +829,10 @@ class RequestTracker {
|
|
|
759
829
|
return result;
|
|
760
830
|
} else if (node.resultCacheKey != null && ifMatch == null) {
|
|
761
831
|
let key = node.resultCacheKey;
|
|
762
|
-
(0,
|
|
763
|
-
|
|
832
|
+
if (!(0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
|
833
|
+
(0, _assert().default)(this.options.cache.hasLargeBlob(key));
|
|
834
|
+
}
|
|
835
|
+
let cachedResult = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? (0, _nullthrows().default)(await this.options.cache.get(key)) : (0, _buildCache().deserialize)(await this.options.cache.getLargeBlob(key));
|
|
764
836
|
node.result = cachedResult;
|
|
765
837
|
return cachedResult;
|
|
766
838
|
}
|
|
@@ -924,74 +996,99 @@ class RequestTracker {
|
|
|
924
996
|
};
|
|
925
997
|
}
|
|
926
998
|
async writeToCache(signal) {
|
|
999
|
+
const options = this.options;
|
|
1000
|
+
async function runCacheImprovements(newPath, oldPath) {
|
|
1001
|
+
if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
|
1002
|
+
(0, _assert().default)(options.cache instanceof _cache().LMDBLiteCache);
|
|
1003
|
+
const result = await newPath(options.cache);
|
|
1004
|
+
return result;
|
|
1005
|
+
} else {
|
|
1006
|
+
const result = await oldPath();
|
|
1007
|
+
return result;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
927
1010
|
let cacheKey = getCacheKey(this.options);
|
|
928
|
-
let requestGraphKey = `requestGraph-${cacheKey}`;
|
|
1011
|
+
let requestGraphKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/RequestGraph` : `requestGraph-${cacheKey}`;
|
|
1012
|
+
let snapshotKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/snapshot` : `snapshot-${cacheKey}`;
|
|
929
1013
|
if (this.options.shouldDisableCache) {
|
|
930
1014
|
return;
|
|
931
1015
|
}
|
|
932
1016
|
let total = 0;
|
|
933
|
-
(
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
size: this.graph.nodes.length
|
|
938
|
-
});
|
|
939
|
-
let serialisedGraph = this.graph.serialize();
|
|
940
|
-
|
|
941
|
-
// Delete an existing request graph cache, to prevent invalid states
|
|
942
|
-
await this.options.cache.deleteLargeBlob(requestGraphKey);
|
|
943
|
-
const serialiseAndSet = async (key, contents) => {
|
|
944
|
-
if (signal !== null && signal !== void 0 && signal.aborted) {
|
|
945
|
-
throw new Error('Serialization was aborted');
|
|
946
|
-
}
|
|
947
|
-
await this.options.cache.setLargeBlob(key, (0, _buildCache().serialize)(contents), signal ? {
|
|
948
|
-
signal: signal
|
|
949
|
-
} : undefined);
|
|
950
|
-
total += 1;
|
|
1017
|
+
await runCacheImprovements(async cache => {
|
|
1018
|
+
await cache.getNativeRef().startWriteTransaction();
|
|
1019
|
+
}, () => Promise.resolve());
|
|
1020
|
+
try {
|
|
951
1021
|
(0, _ReporterRunner.report)({
|
|
952
1022
|
type: 'cache',
|
|
953
|
-
phase: '
|
|
1023
|
+
phase: 'start',
|
|
954
1024
|
total,
|
|
955
1025
|
size: this.graph.nodes.length
|
|
956
1026
|
});
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1027
|
+
if ((0, _featureFlags().getFeatureFlag)('environmentDeduplication')) {
|
|
1028
|
+
await (0, _EnvironmentManager.writeEnvironmentsToCache)(options.cache);
|
|
1029
|
+
}
|
|
1030
|
+
let serialisedGraph = this.graph.serialize();
|
|
1031
|
+
|
|
1032
|
+
// Delete an existing request graph cache, to prevent invalid states
|
|
1033
|
+
await this.options.cache.deleteLargeBlob(requestGraphKey);
|
|
1034
|
+
const serialiseAndSet = async (key, contents) => {
|
|
1035
|
+
if (signal !== null && signal !== void 0 && signal.aborted) {
|
|
1036
|
+
throw new Error('Serialization was aborted');
|
|
1037
|
+
}
|
|
1038
|
+
await runCacheImprovements(cache => {
|
|
1039
|
+
(0, _logger().instrument)(`RequestTracker::writeToCache::cache.put(${key})`, () => {
|
|
1040
|
+
cache.getNativeRef().putNoConfirm(key, (0, _buildCache().serialize)(contents));
|
|
1041
|
+
});
|
|
1042
|
+
return Promise.resolve();
|
|
1043
|
+
}, async () => {
|
|
1044
|
+
await this.options.cache.setLargeBlob(key, (0, _buildCache().serialize)(contents), signal ? {
|
|
1045
|
+
signal: signal
|
|
1046
|
+
} : undefined);
|
|
1047
|
+
});
|
|
1048
|
+
total += 1;
|
|
1049
|
+
(0, _ReporterRunner.report)({
|
|
1050
|
+
type: 'cache',
|
|
1051
|
+
phase: 'write',
|
|
1052
|
+
total,
|
|
1053
|
+
size: this.graph.nodes.length
|
|
1054
|
+
});
|
|
1055
|
+
};
|
|
1056
|
+
let queue = new (_utils().PromiseQueue)({
|
|
1057
|
+
maxConcurrent: 32
|
|
1058
|
+
});
|
|
961
1059
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1060
|
+
// Preallocating a sparse array is faster than pushing when N is high enough
|
|
1061
|
+
let cacheableNodes = new Array(serialisedGraph.nodes.length);
|
|
1062
|
+
for (let i = 0; i < serialisedGraph.nodes.length; i += 1) {
|
|
1063
|
+
let node = serialisedGraph.nodes[i];
|
|
1064
|
+
let resultCacheKey = node === null || node === void 0 ? void 0 : node.resultCacheKey;
|
|
1065
|
+
if ((node === null || node === void 0 ? void 0 : node.type) === REQUEST && resultCacheKey != null && (node === null || node === void 0 ? void 0 : node.result) != null) {
|
|
1066
|
+
queue.add(() => serialiseAndSet(resultCacheKey, node.result));
|
|
969
1067
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1068
|
+
// eslint-disable-next-line no-unused-vars
|
|
1069
|
+
let {
|
|
1070
|
+
result: _,
|
|
1071
|
+
...newNode
|
|
1072
|
+
} = node;
|
|
1073
|
+
cacheableNodes[i] = newNode;
|
|
1074
|
+
} else {
|
|
1075
|
+
cacheableNodes[i] = node;
|
|
1076
|
+
}
|
|
978
1077
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
}
|
|
1078
|
+
let nodeCountsPerBlob = [];
|
|
1079
|
+
for (let i = 0; i * this.graph.nodesPerBlob < cacheableNodes.length; i += 1) {
|
|
1080
|
+
let nodesStartIndex = i * this.graph.nodesPerBlob;
|
|
1081
|
+
let nodesEndIndex = Math.min((i + 1) * this.graph.nodesPerBlob, cacheableNodes.length);
|
|
1082
|
+
nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
|
|
1083
|
+
if (!this.graph.hasCachedRequestChunk(i)) {
|
|
1084
|
+
// We assume the request graph nodes are immutable and won't change
|
|
1085
|
+
let nodesToCache = cacheableNodes.slice(nodesStartIndex, nodesEndIndex);
|
|
1086
|
+
queue.add(() => serialiseAndSet(getRequestGraphNodeKey(i, cacheKey), nodesToCache).then(() => {
|
|
1087
|
+
// Succeeded in writing to disk, save that we have completed this chunk
|
|
1088
|
+
this.graph.setCachedRequestChunk(i);
|
|
1089
|
+
}));
|
|
1090
|
+
}
|
|
992
1091
|
}
|
|
993
|
-
}
|
|
994
|
-
try {
|
|
995
1092
|
await queue.run();
|
|
996
1093
|
|
|
997
1094
|
// Set the request graph after the queue is flushed to avoid writing an invalid state
|
|
@@ -1000,12 +1097,23 @@ class RequestTracker {
|
|
|
1000
1097
|
nodeCountsPerBlob,
|
|
1001
1098
|
nodes: undefined
|
|
1002
1099
|
});
|
|
1100
|
+
await runCacheImprovements(() => serialiseAndSet(`${cacheKey}/cache_metadata`, {
|
|
1101
|
+
version: _constants.ATLASPACK_VERSION,
|
|
1102
|
+
entries: this.options.entries,
|
|
1103
|
+
mode: this.options.mode,
|
|
1104
|
+
shouldBuildLazily: this.options.shouldBuildLazily,
|
|
1105
|
+
watchBackend: this.options.watchBackend
|
|
1106
|
+
}), () => Promise.resolve());
|
|
1003
1107
|
let opts = getWatcherOptions(this.options);
|
|
1004
|
-
let snapshotPath = _path2().default.join(this.options.cacheDir,
|
|
1108
|
+
let snapshotPath = _path2().default.join(this.options.cacheDir, snapshotKey + '.txt');
|
|
1005
1109
|
await this.options.outputFS.writeSnapshot(this.options.watchDir, snapshotPath, opts);
|
|
1006
1110
|
} catch (err) {
|
|
1007
1111
|
// If we have aborted, ignore the error and continue
|
|
1008
1112
|
if (!(signal !== null && signal !== void 0 && signal.aborted)) throw err;
|
|
1113
|
+
} finally {
|
|
1114
|
+
await runCacheImprovements(async cache => {
|
|
1115
|
+
await cache.getNativeRef().commitWriteTransaction();
|
|
1116
|
+
}, () => Promise.resolve());
|
|
1009
1117
|
}
|
|
1010
1118
|
(0, _ReporterRunner.report)({
|
|
1011
1119
|
type: 'cache',
|
|
@@ -1043,17 +1151,30 @@ function getWatcherOptions({
|
|
|
1043
1151
|
};
|
|
1044
1152
|
}
|
|
1045
1153
|
function getCacheKey(options) {
|
|
1154
|
+
if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
|
1155
|
+
const hash = (0, _rust().hashString)(`${_constants.ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${options.shouldBuildLazily ? 'lazy' : 'eager'}:${options.watchBackend ?? ''}`);
|
|
1156
|
+
return `RequestTracker/${_constants.ATLASPACK_VERSION}/${hash}`;
|
|
1157
|
+
}
|
|
1046
1158
|
return (0, _rust().hashString)(`${_constants.ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${options.shouldBuildLazily ? 'lazy' : 'eager'}:${options.watchBackend ?? ''}`);
|
|
1047
1159
|
}
|
|
1048
1160
|
function getRequestGraphNodeKey(index, cacheKey) {
|
|
1161
|
+
if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
|
1162
|
+
return `${cacheKey}/RequestGraph/nodes/${index}`;
|
|
1163
|
+
}
|
|
1049
1164
|
return `requestGraph-nodes-${index}-${cacheKey}`;
|
|
1050
1165
|
}
|
|
1051
1166
|
async function readAndDeserializeRequestGraph(cache, requestGraphKey, cacheKey) {
|
|
1052
1167
|
let bufferLength = 0;
|
|
1053
1168
|
const getAndDeserialize = async key => {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1169
|
+
if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
|
1170
|
+
const buffer = await cache.getBlob(key);
|
|
1171
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1172
|
+
return (0, _buildCache().deserialize)(buffer);
|
|
1173
|
+
} else {
|
|
1174
|
+
const buffer = await cache.getLargeBlob(key);
|
|
1175
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1176
|
+
return (0, _buildCache().deserialize)(buffer);
|
|
1177
|
+
}
|
|
1057
1178
|
};
|
|
1058
1179
|
let serializedRequestGraph = await getAndDeserialize(requestGraphKey);
|
|
1059
1180
|
let nodePromises = serializedRequestGraph.nodeCountsPerBlob.map(async (nodesCount, i) => {
|
|
@@ -1075,19 +1196,33 @@ async function loadRequestGraph(options) {
|
|
|
1075
1196
|
return new RequestGraph();
|
|
1076
1197
|
}
|
|
1077
1198
|
let cacheKey = getCacheKey(options);
|
|
1078
|
-
let requestGraphKey = `requestGraph-${cacheKey}`;
|
|
1199
|
+
let requestGraphKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/RequestGraph` : `requestGraph-${cacheKey}`;
|
|
1079
1200
|
let timeout;
|
|
1080
|
-
const snapshotKey = `snapshot-${cacheKey}`;
|
|
1201
|
+
const snapshotKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/snapshot` : `snapshot-${cacheKey}`;
|
|
1081
1202
|
const snapshotPath = _path2().default.join(options.cacheDir, snapshotKey + '.txt');
|
|
1203
|
+
const commonMeta = {
|
|
1204
|
+
cacheKey,
|
|
1205
|
+
snapshotKey,
|
|
1206
|
+
cacheKeyOptions: {
|
|
1207
|
+
version: _constants.ATLASPACK_VERSION,
|
|
1208
|
+
entries: options.entries,
|
|
1209
|
+
mode: options.mode,
|
|
1210
|
+
shouldBuildLazily: options.shouldBuildLazily,
|
|
1211
|
+
watchBackend: options.watchBackend
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1082
1214
|
_logger().default.verbose({
|
|
1083
1215
|
origin: '@atlaspack/core',
|
|
1084
1216
|
message: 'Loading request graph',
|
|
1085
1217
|
meta: {
|
|
1086
|
-
|
|
1087
|
-
snapshotKey
|
|
1218
|
+
...commonMeta
|
|
1088
1219
|
}
|
|
1089
1220
|
});
|
|
1090
|
-
if (
|
|
1221
|
+
if ((0, _featureFlags().getFeatureFlag)('environmentDeduplication')) {
|
|
1222
|
+
await (0, _EnvironmentManager.loadEnvironmentsFromCache)(options.cache);
|
|
1223
|
+
}
|
|
1224
|
+
const hasRequestGraphInCache = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? await options.cache.has(requestGraphKey) : await options.cache.hasLargeBlob(requestGraphKey);
|
|
1225
|
+
if (hasRequestGraphInCache) {
|
|
1091
1226
|
try {
|
|
1092
1227
|
let {
|
|
1093
1228
|
requestGraph
|
|
@@ -1106,16 +1241,29 @@ async function loadRequestGraph(options) {
|
|
|
1106
1241
|
origin: '@atlaspack/core',
|
|
1107
1242
|
message: `File system event count: ${events.length}`,
|
|
1108
1243
|
meta: {
|
|
1244
|
+
...commonMeta,
|
|
1109
1245
|
trackableEvent: 'watcher_events_count',
|
|
1110
1246
|
watcherEventCount: events.length,
|
|
1111
1247
|
duration: Date.now() - startTime
|
|
1112
1248
|
}
|
|
1113
1249
|
});
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1250
|
+
if ((0, _featureFlags().getFeatureFlag)('verboseRequestInvalidationStats')) {
|
|
1251
|
+
const invalidationStats = await invalidateRequestGraph(requestGraph, options, events);
|
|
1252
|
+
_logger().default.verbose({
|
|
1253
|
+
origin: '@atlaspack/core',
|
|
1254
|
+
message: 'Request track loaded from cache',
|
|
1255
|
+
meta: {
|
|
1256
|
+
...commonMeta,
|
|
1257
|
+
trackableEvent: 'request_tracker_cache_key_hit',
|
|
1258
|
+
invalidationStats
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
} else {
|
|
1262
|
+
requestGraph.invalidateUnpredictableNodes();
|
|
1263
|
+
requestGraph.invalidateOnBuildNodes();
|
|
1264
|
+
requestGraph.invalidateEnvNodes(options.env);
|
|
1265
|
+
requestGraph.invalidateOptionNodes(options);
|
|
1266
|
+
}
|
|
1119
1267
|
return requestGraph;
|
|
1120
1268
|
} catch (e) {
|
|
1121
1269
|
// Prevent logging fs events took too long warning
|
|
@@ -1130,12 +1278,104 @@ async function loadRequestGraph(options) {
|
|
|
1130
1278
|
origin: '@atlaspack/core',
|
|
1131
1279
|
message: 'Cache entry for request tracker was not found, initializing a clean cache.',
|
|
1132
1280
|
meta: {
|
|
1133
|
-
|
|
1134
|
-
|
|
1281
|
+
...commonMeta,
|
|
1282
|
+
trackableEvent: 'request_tracker_cache_key_miss'
|
|
1135
1283
|
}
|
|
1136
1284
|
});
|
|
1137
1285
|
return new RequestGraph();
|
|
1138
1286
|
}
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* A wrapper around an invalidation type / method
|
|
1290
|
+
*/
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Details about an invalidation.
|
|
1294
|
+
*
|
|
1295
|
+
* If this is a fs events invalidation, this key will contain statistics about invalidations
|
|
1296
|
+
* by path.
|
|
1297
|
+
*
|
|
1298
|
+
* If this is a env or option invalidation, this key will contain the list of changed environment
|
|
1299
|
+
* variables or options.
|
|
1300
|
+
*/
|
|
1301
|
+
|
|
1302
|
+
/**
|
|
1303
|
+
* Number of invalidations for a given file-system event.
|
|
1304
|
+
*/
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Information about a certain cache invalidation type.
|
|
1308
|
+
*/
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* Respond to unpredictable, build, environment changes, option changes and file-system events
|
|
1312
|
+
* invalidating RequestGraph nodes.
|
|
1313
|
+
*
|
|
1314
|
+
* Returns the count of nodes invalidated by each invalidation type.
|
|
1315
|
+
*/
|
|
1316
|
+
async function invalidateRequestGraph(requestGraph, options, events) {
|
|
1317
|
+
const invalidationFns = [{
|
|
1318
|
+
key: 'unpredictable',
|
|
1319
|
+
fn: () => requestGraph.invalidateUnpredictableNodes()
|
|
1320
|
+
}, {
|
|
1321
|
+
key: 'onBuild',
|
|
1322
|
+
fn: () => requestGraph.invalidateOnBuildNodes()
|
|
1323
|
+
}, {
|
|
1324
|
+
key: 'env',
|
|
1325
|
+
fn: () => requestGraph.invalidateEnvNodes(options.env)
|
|
1326
|
+
}, {
|
|
1327
|
+
key: 'option',
|
|
1328
|
+
fn: () => requestGraph.invalidateOptionNodes(options)
|
|
1329
|
+
}, {
|
|
1330
|
+
key: 'fsEvents',
|
|
1331
|
+
fn: () => invalidateRequestGraphFSEvents(requestGraph, options, events)
|
|
1332
|
+
}];
|
|
1333
|
+
const invalidations = [];
|
|
1334
|
+
for (const invalidation of invalidationFns) {
|
|
1335
|
+
invalidations.push(await runInvalidation(requestGraph, invalidation));
|
|
1336
|
+
}
|
|
1337
|
+
const invalidatedCount = invalidations.reduce((acc, invalidation) => acc + invalidation.count, 0);
|
|
1338
|
+
const requestCount = requestGraph.nodes.reduce((acc, node) => acc + ((node === null || node === void 0 ? void 0 : node.type) === REQUEST ? 1 : 0), 0);
|
|
1339
|
+
const nodeCount = requestGraph.nodes.length;
|
|
1340
|
+
return {
|
|
1341
|
+
invalidations,
|
|
1342
|
+
nodeCount,
|
|
1343
|
+
requestCount,
|
|
1344
|
+
invalidatedCount,
|
|
1345
|
+
nodeInvalidationRatio: invalidatedCount / nodeCount,
|
|
1346
|
+
requestInvalidationRatio: invalidatedCount / requestCount
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Invalidate the request graph based on file-system events.
|
|
1351
|
+
*
|
|
1352
|
+
* Returns statistics about the invalidations.
|
|
1353
|
+
*/
|
|
1354
|
+
async function invalidateRequestGraphFSEvents(requestGraph, options, events) {
|
|
1355
|
+
const {
|
|
1356
|
+
invalidationsByPath
|
|
1357
|
+
} = await requestGraph.respondToFSEvents(options.unstableFileInvalidations || events, options, 10000, true);
|
|
1358
|
+
const biggestInvalidations = getBiggestFSEventsInvalidations(invalidationsByPath);
|
|
1359
|
+
return {
|
|
1360
|
+
biggestInvalidations
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Runs an invalidation function and reports metrics.
|
|
1365
|
+
*/
|
|
1366
|
+
async function runInvalidation(requestGraph, invalidationFn) {
|
|
1367
|
+
const start = _perf_hooks().performance.now();
|
|
1368
|
+
const startInvalidationCount = requestGraph.getInvalidNodeCount();
|
|
1369
|
+
const result = await invalidationFn.fn();
|
|
1370
|
+
const count = requestGraph.getInvalidNodeCount() - startInvalidationCount;
|
|
1371
|
+
const duration = _perf_hooks().performance.now() - start;
|
|
1372
|
+
return {
|
|
1373
|
+
key: invalidationFn.key,
|
|
1374
|
+
count,
|
|
1375
|
+
detail: result ?? null,
|
|
1376
|
+
duration
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1139
1379
|
function logErrorOnBailout(options, snapshotPath, e) {
|
|
1140
1380
|
if (e.message && e.message.includes('invalid clockspec')) {
|
|
1141
1381
|
const snapshotContents = options.inputFS.readFileSync(snapshotPath, 'utf-8');
|
|
@@ -1175,4 +1415,19 @@ function cleanUpOrphans(graph) {
|
|
|
1175
1415
|
}
|
|
1176
1416
|
});
|
|
1177
1417
|
return removedNodeIds;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Returns paths that invalidated the most nodes
|
|
1422
|
+
*/
|
|
1423
|
+
function getBiggestFSEventsInvalidations(invalidationsByPath, limit = 10) {
|
|
1424
|
+
const invalidations = [];
|
|
1425
|
+
for (const [path, count] of invalidationsByPath) {
|
|
1426
|
+
invalidations.push({
|
|
1427
|
+
path,
|
|
1428
|
+
count
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
invalidations.sort((a, b) => b.count - a.count);
|
|
1432
|
+
return invalidations.slice(0, limit);
|
|
1178
1433
|
}
|