@atlaspack/core 2.17.3 → 2.17.4

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.
@@ -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,
@@ -934,7 +935,10 @@ export class RequestGraph extends ContentGraph<
934
935
  * True if this is the start-up (loading phase) invalidation.
935
936
  */
936
937
  isInitialBuild: boolean = false,
937
- ): Async<boolean> {
938
+ ): Promise<{|
939
+ didInvalidate: boolean,
940
+ invalidationsByPath: Map<string, number>,
941
+ |}> {
938
942
  let didInvalidate = false;
939
943
  let count = 0;
940
944
  let predictedTime = 0;
@@ -972,7 +976,10 @@ export class RequestGraph extends ContentGraph<
972
976
  return above;
973
977
  };
974
978
 
979
+ const invalidationsByPath = new Map();
975
980
  for (let {path: _path, type} of events) {
981
+ const invalidationsBefore = this.getInvalidNodeCount();
982
+
976
983
  if (
977
984
  !enableOptimization &&
978
985
  process.env.ATLASPACK_DISABLE_CACHE_TIMEOUT !== 'true' &&
@@ -1018,7 +1025,10 @@ export class RequestGraph extends ContentGraph<
1018
1025
  this.invalidNodeIds.add(id);
1019
1026
  }
1020
1027
  }
1021
- return true;
1028
+ return {
1029
+ didInvalidate: true,
1030
+ invalidationsByPath: new Map(),
1031
+ };
1022
1032
  }
1023
1033
 
1024
1034
  // sometimes mac os reports update events as create events.
@@ -1131,6 +1141,13 @@ export class RequestGraph extends ContentGraph<
1131
1141
  }
1132
1142
  }
1133
1143
  }
1144
+
1145
+ const invalidationsAfter = this.getInvalidNodeCount();
1146
+ const invalidationsForEvent = invalidationsAfter - invalidationsBefore;
1147
+ invalidationsByPath.set(
1148
+ _path,
1149
+ (invalidationsByPath.get(_path) ?? 0) + invalidationsForEvent,
1150
+ );
1134
1151
  }
1135
1152
 
1136
1153
  if (getFeatureFlag('fixQuadraticCacheInvalidation')) {
@@ -1151,7 +1168,10 @@ export class RequestGraph extends ContentGraph<
1151
1168
  },
1152
1169
  });
1153
1170
 
1154
- return didInvalidate && this.invalidNodeIds.size > 0;
1171
+ return {
1172
+ didInvalidate,
1173
+ invalidationsByPath,
1174
+ };
1155
1175
  }
1156
1176
 
1157
1177
  hasCachedRequestChunk(index: number): boolean {
@@ -1165,6 +1185,13 @@ export class RequestGraph extends ContentGraph<
1165
1185
  removeCachedRequestChunkForNode(nodeId: number): void {
1166
1186
  this.cachedRequestChunks.delete(Math.floor(nodeId / this.nodesPerBlob));
1167
1187
  }
1188
+
1189
+ /**
1190
+ * Returns the number of invalidated nodes in the graph.
1191
+ */
1192
+ getInvalidNodeCount(): number {
1193
+ return this.invalidNodeIds.size;
1194
+ }
1168
1195
  }
1169
1196
 
1170
1197
  export default class RequestTracker {
@@ -1288,7 +1315,13 @@ export default class RequestTracker {
1288
1315
  }
1289
1316
  }
1290
1317
 
1291
- respondToFSEvents(events: Array<Event>, threshold: number): Async<boolean> {
1318
+ respondToFSEvents(
1319
+ events: Array<Event>,
1320
+ threshold: number,
1321
+ ): Promise<{|
1322
+ didInvalidate: boolean,
1323
+ invalidationsByPath: Map<string, number>,
1324
+ |}> {
1292
1325
  return this.graph.respondToFSEvents(events, this.options, threshold);
1293
1326
  }
1294
1327
 
@@ -1499,130 +1532,139 @@ export default class RequestTracker {
1499
1532
  }
1500
1533
  }
1501
1534
 
