@fluidframework/container-runtime 0.59.2000-63294 → 0.59.3000-66610

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.
Files changed (138) hide show
  1. package/.eslintrc.js +0 -1
  2. package/dist/batchTracker.js +1 -1
  3. package/dist/batchTracker.js.map +1 -1
  4. package/dist/blobManager.js +8 -8
  5. package/dist/blobManager.js.map +1 -1
  6. package/dist/connectionTelemetry.js +8 -8
  7. package/dist/connectionTelemetry.js.map +1 -1
  8. package/dist/containerHandleContext.js +1 -1
  9. package/dist/containerHandleContext.js.map +1 -1
  10. package/dist/containerRuntime.d.ts +27 -17
  11. package/dist/containerRuntime.d.ts.map +1 -1
  12. package/dist/containerRuntime.js +149 -174
  13. package/dist/containerRuntime.js.map +1 -1
  14. package/dist/dataStore.js +1 -1
  15. package/dist/dataStore.js.map +1 -1
  16. package/dist/dataStoreContext.d.ts.map +1 -1
  17. package/dist/dataStoreContext.js +44 -44
  18. package/dist/dataStoreContext.js.map +1 -1
  19. package/dist/dataStoreContexts.d.ts +2 -2
  20. package/dist/dataStoreContexts.d.ts.map +1 -1
  21. package/dist/dataStoreContexts.js +8 -8
  22. package/dist/dataStoreContexts.js.map +1 -1
  23. package/dist/dataStores.d.ts +4 -2
  24. package/dist/dataStores.d.ts.map +1 -1
  25. package/dist/dataStores.js +45 -33
  26. package/dist/dataStores.js.map +1 -1
  27. package/dist/garbageCollection.d.ts +23 -23
  28. package/dist/garbageCollection.d.ts.map +1 -1
  29. package/dist/garbageCollection.js +81 -50
  30. package/dist/garbageCollection.js.map +1 -1
  31. package/dist/opTelemetry.js +1 -1
  32. package/dist/opTelemetry.js.map +1 -1
  33. package/dist/orderedClientElection.d.ts.map +1 -1
  34. package/dist/orderedClientElection.js +2 -2
  35. package/dist/orderedClientElection.js.map +1 -1
  36. package/dist/packageVersion.d.ts +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/pendingStateManager.js +17 -17
  40. package/dist/pendingStateManager.js.map +1 -1
  41. package/dist/runWhileConnectedCoordinator.js +1 -1
  42. package/dist/runWhileConnectedCoordinator.js.map +1 -1
  43. package/dist/runningSummarizer.d.ts.map +1 -1
  44. package/dist/runningSummarizer.js +7 -6
  45. package/dist/runningSummarizer.js.map +1 -1
  46. package/dist/summarizer.d.ts.map +1 -1
  47. package/dist/summarizer.js +4 -3
  48. package/dist/summarizer.js.map +1 -1
  49. package/dist/summarizerClientElection.js.map +1 -1
  50. package/dist/summarizerHeuristics.d.ts +1 -1
  51. package/dist/summarizerHeuristics.d.ts.map +1 -1
  52. package/dist/summarizerHeuristics.js +1 -1
  53. package/dist/summarizerHeuristics.js.map +1 -1
  54. package/dist/summarizerTypes.d.ts +4 -2
  55. package/dist/summarizerTypes.d.ts.map +1 -1
  56. package/dist/summarizerTypes.js.map +1 -1
  57. package/dist/summaryCollection.js +2 -2
  58. package/dist/summaryCollection.js.map +1 -1
  59. package/dist/summaryFormat.d.ts +37 -11
  60. package/dist/summaryFormat.d.ts.map +1 -1
  61. package/dist/summaryFormat.js +12 -4
  62. package/dist/summaryFormat.js.map +1 -1
  63. package/dist/summaryGenerator.d.ts.map +1 -1
  64. package/dist/summaryGenerator.js +6 -4
  65. package/dist/summaryGenerator.js.map +1 -1
  66. package/dist/summaryManager.js +5 -5
  67. package/dist/summaryManager.js.map +1 -1
  68. package/dist/throttler.js +2 -2
  69. package/dist/throttler.js.map +1 -1
  70. package/lib/blobManager.js.map +1 -1
  71. package/lib/containerRuntime.d.ts +27 -17
  72. package/lib/containerRuntime.d.ts.map +1 -1
  73. package/lib/containerRuntime.js +68 -93
  74. package/lib/containerRuntime.js.map +1 -1
  75. package/lib/dataStore.js.map +1 -1
  76. package/lib/dataStoreContext.d.ts.map +1 -1
  77. package/lib/dataStoreContext.js.map +1 -1
  78. package/lib/dataStoreContexts.d.ts +2 -2
  79. package/lib/dataStoreContexts.d.ts.map +1 -1
  80. package/lib/dataStoreContexts.js +2 -2
  81. package/lib/dataStoreContexts.js.map +1 -1
  82. package/lib/dataStores.d.ts +4 -2
  83. package/lib/dataStores.d.ts.map +1 -1
  84. package/lib/dataStores.js +22 -10
  85. package/lib/dataStores.js.map +1 -1
  86. package/lib/garbageCollection.d.ts +23 -23
  87. package/lib/garbageCollection.d.ts.map +1 -1
  88. package/lib/garbageCollection.js +68 -37
  89. package/lib/garbageCollection.js.map +1 -1
  90. package/lib/opTelemetry.js.map +1 -1
  91. package/lib/orderedClientElection.d.ts.map +1 -1
  92. package/lib/orderedClientElection.js.map +1 -1
  93. package/lib/packageVersion.d.ts +1 -1
  94. package/lib/packageVersion.js +1 -1
  95. package/lib/packageVersion.js.map +1 -1
  96. package/lib/pendingStateManager.js.map +1 -1
  97. package/lib/runningSummarizer.d.ts.map +1 -1
  98. package/lib/runningSummarizer.js +4 -3
  99. package/lib/runningSummarizer.js.map +1 -1
  100. package/lib/summarizer.d.ts.map +1 -1
  101. package/lib/summarizer.js +1 -0
  102. package/lib/summarizer.js.map +1 -1
  103. package/lib/summarizerClientElection.js.map +1 -1
  104. package/lib/summarizerHeuristics.d.ts +1 -1
  105. package/lib/summarizerHeuristics.d.ts.map +1 -1
  106. package/lib/summarizerHeuristics.js +1 -1
  107. package/lib/summarizerHeuristics.js.map +1 -1
  108. package/lib/summarizerTypes.d.ts +4 -2
  109. package/lib/summarizerTypes.d.ts.map +1 -1
  110. package/lib/summarizerTypes.js.map +1 -1
  111. package/lib/summaryCollection.js.map +1 -1
  112. package/lib/summaryFormat.d.ts +37 -11
  113. package/lib/summaryFormat.d.ts.map +1 -1
  114. package/lib/summaryFormat.js +10 -2
  115. package/lib/summaryFormat.js.map +1 -1
  116. package/lib/summaryGenerator.d.ts.map +1 -1
  117. package/lib/summaryGenerator.js +2 -0
  118. package/lib/summaryGenerator.js.map +1 -1
  119. package/lib/summaryManager.js.map +1 -1
  120. package/lib/throttler.js.map +1 -1
  121. package/package.json +26 -20
  122. package/src/blobManager.ts +3 -3
  123. package/src/containerRuntime.ts +108 -137
  124. package/src/dataStoreContext.ts +8 -11
  125. package/src/dataStoreContexts.ts +5 -5
  126. package/src/dataStores.ts +30 -13
  127. package/src/garbageCollection.ts +100 -57
  128. package/src/orderedClientElection.ts +5 -10
  129. package/src/packageVersion.ts +1 -1
  130. package/src/pendingStateManager.ts +2 -2
  131. package/src/runningSummarizer.ts +8 -9
  132. package/src/summarizer.ts +2 -2
  133. package/src/summarizerHeuristics.ts +1 -1
  134. package/src/summarizerTypes.ts +8 -6
  135. package/src/summaryFormat.ts +38 -11
  136. package/src/summaryGenerator.ts +7 -5
  137. package/src/summaryManager.ts +2 -2
  138. package/src/throttler.ts +1 -1
