@atlaspack/core 2.16.2-dev.14 → 2.16.2-dev.55
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 +86 -0
- package/lib/Atlaspack.js +10 -2
- package/lib/AtlaspackConfig.schema.js +7 -1
- package/lib/BundleGraph.js +2 -100
- package/lib/PackagerRunner.js +44 -9
- package/lib/RequestTracker.js +326 -121
- package/lib/Transformation.js +2 -2
- package/lib/UncommittedAsset.js +17 -0
- 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/public/BundleGraph.js +21 -8
- package/lib/public/Config.js +28 -0
- package/lib/requests/AssetGraphRequest.js +13 -1
- package/lib/requests/BundleGraphRequest.js +13 -1
- package/lib/requests/WriteBundleRequest.js +11 -2
- package/lib/resolveOptions.js +7 -4
- package/lib/worker.js +18 -1
- package/package.json +23 -19
- package/src/Atlaspack.js +13 -5
- package/src/BundleGraph.js +0 -167
- package/src/PackagerRunner.js +60 -9
- package/src/RequestTracker.js +491 -137
- package/src/UncommittedAsset.js +16 -1
- package/src/atlaspack-v3/worker/compat/plugin-config.js +9 -5
- package/src/atlaspack-v3/worker/worker.js +7 -0
- package/src/public/BundleGraph.js +22 -15
- package/src/public/Config.js +39 -5
- package/src/requests/AssetGraphRequest.js +13 -3
- package/src/requests/BundleGraphRequest.js +13 -3
- package/src/requests/WriteBundleRequest.js +9 -2
- package/src/resolveOptions.js +4 -2
- package/test/RequestTracker.test.js +120 -5
- package/test/test-utils.js +1 -7
package/src/RequestTracker.js
CHANGED
|
@@ -69,6 +69,7 @@ import type {
|
|
|
69
69
|
InternalGlob,
|
|
70
70
|
} from './types';
|
|
71
71
|
import {BuildAbortError, assertSignalNotAborted, hashFromOption} from './utils';
|
|
72
|
+
import {performance} from 'perf_hooks';
|
|
72
73
|
|
|
73
74
|
export const requestGraphEdgeTypes = {
|
|
74
75
|
subrequest: 2,
|
|
@@ -446,6 +447,11 @@ export class RequestGraph extends ContentGraph<
|
|
|
446
447
|
this.removeCachedRequestChunkForNode(nodeId);
|
|
447
448
|
}
|
|
448
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Nodes that are invalidated on start-up, such as JavaScript babel configuration files which are
|
|
452
|
+
* imported when the build kicks-off and might doing arbitrary work such as reading from the file
|
|
453
|
+
* system.
|
|
454
|
+
*/
|
|
449
455
|
invalidateUnpredictableNodes() {
|
|
450
456
|
for (let nodeId of this.unpredicatableNodeIds) {
|
|
451
457
|
let node = nullthrows(this.getNode(nodeId));
|
|
@@ -454,6 +460,9 @@ export class RequestGraph extends ContentGraph<
|
|
|
454
460
|
}
|
|
455
461
|
}
|
|
456
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Effectively uncacheable nodes.
|
|
465
|
+
*/
|
|
457
466
|
invalidateOnBuildNodes() {
|
|
458
467
|
for (let nodeId of this.invalidateOnBuildNodeIds) {
|
|
459
468
|
let node = nullthrows(this.getNode(nodeId));
|
|
@@ -462,11 +471,20 @@ export class RequestGraph extends ContentGraph<
|
|
|
462
471
|
}
|
|
463
472
|
}
|
|
464
473
|
|
|
465
|
-
|
|
474
|
+
/**
|
|
475
|
+
* Nodes invalidated by environment changes, corresponds to `env: ...` inputs.
|
|
476
|
+
*/
|
|
477
|
+
invalidateEnvNodes(env: EnvMap): string[] {
|
|
478
|
+
const invalidatedKeys = [];
|
|
479
|
+
|
|
466
480
|
for (let nodeId of this.envNodeIds) {
|
|
467
481
|
let node = nullthrows(this.getNode(nodeId));
|
|
468
482
|
invariant(node.type === ENV);
|
|
469
|
-
|
|
483
|
+
|
|
484
|
+
const key = keyFromEnvContentKey(node.id);
|
|
485
|
+
if (env[key] !== node.value) {
|
|
486
|
+
invalidatedKeys.push(key);
|
|
487
|
+
|
|
470
488
|
let parentNodes = this.getNodeIdsConnectedTo(
|
|
471
489
|
nodeId,
|
|
472
490
|
requestGraphEdgeTypes.invalidated_by_update,
|
|
@@ -476,15 +494,23 @@ export class RequestGraph extends ContentGraph<
|
|
|
476
494
|
}
|
|
477
495
|
}
|
|
478
496
|
}
|
|
497
|
+
|
|
498
|
+
return invalidatedKeys;
|
|
479
499
|
}
|
|
480
500
|
|
|
481
|
-
|
|
501
|
+
/**
|
|
502
|
+
* Nodes invalidated by option changes.
|
|
503
|
+
*/
|
|
504
|
+
invalidateOptionNodes(options: AtlaspackOptions): string[] {
|
|
505
|
+
const invalidatedKeys = [];
|
|
506
|
+
|
|
482
507
|
for (let nodeId of this.optionNodeIds) {
|
|
483
508
|
let node = nullthrows(this.getNode(nodeId));
|
|
484
509
|
invariant(node.type === OPTION);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
) {
|
|
510
|
+
const key = keyFromOptionContentKey(node.id);
|
|
511
|
+
|
|
512
|
+
if (hashFromOption(options[key]) !== node.hash) {
|
|
513
|
+
invalidatedKeys.push(key);
|
|
488
514
|
let parentNodes = this.getNodeIdsConnectedTo(
|
|
489
515
|
nodeId,
|
|
490
516
|
requestGraphEdgeTypes.invalidated_by_update,
|
|
@@ -494,6 +520,8 @@ export class RequestGraph extends ContentGraph<
|
|
|
494
520
|
}
|
|
495
521
|
}
|
|
496
522
|
}
|
|
523
|
+
|
|
524
|
+
return invalidatedKeys;
|
|
497
525
|
}
|
|
498
526
|
|
|
499
527
|
invalidateOnConfigKeyChange(
|
|
@@ -907,7 +935,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
907
935
|
* True if this is the start-up (loading phase) invalidation.
|
|
908
936
|
*/
|
|
909
937
|
isInitialBuild: boolean = false,
|
|
910
|
-
):
|
|
938
|
+
): Promise<{|
|
|
939
|
+
didInvalidate: boolean,
|
|
940
|
+
invalidationsByPath: Map<string, number>,
|
|
941
|
+
|}> {
|
|
911
942
|
let didInvalidate = false;
|
|
912
943
|
let count = 0;
|
|
913
944
|
let predictedTime = 0;
|
|
@@ -945,7 +976,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
945
976
|
return above;
|
|
946
977
|
};
|
|
947
978
|
|
|
979
|
+
const invalidationsByPath = new Map();
|
|
948
980
|
for (let {path: _path, type} of events) {
|
|
981
|
+
const invalidationsBefore = this.getInvalidNodeCount();
|
|
982
|
+
|
|
949
983
|
if (
|
|
950
984
|
!enableOptimization &&
|
|
951
985
|
process.env.ATLASPACK_DISABLE_CACHE_TIMEOUT !== 'true' &&
|
|
@@ -991,7 +1025,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
991
1025
|
this.invalidNodeIds.add(id);
|
|
992
1026
|
}
|
|
993
1027
|
}
|
|
994
|
-
return
|
|
1028
|
+
return {
|
|
1029
|
+
didInvalidate: true,
|
|
1030
|
+
invalidationsByPath: new Map(),
|
|
1031
|
+
};
|
|
995
1032
|
}
|
|
996
1033
|
|
|
997
1034
|
// sometimes mac os reports update events as create events.
|
|
@@ -1104,6 +1141,13 @@ export class RequestGraph extends ContentGraph<
|
|
|
1104
1141
|
}
|
|
1105
1142
|
}
|
|
1106
1143
|
}
|
|
1144
|
+
|
|
1145
|
+
const invalidationsAfter = this.getInvalidNodeCount();
|
|
1146
|
+
const invalidationsForEvent = invalidationsAfter - invalidationsBefore;
|
|
1147
|
+
invalidationsByPath.set(
|
|
1148
|
+
_path,
|
|
1149
|
+
(invalidationsByPath.get(_path) ?? 0) + invalidationsForEvent,
|
|
1150
|
+
);
|
|
1107
1151
|
}
|
|
1108
1152
|
|
|
1109
1153
|
if (getFeatureFlag('fixQuadraticCacheInvalidation')) {
|
|
@@ -1124,7 +1168,10 @@ export class RequestGraph extends ContentGraph<
|
|
|
1124
1168
|
},
|
|
1125
1169
|
});
|
|
1126
1170
|
|
|
1127
|
-
return
|
|
1171
|
+
return {
|
|
1172
|
+
didInvalidate,
|
|
1173
|
+
invalidationsByPath,
|
|
1174
|
+
};
|
|
1128
1175
|
}
|
|
1129
1176
|
|
|
1130
1177
|
hasCachedRequestChunk(index: number): boolean {
|
|
@@ -1138,6 +1185,13 @@ export class RequestGraph extends ContentGraph<
|
|
|
1138
1185
|
removeCachedRequestChunkForNode(nodeId: number): void {
|
|
1139
1186
|
this.cachedRequestChunks.delete(Math.floor(nodeId / this.nodesPerBlob));
|
|
1140
1187
|
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Returns the number of invalidated nodes in the graph.
|
|
1191
|
+
*/
|
|
1192
|
+
getInvalidNodeCount(): number {
|
|
1193
|
+
return this.invalidNodeIds.size;
|
|
1194
|
+
}
|
|
1141
1195
|
}
|
|
1142
1196
|
|
|
1143
1197
|
export default class RequestTracker {
|
|
@@ -1261,7 +1315,13 @@ export default class RequestTracker {
|
|
|
1261
1315
|
}
|
|
1262
1316
|
}
|
|
1263
1317
|
|
|
1264
|
-
respondToFSEvents(
|
|
1318
|
+
respondToFSEvents(
|
|
1319
|
+
events: Array<Event>,
|
|
1320
|
+
threshold: number,
|
|
1321
|
+
): Promise<{|
|
|
1322
|
+
didInvalidate: boolean,
|
|
1323
|
+
invalidationsByPath: Map<string, number>,
|
|
1324
|
+
|}> {
|
|
1265
1325
|
return this.graph.respondToFSEvents(events, this.options, threshold);
|
|
1266
1326
|
}
|
|
1267
1327
|
|
|
@@ -1472,130 +1532,139 @@ export default class RequestTracker {
|
|
|
1472
1532
|
}
|
|
1473
1533
|
}
|
|
1474
1534
|
|
|
1475
|
-
await runCacheImprovements(
|
|
1476
|
-
async (cache) => {
|
|
1477
|
-
await cache.getNativeRef().startWriteTransaction();
|
|
1478
|
-
},
|
|
1479
|
-
() => Promise.resolve(),
|
|
1480
|
-
);
|
|
1481
|
-
|
|
1482
1535
|
let cacheKey = getCacheKey(this.options);
|
|
1483
|
-
let requestGraphKey =
|
|
1484
|
-
|
|
1536
|
+
let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1537
|
+
? `${cacheKey}/RequestGraph`
|
|
1538
|
+
: `requestGraph-${cacheKey}`;
|
|
1539
|
+
let snapshotKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1540
|
+
? `${cacheKey}/snapshot`
|
|
1541
|
+
: `snapshot-${cacheKey}`;
|
|
1485
1542
|
|
|
1486
1543
|
if (this.options.shouldDisableCache) {
|
|
1487
1544
|
return;
|
|
1488
1545
|
}
|
|
1489
1546
|
|
|
1490
1547
|
let total = 0;
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1548
|
+
await runCacheImprovements(
|
|
1549
|
+
async (cache) => {
|
|
1550
|
+
await cache.getNativeRef().startWriteTransaction();
|
|
1551
|
+
},
|
|
1552
|
+
() => Promise.resolve(),
|
|
1553
|
+
);
|
|
1554
|
+
try {
|
|
1555
|
+
report({
|
|
1556
|
+
type: 'cache',
|
|
1557
|
+
phase: 'start',
|
|
1558
|
+
total,
|
|
1559
|
+
size: this.graph.nodes.length,
|
|
1560
|
+
});
|
|
1497
1561
|
|
|
1498
|
-
|
|
1562
|
+
let serialisedGraph = this.graph.serialize();
|
|
1499
1563
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1564
|
+
// Delete an existing request graph cache, to prevent invalid states
|
|
1565
|
+
await this.options.cache.deleteLargeBlob(requestGraphKey);
|
|
1502
1566
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1567
|
+
const serialiseAndSet = async (
|
|
1568
|
+
key: string,
|
|
1569
|
+
// $FlowFixMe serialise input is any type
|
|
1570
|
+
contents: any,
|
|
1571
|
+
): Promise<void> => {
|
|
1572
|
+
if (signal?.aborted) {
|
|
1573
|
+
throw new Error('Serialization was aborted');
|
|
1574
|
+
}
|
|
1511
1575
|
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1576
|
+
await runCacheImprovements(
|
|
1577
|
+
(cache) => {
|
|
1578
|
+
instrument(
|
|
1579
|
+
`RequestTracker::writeToCache::cache.put(${key})`,
|
|
1580
|
+
() => {
|
|
1581
|
+
cache.getNativeRef().putNoConfirm(key, serialize(contents));
|
|
1582
|
+
},
|
|
1583
|
+
);
|
|
1584
|
+
return Promise.resolve();
|
|
1585
|
+
},
|
|
1586
|
+
async () => {
|
|
1587
|
+
await this.options.cache.setLargeBlob(
|
|
1588
|
+
key,
|
|
1589
|
+
serialize(contents),
|
|
1590
|
+
signal
|
|
1591
|
+
? {
|
|
1592
|
+
signal: signal,
|
|
1593
|
+
}
|
|
1594
|
+
: undefined,
|
|
1595
|
+
);
|
|
1596
|
+
},
|
|
1597
|
+
);
|
|
1531
1598
|
|
|
1532
|
-
|
|
1599
|
+
total += 1;
|
|
1533
1600
|
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1601
|
+
report({
|
|
1602
|
+
type: 'cache',
|
|
1603
|
+
phase: 'write',
|
|
1604
|
+
total,
|
|
1605
|
+
size: this.graph.nodes.length,
|
|
1606
|
+
});
|
|
1607
|
+
};
|
|
1541
1608
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1609
|
+
let queue = new PromiseQueue({
|
|
1610
|
+
maxConcurrent: 32,
|
|
1611
|
+
});
|
|
1545
1612
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1613
|
+
// Preallocating a sparse array is faster than pushing when N is high enough
|
|
1614
|
+
let cacheableNodes = new Array(serialisedGraph.nodes.length);
|
|
1615
|
+
for (let i = 0; i < serialisedGraph.nodes.length; i += 1) {
|
|
1616
|
+
let node = serialisedGraph.nodes[i];
|
|
1550
1617
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1618
|
+
let resultCacheKey = node?.resultCacheKey;
|
|
1619
|
+
if (
|
|
1620
|
+
node?.type === REQUEST &&
|
|
1621
|
+
resultCacheKey != null &&
|
|
1622
|
+
node?.result != null
|
|
1623
|
+
) {
|
|
1624
|
+
queue.add(() => serialiseAndSet(resultCacheKey, node.result));
|
|
1558
1625
|
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1626
|
+
// eslint-disable-next-line no-unused-vars
|
|
1627
|
+
let {result: _, ...newNode} = node;
|
|
1628
|
+
cacheableNodes[i] = newNode;
|
|
1629
|
+
} else {
|
|
1630
|
+
cacheableNodes[i] = node;
|
|
1631
|
+
}
|
|
1564
1632
|
}
|
|
1565
|
-
}
|
|
1566
1633
|
|
|
1567
|
-
|
|
1634
|
+
let nodeCountsPerBlob = [];
|
|
1568
1635
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1636
|
+
for (
|
|
1637
|
+
let i = 0;
|
|
1638
|
+
i * this.graph.nodesPerBlob < cacheableNodes.length;
|
|
1639
|
+
i += 1
|
|
1640
|
+
) {
|
|
1641
|
+
let nodesStartIndex = i * this.graph.nodesPerBlob;
|
|
1642
|
+
let nodesEndIndex = Math.min(
|
|
1643
|
+
(i + 1) * this.graph.nodesPerBlob,
|
|
1644
|
+
cacheableNodes.length,
|
|
1645
|
+
);
|
|
1579
1646
|
|
|
1580
|
-
|
|
1647
|
+
nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
|
|
1581
1648
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1649
|
+
if (!this.graph.hasCachedRequestChunk(i)) {
|
|
1650
|
+
// We assume the request graph nodes are immutable and won't change
|
|
1651
|
+
let nodesToCache = cacheableNodes.slice(
|
|
1652
|
+
nodesStartIndex,
|
|
1653
|
+
nodesEndIndex,
|
|
1654
|
+
);
|
|
1585
1655
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1656
|
+
queue.add(() =>
|
|
1657
|
+
serialiseAndSet(
|
|
1658
|
+
getRequestGraphNodeKey(i, cacheKey),
|
|
1659
|
+
nodesToCache,
|
|
1660
|
+
).then(() => {
|
|
1661
|
+
// Succeeded in writing to disk, save that we have completed this chunk
|
|
1662
|
+
this.graph.setCachedRequestChunk(i);
|
|
1663
|
+
}),
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1595
1666
|
}
|
|
1596
|
-
}
|
|
1597
1667
|
|
|
1598
|
-
try {
|
|
1599
1668
|
await queue.run();
|
|
1600
1669
|
|
|
1601
1670
|
// Set the request graph after the queue is flushed to avoid writing an invalid state
|
|
@@ -1607,7 +1676,7 @@ export default class RequestTracker {
|
|
|
1607
1676
|
|
|
1608
1677
|
await runCacheImprovements(
|
|
1609
1678
|
() =>
|
|
1610
|
-
serialiseAndSet(
|
|
1679
|
+
serialiseAndSet(`${cacheKey}/cache_metadata`, {
|
|
1611
1680
|
version: ATLASPACK_VERSION,
|
|
1612
1681
|
entries: this.options.entries,
|
|
1613
1682
|
mode: this.options.mode,
|
|
@@ -1628,15 +1697,15 @@ export default class RequestTracker {
|
|
|
1628
1697
|
} catch (err) {
|
|
1629
1698
|
// If we have aborted, ignore the error and continue
|
|
1630
1699
|
if (!signal?.aborted) throw err;
|
|
1700
|
+
} finally {
|
|
1701
|
+
await runCacheImprovements(
|
|
1702
|
+
async (cache) => {
|
|
1703
|
+
await cache.getNativeRef().commitWriteTransaction();
|
|
1704
|
+
},
|
|
1705
|
+
() => Promise.resolve(),
|
|
1706
|
+
);
|
|
1631
1707
|
}
|
|
1632
1708
|
|
|
1633
|
-
await runCacheImprovements(
|
|
1634
|
-
async (cache) => {
|
|
1635
|
-
await cache.getNativeRef().commitWriteTransaction();
|
|
1636
|
-
},
|
|
1637
|
-
() => Promise.resolve(),
|
|
1638
|
-
);
|
|
1639
|
-
|
|
1640
1709
|
report({type: 'cache', phase: 'end', total, size: this.graph.nodes.length});
|
|
1641
1710
|
}
|
|
1642
1711
|
|
|
@@ -1668,6 +1737,18 @@ export function getWatcherOptions({
|
|
|
1668
1737
|
}
|
|
1669
1738
|
|
|
1670
1739
|
function getCacheKey(options) {
|
|
1740
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1741
|
+
const hash = hashString(
|
|
1742
|
+
`${ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${
|
|
1743
|
+
options.mode
|
|
1744
|
+
}:${options.shouldBuildLazily ? 'lazy' : 'eager'}:${
|
|
1745
|
+
options.watchBackend ?? ''
|
|
1746
|
+
}`,
|
|
1747
|
+
);
|
|
1748
|
+
|
|
1749
|
+
return `RequestTracker/${ATLASPACK_VERSION}/${hash}`;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1671
1752
|
return hashString(
|
|
1672
1753
|
`${ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${
|
|
1673
1754
|
options.shouldBuildLazily ? 'lazy' : 'eager'
|
|
@@ -1676,6 +1757,10 @@ function getCacheKey(options) {
|
|
|
1676
1757
|
}
|
|
1677
1758
|
|
|
1678
1759
|
function getRequestGraphNodeKey(index: number, cacheKey: string) {
|
|
1760
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1761
|
+
return `${cacheKey}/RequestGraph/nodes/${index}`;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1679
1764
|
return `requestGraph-nodes-${index}-${cacheKey}`;
|
|
1680
1765
|
}
|
|
1681
1766
|
|
|
@@ -1687,9 +1772,15 @@ export async function readAndDeserializeRequestGraph(
|
|
|
1687
1772
|
let bufferLength = 0;
|
|
1688
1773
|
|
|
1689
1774
|
const getAndDeserialize = async (key: string) => {
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1775
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
|
1776
|
+
const buffer = await cache.getBlob(key);
|
|
1777
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1778
|
+
return deserialize(buffer);
|
|
1779
|
+
} else {
|
|
1780
|
+
const buffer = await cache.getLargeBlob(key);
|
|
1781
|
+
bufferLength += Buffer.byteLength(buffer);
|
|
1782
|
+
return deserialize(buffer);
|
|
1783
|
+
}
|
|
1693
1784
|
};
|
|
1694
1785
|
|
|
1695
1786
|
let serializedRequestGraph = await getAndDeserialize(requestGraphKey);
|
|
@@ -1722,21 +1813,41 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1722
1813
|
}
|
|
1723
1814
|
|
|
1724
1815
|
let cacheKey = getCacheKey(options);
|
|
1725
|
-
let requestGraphKey =
|
|
1816
|
+
let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1817
|
+
? `${cacheKey}/RequestGraph`
|
|
1818
|
+
: `requestGraph-${cacheKey}`;
|
|
1819
|
+
|
|
1726
1820
|
let timeout;
|
|
1727
|
-
const snapshotKey =
|
|
1821
|
+
const snapshotKey = getFeatureFlag('cachePerformanceImprovements')
|
|
1822
|
+
? `${cacheKey}/snapshot`
|
|
1823
|
+
: `snapshot-${cacheKey}`;
|
|
1728
1824
|
const snapshotPath = path.join(options.cacheDir, snapshotKey + '.txt');
|
|
1729
1825
|
|
|
1826
|
+
const commonMeta = {
|
|
1827
|
+
cacheKey,
|
|
1828
|
+
snapshotKey,
|
|
1829
|
+
cacheKeyOptions: {
|
|
1830
|
+
version: ATLASPACK_VERSION,
|
|
1831
|
+
entries: options.entries,
|
|
1832
|
+
mode: options.mode,
|
|
1833
|
+
shouldBuildLazily: options.shouldBuildLazily,
|
|
1834
|
+
watchBackend: options.watchBackend,
|
|
1835
|
+
},
|
|
1836
|
+
};
|
|
1837
|
+
|
|
1730
1838
|
logger.verbose({
|
|
1731
1839
|
origin: '@atlaspack/core',
|
|
1732
1840
|
message: 'Loading request graph',
|
|
1733
1841
|
meta: {
|
|
1734
|
-
|
|
1735
|
-
snapshotKey,
|
|
1842
|
+
...commonMeta,
|
|
1736
1843
|
},
|
|
1737
1844
|
});
|
|
1738
1845
|
|
|
1739
|
-
|
|
1846
|
+
const hasRequestGraphInCache = getFeatureFlag('cachePerformanceImprovements')
|
|
1847
|
+
? await options.cache.has(requestGraphKey)
|
|
1848
|
+
: await options.cache.hasLargeBlob(requestGraphKey);
|
|
1849
|
+
|
|
1850
|
+
if (hasRequestGraphInCache) {
|
|
1740
1851
|
try {
|
|
1741
1852
|
let {requestGraph} = await readAndDeserializeRequestGraph(
|
|
1742
1853
|
options.cache,
|
|
@@ -1767,23 +1878,36 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1767
1878
|
origin: '@atlaspack/core',
|
|
1768
1879
|
message: `File system event count: ${events.length}`,
|
|
1769
1880
|
meta: {
|
|
1881
|
+
...commonMeta,
|
|
1770
1882
|
trackableEvent: 'watcher_events_count',
|
|
1771
1883
|
watcherEventCount: events.length,
|
|
1772
1884
|
duration: Date.now() - startTime,
|
|
1773
1885
|
},
|
|
1774
1886
|
});
|
|
1775
1887
|
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1888
|
+
if (getFeatureFlag('verboseRequestInvalidationStats')) {
|
|
1889
|
+
const invalidationStats = await invalidateRequestGraph(
|
|
1890
|
+
requestGraph,
|
|
1891
|
+
options,
|
|
1892
|
+
events,
|
|
1893
|
+
);
|
|
1894
|
+
|
|
1895
|
+
logger.verbose({
|
|
1896
|
+
origin: '@atlaspack/core',
|
|
1897
|
+
message: 'Request track loaded from cache',
|
|
1898
|
+
meta: {
|
|
1899
|
+
...commonMeta,
|
|
1900
|
+
trackableEvent: 'request_tracker_cache_key_hit',
|
|
1901
|
+
invalidationStats,
|
|
1902
|
+
},
|
|
1903
|
+
});
|
|
1904
|
+
} else {
|
|
1905
|
+
requestGraph.invalidateUnpredictableNodes();
|
|
1906
|
+
requestGraph.invalidateOnBuildNodes();
|
|
1907
|
+
requestGraph.invalidateEnvNodes(options.env);
|
|
1908
|
+
requestGraph.invalidateOptionNodes(options);
|
|
1909
|
+
}
|
|
1780
1910
|
|
|
1781
|
-
await requestGraph.respondToFSEvents(
|
|
1782
|
-
options.unstableFileInvalidations || events,
|
|
1783
|
-
options,
|
|
1784
|
-
10000,
|
|
1785
|
-
true,
|
|
1786
|
-
);
|
|
1787
1911
|
return requestGraph;
|
|
1788
1912
|
} catch (e) {
|
|
1789
1913
|
// Prevent logging fs events took too long warning
|
|
@@ -1800,13 +1924,227 @@ async function loadRequestGraph(options): Async<RequestGraph> {
|
|
|
1800
1924
|
message:
|
|
1801
1925
|
'Cache entry for request tracker was not found, initializing a clean cache.',
|
|
1802
1926
|
meta: {
|
|
1803
|
-
|
|
1804
|
-
|
|
1927
|
+
...commonMeta,
|
|
1928
|
+
trackableEvent: 'request_tracker_cache_key_miss',
|
|
1805
1929
|
},
|
|
1806
1930
|
});
|
|
1807
1931
|
return new RequestGraph();
|
|
1808
1932
|
}
|
|
1809
1933
|
|
|
1934
|
+
/**
|
|
1935
|
+
* A wrapper around an invalidation type / method
|
|
1936
|
+
*/
|
|
1937
|
+
type InvalidationFn = {|
|
|
1938
|
+
key: string,
|
|
1939
|
+
fn: () =>
|
|
1940
|
+
| InvalidationDetail
|
|
1941
|
+
| Promise<InvalidationDetail>
|
|
1942
|
+
| void
|
|
1943
|
+
| Promise<void>,
|
|
1944
|
+
|};
|
|
1945
|
+
|
|
1946
|
+
type InvalidationStats = {|
|
|
1947
|
+
/**
|
|
1948
|
+
* Total number of request graph nodes
|
|
1949
|
+
*/
|
|
1950
|
+
nodeCount: number,
|
|
1951
|
+
/**
|
|
1952
|
+
* Number of requests in RequestGraph
|
|
1953
|
+
*/
|
|
1954
|
+
requestCount: number,
|
|
1955
|
+
/**
|
|
1956
|
+
* Number of nodes that have been invalidated.
|
|
1957
|
+
*/
|
|
1958
|
+
invalidatedCount: number,
|
|
1959
|
+
/**
|
|
1960
|
+
* Percentage of requests that have been invalidated
|
|
1961
|
+
*/
|
|
1962
|
+
requestInvalidationRatio: number,
|
|
1963
|
+
/**
|
|
1964
|
+
* Percentage of nodes that have been invalidated
|
|
1965
|
+
*/
|
|
1966
|
+
nodeInvalidationRatio: number,
|
|
1967
|
+
/**
|
|
1968
|
+
* Details for each invalidation type
|
|
1969
|
+
*/
|
|
1970
|
+
invalidations: InvalidationFnStats[],
|
|
1971
|
+
|};
|
|
1972
|
+
|
|
1973
|
+
/**
|
|
1974
|
+
* Details about an invalidation.
|
|
1975
|
+
*
|
|
1976
|
+
* If this is a fs events invalidation, this key will contain statistics about invalidations
|
|
1977
|
+
* by path.
|
|
1978
|
+
*
|
|
1979
|
+
* If this is a env or option invalidation, this key will contain the list of changed environment
|
|
1980
|
+
* variables or options.
|
|
1981
|
+
*/
|
|
1982
|
+
type InvalidationDetail = string[] | FSInvalidationStats;
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* Number of invalidations for a given file-system event.
|
|
1986
|
+
*/
|
|
1987
|
+
type FSInvalidation = {|
|
|
1988
|
+
path: string,
|
|
1989
|
+
count: number,
|
|
1990
|
+
|};
|
|
1991
|
+
|
|
1992
|
+
type FSInvalidationStats = {|
|
|
1993
|
+
/**
|
|
1994
|
+
* This list will be sorted by the number of nodes invalidated and only the top 10 will be
|
|
1995
|
+
* included.
|
|
1996
|
+
*/
|
|
1997
|
+
biggestInvalidations: FSInvalidation[],
|
|
1998
|
+
|};
|
|
1999
|
+
|
|
2000
|
+
/**
|
|
2001
|
+
* Information about a certain cache invalidation type.
|
|
2002
|
+
*/
|
|
2003
|
+
type InvalidationFnStats = {|
|
|
2004
|
+
/**
|
|
2005
|
+
* Invalidation type, one of:
|
|
2006
|
+
*
|
|
2007
|
+
* - unpredictable
|
|
2008
|
+
* - onBuild
|
|
2009
|
+
* - env
|
|
2010
|
+
* - option
|
|
2011
|
+
* - fsEvents
|
|
2012
|
+
*/
|
|
2013
|
+
key: string,
|
|
2014
|
+
/**
|
|
2015
|
+
* Number of invalidated nodes coming from this invalidation type.
|
|
2016
|
+
*/
|
|
2017
|
+
count: number,
|
|
2018
|
+
/**
|
|
2019
|
+
* If this is a env or option invalidation, this key will contain the list of changed values.
|
|
2020
|
+
*
|
|
2021
|
+
* If this is a fs events invalidation, this key will contain statistics about invalidations
|
|
2022
|
+
*/
|
|
2023
|
+
detail: null | InvalidationDetail,
|
|
2024
|
+
/**
|
|
2025
|
+
* Time in milliseconds it took to run the invalidation.
|
|
2026
|
+
*/
|
|
2027
|
+
duration: number,
|
|
2028
|
+
|};
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* Respond to unpredictable, build, environment changes, option changes and file-system events
|
|
2032
|
+
* invalidating RequestGraph nodes.
|
|
2033
|
+
*
|
|
2034
|
+
* Returns the count of nodes invalidated by each invalidation type.
|
|
2035
|
+
*/
|
|
2036
|
+
export async function invalidateRequestGraph(
|
|
2037
|
+
requestGraph: RequestGraph,
|
|
2038
|
+
options: AtlaspackOptions,
|
|
2039
|
+
events: Event[],
|
|
2040
|
+
): Promise<InvalidationStats> {
|
|
2041
|
+
const invalidationFns: InvalidationFn[] = [
|
|
2042
|
+
{
|
|
2043
|
+
key: 'unpredictable',
|
|
2044
|
+
fn: () => requestGraph.invalidateUnpredictableNodes(),
|
|
2045
|
+
},
|
|
2046
|
+
{
|
|
2047
|
+
key: 'onBuild',
|
|
2048
|
+
fn: () => requestGraph.invalidateOnBuildNodes(),
|
|
2049
|
+
},
|
|
2050
|
+
{
|
|
2051
|
+
key: 'env',
|
|
2052
|
+
fn: () => requestGraph.invalidateEnvNodes(options.env),
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
key: 'option',
|
|
2056
|
+
fn: () => requestGraph.invalidateOptionNodes(options),
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
key: 'fsEvents',
|
|
2060
|
+
fn: () => invalidateRequestGraphFSEvents(requestGraph, options, events),
|
|
2061
|
+
},
|
|
2062
|
+
];
|
|
2063
|
+
|
|
2064
|
+
const invalidations = [];
|
|
2065
|
+
for (const invalidation of invalidationFns) {
|
|
2066
|
+
invalidations.push(await runInvalidation(requestGraph, invalidation));
|
|
2067
|
+
}
|
|
2068
|
+
const invalidatedCount = invalidations.reduce(
|
|
2069
|
+
(acc, invalidation) => acc + invalidation.count,
|
|
2070
|
+
0,
|
|
2071
|
+
);
|
|
2072
|
+
const requestCount = requestGraph.nodes.reduce(
|
|
2073
|
+
(acc, node) => acc + (node?.type === REQUEST ? 1 : 0),
|
|
2074
|
+
0,
|
|
2075
|
+
);
|
|
2076
|
+
const nodeCount = requestGraph.nodes.length;
|
|
2077
|
+
const nodeInvalidationRatio = invalidatedCount / nodeCount;
|
|
2078
|
+
const requestInvalidationRatio = invalidatedCount / requestCount;
|
|
2079
|
+
|
|
2080
|
+
return {
|
|
2081
|
+
invalidations,
|
|
2082
|
+
nodeCount,
|
|
2083
|
+
requestCount,
|
|
2084
|
+
invalidatedCount,
|
|
2085
|
+
nodeInvalidationRatio,
|
|
2086
|
+
requestInvalidationRatio,
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
interface InvalidateRequestGraphFSEventsInput {
|
|
2091
|
+
respondToFSEvents(
|
|
2092
|
+
events: Event[],
|
|
2093
|
+
options: AtlaspackOptions,
|
|
2094
|
+
timeout: number,
|
|
2095
|
+
shouldLog: boolean,
|
|
2096
|
+
): Promise<{invalidationsByPath: Map<string, number>, ...}>;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
/**
|
|
2100
|
+
* Invalidate the request graph based on file-system events.
|
|
2101
|
+
*
|
|
2102
|
+
* Returns statistics about the invalidations.
|
|
2103
|
+
*/
|
|
2104
|
+
export async function invalidateRequestGraphFSEvents(
|
|
2105
|
+
requestGraph: InvalidateRequestGraphFSEventsInput,
|
|
2106
|
+
options: AtlaspackOptions,
|
|
2107
|
+
events: Event[],
|
|
2108
|
+
): Promise<FSInvalidationStats> {
|
|
2109
|
+
const {invalidationsByPath} = await requestGraph.respondToFSEvents(
|
|
2110
|
+
options.unstableFileInvalidations || events,
|
|
2111
|
+
options,
|
|
2112
|
+
10000,
|
|
2113
|
+
true,
|
|
2114
|
+
);
|
|
2115
|
+
const biggestInvalidations =
|
|
2116
|
+
getBiggestFSEventsInvalidations(invalidationsByPath);
|
|
2117
|
+
|
|
2118
|
+
return {
|
|
2119
|
+
biggestInvalidations,
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
interface RunInvalidationInput {
|
|
2124
|
+
getInvalidNodeCount(): number;
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
/**
|
|
2128
|
+
* Runs an invalidation function and reports metrics.
|
|
2129
|
+
*/
|
|
2130
|
+
export async function runInvalidation(
|
|
2131
|
+
requestGraph: RunInvalidationInput,
|
|
2132
|
+
invalidationFn: InvalidationFn,
|
|
2133
|
+
): Promise<InvalidationFnStats> {
|
|
2134
|
+
const start = performance.now();
|
|
2135
|
+
const startInvalidationCount = requestGraph.getInvalidNodeCount();
|
|
2136
|
+
const result = await invalidationFn.fn();
|
|
2137
|
+
const count = requestGraph.getInvalidNodeCount() - startInvalidationCount;
|
|
2138
|
+
const duration = performance.now() - start;
|
|
2139
|
+
|
|
2140
|
+
return {
|
|
2141
|
+
key: invalidationFn.key,
|
|
2142
|
+
count,
|
|
2143
|
+
detail: result ?? null,
|
|
2144
|
+
duration,
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
|
|
1810
2148
|
function logErrorOnBailout(
|
|
1811
2149
|
options: AtlaspackOptions,
|
|
1812
2150
|
snapshotPath: string,
|
|
@@ -1858,3 +2196,19 @@ export function cleanUpOrphans<N, E: number>(graph: Graph<N, E>): NodeId[] {
|
|
|
1858
2196
|
|
|
1859
2197
|
return removedNodeIds;
|
|
1860
2198
|
}
|
|
2199
|
+
|
|
2200
|
+
/**
|
|
2201
|
+
* Returns paths that invalidated the most nodes
|
|
2202
|
+
*/
|
|
2203
|
+
export function getBiggestFSEventsInvalidations(
|
|
2204
|
+
invalidationsByPath: Map<string, number>,
|
|
2205
|
+
limit: number = 10,
|
|
2206
|
+
): Array<FSInvalidation> {
|
|
2207
|
+
const invalidations = [];
|
|
2208
|
+
for (const [path, count] of invalidationsByPath) {
|
|
2209
|
+
invalidations.push({path, count});
|
|
2210
|
+
}
|
|
2211
|
+
invalidations.sort((a, b) => b.count - a.count);
|
|
2212
|
+
|
|
2213
|
+
return invalidations.slice(0, limit);
|
|
2214
|
+
}
|