@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/src/RequestTracker.js
CHANGED
|
@@ -4,7 +4,7 @@ import invariant, {AssertionError} from 'assert';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
|
|
6
6
|
import {deserialize, serialize} from '@atlaspack/build-cache';
|
|
7
|
-
import type
|
|
7
|
+
import {LMDBLiteCache, type Cache} from '@atlaspack/cache';
|
|
8
8
|
import {getFeatureFlag} from '@atlaspack/feature-flags';
|
|
9
9
|
import {ContentGraph} from '@atlaspack/graph';
|
|
10
10
|
import type {
|
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
SerializedContentGraph,
|
|
15
15
|
Graph,
|
|
16
16
|
} from '@atlaspack/graph';
|
|
17
|
-
import logger from '@atlaspack/logger';
|
|
17
|
+
import logger, {instrument} from '@atlaspack/logger';
|
|
18
18
|
import {hashString} from '@atlaspack/rust';
|
|
19
19
|
import type {Async, EnvMap} from '@atlaspack/types';
|
|
20
20
|
import {
|
|
@@ -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 {
|
|
@@ -69,6 +69,12 @@ import type {
|
|
|
69
69
|
InternalGlob,
|
|
70
70
|
} from './types';
|
|
71
71
|
import {BuildAbortError, assertSignalNotAborted, 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,22 @@ 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
|
+
const isConfigKeyChange =
|
|
1127
|
+
getFeatureFlag('granularTsConfigInvalidation') ||
|
|
1128
|
+
type === 'delete' ||
|
|
1129
|
+
type === 'update';
|
|
1130
|
+
if (configKeyNodes && isConfigKeyChange) {
|
|
1076
1131
|
for (let nodeId of configKeyNodes) {
|
|
1077
1132
|
let isInvalid = type === 'delete';
|
|
1078
1133
|
|
|
1079
|
-
if (type
|
|
1134
|
+
if (type !== 'delete') {
|
|
1080
1135
|
let node = this.getNode(nodeId);
|
|
1081
1136
|
invariant(node && node.type === CONFIG_KEY);
|
|
1082
1137
|
|
|
@@ -1104,6 +1159,13 @@ export class RequestGraph extends ContentGraph<
|
|
|
1104
1159
|
}
|
|
1105
1160
|
}
|
|
1106
1161
|
}
|
|
1162
|
+
|
|
1163
|
+
const invalidationsAfter = this.getInvalidNodeCount();
|
|
1164
|
+
const invalidationsForEvent = invalidationsAfter - invalidationsBefore;
|
|
1165
|
+
invalidationsByPath.set(
|
|
1166
|
+
_path,
|
|
1167
|
+
(invalidationsByPath.get(_path) ?? 0) + invalidationsForEvent,
|
|
1168
|
+
);
|
|
1107
1169
|
}
|
|
1108
1170
|
|
|
1109
1171
|
if (getFeatureFlag('fixQuadraticCacheInvalidation')) {
|
|
@@ -1124,7 +1186,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
1124
1186
|
},
|
|
1125
1187
|
});
|
|
1126
1188
|
|
|
1127
|
-
return
|
|
1189
|
+
return {
|
|
1190
|
+
didInvalidate,
|
|
1191
|
+
invalidationsByPath,
|
|
1192
|
+
};
|
|
1128
1193
|
}
|
|
1129
1194
|
|
|
1130
1195
|
hasCachedRequestChunk(index: number): boolean {
|
|
@@ -1138,6 +1203,13 @@ export class RequestGraph extends ContentGraph<
|
|
|
1138
1203
|
removeCachedRequestChunkForNode(nodeId: number): void {
|
|
1139
1204
|
this.cachedRequestChunks.delete(Math.floor(nodeId / this.nodesPerBlob));
|
|
1140
1205
|
}
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* Returns the number of invalidated nodes in the graph.
|
|
1209
|
+
*/
|
|
1210
|
+
getInvalidNodeCount(): number {
|
|
1211
|
+
return this.invalidNodeIds.size;
|
|
1212
|
+
}
|
|
1141
1213
|
}
|
|
1142
1214
|
|
|
1143
1215
|
export default class RequestTracker {
|
|
@@ -1228,10 +1300,13 @@ export default class RequestTracker {
|
|
|
1228
1300
|
return result;
|
|
1229
1301
|
} else if (node.resultCacheKey != null && ifMatch == null) {
|
|
1230
1302
|
let key = node.resultCacheKey;
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1303
|
+
if (!getFeatureFlag('cachePerformanceImprovements')) {
|
|
1304
|
+
invariant(this.options.cache.hasLargeBlob(key));
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
let cachedResult: T = getFeatureFlag('cachePerformanceImprovements')
|
|
1308
|
+
? nullthrows(await this.options.cache.get<T>(key))
|
|
1309
|
+
: deserialize(await this.options.cache.getLargeBlob(key));
|
|
1235
1310
|
node.result = cachedResult;
|
|
1236
1311
|
return cachedResult;
|
|
1237
1312
|
}
|
|
@@ -1258,7 +1333,13 @@ export default class RequestTracker {
|
|
|
1258
1333
|
}
|
|
1259
1334
|
}
|
|
1260
1335
|
|
|
1261
|
-
respondToFSEvents(
|
|
1336
|
+
respondToFSEvents(
|
|
1337
|
+
events: Array<Event>,
|
|
1338
|
+
threshold: number,
|
|
1339
|
+
): Promise<{|
|
|
1340
|
+
didInvalidate: boolean,
|
|
1341
|
+
invalidationsByPath: Map<string, number>,
|
|
1342
|
+
|}> {
|
|
1262
1343
|
return this.graph.respondToFSEvents(events, this.options, threshold);
|
|
1263
1344
|
}
|
|
1264
1345
|
|
|
@@ -1454,113 +1535,158 @@ export default class RequestTracker {
|
|
|
1454
1535
|
}
|
|
1455
1536
|
|
|
1456
1537
|
async writeToCache(signal?: AbortSignal) {
|
|
1538
|
+
const options = this.options;
|
|
1539
|
+
async function runCacheImprovements<T>(
|
|
1540
|
+
newPath: (cache: LMDBLiteCache) => Promise<T>,
|
|
1541
|
+
oldPath: () => Promise<T>,
|
|
1542
|
+
): Promise<T> {
|
|
1543
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1544
|
+
invariant(options.cache instanceof LMDBLiteCache);
|
|
1545
|
+
const result = await newPath(options.cache);
|
|
1546
|
+
return result;
|
|
1547
|
+
} else {
|
|
1548
|
+
const result = await oldPath();
|
|
1549
|
+
return result;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1457
1553
|
let cacheKey = getCacheKey(this.options);
|
|
1458
|
-
let requestGraphKey =
|
|
1459
|
-
|
|
1554
|
+
let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1555
|
+
? `${cacheKey}/RequestGraph`
|
|
1556
|
+
: `requestGraph-${cacheKey}`;
|
|
1557
|
+
let snapshotKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1558
|
+
? `${cacheKey}/snapshot`
|
|
1559
|
+
: `snapshot-${cacheKey}`;
|
|
1460
1560
|
|
|
1461
1561
|
if (this.options.shouldDisableCache) {
|
|
1462
1562
|
return;
|
|
1463
1563
|
}
|
|
1464
1564
|
|
|
1465
1565
|
let total = 0;
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1566
|
+
await runCacheImprovements(
|
|
1567
|
+
async (cache) => {
|
|
1568
|
+
await cache.getNativeRef().startWriteTransaction();
|
|
1569
|
+
},
|
|
1570
|
+
() => Promise.resolve(),
|
|
1571
|
+
);
|
|
1572
|
+
try {
|
|
1573
|
+
report({
|
|
1574
|
+
type: 'cache',
|
|
1575
|
+
phase: 'start',
|
|
1576
|
+
total,
|
|
1577
|
+
size: this.graph.nodes.length,
|
|
1578
|
+
});
|
|
1472
1579
|
|
|
1473
|
-
|
|
1580
|
+
if (getFeatureFlag('environmentDeduplication')) {
|
|
1581
|
+
await writeEnvironmentsToCache(options.cache);
|
|
1582
|
+
}
|
|
1474
1583
|
|
|
1475
|
-
|
|
1476
|
-
await this.options.cache.deleteLargeBlob(requestGraphKey);
|
|
1584
|
+
let serialisedGraph = this.graph.serialize();
|
|
1477
1585
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
// $FlowFixMe serialise input is any type
|
|
1481
|
-
contents: any,
|
|
1482
|
-
): Promise<void> => {
|
|
1483
|
-
if (signal?.aborted) {
|
|
1484
|
-
throw new Error('Serialization was aborted');
|
|
1485
|
-
}
|
|
1586
|
+
// Delete an existing request graph cache, to prevent invalid states
|
|
1587
|
+
await this.options.cache.deleteLargeBlob(requestGraphKey);
|
|
1486
1588
|
|
|
1487
|
-
|
|
1488
|
-
key,
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
);
|
|
1589
|
+
const serialiseAndSet = async (
|
|
1590
|
+
key: string,
|
|
1591
|
+
// $FlowFixMe serialise input is any type
|
|
1592
|
+
contents: any,
|
|
1593
|
+
): Promise<void> => {
|
|
1594
|
+
if (signal?.aborted) {
|
|
1595
|
+
throw new Error('Serialization was aborted');
|
|
1596
|
+
}
|
|
1496
1597
|
|
|
1497
|
-
|
|
1598
|
+
await runCacheImprovements(
|
|
1599
|
+
(cache) => {
|
|
1600
|
+
instrument(
|
|
1601
|
+
`RequestTracker::writeToCache::cache.put(${key})`,
|
|
1602
|
+
() => {
|
|
1603
|
+
cache.getNativeRef().putNoConfirm(key, serialize(contents));
|
|
1604
|
+
},
|
|
1605
|
+
);
|
|
1606
|
+
return Promise.resolve();
|
|
1607
|
+
},
|
|
1608
|
+
async () => {
|
|
1609
|
+
await this.options.cache.setLargeBlob(
|
|
1610
|
+
key,
|
|
1611
|
+
serialize(contents),
|
|
1612
|
+
signal
|
|
1613
|
+
? {
|
|
1614
|
+
signal: signal,
|
|
1615
|
+
}
|
|
1616
|
+
: undefined,
|
|
1617
|
+
);
|
|
1618
|
+
},
|
|
1619
|
+
);
|
|
1498
1620
|
|
|
1499
|
-
|
|
1500
|
-
type: 'cache',
|
|
1501
|
-
phase: 'write',
|
|
1502
|
-
total,
|
|
1503
|
-
size: this.graph.nodes.length,
|
|
1504
|
-
});
|
|
1505
|
-
};
|
|
1621
|
+
total += 1;
|
|
1506
1622
|
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1623
|
+
report({
|
|
1624
|
+
type: 'cache',
|
|
1625
|
+
phase: 'write',
|
|
1626
|
+
total,
|
|
1627
|
+
size: this.graph.nodes.length,
|
|
1628
|
+
});
|
|
1629
|
+
};
|
|
1510
1630
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
let node = serialisedGraph.nodes[i];
|
|
1631
|
+
let queue = new PromiseQueue({
|
|
1632
|
+
maxConcurrent: 32,
|
|
1633
|
+
});
|
|
1515
1634
|
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
node?.result != null
|
|
1521
|
-
) {
|
|
1522
|
-
queue.add(() => serialiseAndSet(resultCacheKey, node.result));
|
|
1635
|
+
// Preallocating a sparse array is faster than pushing when N is high enough
|
|
1636
|
+
let cacheableNodes = new Array(serialisedGraph.nodes.length);
|
|
1637
|
+
for (let i = 0; i < serialisedGraph.nodes.length; i += 1) {
|
|
1638
|
+
let node = serialisedGraph.nodes[i];
|
|
1523
1639
|
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1640
|
+
let resultCacheKey = node?.resultCacheKey;
|
|
1641
|
+
if (
|
|
1642
|
+
node?.type === REQUEST &&
|
|
1643
|
+
resultCacheKey != null &&
|
|
1644
|
+
node?.result != null
|
|
1645
|
+
) {
|
|
1646
|
+
queue.add(() => serialiseAndSet(resultCacheKey, node.result));
|
|
1647
|
+
|
|
1648
|
+
// eslint-disable-next-line no-unused-vars
|
|
1649
|
+
let {result: _, ...newNode} = node;
|
|
1650
|
+
cacheableNodes[i] = newNode;
|
|
1651
|
+
} else {
|
|
1652
|
+
cacheableNodes[i] = node;
|
|
1653
|
+
}
|
|
1529
1654
|
}
|
|
1530
|
-
}
|
|
1531
1655
|
|
|
1532
|
-
|
|
1656
|
+
let nodeCountsPerBlob = [];
|
|
1533
1657
|
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1658
|
+
for (
|
|
1659
|
+
let i = 0;
|
|
1660
|
+
i * this.graph.nodesPerBlob < cacheableNodes.length;
|
|
1661
|
+
i += 1
|
|
1662
|
+
) {
|
|
1663
|
+
let nodesStartIndex = i * this.graph.nodesPerBlob;
|
|
1664
|
+
let nodesEndIndex = Math.min(
|
|
1665
|
+
(i + 1) * this.graph.nodesPerBlob,
|
|
1666
|
+
cacheableNodes.length,
|
|
1667
|
+
);
|
|
1544
1668
|
|
|
1545
|
-
|
|
1669
|
+
nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
|
|
1546
1670
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1671
|
+
if (!this.graph.hasCachedRequestChunk(i)) {
|
|
1672
|
+
// We assume the request graph nodes are immutable and won't change
|
|
1673
|
+
let nodesToCache = cacheableNodes.slice(
|
|
1674
|
+
nodesStartIndex,
|
|
1675
|
+
nodesEndIndex,
|
|
1676
|
+
);
|
|
1550
1677
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1678
|
+
queue.add(() =>
|
|
1679
|
+
serialiseAndSet(
|
|
1680
|
+
getRequestGraphNodeKey(i, cacheKey),
|
|
1681
|
+
nodesToCache,
|
|
1682
|
+
).then(() => {
|
|
1683
|
+
// Succeeded in writing to disk, save that we have completed this chunk
|
|
1684
|
+
this.graph.setCachedRequestChunk(i);
|
|
1685
|
+
}),
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1560
1688
|
}
|
|
1561
|
-
}
|
|
1562
1689
|
|
|
1563
|
-
try {
|
|
1564
1690
|
await queue.run();
|
|
1565
1691
|
|
|
1566
1692
|
// Set the request graph after the queue is flushed to avoid writing an invalid state
|
|
@@ -1570,6 +1696,18 @@ export default class RequestTracker {
|
|
|
1570
1696
|
nodes: undefined,
|
|
1571
1697
|
});
|
|
1572
1698
|
|
|
1699
|
+
await runCacheImprovements(
|
|
1700
|
+
() =>
|
|
1701
|
+
serialiseAndSet(`${cacheKey}/cache_metadata`, {
|
|
1702
|
+
version: ATLASPACK_VERSION,
|
|
1703
|
+
entries: this.options.entries,
|
|
1704
|
+
mode: this.options.mode,
|
|
1705
|
+
shouldBuildLazily: this.options.shouldBuildLazily,
|
|
1706
|
+
watchBackend: this.options.watchBackend,
|
|
1707
|
+
}),
|
|
1708
|
+
() => Promise.resolve(),
|
|
1709
|
+
);
|
|
1710
|
+
|
|
1573
1711
|
let opts = getWatcherOptions(this.options);
|
|
1574
1712
|
let snapshotPath = path.join(this.options.cacheDir, snapshotKey + '.txt');
|
|
1575
1713
|
|
|
@@ -1581,6 +1719,13 @@ export default class RequestTracker {
|
|
|
1581
1719
|
} catch (err) {
|
|
1582
1720
|
// If we have aborted, ignore the error and continue
|
|
1583
1721
|
if (!signal?.aborted) throw err;
|
|
1722
|
+
} finally {
|
|
1723
|
+
await runCacheImprovements(
|
|
1724
|
+
async (cache) => {
|
|
1725
|
+
await cache.getNativeRef().commitWriteTransaction();
|
|
1726
|
+
},
|
|
1727
|
+
() => Promise.resolve(),
|
|
1728
|
+
);
|
|
1584
1729
|
}
|
|
1585
1730
|
|
|
1586
1731
|
report({type: 'cache', phase: 'end', total, size: this.graph.nodes.length});
|
|
@@ -1614,6 +1759,18 @@ export function getWatcherOptions({
|
|
|
1614
1759
|
}
|
|
1615
1760
|
|
|
1616
1761
|
function getCacheKey(options) {
|
|
1762
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1763
|
+
const hash = hashString(
|
|
1764
|
+
`${ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${
|
|
1765
|
+
options.mode
|
|
1766
|
+
}:${options.shouldBuildLazily ? 'lazy' : 'eager'}:${
|
|
1767
|
+
options.watchBackend ?? ''
|
|
1768
|
+
}`,
|
|
1769
|
+
);
|
|
1770
|
+
|
|
1771
|
+
return `RequestTracker/${ATLASPACK_VERSION}/${hash}`;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1617
1774
|
return hashString(
|
|
1618
1775
|
`${ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${
|
|
1619
1776
|
options.shouldBuildLazily ? 'lazy' : 'eager'
|
|
@@ -1622,6 +1779,10 @@ function getCacheKey(options) {
|
|
|
1622
1779
|
}
|
|
1623
1780
|
|
|
1624
1781
|
function getRequestGraphNodeKey(index: number, cacheKey: string) {
|
|
1782
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1783
|
+
return `${cacheKey}/RequestGraph/nodes/${index}`;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1625
1786
|
return `requestGraph-nodes-${index}-${cacheKey}`;
|
|
1626
1787
|
}
|
|
1627
1788
|
|
|
@@ -1631,10 +1792,17 @@ export async function readAndDeserializeRequestGraph(
|
|
|
1631
1792
|
cacheKey: string,
|
|
1632
1793
|
): Async<{|requestGraph: RequestGraph, bufferLength: number|}> {
|
|
1633
1794
|
let bufferLength = 0;
|
|
1795
|
+
|
|
1634
1796
|
const getAndDeserialize = async (key: string) => {
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1797
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1798
|
+
const buffer = await cache.getBlob(key);
|
|
1799
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1800
|
+
return deserialize(buffer);
|
|
1801
|
+
} else {
|
|
1802
|
+
const buffer = await cache.getLargeBlob(key);
|
|
1803
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1804
|
+
return deserialize(buffer);
|
|
1805
|
+
}
|
|
1638
1806
|
};
|
|
1639
1807
|
|
|
1640
1808
|
let serializedRequestGraph = await getAndDeserialize(requestGraphKey);
|
|
@@ -1667,20 +1835,45 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1667
1835
|
}
|
|
1668
1836
|
|
|
1669
1837
|
let cacheKey = getCacheKey(options);
|
|
1670
|
-
let requestGraphKey =
|
|
1838
|
+
let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1839
|
+
? `${cacheKey}/RequestGraph`
|
|
1840
|
+
: `requestGraph-${cacheKey}`;
|
|
1841
|
+
|
|
1671
1842
|
let timeout;
|
|
1672
|
-
const snapshotKey =
|
|
1843
|
+
const snapshotKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1844
|
+
? `${cacheKey}/snapshot`
|
|
1845
|
+
: `snapshot-${cacheKey}`;
|
|
1673
1846
|
const snapshotPath = path.join(options.cacheDir, snapshotKey + '.txt');
|
|
1674
1847
|
|
|
1848
|
+
const commonMeta = {
|
|
1849
|
+
cacheKey,
|
|
1850
|
+
snapshotKey,
|
|
1851
|
+
cacheKeyOptions: {
|
|
1852
|
+
version: ATLASPACK_VERSION,
|
|
1853
|
+
entries: options.entries,
|
|
1854
|
+
mode: options.mode,
|
|
1855
|
+
shouldBuildLazily: options.shouldBuildLazily,
|
|
1856
|
+
watchBackend: options.watchBackend,
|
|
1857
|
+
},
|
|
1858
|
+
};
|
|
1859
|
+
|
|
1675
1860
|
logger.verbose({
|
|
1676
1861
|
origin: '@atlaspack/core',
|
|
1677
1862
|
message: 'Loading request graph',
|
|
1678
1863
|
meta: {
|
|
1679
|
-
|
|
1680
|
-
snapshotKey,
|
|
1864
|
+
...commonMeta,
|
|
1681
1865
|
},
|
|
1682
1866
|
});
|
|
1683
|
-
|
|
1867
|
+
|
|
1868
|
+
if (getFeatureFlag('environmentDeduplication')) {
|
|
1869
|
+
await loadEnvironmentsFromCache(options.cache);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
const hasRequestGraphInCache = getFeatureFlag('cachePerformanceImprovements')
|
|
1873
|
+
? await options.cache.has(requestGraphKey)
|
|
1874
|
+
: await options.cache.hasLargeBlob(requestGraphKey);
|
|
1875
|
+
|
|
1876
|
+
if (hasRequestGraphInCache) {
|
|
1684
1877
|
try {
|
|
1685
1878
|
let {requestGraph} = await readAndDeserializeRequestGraph(
|
|
1686
1879
|
options.cache,
|
|
@@ -1711,23 +1904,36 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1711
1904
|
origin: '@atlaspack/core',
|
|
1712
1905
|
message: `File system event count: ${events.length}`,
|
|
1713
1906
|
meta: {
|
|
1907
|
+
...commonMeta,
|
|
1714
1908
|
trackableEvent: 'watcher_events_count',
|
|
1715
1909
|
watcherEventCount: events.length,
|
|
1716
1910
|
duration: Date.now() - startTime,
|
|
1717
1911
|
},
|
|
1718
1912
|
});
|
|
1719
1913
|
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1914
|
+
if (getFeatureFlag('verboseRequestInvalidationStats')) {
|
|
1915
|
+
const invalidationStats = await invalidateRequestGraph(
|
|
1916
|
+
requestGraph,
|
|
1917
|
+
options,
|
|
1918
|
+
events,
|
|
1919
|
+
);
|
|
1920
|
+
|
|
1921
|
+
logger.verbose({
|
|
1922
|
+
origin: '@atlaspack/core',
|
|
1923
|
+
message: 'Request track loaded from cache',
|
|
1924
|
+
meta: {
|
|
1925
|
+
...commonMeta,
|
|
1926
|
+
trackableEvent: 'request_tracker_cache_key_hit',
|
|
1927
|
+
invalidationStats,
|
|
1928
|
+
},
|
|
1929
|
+
});
|
|
1930
|
+
} else {
|
|
1931
|
+
requestGraph.invalidateUnpredictableNodes();
|
|
1932
|
+
requestGraph.invalidateOnBuildNodes();
|
|
1933
|
+
requestGraph.invalidateEnvNodes(options.env);
|
|
1934
|
+
requestGraph.invalidateOptionNodes(options);
|
|
1935
|
+
}
|
|
1724
1936
|
|
|
1725
|
-
await requestGraph.respondToFSEvents(
|
|
1726
|
-
options.unstableFileInvalidations || events,
|
|
1727
|
-
options,
|
|
1728
|
-
10000,
|
|
1729
|
-
true,
|
|
1730
|
-
);
|
|
1731
1937
|
return requestGraph;
|
|
1732
1938
|
} catch (e) {
|
|
1733
1939
|
// Prevent logging fs events took too long warning
|
|
@@ -1744,13 +1950,227 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1744
1950
|
message:
|
|
1745
1951
|
'Cache entry for request tracker was not found, initializing a clean cache.',
|
|
1746
1952
|
meta: {
|
|
1747
|
-
|
|
1748
|
-
|
|
1953
|
+
...commonMeta,
|
|
1954
|
+
trackableEvent: 'request_tracker_cache_key_miss',
|
|
1749
1955
|
},
|
|
1750
1956
|
});
|
|
1751
1957
|
return new RequestGraph();
|
|
1752
1958
|
}
|
|
1753
1959
|
|
|
1960
|
+
/**
|
|
1961
|
+
* A wrapper around an invalidation type / method
|
|
1962
|
+
*/
|
|
1963
|
+
type InvalidationFn = {|
|
|
1964
|
+
key: string,
|
|
1965
|
+
fn: () =>
|
|
1966
|
+
| InvalidationDetail
|
|
1967
|
+
| Promise<InvalidationDetail>
|
|
1968
|
+
| void
|
|
1969
|
+
| Promise<void>,
|
|
1970
|
+
|};
|
|
1971
|
+
|
|
1972
|
+
type InvalidationStats = {|
|
|
1973
|
+
/**
|
|
1974
|
+
* Total number of request graph nodes
|
|
1975
|
+
*/
|
|
1976
|
+
nodeCount: number,
|
|
1977
|
+
/**
|
|
1978
|
+
* Number of requests in RequestGraph
|
|
1979
|
+
*/
|
|
1980
|
+
requestCount: number,
|
|
1981
|
+
/**
|
|
1982
|
+
* Number of nodes that have been invalidated.
|
|
1983
|
+
*/
|
|
1984
|
+
invalidatedCount: number,
|
|
1985
|
+
/**
|
|
1986
|
+
* Percentage of requests that have been invalidated
|
|
1987
|
+
*/
|
|
1988
|
+
requestInvalidationRatio: number,
|
|
1989
|
+
/**
|
|
1990
|
+
* Percentage of nodes that have been invalidated
|
|
1991
|
+
*/
|
|
1992
|
+
nodeInvalidationRatio: number,
|
|
1993
|
+
/**
|
|
1994
|
+
* Details for each invalidation type
|
|
1995
|
+
*/
|
|
1996
|
+
invalidations: InvalidationFnStats[],
|
|
1997
|
+
|};
|
|
1998
|
+
|
|
1999
|
+
/**
|
|
2000
|
+
* Details about an invalidation.
|
|
2001
|
+
*
|
|
2002
|
+
* If this is a fs events invalidation, this key will contain statistics about invalidations
|
|
2003
|
+
* by path.
|
|
2004
|
+
*
|
|
2005
|
+
* If this is a env or option invalidation, this key will contain the list of changed environment
|
|
2006
|
+
* variables or options.
|
|
2007
|
+
*/
|
|
2008
|
+
type InvalidationDetail = string[] | FSInvalidationStats;
|
|
2009
|
+
|
|
2010
|
+
/**
|
|
2011
|
+
* Number of invalidations for a given file-system event.
|
|
2012
|
+
*/
|
|
2013
|
+
type FSInvalidation = {|
|
|
2014
|
+
path: string,
|
|
2015
|
+
count: number,
|
|
2016
|
+
|};
|
|
2017
|
+
|
|
2018
|
+
type FSInvalidationStats = {|
|
|
2019
|
+
/**
|
|
2020
|
+
* This list will be sorted by the number of nodes invalidated and only the top 10 will be
|
|
2021
|
+
* included.
|
|
2022
|
+
*/
|
|
2023
|
+
biggestInvalidations: FSInvalidation[],
|
|
2024
|
+
|};
|
|
2025
|
+
|
|
2026
|
+
/**
|
|
2027
|
+
* Information about a certain cache invalidation type.
|
|
2028
|
+
*/
|
|
2029
|
+
type InvalidationFnStats = {|
|
|
2030
|
+
/**
|
|
2031
|
+
* Invalidation type, one of:
|
|
2032
|
+
*
|
|
2033
|
+
* - unpredictable
|
|
2034
|
+
* - onBuild
|
|
2035
|
+
* - env
|
|
2036
|
+
* - option
|
|
2037
|
+
* - fsEvents
|
|
2038
|
+
*/
|
|
2039
|
+
key: string,
|
|
2040
|
+
/**
|
|
2041
|
+
* Number of invalidated nodes coming from this invalidation type.
|
|
2042
|
+
*/
|
|
2043
|
+
count: number,
|
|
2044
|
+
/**
|
|
2045
|
+
* If this is a env or option invalidation, this key will contain the list of changed values.
|
|
2046
|
+
*
|
|
2047
|
+
* If this is a fs events invalidation, this key will contain statistics about invalidations
|
|
2048
|
+
*/
|
|
2049
|
+
detail: null | InvalidationDetail,
|
|
2050
|
+
/**
|
|
2051
|
+
* Time in milliseconds it took to run the invalidation.
|
|
2052
|
+
*/
|
|
2053
|
+
duration: number,
|
|
2054
|
+
|};
|
|
2055
|
+
|
|
2056
|
+
/**
|
|
2057
|
+
* Respond to unpredictable, build, environment changes, option changes and file-system events
|
|
2058
|
+
* invalidating RequestGraph nodes.
|
|
2059
|
+
*
|
|
2060
|
+
* Returns the count of nodes invalidated by each invalidation type.
|
|
2061
|
+
*/
|
|
2062
|
+
export async function invalidateRequestGraph(
|
|
2063
|
+
requestGraph: RequestGraph,
|
|
2064
|
+
options: AtlaspackOptions,
|
|
2065
|
+
events: Event[],
|
|
2066
|
+
): Promise<InvalidationStats> {
|
|
2067
|
+
const invalidationFns: InvalidationFn[] = [
|
|
2068
|
+
{
|
|
2069
|
+
key: 'unpredictable',
|
|
2070
|
+
fn: () => requestGraph.invalidateUnpredictableNodes(),
|
|
2071
|
+
},
|
|
2072
|
+
{
|
|
2073
|
+
key: 'onBuild',
|
|
2074
|
+
fn: () => requestGraph.invalidateOnBuildNodes(),
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
key: 'env',
|
|
2078
|
+
fn: () => requestGraph.invalidateEnvNodes(options.env),
|
|
2079
|
+
},
|
|
2080
|
+
{
|
|
2081
|
+
key: 'option',
|
|
2082
|
+
fn: () => requestGraph.invalidateOptionNodes(options),
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
key: 'fsEvents',
|
|
2086
|
+
fn: () => invalidateRequestGraphFSEvents(requestGraph, options, events),
|
|
2087
|
+
},
|
|
2088
|
+
];
|
|
2089
|
+
|
|
2090
|
+
const invalidations = [];
|
|
2091
|
+
for (const invalidation of invalidationFns) {
|
|
2092
|
+
invalidations.push(await runInvalidation(requestGraph, invalidation));
|
|
2093
|
+
}
|
|
2094
|
+
const invalidatedCount = invalidations.reduce(
|
|
2095
|
+
(acc, invalidation) => acc + invalidation.count,
|
|
2096
|
+
0,
|
|
2097
|
+
);
|
|
2098
|
+
const requestCount = requestGraph.nodes.reduce(
|
|
2099
|
+
(acc, node) => acc + (node?.type === REQUEST ? 1 : 0),
|
|
2100
|
+
0,
|
|
2101
|
+
);
|
|
2102
|
+
const nodeCount = requestGraph.nodes.length;
|
|
2103
|
+
const nodeInvalidationRatio = invalidatedCount / nodeCount;
|
|
2104
|
+
const requestInvalidationRatio = invalidatedCount / requestCount;
|
|
2105
|
+
|
|
2106
|
+
return {
|
|
2107
|
+
invalidations,
|
|
2108
|
+
nodeCount,
|
|
2109
|
+
requestCount,
|
|
2110
|
+
invalidatedCount,
|
|
2111
|
+
nodeInvalidationRatio,
|
|
2112
|
+
requestInvalidationRatio,
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
interface InvalidateRequestGraphFSEventsInput {
|
|
2117
|
+
respondToFSEvents(
|
|
2118
|
+
events: Event[],
|
|
2119
|
+
options: AtlaspackOptions,
|
|
2120
|
+
timeout: number,
|
|
2121
|
+
shouldLog: boolean,
|
|
2122
|
+
): Promise<{invalidationsByPath: Map<string, number>, ...}>;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
/**
|
|
2126
|
+
* Invalidate the request graph based on file-system events.
|
|
2127
|
+
*
|
|
2128
|
+
* Returns statistics about the invalidations.
|
|
2129
|
+
*/
|
|
2130
|
+
export async function invalidateRequestGraphFSEvents(
|
|
2131
|
+
requestGraph: InvalidateRequestGraphFSEventsInput,
|
|
2132
|
+
options: AtlaspackOptions,
|
|
2133
|
+
events: Event[],
|
|
2134
|
+
): Promise<FSInvalidationStats> {
|
|
2135
|
+
const {invalidationsByPath} = await requestGraph.respondToFSEvents(
|
|
2136
|
+
options.unstableFileInvalidations || events,
|
|
2137
|
+
options,
|
|
2138
|
+
10000,
|
|
2139
|
+
true,
|
|
2140
|
+
);
|
|
2141
|
+
const biggestInvalidations =
|
|
2142
|
+
getBiggestFSEventsInvalidations(invalidationsByPath);
|
|
2143
|
+
|
|
2144
|
+
return {
|
|
2145
|
+
biggestInvalidations,
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
interface RunInvalidationInput {
|
|
2150
|
+
getInvalidNodeCount(): number;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
/**
|
|
2154
|
+
* Runs an invalidation function and reports metrics.
|
|
2155
|
+
*/
|
|
2156
|
+
export async function runInvalidation(
|
|
2157
|
+
requestGraph: RunInvalidationInput,
|
|
2158
|
+
invalidationFn: InvalidationFn,
|
|
2159
|
+
): Promise<InvalidationFnStats> {
|
|
2160
|
+
const start = performance.now();
|
|
2161
|
+
const startInvalidationCount = requestGraph.getInvalidNodeCount();
|
|
2162
|
+
const result = await invalidationFn.fn();
|
|
2163
|
+
const count = requestGraph.getInvalidNodeCount() - startInvalidationCount;
|
|
2164
|
+
const duration = performance.now() - start;
|
|
2165
|
+
|
|
2166
|
+
return {
|
|
2167
|
+
key: invalidationFn.key,
|
|
2168
|
+
count,
|
|
2169
|
+
detail: result ?? null,
|
|
2170
|
+
duration,
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
|
|
1754
2174
|
function logErrorOnBailout(
|
|
1755
2175
|
options: AtlaspackOptions,
|
|
1756
2176
|
snapshotPath: string,
|
|
@@ -1802,3 +2222,19 @@ export function cleanUpOrphans<N, E: number>(graph: Graph<N, E>): NodeId[] {
|
|
|
1802
2222
|
|
|
1803
2223
|
return removedNodeIds;
|
|
1804
2224
|
}
|
|
2225
|
+
|
|
2226
|
+
/**
|
|
2227
|
+
* Returns paths that invalidated the most nodes
|
|
2228
|
+
*/
|
|
2229
|
+
export function getBiggestFSEventsInvalidations(
|
|
2230
|
+
invalidationsByPath: Map<string, number>,
|
|
2231
|
+
limit: number = 10,
|
|
2232
|
+
): Array<FSInvalidation> {
|
|
2233
|
+
const invalidations = [];
|
|
2234
|
+
for (const [path, count] of invalidationsByPath) {
|
|
2235
|
+
invalidations.push({path, count});
|
|
2236
|
+
}
|
|
2237
|
+
invalidations.sort((a, b) => b.count - a.count);
|
|
2238
|
+
|
|
2239
|
+
return invalidations.slice(0, limit);
|
|
2240
|
+
}
|