@@ -48,7 +48,7 @@ import {
48
48
  TelemetryDataTag,
49
49
  } from "@fluidframework/telemetry-utils";
50
50
  import { DriverHeader, IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
51
- import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
51
+ import { readAndParse } from "@fluidframework/driver-utils";
52
52
  import {
53
53
  DataCorruptionError,
54
54
  GenericError,
@@ -208,29 +208,34 @@ const DefaultSummaryConfiguration: ISummaryConfiguration = {
208
208
  };
209
209
 
210
210
  export interface IGCRuntimeOptions {
211
- /* Flag that will disable garbage collection if set to true. */
212
- disableGC?: boolean;
213
-
214
211
  /**
215
- * Flag representing the summary's preference for allowing garbage collection.
216
- * This is stored in the summary and unchangeable (for now). So this runtime option
217
- * only takes affect on new containers.
218
- * Currently if this is set to false, it will take priority and any container will
219
- * never run GC.
212
+ * Flag that if true, will enable running garbage collection (GC) in a container. GC has mark phase and sweep phase.
213
+ * In mark phase, unreferenced objects are identified and marked as such in the summary. This option enables the
214
+ * mark phase.
215
+ * In sweep phase, unreferenced objects are eventually deleted from the container if they meet certain conditions.
216
+ * Sweep phase can be enabled via the "sweepAllowed" option.
217
+ * Note: This setting becomes part of the container's summary and cannot be changed.
220
218
  */
221
219
  gcAllowed?: boolean;
222
220
 
223
221
  /**
224
- * Flag that will bypass optimizations and generate GC data for all nodes irrespective of whether the node
225
- * changed or not.
222
+ * Flag that if true, enables GC's sweep phase which will eventually delete unreferenced objects from the container.
223
+ * This flag should only be set to true if "gcAllowed" is true.
224
+ * Note: This setting becomes part of the container's summary and cannot be changed.
226
225
  */
227
- runFullGC?: boolean;
226
+ sweepAllowed?: boolean;
227
+
228
+ /**
229
+ * Flag that will disable garbage collection if set to true. Can be used to disable running GC on container where
230
+ * is allowed via the gcAllowed option.
231
+ */
232
+ disableGC?: boolean;
228
233
 
229
234
  /**
230
- * Flag that if true, will run sweep which may delete unused objects that meet certain criteria. Only takes
231
- * effect if GC is enabled.
235
+ * Flag that will bypass optimizations and generate GC data for all nodes irrespective of whether a node
236
+ * changed or not.
232
237
  */
233
- runSweep?: boolean;
238
+ runFullGC?: boolean;
234
239
 
235
240
  /**
236
241
  * Allows additional GC options to be passed.
@@ -706,8 +711,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
706
711
  IGarbageCollectionRuntime,
707
712
  IRuntime,
708
713
  ISummarizerRuntime,
709
- ISummarizerInternalsProvider
710
- {
714
+ ISummarizerInternalsProvider {
711
715
  public get IContainerRuntime() { return this; }
712
716
  public get IFluidRouter() { return this; }
713
717
 
@@ -746,33 +750,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
746
750
  flushMode = defaultFlushMode,
747
751
  } = runtimeOptions;
748
752
 
749
- // We pack at data store level only. If isolated channels are disabled,
750
- // then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
751
- const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
752
-
753
- let storage = context.storage;
754
- if (context.baseSnapshot) {
755
- // This will patch snapshot in place!
756
- // If storage is provided, it will wrap storage with BlobAggregationStorage that can
757
- // pack & unpack aggregated blobs.
758
- // Note that if storage is provided later by loader layer, we will wrap storage in this.storage getter.
759
- // BlobAggregationStorage is smart enough for double-wrapping to be no-op
760
- if (context.attachState === AttachState.Attached) {
761
- // IContainerContext storage api return type still has undefined in 0.39 package version.
762
- // So once we release 0.40 container-defn package we can remove this check.
763
- assert(context.storage !== undefined, 0x1f4 /* "Attached state should have storage" */);
764
- const aggrStorage = BlobAggregationStorage.wrap(
765
- context.storage,
766
- logger,
767
- undefined /* allowPacking */,
768
- packingLevel,
769
- );
770
- await aggrStorage.unpackSnapshot(context.baseSnapshot);
771
- storage = aggrStorage;
772
- } else {
773
- await BlobAggregationStorage.unpackSnapshot(context.baseSnapshot);
774
- }
775
- }
753
+ const storage = context.storage;
776
754
 
