@atlaspack/core 2.16.2-canary.2 → 2.16.2-canary.20

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 CHANGED
@@ -1,5 +1,67 @@
1
1
  # @atlaspack/core
2
2
 
3
+ ## 2.17.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`a1773d2`](https://github.com/atlassian-labs/atlaspack/commit/a1773d2a62d0ef7805ac7524621dcabcc1afe929), [`556d6ab`](https://github.com/atlassian-labs/atlaspack/commit/556d6ab8ede759fa7f37fcd3f4da336ef1c55e8f)]:
8
+ - @atlaspack/feature-flags@2.15.0
9
+ - @atlaspack/logger@2.14.7
10
+ - @atlaspack/rust@3.3.2
11
+ - @atlaspack/cache@3.2.2
12
+ - @atlaspack/fs@2.15.2
13
+ - @atlaspack/graph@3.4.5
14
+ - @atlaspack/utils@2.14.7
15
+ - @atlaspack/package-manager@2.14.7
16
+ - @atlaspack/workers@2.14.7
17
+ - @atlaspack/profiler@2.14.5
18
+ - @atlaspack/types@2.14.7
19
+ - @atlaspack/plugin@2.14.7
20
+
21
+ ## 2.17.1
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [[`e0f5337`](https://github.com/atlassian-labs/atlaspack/commit/e0f533757bd1019dbd108a04952c87da15286e09)]:
26
+ - @atlaspack/feature-flags@2.14.4
27
+ - @atlaspack/rust@3.3.1
28
+ - @atlaspack/cache@3.2.1
29
+ - @atlaspack/fs@2.15.1
30
+ - @atlaspack/graph@3.4.4
31
+ - @atlaspack/utils@2.14.6
32
+ - @atlaspack/logger@2.14.6
33
+ - @atlaspack/package-manager@2.14.6
34
+ - @atlaspack/profiler@2.14.4
35
+ - @atlaspack/types@2.14.6
36
+ - @atlaspack/workers@2.14.6
37
+ - @atlaspack/plugin@2.14.6
38
+
39
+ ## 2.17.0
40
+
41
+ ### Minor Changes
42
+
43
+ - [#541](https://github.com/atlassian-labs/atlaspack/pull/541) [`e2ba0f6`](https://github.com/atlassian-labs/atlaspack/commit/e2ba0f69702656f3d1ce95ab1454e35062b13b39) Thanks [@yamadapc](https://github.com/yamadapc)! - Add database compaction debug command
44
+
45
+ ### Patch Changes
46
+
47
+ - [#530](https://github.com/atlassian-labs/atlaspack/pull/530) [`2e90c9b`](https://github.com/atlassian-labs/atlaspack/commit/2e90c9bd07d7eb52645f9d84ccbb7f82685cbc8c) Thanks [@yamadapc](https://github.com/yamadapc)! - Write metadata about the cache in a new entry
48
+
49
+ - [#511](https://github.com/atlassian-labs/atlaspack/pull/511) [`11d6f16`](https://github.com/atlassian-labs/atlaspack/commit/11d6f16b6397dee2f217167e5c98b39edb63f7a7) Thanks [@yamadapc](https://github.com/yamadapc)! - Clean-up dylib worker threads segmentation fault bug fix feature-flag
50
+
51
+ - Updated dependencies [[`11d6f16`](https://github.com/atlassian-labs/atlaspack/commit/11d6f16b6397dee2f217167e5c98b39edb63f7a7), [`e2ba0f6`](https://github.com/atlassian-labs/atlaspack/commit/e2ba0f69702656f3d1ce95ab1454e35062b13b39), [`d2c50c2`](https://github.com/atlassian-labs/atlaspack/commit/d2c50c2c020888b33bb25b8690d9320c2b69e2a6), [`46a90dc`](https://github.com/atlassian-labs/atlaspack/commit/46a90dccd019a26b222c878a92d23acc75dc67c5), [`4c17141`](https://github.com/atlassian-labs/atlaspack/commit/4c1714103dab2aa9039c488f381551d2b65d1d01)]:
52
+ - @atlaspack/feature-flags@2.14.3
53
+ - @atlaspack/rust@3.3.0
54
+ - @atlaspack/cache@3.2.0
55
+ - @atlaspack/fs@2.15.0
56
+ - @atlaspack/graph@3.4.3
57
+ - @atlaspack/utils@2.14.5
58
+ - @atlaspack/logger@2.14.5
59
+ - @atlaspack/package-manager@2.14.5
60
+ - @atlaspack/profiler@2.14.3
61
+ - @atlaspack/types@2.14.5
62
+ - @atlaspack/workers@2.14.5
63
+ - @atlaspack/plugin@2.14.5
64
+
3
65
  ## 2.16.1
4
66
 
5
67
  ### Patch Changes
package/lib/Atlaspack.js CHANGED
@@ -166,9 +166,7 @@ class Atlaspack {
166
166
  ...this.#initialOptions.featureFlags
167
167
  };
168
168
  (0, _featureFlags().setFeatureFlags)(featureFlags);
169
- if ((0, _featureFlags().getFeatureFlag)('enableRustWorkerThreadDylibHack')) {
170
- (0, _rustWorkerThreadDylibHack.loadRustWorkerThreadDylibHack)();
171
- }
169
+ (0, _rustWorkerThreadDylibHack.loadRustWorkerThreadDylibHack)();
172
170
  await _sourceMap().init;
173
171
  await (_rust().init === null || _rust().init === void 0 ? void 0 : (0, _rust().init)());
174
172
  this.#disposable = new (_events().Disposable)();
@@ -603,6 +601,19 @@ class Atlaspack {
603
601
  }
604
602
  return result;
605
603
  }
604
+
605
+ /**
606
+ * Copy the cache to a new directory and compact it.
607
+ */
608
+ async unstable_compactCache() {
609
+ await this._init();
610
+ const cache = (0, _nullthrows().default)(this.#resolvedOptions).cache;
611
+ if (cache instanceof _cache().LMDBLiteCache) {
612
+ await cache.compact('parcel-cache-compacted');
613
+ } else {
614
+ throw new Error('Cache is not an LMDBLiteCache');
615
+ }
616
+ }
606
617
  async unstable_transform(options) {
607
618
  var _options$env;
608
619
  if (!this.#initialized) {
@@ -279,6 +279,12 @@ class RequestGraph extends _graph().ContentGraph {
279
279
  // If the node is invalidated, the cached request chunk on disk needs to be re-written
280
280
  this.removeCachedRequestChunkForNode(nodeId);
281
281
  }
282
+
283
+ /**
284
+ * Nodes that are invalidated on start-up, such as JavaScript babel configuration files which are
285
+ * imported when the build kicks-off and might doing arbitrary work such as reading from the file
286
+ * system.
287
+ */
282
288
  invalidateUnpredictableNodes() {
283
289
  for (let nodeId of this.unpredicatableNodeIds) {
284
290
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
@@ -286,6 +292,10 @@ class RequestGraph extends _graph().ContentGraph {
286
292
  this.invalidateNode(nodeId, _constants.STARTUP);
287
293
  }
288
294
  }
295
+
296
+ /**
297
+ * Effectively uncacheable nodes.
298
+ */
289
299
  invalidateOnBuildNodes() {
290
300
  for (let nodeId of this.invalidateOnBuildNodeIds) {
291
301
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
@@ -293,29 +303,45 @@ class RequestGraph extends _graph().ContentGraph {
293
303
  this.invalidateNode(nodeId, _constants.STARTUP);
294
304
  }
295
305
  }
306
+
307
+ /**
308
+ * Nodes invalidated by environment changes, corresponds to `env: ...` inputs.
309
+ */
296
310
  invalidateEnvNodes(env) {
311
+ const invalidatedKeys = [];
297
312
  for (let nodeId of this.envNodeIds) {
298
313
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
299
314
  (0, _assert().default)(node.type === ENV);
300
- if (env[keyFromEnvContentKey(node.id)] !== node.value) {
315
+ const key = keyFromEnvContentKey(node.id);
316
+ if (env[key] !== node.value) {
317
+ invalidatedKeys.push(key);
301
318
  let parentNodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update);
302
319
  for (let parentNode of parentNodes) {
303
320
  this.invalidateNode(parentNode, _constants.ENV_CHANGE);
304
321
  }
305
322
  }
306
323
  }
324
+ return invalidatedKeys;
307
325
  }
326
+
327
+ /**
328
+ * Nodes invalidated by option changes.
329
+ */
308
330
  invalidateOptionNodes(options) {
331
+ const invalidatedKeys = [];
309
332
  for (let nodeId of this.optionNodeIds) {
310
333
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
311
334
  (0, _assert().default)(node.type === OPTION);
312
- if ((0, _utils2.hashFromOption)(options[keyFromOptionContentKey(node.id)]) !== node.hash) {
335
+ const key = keyFromOptionContentKey(node.id);
336
+ if ((0, _utils2.hashFromOption)(options[key]) !== node.hash) {
337
+ invalidatedKeys.push(key);
313
338
  let parentNodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update);
314
339
  for (let parentNode of parentNodes) {
315
340
  this.invalidateNode(parentNode, _constants.OPTION_CHANGE);
316
341
  }
317
342
  }
318
343
  }
344
+ return invalidatedKeys;
319
345
  }
320
346
  invalidateOnConfigKeyChange(requestNodeId, filePath, configKey, contentHash) {
321
347
  let configKeyNodeId = this.addNode(nodeFromConfigKey(filePath, configKey, contentHash));
@@ -1030,6 +1056,13 @@ class RequestTracker {
1030
1056
  nodeCountsPerBlob,
1031
1057
  nodes: undefined
1032
1058
  });
1059
+ await runCacheImprovements(() => serialiseAndSet(`request_tracker:cache_metadata:${cacheKey}`, {
1060
+ version: _constants.ATLASPACK_VERSION,
1061
+ entries: this.options.entries,
1062
+ mode: this.options.mode,
1063
+ shouldBuildLazily: this.options.shouldBuildLazily,
1064
+ watchBackend: this.options.watchBackend
1065
+ }), () => Promise.resolve());
1033
1066
  let opts = getWatcherOptions(this.options);
1034
1067
  let snapshotPath = _path2().default.join(this.options.cacheDir, `snapshot-${cacheKey}` + '.txt');
1035
1068
  await this.options.outputFS.writeSnapshot(this.options.watchDir, snapshotPath, opts);
@@ -1112,12 +1145,22 @@ async function loadRequestGraph(options) {
1112
1145
  let timeout;
1113
1146
  const snapshotKey = `snapshot-${cacheKey}`;
1114
1147
  const snapshotPath = _path2().default.join(options.cacheDir, snapshotKey + '.txt');
1148
+ const commonMeta = {
1149
+ cacheKey,
1150
+ snapshotKey,
1151
+ cacheKeyOptions: {
1152
+ version: _constants.ATLASPACK_VERSION,
1153
+ entries: options.entries,
1154
+ mode: options.mode,
1155
+ shouldBuildLazily: options.shouldBuildLazily,
1156
+ watchBackend: options.watchBackend
1157
+ }
1158
+ };
1115
1159
  _logger().default.verbose({
1116
1160
  origin: '@atlaspack/core',
1117
1161
  message: 'Loading request graph',
1118
1162
  meta: {
1119
- cacheKey,
1120
- snapshotKey
1163
+ ...commonMeta
1121
1164
  }
1122
1165
  });
1123
1166
  if (await options.cache.hasLargeBlob(requestGraphKey)) {
@@ -1139,16 +1182,29 @@ async function loadRequestGraph(options) {
1139
1182
  origin: '@atlaspack/core',
1140
1183
  message: `File system event count: ${events.length}`,
1141
1184
  meta: {
1185
+ ...commonMeta,
1142
1186
  trackableEvent: 'watcher_events_count',
1143
1187
  watcherEventCount: events.length,
1144
1188
  duration: Date.now() - startTime
1145
1189
  }
1146
1190
  });
1147
- requestGraph.invalidateUnpredictableNodes();
1148
- requestGraph.invalidateOnBuildNodes();
1149
- requestGraph.invalidateEnvNodes(options.env);
1150
- requestGraph.invalidateOptionNodes(options);
1151
- await requestGraph.respondToFSEvents(options.unstableFileInvalidations || events, options, 10000, true);
1191
+ if ((0, _featureFlags().getFeatureFlag)('verboseRequestInvalidationStats')) {
1192
+ const invalidationStats = await invalidateRequestGraph(requestGraph, options, events);
1193
+ _logger().default.verbose({
1194
+ origin: '@atlaspack/core',
1195
+ message: 'Request track loaded from cache',
1196
+ meta: {
1197
+ ...commonMeta,
1198
+ trackableEvent: 'request_tracker_cache_key_hit',
1199
+ invalidationStats
1200
+ }
1201
+ });
1202
+ } else {
1203
+ requestGraph.invalidateUnpredictableNodes();
1204
+ requestGraph.invalidateOnBuildNodes();
1205
+ requestGraph.invalidateEnvNodes(options.env);
1206
+ requestGraph.invalidateOptionNodes(options);
1207
+ }
1152
1208
  return requestGraph;
1153
1209
  } catch (e) {
1154
1210
  // Prevent logging fs events took too long warning
@@ -1163,12 +1219,76 @@ async function loadRequestGraph(options) {
1163
1219
  origin: '@atlaspack/core',
1164
1220
  message: 'Cache entry for request tracker was not found, initializing a clean cache.',
1165
1221
  meta: {
1166
- cacheKey,
1167
- snapshotKey
1222
+ ...commonMeta,
1223
+ trackableEvent: 'request_tracker_cache_key_miss'
1168
1224
  }
1169
1225
  });
1170
1226
  return new RequestGraph();
1171
1227
  }
1228
+
1229
+ /**
1230
+ * A wrapper around an invalidation type / method
1231
+ */
1232
+
1233
+ /**
1234
+ * Information about a certain cache invalidation type.
1235
+ */
1236
+
1237
+ /**
1238
+ * Respond to unpredictable, build, environment changes, option changes and file-system events
1239
+ * invalidating RequestGraph nodes.
1240
+ *
1241
+ * Returns the count of nodes invalidated by each invalidation type.
1242
+ */
1243
+ async function invalidateRequestGraph(requestGraph, options, events) {
1244
+ const invalidationFns = [{
1245
+ key: 'unpredictable',
1246
+ fn: () => requestGraph.invalidateUnpredictableNodes()
1247
+ }, {
1248
+ key: 'onBuild',
1249
+ fn: () => requestGraph.invalidateOnBuildNodes()
1250
+ }, {
1251
+ key: 'env',
1252
+ fn: () => requestGraph.invalidateEnvNodes(options.env)
1253
+ }, {
1254
+ key: 'option',
1255
+ fn: () => requestGraph.invalidateOptionNodes(options)
1256
+ }, {
1257
+ key: 'fsEvents',
1258
+ fn: async () => {
1259
+ await requestGraph.respondToFSEvents(options.unstableFileInvalidations || events, options, 10000, true);
1260
+ }
1261
+ }];
1262
+ const invalidations = [];
1263
+ for (const invalidation of invalidationFns) {
1264
+ invalidations.push(await runInvalidation(requestGraph, invalidation));
1265
+ }
1266
+ const invalidatedCount = invalidations.reduce((acc, invalidation) => acc + invalidation.count, 0);
1267
+ const requestCount = requestGraph.nodes.reduce((acc, node) => acc + ((node === null || node === void 0 ? void 0 : node.type) === REQUEST ? 1 : 0), 0);
1268
+ const nodeCount = requestGraph.nodes.length;
1269
+ return {
1270
+ invalidations,
1271
+ nodeCount,
1272
+ requestCount,
1273
+ invalidatedCount,
1274
+ nodeInvalidationRatio: invalidatedCount / nodeCount,
1275
+ requestInvalidationRatio: invalidatedCount / requestCount
1276
+ };
1277
+ }
1278
+
1279
+ /**
1280
+ * Runs an invalidation function and reports metrics.
1281
+ */
1282
+ async function runInvalidation(requestGraph, invalidationFn) {
1283
+ const startInvalidationCount = requestGraph.invalidNodeIds.size;
1284
+ const result = await invalidationFn.fn();
1285
+ const count = requestGraph.invalidNodeIds.size - startInvalidationCount;
1286
+ return {
1287
+ key: invalidationFn.key,
1288
+ count,
1289
+ changes: typeof result === 'object' && Array.isArray(result) ? result : null
1290
+ };
1291
+ }
1172
1292
  function logErrorOnBailout(options, snapshotPath, e) {
1173
1293
  if (e.message && e.message.includes('invalid clockspec')) {
1174
1294
  const snapshotContents = options.inputFS.readFileSync(snapshotPath, 'utf-8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaspack/core",
3
- "version": "2.16.2-canary.2+88fcaf47c",
3
+ "version": "2.16.2-canary.20+30f60175b",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,21 +20,21 @@
20
20
  "check-ts": "tsc --noEmit index.d.ts"
21
21
  },
22
22
  "dependencies": {
23
- "@atlaspack/build-cache": "2.13.3-canary.70+88fcaf47c",
24
- "@atlaspack/cache": "3.1.1-canary.2+88fcaf47c",
25
- "@atlaspack/diagnostic": "2.14.1-canary.70+88fcaf47c",
26
- "@atlaspack/events": "2.14.1-canary.70+88fcaf47c",
27
- "@atlaspack/feature-flags": "2.14.1-canary.70+88fcaf47c",
28
- "@atlaspack/fs": "2.14.5-canary.2+88fcaf47c",
29
- "@atlaspack/graph": "3.4.1-canary.70+88fcaf47c",
30
- "@atlaspack/logger": "2.14.5-canary.2+88fcaf47c",
31
- "@atlaspack/package-manager": "2.14.5-canary.2+88fcaf47c",
32
- "@atlaspack/plugin": "2.14.5-canary.2+88fcaf47c",
33
- "@atlaspack/profiler": "2.14.1-canary.70+88fcaf47c",
34
- "@atlaspack/rust": "3.2.1-canary.2+88fcaf47c",
35
- "@atlaspack/types": "2.14.5-canary.2+88fcaf47c",
36
- "@atlaspack/utils": "2.14.5-canary.2+88fcaf47c",
37
- "@atlaspack/workers": "2.14.5-canary.2+88fcaf47c",
23
+ "@atlaspack/build-cache": "2.13.3-canary.88+30f60175b",
24
+ "@atlaspack/cache": "3.1.1-canary.20+30f60175b",
25
+ "@atlaspack/diagnostic": "2.14.1-canary.88+30f60175b",
26
+ "@atlaspack/events": "2.14.1-canary.88+30f60175b",
27
+ "@atlaspack/feature-flags": "2.14.1-canary.88+30f60175b",
28
+ "@atlaspack/fs": "2.14.5-canary.20+30f60175b",
29
+ "@atlaspack/graph": "3.4.1-canary.88+30f60175b",
30
+ "@atlaspack/logger": "2.14.5-canary.20+30f60175b",
31
+ "@atlaspack/package-manager": "2.14.5-canary.20+30f60175b",
32
+ "@atlaspack/plugin": "2.14.5-canary.20+30f60175b",
33
+ "@atlaspack/profiler": "2.14.1-canary.88+30f60175b",
34
+ "@atlaspack/rust": "3.2.1-canary.20+30f60175b",
35
+ "@atlaspack/types": "2.14.5-canary.20+30f60175b",
36
+ "@atlaspack/utils": "2.14.5-canary.20+30f60175b",
37
+ "@atlaspack/workers": "2.14.5-canary.20+30f60175b",
38
38
  "@mischnic/json-sourcemap": "^0.1.0",
39
39
  "@parcel/source-map": "^2.1.1",
40
40
  "base-x": "^3.0.8",
@@ -67,5 +67,5 @@
67
67
  "./src/serializerCore.js": "./src/serializerCore.browser.js"
68
68
  },
69
69
  "type": "commonjs",
70
- "gitHead": "88fcaf47c77a6b7a16003028dd65a4b7a1f19dd7"
70
+ "gitHead": "30f60175ba4d272c5fc193973c63bc298584775b"
71
71
  }
package/src/Atlaspack.js CHANGED
@@ -58,11 +58,7 @@ import {
58
58
  fromProjectPathRelative,
59
59
  } from './projectPath';
60
60
  import {tracer} from '@atlaspack/profiler';
61
- import {
62
- getFeatureFlag,
63
- setFeatureFlags,
64
- DEFAULT_FEATURE_FLAGS,
65
- } from '@atlaspack/feature-flags';
61
+ import {setFeatureFlags, DEFAULT_FEATURE_FLAGS} from '@atlaspack/feature-flags';
66
62
  import {AtlaspackV3, FileSystemV3} from './atlaspack-v3';
67
63
  import createAssetGraphRequestJS from './requests/AssetGraphRequest';
68
64
  import {createAssetGraphRequestRust} from './requests/AssetGraphRequestRust';
@@ -122,9 +118,7 @@ export default class Atlaspack {
122
118
  };
123
119
  setFeatureFlags(featureFlags);
124
120
 
125
- if (getFeatureFlag('enableRustWorkerThreadDylibHack')) {
126
- loadRustWorkerThreadDylibHack();
127
- }
121
+ loadRustWorkerThreadDylibHack();
128
122
 
129
123
  await initSourcemaps;
130
124
  await initRust?.();
@@ -645,6 +639,20 @@ export default class Atlaspack {
645
639
  return result;
646
640
  }
647
641
 
642
+ /**
643
+ * Copy the cache to a new directory and compact it.
644
+ */
645
+ async unstable_compactCache(): Promise<void> {
646
+ await this._init();
647
+
648
+ const cache = nullthrows(this.#resolvedOptions).cache;
649
+ if (cache instanceof LMDBLiteCache) {
650
+ await cache.compact('parcel-cache-compacted');
651
+ } else {
652
+ throw new Error('Cache is not an LMDBLiteCache');
653
+ }
654
+ }
655
+
648
656
  async unstable_transform(
649
657
  options: AtlaspackTransformOptions,
650
658
  ): Promise<Array<Asset>> {
@@ -446,6 +446,11 @@ export class RequestGraph extends ContentGraph<
446
446
  this.removeCachedRequestChunkForNode(nodeId);
447
447
  }
448
448
 
449
+ /**
450
+ * Nodes that are invalidated on start-up, such as JavaScript babel configuration files which are
451
+ * imported when the build kicks-off and might doing arbitrary work such as reading from the file
452
+ * system.
453
+ */
449
454
  invalidateUnpredictableNodes() {
450
455
  for (let nodeId of this.unpredicatableNodeIds) {
451
456
  let node = nullthrows(this.getNode(nodeId));
@@ -454,6 +459,9 @@ export class RequestGraph extends ContentGraph<
454
459
  }
455
460
  }
456
461
 
462
+ /**
463
+ * Effectively uncacheable nodes.
464
+ */
457
465
  invalidateOnBuildNodes() {
458
466
  for (let nodeId of this.invalidateOnBuildNodeIds) {
459
467
  let node = nullthrows(this.getNode(nodeId));
@@ -462,11 +470,20 @@ export class RequestGraph extends ContentGraph<
462
470
  }
463
471
  }
464
472
 
465
- invalidateEnvNodes(env: EnvMap) {
473
+ /**
474
+ * Nodes invalidated by environment changes, corresponds to `env: ...` inputs.
475
+ */
476
+ invalidateEnvNodes(env: EnvMap): string[] {
477
+ const invalidatedKeys = [];
478
+
466
479
  for (let nodeId of this.envNodeIds) {
467
480
  let node = nullthrows(this.getNode(nodeId));
468
481
  invariant(node.type === ENV);
469
- if (env[keyFromEnvContentKey(node.id)] !== node.value) {
482
+
483
+ const key = keyFromEnvContentKey(node.id);
484
+ if (env[key] !== node.value) {
485
+ invalidatedKeys.push(key);
486
+
470
487
  let parentNodes = this.getNodeIdsConnectedTo(
471
488
  nodeId,
472
489
  requestGraphEdgeTypes.invalidated_by_update,
@@ -476,15 +493,23 @@ export class RequestGraph extends ContentGraph<
476
493
  }
477
494
  }
478
495
  }
496
+
497
+ return invalidatedKeys;
479
498
  }
480
499
 
481
- invalidateOptionNodes(options: AtlaspackOptions) {
500
+ /**
501
+ * Nodes invalidated by option changes.
502
+ */
503
+ invalidateOptionNodes(options: AtlaspackOptions): string[] {
504
+ const invalidatedKeys = [];
505
+
482
506
  for (let nodeId of this.optionNodeIds) {
483
507
  let node = nullthrows(this.getNode(nodeId));
484
508
  invariant(node.type === OPTION);
485
- if (
486
- hashFromOption(options[keyFromOptionContentKey(node.id)]) !== node.hash
487
- ) {
509
+ const key = keyFromOptionContentKey(node.id);
510
+
511
+ if (hashFromOption(options[key]) !== node.hash) {
512
+ invalidatedKeys.push(key);
488
513
  let parentNodes = this.getNodeIdsConnectedTo(
489
514
  nodeId,
490
515
  requestGraphEdgeTypes.invalidated_by_update,
@@ -494,6 +519,8 @@ export class RequestGraph extends ContentGraph<
494
519
  }
495
520
  }
496
521
  }
522
+
523
+ return invalidatedKeys;
497
524
  }
498
525
 
499
526
  invalidateOnConfigKeyChange(
@@ -1605,6 +1632,18 @@ export default class RequestTracker {
1605
1632
  nodes: undefined,
1606
1633
  });
1607
1634
 
1635
+ await runCacheImprovements(
1636
+ () =>
1637
+ serialiseAndSet(`request_tracker:cache_metadata:${cacheKey}`, {
1638
+ version: ATLASPACK_VERSION,
1639
+ entries: this.options.entries,
1640
+ mode: this.options.mode,
1641
+ shouldBuildLazily: this.options.shouldBuildLazily,
1642
+ watchBackend: this.options.watchBackend,
1643
+ }),
1644
+ () => Promise.resolve(),
1645
+ );
1646
+
1608
1647
  let opts = getWatcherOptions(this.options);
1609
1648
  let snapshotPath = path.join(this.options.cacheDir, snapshotKey + '.txt');
1610
1649
 
@@ -1715,12 +1754,23 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1715
1754
  const snapshotKey = `snapshot-${cacheKey}`;
1716
1755
  const snapshotPath = path.join(options.cacheDir, snapshotKey + '.txt');
1717
1756
 
1757
+ const commonMeta = {
1758
+ cacheKey,
1759
+ snapshotKey,
1760
+ cacheKeyOptions: {
1761
+ version: ATLASPACK_VERSION,
1762
+ entries: options.entries,
1763
+ mode: options.mode,
1764
+ shouldBuildLazily: options.shouldBuildLazily,
1765
+ watchBackend: options.watchBackend,
1766
+ },
1767
+ };
1768
+
1718
1769
  logger.verbose({
1719
1770
  origin: '@atlaspack/core',
1720
1771
  message: 'Loading request graph',
1721
1772
  meta: {
1722
- cacheKey,
1723
- snapshotKey,
1773
+ ...commonMeta,
1724
1774
  },
1725
1775
  });
1726
1776
 
@@ -1755,23 +1805,36 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1755
1805
  origin: '@atlaspack/core',
1756
1806
  message: `File system event count: ${events.length}`,
1757
1807
  meta: {
1808
+ ...commonMeta,
1758
1809
  trackableEvent: 'watcher_events_count',
1759
1810
  watcherEventCount: events.length,
1760
1811
  duration: Date.now() - startTime,
1761
1812
  },
1762
1813
  });
1763
1814
 
1764
- requestGraph.invalidateUnpredictableNodes();
1765
- requestGraph.invalidateOnBuildNodes();
1766
- requestGraph.invalidateEnvNodes(options.env);
1767
- requestGraph.invalidateOptionNodes(options);
1815
+ if (getFeatureFlag('verboseRequestInvalidationStats')) {
1816
+ const invalidationStats = await invalidateRequestGraph(
1817
+ requestGraph,
1818
+ options,
1819
+ events,
1820
+ );
1821
+
1822
+ logger.verbose({
1823
+ origin: '@atlaspack/core',
1824
+ message: 'Request track loaded from cache',
1825
+ meta: {
1826
+ ...commonMeta,
1827
+ trackableEvent: 'request_tracker_cache_key_hit',
1828
+ invalidationStats,
1829
+ },
1830
+ });
1831
+ } else {
1832
+ requestGraph.invalidateUnpredictableNodes();
1833
+ requestGraph.invalidateOnBuildNodes();
1834
+ requestGraph.invalidateEnvNodes(options.env);
1835
+ requestGraph.invalidateOptionNodes(options);
1836
+ }
1768
1837
 
1769
- await requestGraph.respondToFSEvents(
1770
- options.unstableFileInvalidations || events,
1771
- options,
1772
- 10000,
1773
- true,
1774
- );
1775
1838
  return requestGraph;
1776
1839
  } catch (e) {
1777
1840
  // Prevent logging fs events took too long warning
@@ -1788,13 +1851,158 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1788
1851
  message:
1789
1852
  'Cache entry for request tracker was not found, initializing a clean cache.',
1790
1853
  meta: {
1791
- cacheKey,
1792
- snapshotKey,
1854
+ ...commonMeta,
1855
+ trackableEvent: 'request_tracker_cache_key_miss',
1793
1856
  },
1794
1857
  });
1795
1858
  return new RequestGraph();
1796
1859
  }
1797
1860
 
1861
+ /**
1862
+ * A wrapper around an invalidation type / method
1863
+ */
1864
+ type InvalidationFn = {|
1865
+ key: string,
1866
+ fn: () => string[] | void | Promise<void>,
1867
+ |};
1868
+
1869
+ type InvalidationStats = {|
1870
+ /**
1871
+ * Total number of request graph nodes
1872
+ */
1873
+ nodeCount: number,
1874
+ /**
1875
+ * Number of requests in RequestGraph
1876
+ */
1877
+ requestCount: number,
1878
+ /**
1879
+ * Number of nodes that have been invalidated.
1880
+ */
1881
+ invalidatedCount: number,
1882
+ /**
1883
+ * Percentage of requests that have been invalidated
1884
+ */
1885
+ requestInvalidationRatio: number,
1886
+ /**
1887
+ * Percentage of nodes that have been invalidated
1888
+ */
1889
+ nodeInvalidationRatio: number,
1890
+ /**
1891
+ * Details for each invalidation type
1892
+ */
1893
+ invalidations: InvalidationFnStats[],
1894
+ |};
1895
+
1896
+ /**
1897
+ * Information about a certain cache invalidation type.
1898
+ */
1899
+ type InvalidationFnStats = {|
1900
+ /**
1901
+ * Invalidation type, one of:
1902
+ *
1903
+ * - unpredictable
1904
+ * - onBuild
1905
+ * - env
1906
+ * - option
1907
+ * - fsEvents
1908
+ */
1909
+ key: string,
1910
+ /**
1911
+ * Number of invalidated nodes coming from this invalidation type.
1912
+ */
1913
+ count: number,
1914
+ /**
1915
+ * If this is a env or option invalidation, this key will contain the list of changed values.
1916
+ */
1917
+ changes: null | string[],
1918
+ |};
1919
+
1920
+ /**
1921
+ * Respond to unpredictable, build, environment changes, option changes and file-system events
1922
+ * invalidating RequestGraph nodes.
1923
+ *
1924
+ * Returns the count of nodes invalidated by each invalidation type.
1925
+ */
1926
+ async function invalidateRequestGraph(
1927
+ requestGraph: RequestGraph,
1928
+ options: AtlaspackOptions,
1929
+ events: Event[],
1930
+ ): Promise<InvalidationStats> {
1931
+ const invalidationFns: InvalidationFn[] = [
1932
+ {
1933
+ key: 'unpredictable',
1934
+ fn: () => requestGraph.invalidateUnpredictableNodes(),
1935
+ },
1936
+ {
1937
+ key: 'onBuild',
1938
+ fn: () => requestGraph.invalidateOnBuildNodes(),
1939
+ },
1940
+ {
1941
+ key: 'env',
1942
+ fn: () => requestGraph.invalidateEnvNodes(options.env),
1943
+ },
1944
+ {
1945
+ key: 'option',
1946
+ fn: () => requestGraph.invalidateOptionNodes(options),
1947
+ },
1948
+ {
1949
+ key: 'fsEvents',
1950
+ fn: async () => {
1951
+ await requestGraph.respondToFSEvents(
1952
+ options.unstableFileInvalidations || events,
1953
+ options,
1954
+ 10000,
1955
+ true,
1956
+ );
1957
+ },
1958
+ },
1959
+ ];
1960
+
1961
+ const invalidations = [];
1962
+ for (const invalidation of invalidationFns) {
1963
+ invalidations.push(await runInvalidation(requestGraph, invalidation));
1964
+ }
1965
+ const invalidatedCount = invalidations.reduce(
1966
+ (acc, invalidation) => acc + invalidation.count,
1967
+ 0,
1968
+ );
1969
+ const requestCount = requestGraph.nodes.reduce(
1970
+ (acc, node) => acc + (node?.type === REQUEST ? 1 : 0),
1971
+ 0,
1972
+ );
1973
+ const nodeCount = requestGraph.nodes.length;
1974
+ const nodeInvalidationRatio = invalidatedCount / nodeCount;
1975
+ const requestInvalidationRatio = invalidatedCount / requestCount;
1976
+
1977
+ return {
1978
+ invalidations,
1979
+ nodeCount,
1980
+ requestCount,
1981
+ invalidatedCount,
1982
+ nodeInvalidationRatio,
1983
+ requestInvalidationRatio,
1984
+ };
1985
+ }
1986
+
1987
+ /**
1988
+ * Runs an invalidation function and reports metrics.
1989
+ */
1990
+ async function runInvalidation(
1991
+ requestGraph: RequestGraph,
1992
+ invalidationFn: InvalidationFn,
1993
+ ): Promise<InvalidationFnStats> {
1994
+ const startInvalidationCount = requestGraph.invalidNodeIds.size;
1995
+ const result = await invalidationFn.fn();
1996
+ const count = requestGraph.invalidNodeIds.size - startInvalidationCount;
1997
+
1998
+ return {
1999
+ key: invalidationFn.key,
2000
+ count,
2001
+ changes:
2002
+ typeof result === 'object' && Array.isArray(result) ? result : null,
2003
+ };
2004
+ }
2005
+
1798
2006
  function logErrorOnBailout(
1799
2007
  options: AtlaspackOptions,
1800
2008
  snapshotPath: string,