1502
- await runCacheImprovements(
1503
- async (cache) => {
1504
- await cache.getNativeRef().startWriteTransaction();
1505
- },
1506
- () => Promise.resolve(),
1507
- );
1508
-
1509
1535
  let cacheKey = getCacheKey(this.options);
1510
- let requestGraphKey = `requestGraph-${cacheKey}`;
1511
- let snapshotKey = `snapshot-${cacheKey}`;
1536
+ let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
1537
+ ? `${cacheKey}/RequestGraph`
1538
+ : `requestGraph-${cacheKey}`;
1539
+ let snapshotKey = getFeatureFlag('cachePerformanceImprovements')
1540
+ ? `${cacheKey}/snapshot`
1541
+ : `snapshot-${cacheKey}`;
1512
1542
 
1513
1543
  if (this.options.shouldDisableCache) {
1514
1544
  return;
1515
1545
  }
1516
1546
 
1517
1547
  let total = 0;
1518
- report({
1519
- type: 'cache',
1520
- phase: 'start',
1521
- total,
1522
- size: this.graph.nodes.length,
1523
- });
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
+ });
1524
1561
 
1525
- let serialisedGraph = this.graph.serialize();
1562
+ let serialisedGraph = this.graph.serialize();
1526
1563
 
1527
- // Delete an existing request graph cache, to prevent invalid states
1528
- await this.options.cache.deleteLargeBlob(requestGraphKey);
1564
+ // Delete an existing request graph cache, to prevent invalid states
1565
+ await this.options.cache.deleteLargeBlob(requestGraphKey);
1529
1566
 
1530
- const serialiseAndSet = async (
1531
- key: string,
1532
- // $FlowFixMe serialise input is any type
1533
- contents: any,
1534
- ): Promise<void> => {
1535
- if (signal?.aborted) {
1536
- throw new Error('Serialization was aborted');
1537
- }
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
+ }
1538
1575
 
1539
- await runCacheImprovements(
1540
- (cache) => {
1541
- instrument(`cache.put(${key})`, () => {
1542
- cache.getNativeRef().putNoConfirm(key, serialize(contents));
1543
- });
1544
- return Promise.resolve();
1545
- },
1546
- async () => {
1547
- await this.options.cache.setLargeBlob(
1548
- key,
1549
- serialize(contents),
1550
- signal
1551
- ? {
1552
- signal: signal,
1553
- }
1554
- : undefined,
1555
- );
1556
- },
1557
- );
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
+ );
1558
1598
 
1559
- total += 1;
1599
+ total += 1;
1560
1600
 
1561
- report({
1562
- type: 'cache',
1563
- phase: 'write',
1564
- total,
1565
- size: this.graph.nodes.length,
1566
- });
1567
- };
1601
+ report({
1602
+ type: 'cache',
1603
+ phase: 'write',
1604
+ total,
1605
+ size: this.graph.nodes.length,
1606
+ });
1607
+ };
1568
1608
 
1569
- let queue = new PromiseQueue({
1570
- maxConcurrent: 32,
1571
- });
1609
+ let queue = new PromiseQueue({
1610
+ maxConcurrent: 32,
1611
+ });
1572
1612
 
1573
- // Preallocating a sparse array is faster than pushing when N is high enough
1574
- let cacheableNodes = new Array(serialisedGraph.nodes.length);
1575
- for (let i = 0; i < serialisedGraph.nodes.length; i += 1) {
1576
- let node = serialisedGraph.nodes[i];
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];
1577
1617
 
1578
- let resultCacheKey = node?.resultCacheKey;
1579
- if (
1580
- node?.type === REQUEST &&
1581
- resultCacheKey != null &&
1582
- node?.result != null
1583
- ) {
1584
- queue.add(() => serialiseAndSet(resultCacheKey, node.result));
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));
1585
1625
 
1586
- // eslint-disable-next-line no-unused-vars
1587
- let {result: _, ...newNode} = node;
1588
- cacheableNodes[i] = newNode;
1589
- } else {
1590
- cacheableNodes[i] = node;
1626
+ // eslint-disable-next-line no-unused-vars
1627
+ let {result: _, ...newNode} = node;
1628
+ cacheableNodes[i] = newNode;
1629
+ } else {
1630
+ cacheableNodes[i] = node;
1631
+ }
1591
1632
  }
