@atlaspack/core 2.16.2-canary.13 → 2.16.2-canary.131
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 +300 -0
- package/index.d.ts +4 -0
- package/lib/AssetGraph.js +27 -7
- package/lib/Atlaspack.js +35 -27
- package/lib/AtlaspackConfig.schema.js +7 -1
- package/lib/BundleGraph.js +8 -5
- package/lib/Dependency.js +6 -2
- package/lib/Environment.js +5 -3
- package/lib/EnvironmentManager.js +137 -0
- package/lib/InternalConfig.js +3 -2
- package/lib/PackagerRunner.js +54 -16
- package/lib/RequestTracker.js +345 -132
- package/lib/SymbolPropagation.js +14 -0
- package/lib/Transformation.js +2 -2
- package/lib/UncommittedAsset.js +20 -2
- package/lib/applyRuntimes.js +2 -1
- package/lib/assetUtils.js +2 -1
- package/lib/atlaspack-v3/AtlaspackV3.js +16 -3
- package/lib/atlaspack-v3/worker/compat/environment.js +2 -2
- package/lib/atlaspack-v3/worker/compat/mutable-asset.js +6 -6
- package/lib/atlaspack-v3/worker/compat/plugin-config.js +5 -5
- package/lib/atlaspack-v3/worker/index.js +3 -0
- package/lib/atlaspack-v3/worker/worker.js +8 -0
- package/lib/dumpGraphToGraphViz.js +1 -1
- package/lib/index.js +29 -1
- package/lib/public/Asset.js +7 -9
- package/lib/public/Bundle.js +12 -13
- package/lib/public/BundleGraph.js +3 -2
- package/lib/public/BundleGroup.js +2 -3
- package/lib/public/Config.js +95 -8
- package/lib/public/Dependency.js +4 -4
- package/lib/public/Environment.js +2 -3
- package/lib/public/MutableBundleGraph.js +5 -4
- package/lib/public/PluginOptions.js +1 -2
- package/lib/public/Target.js +4 -4
- package/lib/requests/AssetGraphRequest.js +13 -1
- package/lib/requests/AssetGraphRequestRust.js +17 -2
- package/lib/requests/AssetRequest.js +2 -1
- package/lib/requests/BundleGraphRequest.js +13 -1
- package/lib/requests/ConfigRequest.js +27 -4
- package/lib/requests/DevDepRequest.js +11 -1
- package/lib/requests/PathRequest.js +10 -0
- package/lib/requests/TargetRequest.js +18 -16
- package/lib/requests/WriteBundleRequest.js +15 -3
- package/lib/requests/WriteBundlesRequest.js +22 -1
- package/lib/resolveOptions.js +7 -4
- package/lib/worker.js +18 -1
- package/package.json +18 -25
- package/src/AssetGraph.js +30 -7
- package/src/Atlaspack.js +40 -23
- package/src/BundleGraph.js +13 -8
- package/src/Dependency.js +13 -5
- package/src/Environment.js +9 -6
- package/src/EnvironmentManager.js +145 -0
- package/src/InternalConfig.js +6 -5
- package/src/PackagerRunner.js +72 -20
- package/src/RequestTracker.js +526 -157
- package/src/SymbolPropagation.js +13 -1
- package/src/UncommittedAsset.js +23 -3
- package/src/applyRuntimes.js +6 -1
- package/src/assetUtils.js +4 -3
- package/src/atlaspack-v3/AtlaspackV3.js +24 -3
- package/src/atlaspack-v3/worker/compat/plugin-config.js +9 -5
- package/src/atlaspack-v3/worker/index.js +2 -1
- package/src/atlaspack-v3/worker/worker.js +7 -0
- package/src/index.js +5 -1
- package/src/public/Asset.js +13 -6
- package/src/public/Bundle.js +12 -11
- package/src/public/BundleGraph.js +10 -2
- package/src/public/BundleGroup.js +2 -2
- package/src/public/Config.js +132 -18
- package/src/public/Dependency.js +4 -3
- package/src/public/Environment.js +2 -2
- package/src/public/MutableBundleGraph.js +8 -5
- package/src/public/PluginOptions.js +1 -1
- package/src/public/Target.js +4 -3
- package/src/requests/AssetGraphRequest.js +13 -3
- package/src/requests/AssetGraphRequestRust.js +14 -2
- package/src/requests/AssetRequest.js +2 -1
- package/src/requests/BundleGraphRequest.js +13 -3
- package/src/requests/ConfigRequest.js +33 -9
- package/src/requests/DevDepRequest.js +22 -9
- package/src/requests/PathRequest.js +4 -0
- package/src/requests/TargetRequest.js +19 -25
- package/src/requests/WriteBundleRequest.js +14 -8
- package/src/requests/WriteBundlesRequest.js +31 -3
- package/src/resolveOptions.js +4 -2
- package/src/types.js +10 -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 +124 -29
- package/test/public/Config.test.js +108 -0
- package/test/requests/ConfigRequest.test.js +199 -7
- package/test/test-utils.js +4 -9
package/src/RequestTracker.js
CHANGED
|
@@ -30,15 +30,15 @@ import nullthrows from 'nullthrows';
|
|
|
30
30
|
|
|
31
31
|
import {
|
|
32
32
|
ATLASPACK_VERSION,
|
|
33
|
-
VALID,
|
|
34
|
-
INITIAL_BUILD,
|
|
35
33
|
FILE_CREATE,
|
|
36
|
-
FILE_UPDATE,
|
|
37
34
|
FILE_DELETE,
|
|
35
|
+
FILE_UPDATE,
|
|
38
36
|
ENV_CHANGE,
|
|
37
|
+
ERROR,
|
|
38
|
+
INITIAL_BUILD,
|
|
39
39
|
OPTION_CHANGE,
|
|
40
40
|
STARTUP,
|
|
41
|
-
|
|
41
|
+
VALID,
|
|
42
42
|
} from './constants';
|
|
43
43
|
import type {AtlaspackV3} from './atlaspack-v3/AtlaspackV3';
|
|
44
44
|
import {
|
|
@@ -68,7 +68,13 @@ import type {
|
|
|
68
68
|
InternalFileCreateInvalidation,
|
|
69
69
|
InternalGlob,
|
|
70
70
|
} from './types';
|
|
71
|
-
import {BuildAbortError,
|
|
71
|
+
import {BuildAbortError, hashFromOption} from './utils';
|
|
72
|
+
import {performance} from 'perf_hooks';
|
|
73
|
+
|
|
74
|
+
import {
|
|
75
|
+
loadEnvironmentsFromCache,
|
|
76
|
+
writeEnvironmentsToCache,
|
|
77
|
+
} from './EnvironmentManager';
|
|
72
78
|
|
|
73
79
|
export const requestGraphEdgeTypes = {
|
|
74
80
|
subrequest: 2,
|
|
@@ -143,7 +149,7 @@ type OptionNode = {|
|
|
|
143
149
|
type ConfigKeyNode = {|
|
|
144
150
|
id: ContentKey,
|
|
145
151
|
+type: typeof CONFIG_KEY,
|
|
146
|
-
configKey: string,
|
|
152
|
+
configKey: string[],
|
|
147
153
|
contentHash: string,
|
|
148
154
|
|};
|
|
149
155
|
|
|
@@ -215,7 +221,7 @@ export type RunAPI<TResult: RequestResult> = {|
|
|
|
215
221
|
invalidateOnFileUpdate: (ProjectPath) => void,
|
|
216
222
|
invalidateOnConfigKeyChange: (
|
|
217
223
|
filePath: ProjectPath,
|
|
218
|
-
configKey: string,
|
|
224
|
+
configKey: string[],
|
|
219
225
|
contentHash: string,
|
|
220
226
|
) => void,
|
|
221
227
|
invalidateOnStartup: () => void,
|
|
@@ -282,10 +288,12 @@ const nodeFromOption = (option: string, value: mixed): RequestGraphNode => ({
|
|
|
282
288
|
|
|
283
289
|
const nodeFromConfigKey = (
|
|
284
290
|
fileName: ProjectPath,
|
|
285
|
-
configKey: string,
|
|
291
|
+
configKey: string[],
|
|
286
292
|
contentHash: string,
|
|
287
293
|
): RequestGraphNode => ({
|
|
288
|
-
id: `config_key:${fromProjectPathRelative(fileName)}:${
|
|
294
|
+
id: `config_key:${fromProjectPathRelative(fileName)}:${JSON.stringify(
|
|
295
|
+
configKey,
|
|
296
|
+
)}`,
|
|
289
297
|
type: CONFIG_KEY,
|
|
290
298
|
configKey,
|
|
291
299
|
contentHash,
|
|
@@ -446,6 +454,11 @@ export class RequestGraph extends ContentGraph<
|
|
|
446
454
|
this.removeCachedRequestChunkForNode(nodeId);
|
|
447
455
|
}
|
|
448
456
|
|
|
457
|
+
/**
|
|
458
|
+
* Nodes that are invalidated on start-up, such as JavaScript babel configuration files which are
|
|
459
|
+
* imported when the build kicks-off and might doing arbitrary work such as reading from the file
|
|
460
|
+
* system.
|
|
461
|
+
*/
|
|
449
462
|
invalidateUnpredictableNodes() {
|
|
450
463
|
for (let nodeId of this.unpredicatableNodeIds) {
|
|
451
464
|
let node = nullthrows(this.getNode(nodeId));
|
|
@@ -454,6 +467,9 @@ export class RequestGraph extends ContentGraph<
|
|
|
454
467
|
}
|
|
455
468
|
}
|
|
456
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Effectively uncacheable nodes.
|
|
472
|
+
*/
|
|
457
473
|
invalidateOnBuildNodes() {
|
|
458
474
|
for (let nodeId of this.invalidateOnBuildNodeIds) {
|
|
459
475
|
let node = nullthrows(this.getNode(nodeId));
|
|
@@ -462,11 +478,20 @@ export class RequestGraph extends ContentGraph<
|
|
|
462
478
|
}
|
|
463
479
|
}
|
|
464
480
|
|
|
465
|
-
|
|
481
|
+
/**
|
|
482
|
+
* Nodes invalidated by environment changes, corresponds to `env: ...` inputs.
|
|
483
|
+
*/
|
|
484
|
+
invalidateEnvNodes(env: EnvMap): string[] {
|
|
485
|
+
const invalidatedKeys = [];
|
|
486
|
+
|
|
466
487
|
for (let nodeId of this.envNodeIds) {
|
|
467
488
|
let node = nullthrows(this.getNode(nodeId));
|
|
468
489
|
invariant(node.type === ENV);
|
|
469
|
-
|
|
490
|
+
|
|
491
|
+
const key = keyFromEnvContentKey(node.id);
|
|
492
|
+
if (env[key] !== node.value) {
|
|
493
|
+
invalidatedKeys.push(key);
|
|
494
|
+
|
|
470
495
|
let parentNodes = this.getNodeIdsConnectedTo(
|
|
471
496
|
nodeId,
|
|
472
497
|
requestGraphEdgeTypes.invalidated_by_update,
|
|
@@ -476,15 +501,23 @@ export class RequestGraph extends ContentGraph<
|
|
|
476
501
|
}
|
|
477
502
|
}
|
|
478
503
|
}
|
|
504
|
+
|
|
505
|
+
return invalidatedKeys;
|
|
479
506
|
}
|
|
480
507
|
|
|
481
|
-
|
|
508
|
+
/**
|
|
509
|
+
* Nodes invalidated by option changes.
|
|
510
|
+
*/
|
|
511
|
+
invalidateOptionNodes(options: AtlaspackOptions): string[] {
|
|
512
|
+
const invalidatedKeys = [];
|
|
513
|
+
|
|
482
514
|
for (let nodeId of this.optionNodeIds) {
|
|
483
515
|
let node = nullthrows(this.getNode(nodeId));
|
|
484
516
|
invariant(node.type === OPTION);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
) {
|
|
517
|
+
const key = keyFromOptionContentKey(node.id);
|
|
518
|
+
|
|
519
|
+
if (hashFromOption(options[key]) !== node.hash) {
|
|
520
|
+
invalidatedKeys.push(key);
|
|
488
521
|
let parentNodes = this.getNodeIdsConnectedTo(
|
|
489
522
|
nodeId,
|
|
490
523
|
requestGraphEdgeTypes.invalidated_by_update,
|
|
@@ -494,12 +527,14 @@ export class RequestGraph extends ContentGraph<
|
|
|
494
527
|
}
|
|
495
528
|
}
|
|
496
529
|
}
|
|
530
|
+
|
|
531
|
+
return invalidatedKeys;
|
|
497
532
|
}
|
|
498
533
|
|
|
499
534
|
invalidateOnConfigKeyChange(
|
|
500
535
|
requestNodeId: NodeId,
|
|
501
536
|
filePath: ProjectPath,
|
|
502
|
-
configKey: string,
|
|
537
|
+
configKey: string[],
|
|
503
538
|
contentHash: string,
|
|
504
539
|
) {
|
|
505
540
|
let configKeyNodeId = this.addNode(
|
|
@@ -686,8 +721,8 @@ export class RequestGraph extends ContentGraph<
|
|
|
686
721
|
env: string,
|
|
687
722
|
value: string | void,
|
|
688
723
|
) {
|
|
689
|
-
|
|
690
|
-
|
|
724
|
+
const envNode = nodeFromEnv(env, value);
|
|
725
|
+
const envNodeId = this.addNode(envNode);
|
|
691
726
|
|
|
692
727
|
if (
|
|
693
728
|
!this.hasEdge(
|
|
@@ -907,7 +942,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
907
942
|
* True if this is the start-up (loading phase) invalidation.
|
|
908
943
|
*/
|
|
909
944
|
isInitialBuild: boolean = false,
|
|
910
|
-
):
|
|
945
|
+
): Promise<{|
|
|
946
|
+
didInvalidate: boolean,
|
|
947
|
+
invalidationsByPath: Map<string, number>,
|
|
948
|
+
|}> {
|
|
911
949
|
let didInvalidate = false;
|
|
912
950
|
let count = 0;
|
|
913
951
|
let predictedTime = 0;
|
|
@@ -945,7 +983,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
945
983
|
return above;
|
|
946
984
|
};
|
|
947
985
|
|
|
986
|
+
const invalidationsByPath = new Map();
|
|
948
987
|
for (let {path: _path, type} of events) {
|
|
988
|
+
const invalidationsBefore = this.getInvalidNodeCount();
|
|
989
|
+
|
|
949
990
|
if (
|
|
950
991
|
!enableOptimization &&
|
|
951
992
|
process.env.ATLASPACK_DISABLE_CACHE_TIMEOUT !== 'true' &&
|
|
@@ -991,7 +1032,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
991
1032
|
this.invalidNodeIds.add(id);
|
|
992
1033
|
}
|
|
993
1034
|
}
|
|
994
|
-
return
|
|
1035
|
+
return {
|
|
1036
|
+
didInvalidate: true,
|
|
1037
|
+
invalidationsByPath: new Map(),
|
|
1038
|
+
};
|
|
995
1039
|
}
|
|
996
1040
|
|
|
997
1041
|
// sometimes mac os reports update events as create events.
|
|
@@ -1072,11 +1116,18 @@ export class RequestGraph extends ContentGraph<
|
|
|
1072
1116
|
}
|
|
1073
1117
|
|
|
1074
1118
|
let configKeyNodes = this.configKeyNodes.get(_filePath);
|
|
1075
|
-
|
|
1119
|
+
|
|
1120
|
+
// With granular invalidations we will always run this block,
|
|
1121
|
+
// so even if we get a create event (for whatever reason), we will still
|
|
1122
|
+
// try to limit invalidations from config key changes through hashing.
|
|
1123
|
+
//
|
|
1124
|
+
// Currently create events can invalidate a large number of nodes due to
|
|
1125
|
+
// "create above" invalidations.
|
|
1126
|
+
if (configKeyNodes) {
|
|
1076
1127
|
for (let nodeId of configKeyNodes) {
|
|
1077
1128
|
let isInvalid = type === 'delete';
|
|
1078
1129
|
|
|
1079
|
-
if (type
|
|
1130
|
+
if (type !== 'delete') {
|
|
1080
1131
|
let node = this.getNode(nodeId);
|
|
1081
1132
|
invariant(node && node.type === CONFIG_KEY);
|
|
1082
1133
|
|
|
@@ -1104,6 +1155,13 @@ export class RequestGraph extends ContentGraph<
|
|
|
1104
1155
|
}
|
|
1105
1156
|
}
|
|
1106
1157
|
}
|
|
1158
|
+
|
|
1159
|
+
const invalidationsAfter = this.getInvalidNodeCount();
|
|
1160
|
+
const invalidationsForEvent = invalidationsAfter - invalidationsBefore;
|
|
1161
|
+
invalidationsByPath.set(
|
|
1162
|
+
_path,
|
|
1163
|
+
(invalidationsByPath.get(_path) ?? 0) + invalidationsForEvent,
|
|
1164
|
+
);
|
|
1107
1165
|
}
|
|
1108
1166
|
|
|
1109
1167
|
if (getFeatureFlag('fixQuadraticCacheInvalidation')) {
|
|
@@ -1124,7 +1182,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
1124
1182
|
},
|
|
1125
1183
|
});
|
|
1126
1184
|
|
|
1127
|
-
return
|
|
1185
|
+
return {
|
|
1186
|
+
didInvalidate,
|
|
1187
|
+
invalidationsByPath,
|
|
1188
|
+
};
|
|
1128
1189
|
}
|
|
1129
1190
|
|
|
1130
1191
|
hasCachedRequestChunk(index: number): boolean {
|
|
@@ -1138,6 +1199,13 @@ export class RequestGraph extends ContentGraph<
|
|
|
1138
1199
|
removeCachedRequestChunkForNode(nodeId: number): void {
|
|
1139
1200
|
this.cachedRequestChunks.delete(Math.floor(nodeId / this.nodesPerBlob));
|
|
1140
1201
|
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* Returns the number of invalidated nodes in the graph.
|
|
1205
|
+
*/
|
|
1206
|
+
getInvalidNodeCount(): number {
|
|
1207
|
+
return this.invalidNodeIds.size;
|
|
1208
|
+
}
|
|
1141
1209
|
}
|
|
1142
1210
|
|
|
1143
1211
|
export default class RequestTracker {
|
|
@@ -1145,7 +1213,6 @@ export default class RequestTracker {
|
|
|
1145
1213
|
farm: WorkerFarm;
|
|
1146
1214
|
options: AtlaspackOptions;
|
|
1147
1215
|
rustAtlaspack: ?AtlaspackV3;
|
|
1148
|
-
signal: ?AbortSignal;
|
|
1149
1216
|
stats: Map<RequestType, number> = new Map();
|
|
1150
1217
|
|
|
1151
1218
|
constructor({
|
|
@@ -1165,11 +1232,6 @@ export default class RequestTracker {
|
|
|
1165
1232
|
this.rustAtlaspack = rustAtlaspack;
|
|
1166
1233
|
}
|
|
1167
1234
|
|
|
1168
|
-
// TODO: refactor (abortcontroller should be created by RequestTracker)
|
|
1169
|
-
setSignal(signal?: AbortSignal) {
|
|
1170
|
-
this.signal = signal;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
1235
|
startRequest(request: RequestNode): {|
|
|
1174
1236
|
requestNodeId: NodeId,
|
|
1175
1237
|
deferred: Deferred<boolean>,
|
|
@@ -1261,7 +1323,13 @@ export default class RequestTracker {
|
|
|
1261
1323
|
}
|
|
1262
1324
|
}
|
|
1263
1325
|
|
|
1264
|
-
respondToFSEvents(
|
|
1326
|
+
respondToFSEvents(
|
|
1327
|
+
events: Array<Event>,
|
|
1328
|
+
threshold: number,
|
|
1329
|
+
): Promise<{|
|
|
1330
|
+
didInvalidate: boolean,
|
|
1331
|
+
invalidationsByPath: Map<string, number>,
|
|
1332
|
+
|}> {
|
|
1265
1333
|
return this.graph.respondToFSEvents(events, this.options, threshold);
|
|
1266
1334
|
}
|
|
1267
1335
|
|
|
@@ -1344,7 +1412,6 @@ export default class RequestTracker {
|
|
|
1344
1412
|
rustAtlaspack: this.rustAtlaspack,
|
|
1345
1413
|
});
|
|
1346
1414
|
|
|
1347
|
-
assertSignalNotAborted(this.signal);
|
|
1348
1415
|
this.completeRequest(requestNodeId);
|
|
1349
1416
|
|
|
1350
1417
|
deferred.resolve(true);
|
|
@@ -1472,130 +1539,143 @@ export default class RequestTracker {
|
|
|
1472
1539
|
}
|
|
1473
1540
|
}
|
|
1474
1541
|
|
|
1542
|
+
let cacheKey = getCacheKey(this.options);
|
|
1543
|
+
let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1544
|
+
? `${cacheKey}/RequestGraph`
|
|
1545
|
+
: `requestGraph-${cacheKey}`;
|
|
1546
|
+
let snapshotKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1547
|
+
? `${cacheKey}/snapshot`
|
|
1548
|
+
: `snapshot-${cacheKey}`;
|
|
1549
|
+
|
|
1550
|
+
if (this.options.shouldDisableCache) {
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
let total = 0;
|
|
1475
1555
|
await runCacheImprovements(
|
|
1476
1556
|
async (cache) => {
|
|
1477
1557
|
await cache.getNativeRef().startWriteTransaction();
|
|
1478
1558
|
},
|
|
1479
1559
|
() => Promise.resolve(),
|
|
1480
1560
|
);
|
|
1561
|
+
try {
|
|
1562
|
+
report({
|
|
1563
|
+
type: 'cache',
|
|
1564
|
+
phase: 'start',
|
|
1565
|
+
total,
|
|
1566
|
+
size: this.graph.nodes.length,
|
|
1567
|
+
});
|
|
1481
1568
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
if (this.options.shouldDisableCache) {
|
|
1487
|
-
return;
|
|
1488
|
-
}
|
|
1569
|
+
if (getFeatureFlag('environmentDeduplication')) {
|
|
1570
|
+
await writeEnvironmentsToCache(options.cache);
|
|
1571
|
+
}
|
|
1489
1572
|
|
|
1490
|
-
|
|
1491
|
-
report({
|
|
1492
|
-
type: 'cache',
|
|
1493
|
-
phase: 'start',
|
|
1494
|
-
total,
|
|
1495
|
-
size: this.graph.nodes.length,
|
|
1496
|
-
});
|
|
1573
|
+
let serialisedGraph = this.graph.serialize();
|
|
1497
1574
|
|
|
1498
|
-
|
|
1575
|
+
// Delete an existing request graph cache, to prevent invalid states
|
|
1576
|
+
await this.options.cache.deleteLargeBlob(requestGraphKey);
|
|
1499
1577
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1578
|
+
const serialiseAndSet = async (
|
|
1579
|
+
key: string,
|
|
1580
|
+
// $FlowFixMe serialise input is any type
|
|
1581
|
+
contents: any,
|
|
1582
|
+
): Promise<void> => {
|
|
1583
|
+
if (signal?.aborted) {
|
|
1584
|
+
throw new Error('Serialization was aborted');
|
|
1585
|
+
}
|
|
1502
1586
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1587
|
+
await runCacheImprovements(
|
|
1588
|
+
(cache) => {
|
|
1589
|
+
instrument(
|
|
1590
|
+
`RequestTracker::writeToCache::cache.put(${key})`,
|
|
1591
|
+
() => {
|
|
1592
|
+
cache.getNativeRef().putNoConfirm(key, serialize(contents));
|
|
1593
|
+
},
|
|
1594
|
+
);
|
|
1595
|
+
return Promise.resolve();
|
|
1596
|
+
},
|
|
1597
|
+
async () => {
|
|
1598
|
+
await this.options.cache.setLargeBlob(
|
|
1599
|
+
key,
|
|
1600
|
+
serialize(contents),
|
|
1601
|
+
signal
|
|
1602
|
+
? {
|
|
1603
|
+
signal: signal,
|
|
1604
|
+
}
|
|
1605
|
+
: undefined,
|
|
1606
|
+
);
|
|
1607
|
+
},
|
|
1608
|
+
);
|
|
1511
1609
|
|
|
1512
|
-
|
|
1513
|
-
(cache) => {
|
|
1514
|
-
instrument(`cache.put(${key})`, () => {
|
|
1515
|
-
cache.getNativeRef().putNoConfirm(key, serialize(contents));
|
|
1516
|
-
});
|
|
1517
|
-
return Promise.resolve();
|
|
1518
|
-
},
|
|
1519
|
-
async () => {
|
|
1520
|
-
await this.options.cache.setLargeBlob(
|
|
1521
|
-
key,
|
|
1522
|
-
serialize(contents),
|
|
1523
|
-
signal
|
|
1524
|
-
? {
|
|
1525
|
-
signal: signal,
|
|
1526
|
-
}
|
|
1527
|
-
: undefined,
|
|
1528
|
-
);
|
|
1529
|
-
},
|
|
1530
|
-
);
|
|
1610
|
+
total += 1;
|
|
1531
1611
|
|
|
1532
|
-
|
|
1612
|
+
report({
|
|
1613
|
+
type: 'cache',
|
|
1614
|
+
phase: 'write',
|
|
1615
|
+
total,
|
|
1616
|
+
size: this.graph.nodes.length,
|
|
1617
|
+
});
|
|
1618
|
+
};
|
|
1533
1619
|
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
phase: 'write',
|
|
1537
|
-
total,
|
|
1538
|
-
size: this.graph.nodes.length,
|
|
1620
|
+
let queue = new PromiseQueue({
|
|
1621
|
+
maxConcurrent: 32,
|
|
1539
1622
|
});
|
|
1540
|
-
};
|
|
1541
1623
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1624
|
+
// Preallocating a sparse array is faster than pushing when N is high enough
|
|
1625
|
+
let cacheableNodes = new Array(serialisedGraph.nodes.length);
|
|
1626
|
+
for (let i = 0; i < serialisedGraph.nodes.length; i += 1) {
|
|
1627
|
+
let node = serialisedGraph.nodes[i];
|
|
1545
1628
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
node?.type === REQUEST &&
|
|
1554
|
-
resultCacheKey != null &&
|
|
1555
|
-
node?.result != null
|
|
1556
|
-
) {
|
|
1557
|
-
queue.add(() => serialiseAndSet(resultCacheKey, node.result));
|
|
1629
|
+
let resultCacheKey = node?.resultCacheKey;
|
|
1630
|
+
if (
|
|
1631
|
+
node?.type === REQUEST &&
|
|
1632
|
+
resultCacheKey != null &&
|
|
1633
|
+
node?.result != null
|
|
1634
|
+
) {
|
|
1635
|
+
queue.add(() => serialiseAndSet(resultCacheKey, node.result));
|
|
1558
1636
|
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1637
|
+
// eslint-disable-next-line no-unused-vars
|
|
1638
|
+
let {result: _, ...newNode} = node;
|
|
1639
|
+
cacheableNodes[i] = newNode;
|
|
1640
|
+
} else {
|
|
1641
|
+
cacheableNodes[i] = node;
|
|
1642
|
+
}
|
|
1564
1643
|
}
|
|
1565
|
-
}
|
|
1566
1644
|
|
|
1567
|
-
|
|
1645
|
+
let nodeCountsPerBlob = [];
|
|
1568
1646
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1647
|
+
for (
|
|
1648
|
+
let i = 0;
|
|
1649
|
+
i * this.graph.nodesPerBlob < cacheableNodes.length;
|
|
1650
|
+
i += 1
|
|
1651
|
+
) {
|
|
1652
|
+
let nodesStartIndex = i * this.graph.nodesPerBlob;
|
|
1653
|
+
let nodesEndIndex = Math.min(
|
|
1654
|
+
(i + 1) * this.graph.nodesPerBlob,
|
|
1655
|
+
cacheableNodes.length,
|
|
1656
|
+
);
|
|
1579
1657
|
|
|
1580
|
-
|
|
1658
|
+
nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
|
|
1581
1659
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1660
|
+
if (!this.graph.hasCachedRequestChunk(i)) {
|
|
1661
|
+
// We assume the request graph nodes are immutable and won't change
|
|
1662
|
+
let nodesToCache = cacheableNodes.slice(
|
|
1663
|
+
nodesStartIndex,
|
|
1664
|
+
nodesEndIndex,
|
|
1665
|
+
);
|
|
1585
1666
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1667
|
+
queue.add(() =>
|
|
1668
|
+
serialiseAndSet(
|
|
1669
|
+
getRequestGraphNodeKey(i, cacheKey),
|
|
1670
|
+
nodesToCache,
|
|
1671
|
+
).then(() => {
|
|
1672
|
+
// Succeeded in writing to disk, save that we have completed this chunk
|
|
1673
|
+
this.graph.setCachedRequestChunk(i);
|
|
1674
|
+
}),
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1595
1677
|
}
|
|
1596
|
-
}
|
|
1597
1678
|
|
|
1598
|
-
try {
|
|
1599
1679
|
await queue.run();
|
|
1600
1680
|
|
|
1601
1681
|
// Set the request graph after the queue is flushed to avoid writing an invalid state
|
|
@@ -1607,7 +1687,7 @@ export default class RequestTracker {
|
|
|
1607
1687
|
|
|
1608
1688
|
await runCacheImprovements(
|
|
1609
1689
|
() =>
|
|
1610
|
-
serialiseAndSet(
|
|
1690
|
+
serialiseAndSet(`${cacheKey}/cache_metadata`, {
|
|
1611
1691
|
version: ATLASPACK_VERSION,
|
|
1612
1692
|
entries: this.options.entries,
|
|
1613
1693
|
mode: this.options.mode,
|
|
@@ -1628,15 +1708,15 @@ export default class RequestTracker {
|
|
|
1628
1708
|
} catch (err) {
|
|
1629
1709
|
// If we have aborted, ignore the error and continue
|
|
1630
1710
|
if (!signal?.aborted) throw err;
|
|
1711
|
+
} finally {
|
|
1712
|
+
await runCacheImprovements(
|
|
1713
|
+
async (cache) => {
|
|
1714
|
+
await cache.getNativeRef().commitWriteTransaction();
|
|
1715
|
+
},
|
|
1716
|
+
() => Promise.resolve(),
|
|
1717
|
+
);
|
|
1631
1718
|
}
|
|
1632
1719
|
|
|
1633
|
-
await runCacheImprovements(
|
|
1634
|
-
async (cache) => {
|
|
1635
|
-
await cache.getNativeRef().commitWriteTransaction();
|
|
1636
|
-
},
|
|
1637
|
-
() => Promise.resolve(),
|
|
1638
|
-
);
|
|
1639
|
-
|
|
1640
1720
|
report({type: 'cache', phase: 'end', total, size: this.graph.nodes.length});
|
|
1641
1721
|
}
|
|
1642
1722
|
|
|
@@ -1668,6 +1748,18 @@ export function getWatcherOptions({
|
|
|
1668
1748
|
}
|
|
1669
1749
|
|
|
1670
1750
|
function getCacheKey(options) {
|
|
1751
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1752
|
+
const hash = hashString(
|
|
1753
|
+
`${ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${
|
|
1754
|
+
options.mode
|
|
1755
|
+
}:${options.shouldBuildLazily ? 'lazy' : 'eager'}:${
|
|
1756
|
+
options.watchBackend ?? ''
|
|
1757
|
+
}`,
|
|
1758
|
+
);
|
|
1759
|
+
|
|
1760
|
+
return `RequestTracker/${ATLASPACK_VERSION}/${hash}`;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1671
1763
|
return hashString(
|
|
1672
1764
|
`${ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${
|
|
1673
1765
|
options.shouldBuildLazily ? 'lazy' : 'eager'
|
|
@@ -1676,6 +1768,10 @@ function getCacheKey(options) {
|
|
|
1676
1768
|
}
|
|
1677
1769
|
|
|
1678
1770
|
function getRequestGraphNodeKey(index: number, cacheKey: string) {
|
|
1771
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1772
|
+
return `${cacheKey}/RequestGraph/nodes/${index}`;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1679
1775
|
return `requestGraph-nodes-${index}-${cacheKey}`;
|
|
1680
1776
|
}
|
|
1681
1777
|
|
|
@@ -1687,9 +1783,15 @@ export async function readAndDeserializeRequestGraph(
|
|
|
1687
1783
|
let bufferLength = 0;
|
|
1688
1784
|
|
|
1689
1785
|
const getAndDeserialize = async (key: string) => {
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1786
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1787
|
+
const buffer = await cache.getBlob(key);
|
|
1788
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1789
|
+
return deserialize(buffer);
|
|
1790
|
+
} else {
|
|
1791
|
+
const buffer = await cache.getLargeBlob(key);
|
|
1792
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1793
|
+
return deserialize(buffer);
|
|
1794
|
+
}
|
|
1693
1795
|
};
|
|
1694
1796
|
|
|
1695
1797
|
let serializedRequestGraph = await getAndDeserialize(requestGraphKey);
|
|
@@ -1722,21 +1824,45 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1722
1824
|
}
|
|
1723
1825
|
|
|
1724
1826
|
let cacheKey = getCacheKey(options);
|
|
1725
|
-
let requestGraphKey =
|
|
1827
|
+
let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1828
|
+
? `${cacheKey}/RequestGraph`
|
|
1829
|
+
: `requestGraph-${cacheKey}`;
|
|
1830
|
+
|
|
1726
1831
|
let timeout;
|
|
1727
|
-
const snapshotKey =
|
|
1832
|
+
const snapshotKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1833
|
+
? `${cacheKey}/snapshot`
|
|
1834
|
+
: `snapshot-${cacheKey}`;
|
|
1728
1835
|
const snapshotPath = path.join(options.cacheDir, snapshotKey + '.txt');
|
|
1729
1836
|
|
|
1837
|
+
const commonMeta = {
|
|
1838
|
+
cacheKey,
|
|
1839
|
+
snapshotKey,
|
|
1840
|
+
cacheKeyOptions: {
|
|
1841
|
+
version: ATLASPACK_VERSION,
|
|
1842
|
+
entries: options.entries,
|
|
1843
|
+
mode: options.mode,
|
|
1844
|
+
shouldBuildLazily: options.shouldBuildLazily,
|
|
1845
|
+
watchBackend: options.watchBackend,
|
|
1846
|
+
},
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1730
1849
|
logger.verbose({
|
|
1731
1850
|
origin: '@atlaspack/core',
|
|
1732
1851
|
message: 'Loading request graph',
|
|
1733
1852
|
meta: {
|
|
1734
|
-
|
|
1735
|
-
snapshotKey,
|
|
1853
|
+
...commonMeta,
|
|
1736
1854
|
},
|
|
1737
1855
|
});
|
|
1738
1856
|
|
|
1739
|
-
if (
|
|
1857
|
+
if (getFeatureFlag('environmentDeduplication')) {
|
|
1858
|
+
await loadEnvironmentsFromCache(options.cache);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
const hasRequestGraphInCache = getFeatureFlag('cachePerformanceImprovements')
|
|
1862
|
+
? await options.cache.has(requestGraphKey)
|
|
1863
|
+
: await options.cache.hasLargeBlob(requestGraphKey);
|
|
1864
|
+
|
|
1865
|
+
if (hasRequestGraphInCache) {
|
|
1740
1866
|
try {
|
|
1741
1867
|
let {requestGraph} = await readAndDeserializeRequestGraph(
|
|
1742
1868
|
options.cache,
|
|
@@ -1767,23 +1893,36 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1767
1893
|
origin: '@atlaspack/core',
|
|
1768
1894
|
message: `File system event count: ${events.length}`,
|
|
1769
1895
|
meta: {
|
|
1896
|
+
...commonMeta,
|
|
1770
1897
|
trackableEvent: 'watcher_events_count',
|
|
1771
1898
|
watcherEventCount: events.length,
|
|
1772
1899
|
duration: Date.now() - startTime,
|
|
1773
1900
|
},
|
|
1774
1901
|
});
|
|
1775
1902
|
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1903
|
+
if (getFeatureFlag('verboseRequestInvalidationStats')) {
|
|
1904
|
+
const invalidationStats = await invalidateRequestGraph(
|
|
1905
|
+
requestGraph,
|
|
1906
|
+
options,
|
|
1907
|
+
events,
|
|
1908
|
+
);
|
|
1909
|
+
|
|
1910
|
+
logger.verbose({
|
|
1911
|
+
origin: '@atlaspack/core',
|
|
1912
|
+
message: 'Request track loaded from cache',
|
|
1913
|
+
meta: {
|
|
1914
|
+
...commonMeta,
|
|
1915
|
+
trackableEvent: 'request_tracker_cache_key_hit',
|
|
1916
|
+
invalidationStats,
|
|
1917
|
+
},
|
|
1918
|
+
});
|
|
1919
|
+
} else {
|
|
1920
|
+
requestGraph.invalidateUnpredictableNodes();
|
|
1921
|
+
requestGraph.invalidateOnBuildNodes();
|
|
1922
|
+
requestGraph.invalidateEnvNodes(options.env);
|
|
1923
|
+
requestGraph.invalidateOptionNodes(options);
|
|
1924
|
+
}
|
|
1780
1925
|
|
|
1781
|
-
await requestGraph.respondToFSEvents(
|
|
1782
|
-
options.unstableFileInvalidations || events,
|
|
1783
|
-
options,
|
|
1784
|
-
10000,
|
|
1785
|
-
true,
|
|
1786
|
-
);
|
|
1787
1926
|
return requestGraph;
|
|
1788
1927
|
} catch (e) {
|
|
1789
1928
|
// Prevent logging fs events took too long warning
|
|
@@ -1800,13 +1939,227 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1800
1939
|
message:
|
|
1801
1940
|
'Cache entry for request tracker was not found, initializing a clean cache.',
|
|
1802
1941
|
meta: {
|
|
1803
|
-
|
|
1804
|
-
|
|
1942
|
+
...commonMeta,
|
|
1943
|
+
trackableEvent: 'request_tracker_cache_key_miss',
|
|
1805
1944
|
},
|
|
1806
1945
|
});
|
|
1807
1946
|
return new RequestGraph();
|
|
1808
1947
|
}
|
|
1809
1948
|
|
|
1949
|
+
/**
|
|
1950
|
+
* A wrapper around an invalidation type / method
|
|
1951
|
+
*/
|
|
1952
|
+
type InvalidationFn = {|
|
|
1953
|
+
key: string,
|
|
1954
|
+
fn: () =>
|
|
1955
|
+
| InvalidationDetail
|
|
1956
|
+
| Promise<InvalidationDetail>
|
|
1957
|
+
| void
|
|
1958
|
+
| Promise<void>,
|
|
1959
|
+
|};
|
|
1960
|
+
|
|
1961
|
+
type InvalidationStats = {|
|
|
1962
|
+
/**
|
|
1963
|
+
* Total number of request graph nodes
|
|
1964
|
+
*/
|
|
1965
|
+
nodeCount: number,
|
|
1966
|
+
/**
|
|
1967
|
+
* Number of requests in RequestGraph
|
|
1968
|
+
*/
|
|
1969
|
+
requestCount: number,
|
|
1970
|
+
/**
|
|
1971
|
+
* Number of nodes that have been invalidated.
|
|
1972
|
+
*/
|
|
1973
|
+
invalidatedCount: number,
|
|
1974
|
+
/**
|
|
1975
|
+
* Percentage of requests that have been invalidated
|
|
1976
|
+
*/
|
|
1977
|
+
requestInvalidationRatio: number,
|
|
1978
|
+
/**
|
|
1979
|
+
* Percentage of nodes that have been invalidated
|
|
1980
|
+
*/
|
|
1981
|
+
nodeInvalidationRatio: number,
|
|
1982
|
+
/**
|
|
1983
|
+
* Details for each invalidation type
|
|
1984
|
+
*/
|
|
1985
|
+
invalidations: InvalidationFnStats[],
|
|
1986
|
+
|};
|
|
1987
|
+
|
|
1988
|
+
/**
|
|
1989
|
+
* Details about an invalidation.
|
|
1990
|
+
*
|
|
1991
|
+
* If this is a fs events invalidation, this key will contain statistics about invalidations
|
|
1992
|
+
* by path.
|
|
1993
|
+
*
|
|
1994
|
+
* If this is a env or option invalidation, this key will contain the list of changed environment
|
|
1995
|
+
* variables or options.
|
|
1996
|
+
*/
|
|
1997
|
+
type InvalidationDetail = string[] | FSInvalidationStats;
|
|
1998
|
+
|
|
1999
|
+
/**
|
|
2000
|
+
* Number of invalidations for a given file-system event.
|
|
2001
|
+
*/
|
|
2002
|
+
type FSInvalidation = {|
|
|
2003
|
+
path: string,
|
|
2004
|
+
count: number,
|
|
2005
|
+
|};
|
|
2006
|
+
|
|
2007
|
+
type FSInvalidationStats = {|
|
|
2008
|
+
/**
|
|
2009
|
+
* This list will be sorted by the number of nodes invalidated and only the top 10 will be
|
|
2010
|
+
* included.
|
|
2011
|
+
*/
|
|
2012
|
+
biggestInvalidations: FSInvalidation[],
|
|
2013
|
+
|};
|
|
2014
|
+
|
|
2015
|
+
/**
|
|
2016
|
+
* Information about a certain cache invalidation type.
|
|
2017
|
+
*/
|
|
2018
|
+
type InvalidationFnStats = {|
|
|
2019
|
+
/**
|
|
2020
|
+
* Invalidation type, one of:
|
|
2021
|
+
*
|
|
2022
|
+
* - unpredictable
|
|
2023
|
+
* - onBuild
|
|
2024
|
+
* - env
|
|
2025
|
+
* - option
|
|
2026
|
+
* - fsEvents
|
|
2027
|
+
*/
|
|
2028
|
+
key: string,
|
|
2029
|
+
/**
|
|
2030
|
+
* Number of invalidated nodes coming from this invalidation type.
|
|
2031
|
+
*/
|
|
2032
|
+
count: number,
|
|
2033
|
+
/**
|
|
2034
|
+
* If this is a env or option invalidation, this key will contain the list of changed values.
|
|
2035
|
+
*
|
|
2036
|
+
* If this is a fs events invalidation, this key will contain statistics about invalidations
|
|
2037
|
+
*/
|
|
2038
|
+
detail: null | InvalidationDetail,
|
|
2039
|
+
/**
|
|
2040
|
+
* Time in milliseconds it took to run the invalidation.
|
|
2041
|
+
*/
|
|
2042
|
+
duration: number,
|
|
2043
|
+
|};
|
|
2044
|
+
|
|
2045
|
+
/**
|
|
2046
|
+
* Respond to unpredictable, build, environment changes, option changes and file-system events
|
|
2047
|
+
* invalidating RequestGraph nodes.
|
|
2048
|
+
*
|
|
2049
|
+
* Returns the count of nodes invalidated by each invalidation type.
|
|
2050
|
+
*/
|
|
2051
|
+
export async function invalidateRequestGraph(
|
|
2052
|
+
requestGraph: RequestGraph,
|
|
2053
|
+
options: AtlaspackOptions,
|
|
2054
|
+
events: Event[],
|
|
2055
|
+
): Promise<InvalidationStats> {
|
|
2056
|
+
const invalidationFns: InvalidationFn[] = [
|
|
2057
|
+
{
|
|
2058
|
+
key: 'unpredictable',
|
|
2059
|
+
fn: () => requestGraph.invalidateUnpredictableNodes(),
|
|
2060
|
+
},
|
|
2061
|
+
{
|
|
2062
|
+
key: 'onBuild',
|
|
2063
|
+
fn: () => requestGraph.invalidateOnBuildNodes(),
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
key: 'env',
|
|
2067
|
+
fn: () => requestGraph.invalidateEnvNodes(options.env),
|
|
2068
|
+
},
|
|
2069
|
+
{
|
|
2070
|
+
key: 'option',
|
|
2071
|
+
fn: () => requestGraph.invalidateOptionNodes(options),
|
|
2072
|
+
},
|
|
2073
|
+
{
|
|
2074
|
+
key: 'fsEvents',
|
|
2075
|
+
fn: () => invalidateRequestGraphFSEvents(requestGraph, options, events),
|
|
2076
|
+
},
|
|
2077
|
+
];
|
|
2078
|
+
|
|
2079
|
+
const invalidations = [];
|
|
2080
|
+
for (const invalidation of invalidationFns) {
|
|
2081
|
+
invalidations.push(await runInvalidation(requestGraph, invalidation));
|
|
2082
|
+
}
|
|
2083
|
+
const invalidatedCount = invalidations.reduce(
|
|
2084
|
+
(acc, invalidation) => acc + invalidation.count,
|
|
2085
|
+
0,
|
|
2086
|
+
);
|
|
2087
|
+
const requestCount = requestGraph.nodes.reduce(
|
|
2088
|
+
(acc, node) => acc + (node?.type === REQUEST ? 1 : 0),
|
|
2089
|
+
0,
|
|
2090
|
+
);
|
|
2091
|
+
const nodeCount = requestGraph.nodes.length;
|
|
2092
|
+
const nodeInvalidationRatio = invalidatedCount / nodeCount;
|
|
2093
|
+
const requestInvalidationRatio = invalidatedCount / requestCount;
|
|
2094
|
+
|
|
2095
|
+
return {
|
|
2096
|
+
invalidations,
|
|
2097
|
+
nodeCount,
|
|
2098
|
+
requestCount,
|
|
2099
|
+
invalidatedCount,
|
|
2100
|
+
nodeInvalidationRatio,
|
|
2101
|
+
requestInvalidationRatio,
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
interface InvalidateRequestGraphFSEventsInput {
|
|
2106
|
+
respondToFSEvents(
|
|
2107
|
+
events: Event[],
|
|
2108
|
+
options: AtlaspackOptions,
|
|
2109
|
+
timeout: number,
|
|
2110
|
+
shouldLog: boolean,
|
|
2111
|
+
): Promise<{invalidationsByPath: Map<string, number>, ...}>;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
/**
|
|
2115
|
+
* Invalidate the request graph based on file-system events.
|
|
2116
|
+
*
|
|
2117
|
+
* Returns statistics about the invalidations.
|
|
2118
|
+
*/
|
|
2119
|
+
export async function invalidateRequestGraphFSEvents(
|
|
2120
|
+
requestGraph: InvalidateRequestGraphFSEventsInput,
|
|
2121
|
+
options: AtlaspackOptions,
|
|
2122
|
+
events: Event[],
|
|
2123
|
+
): Promise<FSInvalidationStats> {
|
|
2124
|
+
const {invalidationsByPath} = await requestGraph.respondToFSEvents(
|
|
2125
|
+
options.unstableFileInvalidations || events,
|
|
2126
|
+
options,
|
|
2127
|
+
10000,
|
|
2128
|
+
true,
|
|
2129
|
+
);
|
|
2130
|
+
const biggestInvalidations =
|
|
2131
|
+
getBiggestFSEventsInvalidations(invalidationsByPath);
|
|
2132
|
+
|
|
2133
|
+
return {
|
|
2134
|
+
biggestInvalidations,
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
interface RunInvalidationInput {
|
|
2139
|
+
getInvalidNodeCount(): number;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
/**
|
|
2143
|
+
* Runs an invalidation function and reports metrics.
|
|
2144
|
+
*/
|
|
2145
|
+
export async function runInvalidation(
|
|
2146
|
+
requestGraph: RunInvalidationInput,
|
|
2147
|
+
invalidationFn: InvalidationFn,
|
|
2148
|
+
): Promise<InvalidationFnStats> {
|
|
2149
|
+
const start = performance.now();
|
|
2150
|
+
const startInvalidationCount = requestGraph.getInvalidNodeCount();
|
|
2151
|
+
const result = await invalidationFn.fn();
|
|
2152
|
+
const count = requestGraph.getInvalidNodeCount() - startInvalidationCount;
|
|
2153
|
+
const duration = performance.now() - start;
|
|
2154
|
+
|
|
2155
|
+
return {
|
|
2156
|
+
key: invalidationFn.key,
|
|
2157
|
+
count,
|
|
2158
|
+
detail: result ?? null,
|
|
2159
|
+
duration,
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
|
|
1810
2163
|
function logErrorOnBailout(
|
|
1811
2164
|
options: AtlaspackOptions,
|
|
1812
2165
|
snapshotPath: string,
|
|
@@ -1858,3 +2211,19 @@ export function cleanUpOrphans<N, E: number>(graph: Graph<N, E>): NodeId[] {
|
|
|
1858
2211
|
|
|
1859
2212
|
return removedNodeIds;
|
|
1860
2213
|
}
|
|
2214
|
+
|
|
2215
|
+
/**
|
|
2216
|
+
* Returns paths that invalidated the most nodes
|
|
2217
|
+
*/
|
|
2218
|
+
export function getBiggestFSEventsInvalidations(
|
|
2219
|
+
invalidationsByPath: Map<string, number>,
|
|
2220
|
+
limit: number = 10,
|
|
2221
|
+
): Array<FSInvalidation> {
|
|
2222
|
+
const invalidations = [];
|
|
2223
|
+
for (const [path, count] of invalidationsByPath) {
|
|
2224
|
+
invalidations.push({path, count});
|
|
2225
|
+
}
|
|
2226
|
+
invalidations.sort((a, b) => b.count - a.count);
|
|
2227
|
+
|
|
2228
|
+
return invalidations.slice(0, limit);
|
|
2229
|
+
}
|