777
755
  const registry = new FluidDataStoreRegistry(registryEntries);
778
756
 
@@ -846,7 +824,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
846
824
  loadExisting,
847
825
  blobManagerSnapshot,
848
826
  requestHandler,
849
- storage,
850
827
  );
851
828
 
852
829
  return runtime;
@@ -869,25 +846,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
869
846
  }
870
847
 
871
848
  public get storage(): IDocumentStorageService {
872
- // This code is plain wrong. It lies that it never returns undefined!!!
873
- // All callers should be fixed, as this API is called in detached state of container when we have
874
- // no storage and it's passed down the stack without right typing.
875
- // back-compat 0.40 NoStorageInDetachedMode. Also, IContainerContext storage api return type still
876
- // has undefined in 0.39 package version.
877
- // So once we release 0.40 container-defn package we can remove this check.
878
- if (!this._storage && this.context.storage) {
879
- // Note: BlobAggregationStorage is smart enough for double-wrapping to be no-op
880
- // If isolated channels are disabled, then there are no .channel layers, we pack at level 1,
881
- // otherwise we pack at level 2
882
- this._storage = BlobAggregationStorage.wrap(
883
- this.context.storage,
884
- this.logger,
885
- undefined /* allowPacking */,
886
- this.disableIsolatedChannels ? 1 : 2,
887
- );
888
- }
889
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
890
- return this._storage!;
849
+ return this.context.storage;
891
850
  }