1592
- }
1593
1633
 
1594
- let nodeCountsPerBlob = [];
1634
+ let nodeCountsPerBlob = [];
1595
1635
 
1596
- for (
1597
- let i = 0;
1598
- i * this.graph.nodesPerBlob < cacheableNodes.length;
1599
- i += 1
1600
- ) {
1601
- let nodesStartIndex = i * this.graph.nodesPerBlob;
1602
- let nodesEndIndex = Math.min(
1603
- (i + 1) * this.graph.nodesPerBlob,
1604
- cacheableNodes.length,
1605
- );
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
+ );
1606
1646
 
1607
- nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
1647
+ nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
1608
1648
 
1609
- if (!this.graph.hasCachedRequestChunk(i)) {
1610
- // We assume the request graph nodes are immutable and won't change
1611
- let nodesToCache = cacheableNodes.slice(nodesStartIndex, nodesEndIndex);
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
+ );
1612
1655
 
1613
- queue.add(() =>
1614
- serialiseAndSet(
1615
- getRequestGraphNodeKey(i, cacheKey),
1616
- nodesToCache,
1617
- ).then(() => {
1618
- // Succeeded in writing to disk, save that we have completed this chunk
1619
- this.graph.setCachedRequestChunk(i);
1620
- }),
1621
- );
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
+ }
1622
1666
  }
1623
- }
1624
1667
 
