@fluidframework/container-runtime 0.58.2001 → 0.59.2000-61729

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 (124) hide show
  1. package/dist/blobManager.d.ts +15 -2
  2. package/dist/blobManager.d.ts.map +1 -1
  3. package/dist/blobManager.js +65 -9
  4. package/dist/blobManager.js.map +1 -1
  5. package/dist/connectionTelemetry.d.ts.map +1 -1
  6. package/dist/connectionTelemetry.js +63 -23
  7. package/dist/connectionTelemetry.js.map +1 -1
  8. package/dist/containerRuntime.d.ts +39 -7
  9. package/dist/containerRuntime.d.ts.map +1 -1
  10. package/dist/containerRuntime.js +161 -29
  11. package/dist/containerRuntime.js.map +1 -1
  12. package/dist/dataStore.js +8 -1
  13. package/dist/dataStore.js.map +1 -1
  14. package/dist/dataStoreContext.d.ts +9 -3
  15. package/dist/dataStoreContext.d.ts.map +1 -1
  16. package/dist/dataStoreContext.js +22 -6
  17. package/dist/dataStoreContext.js.map +1 -1
  18. package/dist/dataStores.d.ts +13 -5
  19. package/dist/dataStores.d.ts.map +1 -1
  20. package/dist/dataStores.js +39 -18
  21. package/dist/dataStores.js.map +1 -1
  22. package/dist/deltaScheduler.d.ts +4 -5
  23. package/dist/deltaScheduler.d.ts.map +1 -1
  24. package/dist/deltaScheduler.js +54 -35
  25. package/dist/deltaScheduler.js.map +1 -1
  26. package/dist/garbageCollection.d.ts +31 -27
  27. package/dist/garbageCollection.d.ts.map +1 -1
  28. package/dist/garbageCollection.js +76 -75
  29. package/dist/garbageCollection.js.map +1 -1
  30. package/dist/opTelemetry.d.ts +22 -0
  31. package/dist/opTelemetry.d.ts.map +1 -0
  32. package/dist/opTelemetry.js +59 -0
  33. package/dist/opTelemetry.js.map +1 -0
  34. package/dist/orderedClientElection.d.ts +57 -6
  35. package/dist/orderedClientElection.d.ts.map +1 -1
  36. package/dist/orderedClientElection.js +141 -26
  37. package/dist/orderedClientElection.js.map +1 -1
  38. package/dist/packageVersion.d.ts +1 -1
  39. package/dist/packageVersion.d.ts.map +1 -1
  40. package/dist/packageVersion.js +1 -1
  41. package/dist/packageVersion.js.map +1 -1
  42. package/dist/summarizerClientElection.d.ts +2 -0
  43. package/dist/summarizerClientElection.d.ts.map +1 -1
  44. package/dist/summarizerClientElection.js +15 -2
  45. package/dist/summarizerClientElection.js.map +1 -1
  46. package/dist/summarizerTypes.d.ts +9 -0
  47. package/dist/summarizerTypes.d.ts.map +1 -1
  48. package/dist/summarizerTypes.js.map +1 -1
  49. package/dist/summaryGenerator.d.ts.map +1 -1
  50. package/dist/summaryGenerator.js +3 -4
  51. package/dist/summaryGenerator.js.map +1 -1
  52. package/dist/summaryManager.d.ts.map +1 -1
  53. package/dist/summaryManager.js +14 -3
  54. package/dist/summaryManager.js.map +1 -1
  55. package/lib/blobManager.d.ts +15 -2
  56. package/lib/blobManager.d.ts.map +1 -1
  57. package/lib/blobManager.js +66 -10
  58. package/lib/blobManager.js.map +1 -1
  59. package/lib/connectionTelemetry.d.ts.map +1 -1
  60. package/lib/connectionTelemetry.js +63 -23
  61. package/lib/connectionTelemetry.js.map +1 -1
  62. package/lib/containerRuntime.d.ts +39 -7
  63. package/lib/containerRuntime.d.ts.map +1 -1
  64. package/lib/containerRuntime.js +163 -31
  65. package/lib/containerRuntime.js.map +1 -1
  66. package/lib/dataStore.js +8 -1
  67. package/lib/dataStore.js.map +1 -1
  68. package/lib/dataStoreContext.d.ts +9 -3
  69. package/lib/dataStoreContext.d.ts.map +1 -1
  70. package/lib/dataStoreContext.js +22 -6
  71. package/lib/dataStoreContext.js.map +1 -1
  72. package/lib/dataStores.d.ts +13 -5
  73. package/lib/dataStores.d.ts.map +1 -1
  74. package/lib/dataStores.js +39 -18
  75. package/lib/dataStores.js.map +1 -1
  76. package/lib/deltaScheduler.d.ts +4 -5
  77. package/lib/deltaScheduler.d.ts.map +1 -1
  78. package/lib/deltaScheduler.js +54 -35
  79. package/lib/deltaScheduler.js.map +1 -1
  80. package/lib/garbageCollection.d.ts +31 -27
  81. package/lib/garbageCollection.d.ts.map +1 -1
  82. package/lib/garbageCollection.js +75 -74
  83. package/lib/garbageCollection.js.map +1 -1
  84. package/lib/opTelemetry.d.ts +22 -0
  85. package/lib/opTelemetry.d.ts.map +1 -0
  86. package/lib/opTelemetry.js +55 -0
  87. package/lib/opTelemetry.js.map +1 -0
  88. package/lib/orderedClientElection.d.ts +57 -6
  89. package/lib/orderedClientElection.d.ts.map +1 -1
  90. package/lib/orderedClientElection.js +141 -26
  91. package/lib/orderedClientElection.js.map +1 -1
  92. package/lib/packageVersion.d.ts +1 -1
  93. package/lib/packageVersion.d.ts.map +1 -1
  94. package/lib/packageVersion.js +1 -1
  95. package/lib/packageVersion.js.map +1 -1
  96. package/lib/summarizerClientElection.d.ts +2 -0
  97. package/lib/summarizerClientElection.d.ts.map +1 -1
  98. package/lib/summarizerClientElection.js +15 -2
  99. package/lib/summarizerClientElection.js.map +1 -1
  100. package/lib/summarizerTypes.d.ts +9 -0
  101. package/lib/summarizerTypes.d.ts.map +1 -1
  102. package/lib/summarizerTypes.js.map +1 -1
  103. package/lib/summaryGenerator.d.ts.map +1 -1
  104. package/lib/summaryGenerator.js +3 -4
  105. package/lib/summaryGenerator.js.map +1 -1
  106. package/lib/summaryManager.d.ts.map +1 -1
  107. package/lib/summaryManager.js +14 -3
  108. package/lib/summaryManager.js.map +1 -1
  109. package/package.json +63 -19
  110. package/src/blobManager.ts +78 -11
  111. package/src/connectionTelemetry.ts +110 -19
  112. package/src/containerRuntime.ts +191 -36
  113. package/src/dataStore.ts +7 -1
  114. package/src/dataStoreContext.ts +22 -7
  115. package/src/dataStores.ts +40 -19
  116. package/src/deltaScheduler.ts +65 -39
  117. package/src/garbageCollection.ts +92 -78
  118. package/src/opTelemetry.ts +71 -0
  119. package/src/orderedClientElection.ts +155 -25
  120. package/src/packageVersion.ts +1 -1
  121. package/src/summarizerClientElection.ts +15 -2
  122. package/src/summarizerTypes.ts +9 -0
  123. package/src/summaryGenerator.ts +10 -8
  124. package/src/summaryManager.ts +15 -4