892
851
 
893
852
  public get reSubmitFn(): (
@@ -1015,7 +974,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1015
974
  }
1016
975
 
1017
976
  private readonly createContainerMetadata: ICreateContainerMetadata;
1018
- private summaryCount: number | undefined;
977
+ /**
978
+ * The summary number of the next summary that will be generated for this container. This is incremented every time
979
+ * a summary is generated.
980
+ */
981
+ private nextSummaryNumber: number;
1019
982
  private readonly opTracker: OpTracker;
1020
983
 
1021
984
  private constructor(
@@ -1031,27 +994,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1031
994
  existing: boolean,
1032
995
  blobManagerSnapshot: IBlobManagerLoadInfo,
1033
996
  private readonly requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>,
1034
- private _storage?: IDocumentStorageService,
1035
997
  ) {
1036
998
  super();
1037
999
 
1038
1000
  this.messageAtLastSummary = metadata?.message;
1039
1001
 
1040
- // If this is an existing container, we get values from metadata.
1041
- // otherwise, we initialize them.
1042
- if (existing) {
1043
- this.createContainerMetadata = {
1044
- createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
1045
- createContainerTimestamp: metadata?.createContainerTimestamp,
1046
- };
1047
- this.summaryCount = metadata?.summaryCount;
1048
- } else {
1049
- this.createContainerMetadata = {
1050
- createContainerRuntimeVersion: pkgVersion,
1051
- createContainerTimestamp: Date.now(),
1052
- };
1053
- }
1054
-
1055
1002
  // Default to false (enabled).
1056
1003
  this.disableIsolatedChannels = this.runtimeOptions.summaryOptions.disableIsolatedChannels ?? false;
1057
1004
 
@@ -1179,8 +1126,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1179
1126
 
1180
1127
  if (this.summariesDisabled) {
1181
1128
  this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
1182
- }
1183
- else {
1129
+ } else {
1184
1130
  const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
1185
1131
  const orderedClientCollection = new OrderedClientCollection(
1186
1132
  orderedClientLogger,
@@ -1216,18 +1162,17 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1216
1162
  this.summaryCollection,
1217
1163
  async (runtime: IConnectableRuntime) => RunWhileConnectedCoordinator.create(runtime),
1218
1164
  );
1219
- }
1220
- else if (SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)) {
1165
+ } else if (SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)) {
1221
1166
  // Only create a SummaryManager and SummarizerClientElection
1222
1167
  // if summaries are enabled and we are not the summarizer client.
1223
1168
  const defaultAction = () => {
1224
1169
  if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
1225
- this.logger.sendErrorEvent({eventName: "SummaryStatus:Behind"});
1170
+ this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
1226
1171
  // unregister default to no log on every op after falling behind
1227
1172
  // and register summary ack handler to re-register this handler
1228
1173
  // after successful summary
1229
1174
  this.summaryCollection.once(MessageType.SummaryAck, () => {
1230
- this.logger.sendTelemetryEvent({eventName: "SummaryStatus:CaughtUp"});
1175
+ this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:CaughtUp" });
1231
1176
  // we've caught up, so re-register the default action to monitor for
1232
1177
  // falling behind, and unregister ourself
1233
1178
  this.summaryCollection.on("default", defaultAction);
@@ -1289,16 +1234,35 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1289
1234
 
1290
1235
  // logging hardware telemetry
1291
1236
  logger.sendTelemetryEvent({
1292
- eventName:"DeviceSpec",
1237
+ eventName: "DeviceSpec",
1293
1238
  ...getDeviceSpec(),
1294
1239
  });
1295
1240
 
1296
- // logging container load stats
1241
+ let loadSummaryNumber: number;
1242
+ // Get the container creation metadata. For new container, we initialize these. For existing containers,
1243
+ // get the values from the metadata blob.
1244
+ if (existing) {
1245
+ this.createContainerMetadata = {
1246
+ createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
1247
+ createContainerTimestamp: metadata?.createContainerTimestamp,
1248
+ };
1249
+ // back-compat 0.59.3000 - Older document may either write summaryCount or not write it at all. If it does
1250
+ // not write it, initialize summaryNumber to 0.
1251
+ loadSummaryNumber = metadata?.summaryNumber ?? metadata?.summaryCount ?? 0;
1252
+ } else {
1253
+ this.createContainerMetadata = {
1254
+ createContainerRuntimeVersion: pkgVersion,
1255
+ createContainerTimestamp: Date.now(),
1256
+ };
1257
+ loadSummaryNumber = 0;
1258
+ }
1259
+ this.nextSummaryNumber = loadSummaryNumber + 1;
1260
+
1297
1261
  this.logger.sendTelemetryEvent({
1298
1262
  eventName: "ContainerLoadStats",
1299
1263
  ...this.createContainerMetadata,
1300
1264
  ...this.dataStores.containerLoadStats,
1301
- summaryCount: this.summaryCount,
1265
+ summaryNumber: loadSummaryNumber,
1302
1266
  summaryFormatVersion: metadata?.summaryFormatVersion,
1303
1267
  disableIsolatedChannels: metadata?.disableIsolatedChannels,
1304
1268
  gcVersion: metadata?.gcFeature,
@@ -1446,22 +1410,27 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1446
1410
  return dataStoreChannel;
1447
1411
  }
1448
1412
 
1449
- private formMetadata(): IContainerRuntimeMetadata {
1450
- return {
1413
+ /** Adds the container's metadata to the given summary tree. */
1414
+ private addMetadataToSummary(summaryTree: ISummaryTreeWithStats) {
1415
+ const metadata: IContainerRuntimeMetadata = {
1451
1416
  ...this.createContainerMetadata,
1452
- summaryCount: this.summaryCount,
1417
+ // back-compat 0.59.3000: This is renamed to summaryNumber. Can be removed when 0.59.3000 saturates.
1418
+ summaryCount: this.nextSummaryNumber,
1419
+ // Increment the summary number for the next summary that will be generated.
1420
+ summaryNumber: this.nextSummaryNumber++,
1453
1421
  summaryFormatVersion: 1,
1454
1422
  disableIsolatedChannels: this.disableIsolatedChannels || undefined,
1455
- gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
1423
+ ...this.garbageCollector.getMetadata(),
1456
1424
  // The last message processed at the time of summary. If there are no new messages, use the message from the
1457
1425
  // last summary.
1458
1426
  message: extractSummaryMetadataMessage(this.deltaManager.lastMessage) ?? this.messageAtLastSummary,
1459
- sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs,
1460
1427
  };
1428
+ addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(metadata));
1461
1429
  }
1462
1430
 
1463
1431
  private addContainerStateToSummary(summaryTree: ISummaryTreeWithStats) {
1464
- addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(this.formMetadata()));
1432
+ this.addMetadataToSummary(summaryTree);
1433
+
1465
1434
  if (this.chunkMap.size > 0) {
1466
1435
  const content = JSON.stringify([...this.chunkMap]);
1467
1436
  addBlobToSummary(summaryTree, chunksBlobName, content);
@@ -1477,11 +1446,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1477
1446
  addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
1478
1447
  }
1479
1448
 
1480
- const summary = this.blobManager.summarize();
1449
+ const blobManagerSummary = this.blobManager.summarize();
1481
1450
  // Some storage (like git) doesn't allow empty tree, so we can omit it.
1482
1451
  // and the blob manager can handle the tree not existing when loading
1483
- if (Object.keys(summary.summary.tree).length > 0) {
1484
- addTreeToSummary(summaryTree, blobsTreeName, summary);
1452
+ if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
1453
+ addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
1485
1454
  }
1486
1455
 
1487
1456
  if (this.garbageCollector.writeDataAtRoot) {
@@ -1880,8 +1849,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1880
1849
 
1881
1850
  public createDetachedRootDataStore(
1882
1851
  pkg: Readonly<string[]>,
1883
- rootDataStoreId: string): IFluidDataStoreContextDetached
1884
- {
1852
+ rootDataStoreId: string): IFluidDataStoreContextDetached {
1885
1853
  return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
1886
1854
  }
1887
1855
 
@@ -2064,17 +2032,17 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2064
2032
  */
2065
2033
  public async summarize(options: {
2066
2034
  /** True to generate the full tree with no handle reuse optimizations; defaults to false */
2067
- fullTree?: boolean,
2035
+ fullTree?: boolean;
2068
2036
  /** True to track the state for this summary in the SummarizerNodes; defaults to true */
2069
- trackState?: boolean,
2037
+ trackState?: boolean;
2070
2038
  /** Logger to use for correlated summary events */
2071
- summaryLogger?: ITelemetryLogger,
2039
+ summaryLogger?: ITelemetryLogger;
2072
2040
  /** True to run garbage collection before summarizing; defaults to true */
2073
- runGC?: boolean,
2041
+ runGC?: boolean;
2074
2042
  /** True to generate full GC data */
2075
- fullGC?: boolean,
2043
+ fullGC?: boolean;
2076
2044
  /** True to run GC sweep phase after the mark phase */
2077
- runSweep?: boolean,
2045
+ runSweep?: boolean;
2078
2046
  }): Promise<IRootSummaryTreeWithStats> {
2079
2047
  this.verifyNotClosed();
2080
2048
 
@@ -2178,18 +2146,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2178
2146
  }
2179
2147
 
2180
2148
  /**
2181
- * Returns the type of the GC node. Currently, there are nodes that belong to data store and nodes that belong
2182
- * to the blob manager.
2149
+ * Returns the type of the GC node. Currently, there are nodes that belong to the root ("/"), data stores or
2150
+ * blob manager.
2183
2151
  */
2184
2152
  public getNodeType(nodePath: string): GCNodeType {
2185
2153
  if (this.isBlobPath(nodePath)) {
2186
2154
  return GCNodeType.Blob;
2187
2155
  }
2188
- if (this.dataStores.isDataStoreNode(nodePath)) {
2189
- return GCNodeType.DataStore;
2190
- }
2191
- // Root node ("/") and DDS nodes belong to "Other" node types.
2192
- return GCNodeType.Other;
2156
+ return this.dataStores.getGCNodeType(nodePath) ?? GCNodeType.Other;
2193
2157
  }
2194
2158
 
2195
2159
  /**
@@ -2197,13 +2161,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2197
2161
  * data store or an attachment blob.
2198
2162
  */
2199
2163
  public getGCNodePackagePath(nodePath: string): readonly string[] | undefined {
2200
- // If the node is a blob, return "_blobs" as the package path.
2201
- if (this.isBlobPath(nodePath)) {
2202
- return ["_blobs"];
2164
+ switch (this.getNodeType(nodePath)) {
2165
+ case GCNodeType.Blob:
2166
+ return ["_blobs"];
2167
+ case GCNodeType.DataStore:
2168
+ case GCNodeType.SubDataStore:
2169
+ return this.dataStores.getDataStorePackagePath(nodePath);
2170
+ default:
2171
+ assert(false, 0x2de /* "Package path requested for unsupported node type." */);
2203
2172
  }
2204
- const dataStorePkgPath = this.dataStores.getDataStorePackagePath(nodePath);
2205
- assert(dataStorePkgPath !== undefined, 0x2d6 /* "Package path requested for unknown node type." */);
2206
- return dataStorePkgPath;
2207
2173
  }
2208
2174
 
2209
2175
  /**
@@ -2224,11 +2190,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2224
2190
  public async collectGarbage(
2225
2191
  options: {
2226
2192
  /** Logger to use for logging GC events */
2227
- logger?: ITelemetryLogger,
2193
+ logger?: ITelemetryLogger;
2228
2194
  /** True to run GC sweep phase after the mark phase */
2229
- runSweep?: boolean,
2195
+ runSweep?: boolean;
2230
2196
  /** True to generate full GC data */
2231
- fullGC?: boolean,
2197
+ fullGC?: boolean;
2232
2198
  },
2233
2199
  ): Promise<IGCStats> {
2234
2200
  return this.garbageCollector.collectGarbage(options);
@@ -2254,14 +2220,25 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2254
2220
  */
2255
2221
  public async submitSummary(options: ISubmitSummaryOptions): Promise<SubmitSummaryResult> {
2256
2222
  const { fullTree, refreshLatestAck, summaryLogger } = options;
2223
+ // The summary number for this summary. This will be updated during the summary process, so get it now and
2224
+ // use it for all events logged during this summary.
2225
+ const summaryNumber = this.nextSummaryNumber;
2226
+ const summaryNumberLogger = ChildLogger.create(
2227
+ summaryLogger,
2228
+ undefined,
2229
+ {
2230
+ all: { summaryNumber },
2231
+ },
2232
+ );
2233
+
2257
2234
  if (refreshLatestAck) {
2258
2235
  const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(
2259
- ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
2236
+ ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
2260
2237
 
2261
2238
  if (latestSummaryRefSeq > this.deltaManager.lastSequenceNumber) {
2262
2239
  // We need to catch up to the latest summary's reference sequence number before pausing.
2263
2240
  await PerformanceEvent.timedExecAsync(
2264
- summaryLogger,
2241
+ summaryNumberLogger,
2265
2242
  {
2266
2243
  eventName: "WaitingForSeq",
2267
2244
  lastSequenceNumber: this.deltaManager.lastSequenceNumber,
@@ -2284,16 +2261,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2284
2261
  // We should be here is we haven't processed be here. If we are of if the last message's sequence number
2285
2262
  // doesn't match the last processed sequence number, log an error.
2286
2263
  if (summaryRefSeqNum !== this.deltaManager.lastMessage?.sequenceNumber) {
2287
- summaryLogger.sendErrorEvent({
2264
+ summaryNumberLogger.sendErrorEvent({
2288
2265
  eventName: "LastSequenceMismatch",
2289
2266
  error: message,
2290
2267
  });
2291
2268
  }
2292
2269
 
2293
- this.summarizerNode.startSummary(summaryRefSeqNum, summaryLogger);
2270
+ this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
2294
2271
 
2295
2272
  // Helper function to check whether we should still continue between each async step.
2296
- const checkContinue = (): { continue: true; } | { continue: false; error: string } => {
2273
+ const checkContinue = (): { continue: true; } | { continue: false; error: string; } => {
2297
2274
  // Do not check for loss of connectivity directly! Instead leave it up to
2298
2275
  // RunWhileConnectedCoordinator to control policy in a single place.
2299
2276
  // This will allow easier change of design if we chose to. For example, we may chose to allow
@@ -2331,13 +2308,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2331
2308
  };
2332
2309
  }
2333
2310
 
2334
- // increment summary count
2335
- if (this.summaryCount !== undefined) {
2336
- this.summaryCount++;
2337
- } else {
2338
- this.summaryCount = 1;
2339
- }
2340
-
2341
2311
  const trace = Trace.start();
2342
2312
  let summarizeResult: IRootSummaryTreeWithStats;
2343
2313
  // If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
@@ -2347,7 +2317,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2347
2317
  summarizeResult = await this.summarize({
2348
2318
  fullTree: fullTree || forcedFullTree,
2349
2319
  trackState: true,
2350
- summaryLogger,
2320
+ summaryLogger: summaryNumberLogger,
2351
2321
  runGC: this.garbageCollector.shouldRunGC,
2352
2322
  });
2353
2323
  } catch (error) {
@@ -2383,6 +2353,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2383
2353
  gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
2384
2354
  opsSizesSinceLastSummary: this.opTracker.opsSizeAccumulator,
2385
2355
  nonSystemOpsSinceLastSummary: this.opTracker.nonSystemOpCount,
2356
+ summaryNumber,
2386
2357
  ...partialStats,
2387
2358
  };
2388
2359
  const generateSummaryData = {
@@ -2806,7 +2777,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2806
2777
  getVersionDuration?: number | undefined;
2807
2778
  getSnapshotDuration?: number | undefined;
2808
2779
  }) => void; }) => {
2809
- const stats: { getVersionDuration?: number; getSnapshotDuration?: number } = {};
2780
+ const stats: { getVersionDuration?: number; getSnapshotDuration?: number; } = {};
2810
2781
  const trace = Trace.start();
2811
2782
 
2812
2783
  const versions = await this.storage.getVersions(versionId, 1);
@@ -246,7 +246,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
246
246
 
247
247
  // The used state of this node as per the last GC run. This is used to update the used state of the channel
248
248
  // if it realizes after GC is run.
249
- private lastUsedState: { usedRoutes: string[], gcTimestamp?: number } | undefined;
249
+ private lastUsedState: { usedRoutes: string[]; gcTimestamp?: number; } | undefined;
250
250
 
251
251
  public readonly id: string;
252
252
  private readonly _containerRuntime: ContainerRuntime;
@@ -317,7 +317,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
317
317
  }
318
318
 
319
319
  private rejectDeferredRealize(reason: string, packageName?: string): never {
320
- throw new LoggingError(reason, { packageName: { value: packageName, tag: TelemetryDataTag.PackageData }});
320
+ throw new LoggingError(reason, { packageName: { value: packageName, tag: TelemetryDataTag.PackageData } });
321
321
  }
322
322
 
323
323
  public async realize(): Promise<IFluidDataStoreChannel> {
@@ -326,9 +326,9 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
326
326
  this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
327
327
  this.realizeCore(this.existing).catch((error) => {
328
328
  const errorWrapped = DataProcessingError.wrapIfUnrecognized(error, "realizeFluidDataStoreContext");
329
- errorWrapped.addTelemetryProperties({ fluidDataStoreId: { value: this.id, tag: "PackageData"} });
329
+ errorWrapped.addTelemetryProperties({ fluidDataStoreId: { value: this.id, tag: "PackageData" } });
330
330
  this.channelDeferred?.reject(errorWrapped);
331
- this.logger.sendErrorEvent({ eventName: "RealizeError"}, errorWrapped);
331
+ this.logger.sendErrorEvent({ eventName: "RealizeError" }, errorWrapped);
332
332
  });
333
333
  }
334
334
  return this.channelDeferred.promise;
@@ -641,8 +641,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
641
641
  throw new Error("Runtime already bound");
642
642
  }
643
643
 
644
- try
645
- {
644
+ try {
646
645
  assert(!this.detachedRuntimeCreation, 0x148 /* "Detached runtime creation on runtime bind" */);
647
646
  assert(this.channelDeferred !== undefined, 0x149 /* "Undefined channel deferral" */);
648
647
  assert(this.pkg !== undefined, 0x14a /* "Undefined package path" */);
@@ -680,7 +679,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
680
679
  } catch (error) {
681
680
  this.channelDeferred?.reject(error);
682
681
  this.logger.sendErrorEvent(
683
- { eventName: "BindRuntimeError", fluidDataStoreId: { value: this.id, tag: "PackageData"} },
682
+ { eventName: "BindRuntimeError", fluidDataStoreId: { value: this.id, tag: "PackageData" } },
684
683
  error);
685
684
  }
686
685
  }
@@ -993,8 +992,7 @@ export class LocalFluidDataStoreContext extends LocalFluidDataStoreContextBase {
993
992
  */
994
993
  export class LocalDetachedFluidDataStoreContext
995
994
  extends LocalFluidDataStoreContextBase
996
- implements IFluidDataStoreContextDetached
997
- {
995
+ implements IFluidDataStoreContextDetached {
998
996
  constructor(props: ILocalFluidDataStoreContextProps) {
999
997
  super(props);
1000
998
  this.detachedRuntimeCreation = true;
@@ -1002,8 +1000,7 @@ export class LocalDetachedFluidDataStoreContext
1002
1000
 
1003
1001
  public async attachRuntime(
1004
1002
  registry: IProvideFluidDataStoreFactory,
1005
- dataStoreChannel: IFluidDataStoreChannel)
1006
- {
1003
+ dataStoreChannel: IFluidDataStoreChannel) {
1007
1004
  assert(this.detachedRuntimeCreation, 0x154 /* "runtime creation is already attached" */);
1008
1005
  assert(this.channelDeferred === undefined, 0x155 /* "channel deferral is already set" */);
1009
1006
 
@@ -8,7 +8,7 @@ import { assert, Deferred, Lazy } from "@fluidframework/common-utils";
8
8
  import { ChildLogger } from "@fluidframework/telemetry-utils";
9
9
  import { FluidDataStoreContext, LocalFluidDataStoreContext } from "./dataStoreContext";
10
10
 
11
- export class DataStoreContexts implements Iterable<[string,FluidDataStoreContext]>, IDisposable {
11
+ export class DataStoreContexts implements Iterable<[string, FluidDataStoreContext]>, IDisposable {
12
12
  private readonly notBoundContexts = new Set<string>();
13
13
 
14
14
  /** Attached and loaded context proxies */
@@ -23,7 +23,7 @@ import { FluidDataStoreContext, LocalFluidDataStoreContext } from "./dataStoreCo
23
23
  */
24
24
  private readonly deferredContexts = new Map<string, Deferred<FluidDataStoreContext>>();
25
25
 
26
- private readonly disposeOnce = new Lazy<void>(()=>{
26
+ private readonly disposeOnce = new Lazy<void>(() => {
27
27
  // close/stop all store contexts
28
28
  for (const [fluidDataStoreId, contextD] of this.deferredContexts) {
29
29
  contextD.promise.then((context) => {
@@ -52,7 +52,7 @@ import { FluidDataStoreContext, LocalFluidDataStoreContext } from "./dataStoreCo
52
52
  return this._contexts.size;
53
53
  }
54
54
 
55
- public get disposed() { return this.disposeOnce.evaluated;}
55
+ public get disposed() { return this.disposeOnce.evaluated; }
56
56
  public readonly dispose = () => this.disposeOnce.value;
57
57
 
58
58
  public notBoundLength() {
@@ -106,8 +106,8 @@ import { FluidDataStoreContext, LocalFluidDataStoreContext } from "./dataStoreCo
106
106
  /**
107
107
  * Get the context with the given id, once it exists locally and is attached.
108
108
  * e.g. If created locally, it must be bound, or if created remotely then it's fine as soon as it's sync'd in.
109
- * @param id The id of the context to get
110
- * @param wait If false, return undefined if the context isn't present and ready now. Otherwise, wait for it.
109
+ * @param id - The id of the context to get
110
+ * @param wait - If false, return undefined if the context isn't present and ready now. Otherwise, wait for it.
111
111
  */
112
112
  public async getBoundOrRemoted(id: string, wait: boolean): Promise<FluidDataStoreContext | undefined> {
113
113
  const deferredContext = this.ensureDeferred(id);