1625
- try {
1626
1668
  await queue.run();
1627
1669
 
1628
1670
  // Set the request graph after the queue is flushed to avoid writing an invalid state
@@ -1634,7 +1676,7 @@ export default class RequestTracker {
1634
1676
 
1635
1677
  await runCacheImprovements(
1636
1678
  () =>
1637
- serialiseAndSet(`request_tracker:cache_metadata:${cacheKey}`, {
1679
+ serialiseAndSet(`${cacheKey}/cache_metadata`, {
1638
1680
  version: ATLASPACK_VERSION,
1639
1681
  entries: this.options.entries,
1640
1682
  mode: this.options.mode,
@@ -1655,15 +1697,15 @@ export default class RequestTracker {
1655
1697
  } catch (err) {
1656
1698
  // If we have aborted, ignore the error and continue
1657
1699
  if (!signal?.aborted) throw err;
1700
+ } finally {
1701
+ await runCacheImprovements(
1702
+ async (cache) => {
1703
+ await cache.getNativeRef().commitWriteTransaction();
1704
+ },
1705
+ () => Promise.resolve(),
1706
+ );
1658
1707
  }
1659
1708
 
1660
- await runCacheImprovements(
1661
- async (cache) => {
1662
- await cache.getNativeRef().commitWriteTransaction();
1663
- },
1664
- () => Promise.resolve(),
1665
- );
1666
-
1667
1709
  report({type: 'cache', phase: 'end', total, size: this.graph.nodes.length});
1668
1710
  }
1669
1711
 
@@ -1695,6 +1737,18 @@ export function getWatcherOptions({
1695
1737
  }
1696
1738
 
1697
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
+
1698
1752
  return hashString(
1699
1753
  `${ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${
1700
1754
  options.shouldBuildLazily ? 'lazy' : 'eager'
@@ -1703,6 +1757,10 @@ function getCacheKey(options) {
1703
1757
  }
1704
1758
 
1705
1759
  function getRequestGraphNodeKey(index: number, cacheKey: string) {
1760
+ if (getFeatureFlag('cachePerformanceImprovements')) {
1761
+ return `${cacheKey}/RequestGraph/nodes/${index}`;
1762
+ }
1763
+
1706
1764
  return `requestGraph-nodes-${index}-${cacheKey}`;
1707
1765
  }
1708
1766
 
@@ -1714,9 +1772,15 @@ export async function readAndDeserializeRequestGraph(
1714
1772
  let bufferLength = 0;
1715
1773
 
1716
1774
  const getAndDeserialize = async (key: string) => {
1717
- let buffer = await cache.getLargeBlob(key);
1718
- bufferLength += Buffer.byteLength(buffer);
1719
- return deserialize(buffer);
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
+ }
1720
1784
  };
1721
1785
 
1722
1786
  let serializedRequestGraph = await getAndDeserialize(requestGraphKey);
@@ -1749,9 +1813,14 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1749
1813
  }
1750
1814
 
1751
1815
  let cacheKey = getCacheKey(options);
1752
- let requestGraphKey = `requestGraph-${cacheKey}`;
1816
+ let requestGraphKey = getFeatureFlag('cachePerformanceImprovements')
1817
+ ? `${cacheKey}/RequestGraph`
1818
+ : `requestGraph-${cacheKey}`;
1819
+
1753
1820
  let timeout;
1754
- const snapshotKey = `snapshot-${cacheKey}`;
1821
+ const snapshotKey = getFeatureFlag('cachePerformanceImprovements')
1822
+ ? `${cacheKey}/snapshot`
1823
+ : `snapshot-${cacheKey}`;
1755
1824
  const snapshotPath = path.join(options.cacheDir, snapshotKey + '.txt');
1756
1825
 
1757
1826
  const commonMeta = {
@@ -1774,7 +1843,11 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1774
1843
  },
1775
1844
  });
1776
1845
 
1777
- if (await options.cache.hasLargeBlob(requestGraphKey)) {
1846
+ const hasRequestGraphInCache = getFeatureFlag('cachePerformanceImprovements')
1847
+ ? await options.cache.has(requestGraphKey)
1848
+ : await options.cache.hasLargeBlob(requestGraphKey);
1849
+
1850
+ if (hasRequestGraphInCache) {
1778
1851
  try {
1779
1852
  let {requestGraph} = await readAndDeserializeRequestGraph(
1780
1853
  options.cache,
@@ -1863,7 +1936,11 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1863
1936
  */
1864
1937
  type InvalidationFn = {|
1865
1938
  key: string,
1866
- fn: () => string[] | void | Promise<void>,
1939
+ fn: () =>
1940
+ | InvalidationDetail
1941
+ | Promise<InvalidationDetail>
1942
+ | void
1943
+ | Promise<void>,
1867
1944
  |};
1868
1945
 
1869
1946
  type InvalidationStats = {|
@@ -1893,6 +1970,33 @@ type InvalidationStats = {|
1893
1970
  invalidations: InvalidationFnStats[],
1894
1971
  |};
1895
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
+
1896
2000
  /**
1897
2001
  * Information about a certain cache invalidation type.
1898
2002
  */
@@ -1913,8 +2017,14 @@ type InvalidationFnStats = {|
1913
2017
  count: number,
1914
2018
  /**
1915
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
1916
2022
  */
1917
- changes: null | string[],
2023
+ detail: null | InvalidationDetail,
2024
+ /**
2025
+ * Time in milliseconds it took to run the invalidation.
2026
+ */
2027
+ duration: number,
1918
2028
  |};
1919
2029
 
1920
2030
  /**
@@ -1923,7 +2033,7 @@ type InvalidationFnStats = {|
1923
2033
  *
1924
2034
  * Returns the count of nodes invalidated by each invalidation type.
1925
2035
  */
1926
- async function invalidateRequestGraph(
2036
+ export async function invalidateRequestGraph(
1927
2037
  requestGraph: RequestGraph,
1928
2038
  options: AtlaspackOptions,
1929
2039
  events: Event[],
@@ -1947,14 +2057,7 @@ async function invalidateRequestGraph(
1947
2057
  },
1948
2058
  {
1949
2059
  key: 'fsEvents',
1950
- fn: async () => {
1951
- await requestGraph.respondToFSEvents(
1952
- options.unstableFileInvalidations || events,
1953
- options,
1954
- 10000,
1955
- true,
1956
- );
1957
- },
2060
+ fn: () => invalidateRequestGraphFSEvents(requestGraph, options, events),
1958
2061
  },
1959
2062
  ];
1960
2063
 
@@ -1984,22 +2087,61 @@ async function invalidateRequestGraph(
1984
2087
  };
1985
2088
  }
1986
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
+
1987
2127
  /**
1988
2128
  * Runs an invalidation function and reports metrics.
1989
2129
  */
1990
- async function runInvalidation(
1991
- requestGraph: RequestGraph,
2130
+ export async function runInvalidation(
2131
+ requestGraph: RunInvalidationInput,
1992
2132
  invalidationFn: InvalidationFn,
1993
2133
  ): Promise<InvalidationFnStats> {
1994
- const startInvalidationCount = requestGraph.invalidNodeIds.size;
2134
+ const start = performance.now();
2135
+ const startInvalidationCount = requestGraph.getInvalidNodeCount();
1995
2136
  const result = await invalidationFn.fn();
1996
- const count = requestGraph.invalidNodeIds.size - startInvalidationCount;
2137
+ const count = requestGraph.getInvalidNodeCount() - startInvalidationCount;
2138
+ const duration = performance.now() - start;
1997
2139
 
1998
2140
  return {
1999
2141
  key: invalidationFn.key,
2000
2142
  count,
2001
- changes:
2002
- typeof result === 'object' && Array.isArray(result) ? result : null,
2143
+ detail: result ?? null,
2144
+ duration,
2003
2145
  };
2004
2146
  }
2005
2147
 
@@ -2054,3 +2196,19 @@ export function cleanUpOrphans<N, E: number>(graph: Graph<N, E>): NodeId[] {
2054
2196
 
2055
2197
  return removedNodeIds;
2056
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
+ }
@@ -30,7 +30,12 @@ import {ATLASPACK_VERSION} from './constants';
30
30
  import {createAsset, createAssetIdFromOptions} from './assetUtils';
31
31
  import {BundleBehaviorNames} from './types';
32
32
  import {invalidateOnFileCreateToInternal, createInvalidations} from './utils';
33
- import {type ProjectPath, fromProjectPath} from './projectPath';
33
+ import {
34
+ type ProjectPath,
35
+ fromProjectPath,
36
+ fromProjectPathRelative,
37
+ } from './projectPath';
38
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
34
39
 
35
40
  type UncommittedAssetOptions = {|
36
41
  value: Asset,
@@ -153,7 +158,9 @@ export default class UncommittedAsset {
153
158
  size = content.length;
154
159
  }
155
160
 
161
+ // Maybe we should just store this in a file instead of LMDB
156
162
  await this.options.cache.setBlob(contentKey, content);
163
+
157
164
  return {size, hash};
158
165
  }
159
166
 
@@ -214,6 +221,9 @@ export default class UncommittedAsset {
214
221
  this.clearAST();
215
222
  }
216
223
 
224
+ /**
225
+ * @deprecated This has been broken on any cache other than FSCache for a long time.
226
+ */
217
227
  setStream(stream: Readable) {
218
228
  this.content = stream;
219
229
  this.clearAST();
@@ -296,6 +306,11 @@ export default class UncommittedAsset {
296
306
  }
297
307
 
298
308
  getCacheKey(key: string): string {
309
+ if (getFeatureFlag('cachePerformanceImprovements')) {
310
+ const filePath = fromProjectPathRelative(this.value.filePath);
311
+ return `Asset/${ATLASPACK_VERSION}/${filePath}/${this.value.id}/${key}`;
312
+ }
313
+
299
314
  return hashString(ATLASPACK_VERSION + key + this.value.id);
300
315
  }
301
316
 
@@ -100,11 +100,15 @@ export class PluginConfig implements IPluginConfig {
100
100
  // eslint-disable-next-line no-unused-vars
101
101
  filePaths: Array<FilePath>,
102
102
  // eslint-disable-next-line no-unused-vars
103
- options?: {|
104
- packageKey?: string,
105
- parse?: boolean,
106
- exclude?: boolean,
107
- |},
103
+ options?:
104
+ | {|
105
+ packageKey?: string,
106
+ parse?: boolean,
107
+ exclude?: boolean,
108
+ |}
109
+ | {|
110
+ configKey?: string,
111
+ |},
108
112
  ): Promise<?ConfigResultWithFilePath<T>> {
109
113
  return this.#inner.getConfigFrom(searchPath, filePaths, options);
110
114
  }