@@ -6,7 +6,8 @@ import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-uti
6
6
  import { DataCorruptionError, GenericError, UsageError, extractSafePropertiesFromMessage, } from "@fluidframework/container-utils";
7
7
  import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
8
8
  import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
9
- import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, } from "@fluidframework/runtime-utils";
9
+ import { addBlobToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, } from "@fluidframework/runtime-utils";
10
+ import { GCDataBuilder } from "@fluidframework/garbage-collector";
10
11
  import { v4 as uuid } from "uuid";
11
12
  import { ContainerFluidHandleContext } from "./containerHandleContext";
12
13
  import { FluidDataStoreRegistry } from "./dataStoreRegistry";
@@ -24,9 +25,10 @@ import { OrderedClientCollection, OrderedClientElection } from "./orderedClientE
24
25
  import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
25
26
  import { formExponentialFn, Throttler } from "./throttler";
26
27
  import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
27
- import { GarbageCollector, gcTreeKey, } from "./garbageCollection";
28
+ import { GarbageCollector, GCNodeType, gcTreeKey, } from "./garbageCollection";
28
29
  import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
29
30
  import { BindBatchTracker } from "./batchTracker";
31
+ import { OpTracker } from "./opTelemetry";
30
32
  export var ContainerMessageType;
31
33
  (function (ContainerMessageType) {
32
34
  // An op to be delivered to store
@@ -79,6 +81,11 @@ const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
79
81
  // in order to account for some extra overhead from serialization
80
82
  // to not reach the 1MB limits in socket.io and Kafka.
81
83
  const defaultMaxOpSizeInBytes = 768000;
84
+ // By default, the size of the contents for the incoming ops is tracked.
85
+ // However, in certain situations, this may incur a performance hit.
86
+ // The feature-gate below can be used to disable this feature.
87
+ const disableOpTrackingKey = "Fluid.ContainerRuntime.DisableOpTracking";
88
+ const defaultFlushMode = FlushMode.TurnBased;
82
89
  export var RuntimeMessage;
83
90
  (function (RuntimeMessage) {
84
91
  RuntimeMessage["FluidDataStoreOp"] = "component";
@@ -127,6 +134,7 @@ class ScheduleManagerCore {
127
134
  this.logger = logger;
128
135
  this.localPaused = false;
129
136
  this.timePaused = 0;
137
+ this.batchCount = 0;
130
138
  // Listen for delta manager sends and add batch metadata to messages
131
139
  this.deltaManager.on("prepareSend", (messages) => {
132
140
  if (messages.length === 0) {
@@ -193,13 +201,26 @@ class ScheduleManagerCore {
193
201
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
194
202
  this.deltaManager.inbound.pause();
195
203
  }
196
- resumeQueue(startBatch, endBatch) {
204
+ resumeQueue(startBatch, messageEndBatch) {
205
+ const endBatch = messageEndBatch.sequenceNumber;
206
+ const duration = performance.now() - this.timePaused;
207
+ this.batchCount++;
208
+ if (this.batchCount % 1000 === 1) {
209
+ this.logger.sendTelemetryEvent({
210
+ eventName: "BatchStats",
211
+ sequenceNumber: endBatch,
212
+ length: endBatch - startBatch + 1,
213
+ msnDistance: endBatch - messageEndBatch.minimumSequenceNumber,
214
+ duration,
215
+ batchCount: this.batchCount,
216
+ interrupted: this.localPaused,
217
+ });
218
+ }
197
219
  // Return early if no change in value
198
220
  if (!this.localPaused) {
199
221
  return;
200
222
  }
201
223
  this.localPaused = false;
202
- const duration = performance.now() - this.timePaused;
203
224
  // Random round number - we want to know when batch waiting paused op processing.
204
225
  if (duration > latencyThreshold) {
205
226
  this.logger.sendErrorEvent({
@@ -260,7 +281,7 @@ class ScheduleManagerCore {
260
281
  else if (batchMetadata === false) {
261
282
  assert(this.pauseSequenceNumber !== undefined, 0x2a0 /* "batch presence was validated above" */);
262
283
  // Batch is complete, we can process it!
263
- this.resumeQueue(this.pauseSequenceNumber, message.sequenceNumber);
284
+ this.resumeQueue(this.pauseSequenceNumber, message);
264
285
  this.pauseSequenceNumber = undefined;
265
286
  this.currentBatchClientId = undefined;
266
287
  }
@@ -292,7 +313,7 @@ export class ScheduleManager {
292
313
  assert(this.batchClientId === undefined, 0x2a2 /* "Batch is interrupted by other client op. Should be caught by trackPending()" */);
293
314
  // This could be the beginning of a new batch or an individual message.
294
315
  this.emitter.emit("batchBegin", message);
295
- this.deltaScheduler.batchBegin();
316
+ this.deltaScheduler.batchBegin(message);
296
317
  const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
297
318
  if (batch) {
298
319
  this.batchClientId = message.clientId;
@@ -312,7 +333,7 @@ export class ScheduleManager {
312
333
  this.hitError = true;
313
334
  this.batchClientId = undefined;
314
335
  this.emitter.emit("batchEnd", error, message);
315
- this.deltaScheduler.batchEnd();
336
+ this.deltaScheduler.batchEnd(message);
316
337
  return;
317
338
  }
318
339
  const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
@@ -321,7 +342,7 @@ export class ScheduleManager {
321
342
  if (this.batchClientId === undefined || batch === false) {
322
343
  this.batchClientId = undefined;
323
344
  this.emitter.emit("batchEnd", undefined, message);
324
- this.deltaScheduler.batchEnd();
345
+ this.deltaScheduler.batchEnd(message);
325
346
  return;
326
347
  }
327
348
  }
@@ -363,7 +384,6 @@ export class ContainerRuntime extends TypedEventEmitter {
363
384
  this._storage = _storage;
364
385
  this.defaultMaxConsecutiveReconnects = 15;
365
386
  this._orderSequentiallyCalls = 0;
366
- this._flushMode = FlushMode.TurnBased;
367
387
  this.needsFlush = false;
368
388
  this.flushTrigger = false;
369
389
  this.paused = false;
@@ -446,12 +466,8 @@ export class ContainerRuntime extends TypedEventEmitter {
446
466
  ((_c = runtimeOptions.useDataStoreAliasing) !== null && _c !== void 0 ? _c : false);
447
467
  this._maxOpSizeInBytes = ((_d = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _d !== void 0 ? _d : defaultMaxOpSizeInBytes);
448
468
  this.maxConsecutiveReconnects = (_e = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _e !== void 0 ? _e : this.defaultMaxConsecutiveReconnects;
449
- this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
450
- /**
451
- * Returns the timestamp of the last message seen by this client. This is used by garbage collector as
452
- * the current reference timestamp for tracking unreferenced objects.
453
- */
454
- () => { var _a, _b, _c; return (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : (_c = this.messageAtLastSummary) === null || _c === void 0 ? void 0 : _c.timestamp; }, () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; }, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
469
+ this._flushMode = runtimeOptions.flushMode;
470
+ this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (nodePath) => this.getGCNodePackagePath(nodePath), () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; }, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
455
471
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
456
472
  this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
457
473
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
@@ -472,7 +488,7 @@ export class ContainerRuntime extends TypedEventEmitter {
472
488
  if (this.context.baseSnapshot) {
473
489
  this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
474
490
  }
475
- this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getDataStoreBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
491
+ this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
476
492
  this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
477
493
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
478
494
  this.deltaSender = this.deltaManager;
@@ -560,6 +576,7 @@ export class ContainerRuntime extends TypedEventEmitter {
560
576
  this.logger.sendTelemetryEvent(Object.assign(Object.assign(Object.assign({ eventName: "ContainerLoadStats" }, this.createContainerMetadata), this.dataStores.containerLoadStats), { summaryCount: this.summaryCount, summaryFormatVersion: metadata === null || metadata === void 0 ? void 0 : metadata.summaryFormatVersion, disableIsolatedChannels: metadata === null || metadata === void 0 ? void 0 : metadata.disableIsolatedChannels, gcVersion: metadata === null || metadata === void 0 ? void 0 : metadata.gcFeature }));
561
577
  ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
562
578
  BindBatchTracker(this, this.logger);
579
+ this.opTracker = new OpTracker(this.deltaManager, this.mc.config.getBoolean(disableOpTrackingKey) === true);
563
580
  }
564
581
  get IContainerRuntime() { return this; }
565
582
  get IFluidRouter() { return this; }
@@ -582,7 +599,7 @@ export class ContainerRuntime extends TypedEventEmitter {
582
599
  runtimeVersion: pkgVersion,
583
600
  },
584
601
  });
585
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
602
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, } = runtimeOptions;
586
603
  // We pack at data store level only. If isolated channels are disabled,
587
604
  // then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
588
605
  const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
@@ -653,6 +670,7 @@ export class ContainerRuntime extends TypedEventEmitter {
653
670
  gcOptions,
654
671
  loadSequenceNumberVerification,
655
672
  useDataStoreAliasing,
673
+ flushMode,
656
674
  }, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
657
675
  return runtime;
658
676
  }
@@ -871,12 +889,11 @@ export class ContainerRuntime extends TypedEventEmitter {
871
889
  const electedSummarizerContent = JSON.stringify((_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.serialize());
872
890
  addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
873
891
  }
874
- const snapshot = this.blobManager.snapshot();
892
+ const summary = this.blobManager.summarize();
875
893
  // Some storage (like git) doesn't allow empty tree, so we can omit it.
876
894
  // and the blob manager can handle the tree not existing when loading
877
- if (snapshot.entries.length !== 0) {
878
- const blobsTree = convertToSummaryTree(snapshot, false);
879
- addTreeToSummary(summaryTree, blobsTreeName, blobsTree);
895
+ if (Object.keys(summary.summary.tree).length > 0) {
896
+ addTreeToSummary(summaryTree, blobsTreeName, summary);
880
897
  }
881
898
  if (this.garbageCollector.writeDataAtRoot) {
882
899
  const gcSummary = this.garbageCollector.summarize();
@@ -1065,6 +1082,11 @@ export class ContainerRuntime extends TypedEventEmitter {
1065
1082
  if (mode === this._flushMode) {
1066
1083
  return;
1067
1084
  }
1085
+ this.mc.logger.sendTelemetryEvent({
1086
+ eventName: "FlushMode Updated",
1087
+ old: this._flushMode,
1088
+ new: mode,
1089
+ });
1068
1090
  // Flush any pending batches if switching to immediate
1069
1091
  if (mode === FlushMode.Immediate) {
1070
1092
  this.flush();
@@ -1137,7 +1159,14 @@ export class ContainerRuntime extends TypedEventEmitter {
1137
1159
  */
1138
1160
  async createRootDataStoreLegacy(pkg, rootDataStoreId) {
1139
1161
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1140
- fluidDataStore.bindToContext();
1162
+ // back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel. For
1163
+ // older versions, we still have to call bindToContext.
1164
+ if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
1165
+ fluidDataStore.makeVisibleAndAttachGraph();
1166
+ }
1167
+ else {
1168
+ fluidDataStore.bindToContext();
1169
+ }
1141
1170
  return fluidDataStore;
1142
1171
  }
1143
1172
  async createRootDataStore(pkg, rootDataStoreId) {
@@ -1191,7 +1220,18 @@ export class ContainerRuntime extends TypedEventEmitter {
1191
1220
  async _createDataStoreWithPropsLegacy(pkg, props, id = uuid(), isRoot = false) {
1192
1221
  const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
1193
1222
  if (isRoot) {
1194
- fluidDataStore.bindToContext();
1223
+ // back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
1224
+ // For older versions, we still have to call bindToContext.
1225
+ if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
1226
+ fluidDataStore.makeVisibleAndAttachGraph();
1227
+ }
1228
+ else {
1229
+ fluidDataStore.bindToContext();
1230
+ }
1231
+ this.logger.sendTelemetryEvent({
1232
+ eventName: "Root datastore with props",
1233
+ hasProps: props !== undefined,
1234
+ });
1195
1235
  }
1196
1236
  return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
1197
1237
  }
@@ -1315,9 +1355,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1315
1355
  if (runGC) {
1316
1356
  gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1317
1357
  }
1318
- const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
1319
- assert(summarizeResult.summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1320
- return Object.assign(Object.assign({}, summarizeResult), { gcStats });
1358
+ const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState);
1359
+ assert(summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1360
+ return { stats, summary, gcStats };
1321
1361
  }
1322
1362
  /**
1323
1363
  * Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
@@ -1334,7 +1374,12 @@ export class ContainerRuntime extends TypedEventEmitter {
1334
1374
  * @param fullGC - true to bypass optimizations and force full generation of GC data.
1335
1375
  */
1336
1376
  async getGCData(fullGC) {
1337
- return this.dataStores.getGCData(fullGC);
1377
+ const builder = new GCDataBuilder();
1378
+ const dsGCData = await this.dataStores.getGCData(fullGC);
1379
+ builder.addNodes(dsGCData.gcNodes);
1380
+ const blobsGCData = this.blobManager.getGCData(fullGC);
1381
+ builder.addNodes(blobsGCData.gcNodes);
1382
+ return builder.getGCData();
1338
1383
  }
1339
1384
  /**
1340
1385
  * Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
@@ -1348,7 +1393,78 @@ export class ContainerRuntime extends TypedEventEmitter {
1348
1393
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1349
1394
  // always referenced, so the used routes is only self-route (empty string).
1350
1395
  this.summarizerNode.updateUsedRoutes([""]);
1351
- return this.dataStores.updateUsedRoutes(usedRoutes, gcTimestamp);
1396
+ const dataStoreUsedRoutes = [];
1397
+ for (const route of usedRoutes) {
1398
+ if (route.split("/")[1] !== BlobManager.basePath) {
1399
+ dataStoreUsedRoutes.push(route);
1400
+ }
1401
+ }
1402
+ return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes, gcTimestamp);
1403
+ }
1404
+ /**
1405
+ * When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
1406
+ * scenarios with accessing deleted content.
1407
+ * @param unusedRoutes - The routes that are unused in all data stores in this Container.
1408
+ */
1409
+ deleteUnusedRoutes(unusedRoutes) {
1410
+ const blobManagerUnusedRoutes = [];
1411
+ const dataStoreUnusedRoutes = [];
1412
+ for (const route of unusedRoutes) {
1413
+ if (this.isBlobPath(route)) {
1414
+ blobManagerUnusedRoutes.push(route);
1415
+ }
1416
+ else {
1417
+ dataStoreUnusedRoutes.push(route);
1418
+ }
1419
+ }
1420
+ this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
1421
+ this.dataStores.deleteUnusedRoutes(dataStoreUnusedRoutes);
1422
+ }
1423
+ /**
1424
+ * Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
1425
+ */
1426
+ getCurrentReferenceTimestampMs() {
1427
+ var _a, _b, _c;
1428
+ // Use the timestamp of the last message seen by this client as that is server generated. If no messages have
1429
+ // been processed, use the timestamp of the message from the last summary.
1430
+ return (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : (_c = this.messageAtLastSummary) === null || _c === void 0 ? void 0 : _c.timestamp;
1431
+ }
1432
+ /**
1433
+ * Returns the type of the GC node. Currently, there are nodes that belong to data store and nodes that belong
1434
+ * to the blob manager.
1435
+ */
1436
+ getNodeType(nodePath) {
1437
+ if (this.isBlobPath(nodePath)) {
1438
+ return GCNodeType.Blob;
1439
+ }
1440
+ if (this.dataStores.isDataStoreNode(nodePath)) {
1441
+ return GCNodeType.DataStore;
1442
+ }
1443
+ // Root node ("/") and DDS nodes belong to "Other" node types.
1444
+ return GCNodeType.Other;
1445
+ }
1446
+ /**
1447
+ * Called by GC to retrieve the package path of the node with the given path. The node should belong to a
1448
+ * data store or an attachment blob.
1449
+ */
1450
+ getGCNodePackagePath(nodePath) {
1451
+ // If the node is a blob, return "_blobs" as the package path.
1452
+ if (this.isBlobPath(nodePath)) {
1453
+ return ["_blobs"];
1454
+ }
1455
+ const dataStorePkgPath = this.dataStores.getDataStorePackagePath(nodePath);
1456
+ assert(dataStorePkgPath !== undefined, 0x2d6 /* "Package path requested for unknown node type." */);
1457
+ return dataStorePkgPath;
1458
+ }
1459
+ /**
1460
+ * Returns whether a given path is for attachment blobs that are in the format - "/BlobManager.basePath/...".
1461
+ */
1462
+ isBlobPath(path) {
1463
+ const pathParts = path.split("/");
1464
+ if (pathParts.length < 2 || pathParts[1] !== BlobManager.basePath) {
1465
+ return false;
1466
+ }
1467
+ return true;
1352
1468
  }
1353
1469
  /**
1354
1470
  * Runs garbage collection and updates the reference / used state of the nodes in the container.
@@ -1392,6 +1508,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1392
1508
  try {
1393
1509
  await this.deltaManager.inbound.pause();
1394
1510
  const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
1511
+ const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
1395
1512
  const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
1396
1513
  // We should be here is we haven't processed be here. If we are of if the last message's sequence number
1397
1514
  // doesn't match the last processed sequence number, log an error.
@@ -1431,7 +1548,12 @@ export class ContainerRuntime extends TypedEventEmitter {
1431
1548
  };
1432
1549
  let continueResult = checkContinue();
1433
1550
  if (!continueResult.continue) {
1434
- return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error: continueResult.error };
1551
+ return {
1552
+ stage: "base",
1553
+ referenceSequenceNumber: summaryRefSeqNum,
1554
+ minimumSequenceNumber,
1555
+ error: continueResult.error,
1556
+ };
1435
1557
  }
1436
1558
  // increment summary count
1437
1559
  if (this.summaryCount !== undefined) {
@@ -1454,7 +1576,12 @@ export class ContainerRuntime extends TypedEventEmitter {
1454
1576
  });
1455
1577
  }
1456
1578
  catch (error) {
1457
- return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error };
1579
+ return {
1580
+ stage: "base",
1581
+ referenceSequenceNumber: summaryRefSeqNum,
1582
+ minimumSequenceNumber,
1583
+ error,
1584
+ };
1458
1585
  }
1459
1586
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
1460
1587
  // Now that we have generated the summary, update the message at last summary to the last message processed.
@@ -1465,9 +1592,13 @@ export class ContainerRuntime extends TypedEventEmitter {
1465
1592
  const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[channelsTreeName];
1466
1593
  assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1467
1594
  const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === SummaryType.Handle).length;
1468
- const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_b = summarizeResult.gcStats) === null || _b === void 0 ? void 0 : _b.updatedDataStoreCount }, partialStats);
1595
+ const gcSummaryTreeStats = summaryTree.tree[gcTreeKey]
1596
+ ? calculateStats(summaryTree.tree[gcTreeKey])
1597
+ : undefined;
1598
+ const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_b = summarizeResult.gcStats) === null || _b === void 0 ? void 0 : _b.updatedDataStoreCount, gcBlobNodeCount: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.blobNodeCount, gcTotalBlobsSize: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.totalBlobSize, opsSizesSinceLastSummary: this.opTracker.opsSizeAccumulator, nonSystemOpsSinceLastSummary: this.opTracker.nonSystemOpCount }, partialStats);
1469
1599
  const generateSummaryData = {
1470
1600
  referenceSequenceNumber: summaryRefSeqNum,
1601
+ minimumSequenceNumber,
1471
1602
  summaryTree,
1472
1603
  summaryStats,
1473
1604
  generateDuration: trace.trace().duration,
@@ -1518,6 +1649,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1518
1649
  }
1519
1650
  const submitData = Object.assign(Object.assign({ stage: "submit" }, uploadData), { clientSequenceNumber, submitOpDuration: trace.trace().duration });
1520
1651
  this.summarizerNode.completeSummary(handle);
1652
+ this.opTracker.reset();
1521
1653
  return submitData;
1522
1654
  }
1523
1655
  finally {