@fluidframework/container-runtime 0.58.2002 → 0.59.1000

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 (97) 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/packageVersion.d.ts +1 -1
  35. package/dist/packageVersion.js +1 -1
  36. package/dist/packageVersion.js.map +1 -1
  37. package/dist/summarizerTypes.d.ts +9 -0
  38. package/dist/summarizerTypes.d.ts.map +1 -1
  39. package/dist/summarizerTypes.js.map +1 -1
  40. package/dist/summaryGenerator.d.ts.map +1 -1
  41. package/dist/summaryGenerator.js +1 -1
  42. package/dist/summaryGenerator.js.map +1 -1
  43. package/lib/blobManager.d.ts +15 -2
  44. package/lib/blobManager.d.ts.map +1 -1
  45. package/lib/blobManager.js +66 -10
  46. package/lib/blobManager.js.map +1 -1
  47. package/lib/connectionTelemetry.d.ts.map +1 -1
  48. package/lib/connectionTelemetry.js +63 -23
  49. package/lib/connectionTelemetry.js.map +1 -1
  50. package/lib/containerRuntime.d.ts +39 -7
  51. package/lib/containerRuntime.d.ts.map +1 -1
  52. package/lib/containerRuntime.js +163 -31
  53. package/lib/containerRuntime.js.map +1 -1
  54. package/lib/dataStore.js +8 -1
  55. package/lib/dataStore.js.map +1 -1
  56. package/lib/dataStoreContext.d.ts +9 -3
  57. package/lib/dataStoreContext.d.ts.map +1 -1
  58. package/lib/dataStoreContext.js +22 -6
  59. package/lib/dataStoreContext.js.map +1 -1
  60. package/lib/dataStores.d.ts +13 -5
  61. package/lib/dataStores.d.ts.map +1 -1
  62. package/lib/dataStores.js +39 -18
  63. package/lib/dataStores.js.map +1 -1
  64. package/lib/deltaScheduler.d.ts +4 -5
  65. package/lib/deltaScheduler.d.ts.map +1 -1
  66. package/lib/deltaScheduler.js +54 -35
  67. package/lib/deltaScheduler.js.map +1 -1
  68. package/lib/garbageCollection.d.ts +31 -27
  69. package/lib/garbageCollection.d.ts.map +1 -1
  70. package/lib/garbageCollection.js +75 -74
  71. package/lib/garbageCollection.js.map +1 -1
  72. package/lib/opTelemetry.d.ts +22 -0
  73. package/lib/opTelemetry.d.ts.map +1 -0
  74. package/lib/opTelemetry.js +55 -0
  75. package/lib/opTelemetry.js.map +1 -0
  76. package/lib/packageVersion.d.ts +1 -1
  77. package/lib/packageVersion.js +1 -1
  78. package/lib/packageVersion.js.map +1 -1
  79. package/lib/summarizerTypes.d.ts +9 -0
  80. package/lib/summarizerTypes.d.ts.map +1 -1
  81. package/lib/summarizerTypes.js.map +1 -1
  82. package/lib/summaryGenerator.d.ts.map +1 -1
  83. package/lib/summaryGenerator.js +1 -1
  84. package/lib/summaryGenerator.js.map +1 -1
  85. package/package.json +63 -19
  86. package/src/blobManager.ts +78 -11
  87. package/src/connectionTelemetry.ts +110 -19
  88. package/src/containerRuntime.ts +191 -36
  89. package/src/dataStore.ts +7 -1
  90. package/src/dataStoreContext.ts +22 -7
  91. package/src/dataStores.ts +40 -19
  92. package/src/deltaScheduler.ts +65 -39
  93. package/src/garbageCollection.ts +92 -78
  94. package/src/opTelemetry.ts +71 -0
  95. package/src/packageVersion.ts +1 -1
  96. package/src/summarizerTypes.ts +9 -0
  97. package/src/summaryGenerator.ts +9 -1
@@ -10,6 +10,7 @@ const container_utils_1 = require("@fluidframework/container-utils");
10
10
  const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
11
11
  const runtime_definitions_1 = require("@fluidframework/runtime-definitions");
12
12
  const runtime_utils_1 = require("@fluidframework/runtime-utils");
13
+ const garbage_collector_1 = require("@fluidframework/garbage-collector");
13
14
  const uuid_1 = require("uuid");
14
15
  const containerHandleContext_1 = require("./containerHandleContext");
15
16
  const dataStoreRegistry_1 = require("./dataStoreRegistry");
@@ -30,6 +31,7 @@ const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator")
30
31
  const garbageCollection_1 = require("./garbageCollection");
31
32
  const dataStore_1 = require("./dataStore");
32
33
  const batchTracker_1 = require("./batchTracker");
34
+ const opTelemetry_1 = require("./opTelemetry");
33
35
  var ContainerMessageType;
34
36
  (function (ContainerMessageType) {
35
37
  // An op to be delivered to store
@@ -82,6 +84,11 @@ const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
82
84
  // in order to account for some extra overhead from serialization
83
85
  // to not reach the 1MB limits in socket.io and Kafka.
84
86
  const defaultMaxOpSizeInBytes = 768000;
87
+ // By default, the size of the contents for the incoming ops is tracked.
88
+ // However, in certain situations, this may incur a performance hit.
89
+ // The feature-gate below can be used to disable this feature.
90
+ const disableOpTrackingKey = "Fluid.ContainerRuntime.DisableOpTracking";
91
+ const defaultFlushMode = runtime_definitions_1.FlushMode.TurnBased;
85
92
  var RuntimeMessage;
86
93
  (function (RuntimeMessage) {
87
94
  RuntimeMessage["FluidDataStoreOp"] = "component";
@@ -132,6 +139,7 @@ class ScheduleManagerCore {
132
139
  this.logger = logger;
133
140
  this.localPaused = false;
134
141
  this.timePaused = 0;
142
+ this.batchCount = 0;
135
143
  // Listen for delta manager sends and add batch metadata to messages
136
144
  this.deltaManager.on("prepareSend", (messages) => {
137
145
  if (messages.length === 0) {
@@ -198,13 +206,26 @@ class ScheduleManagerCore {
198
206
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
199
207
  this.deltaManager.inbound.pause();
200
208
  }
201
- resumeQueue(startBatch, endBatch) {
209
+ resumeQueue(startBatch, messageEndBatch) {
210
+ const endBatch = messageEndBatch.sequenceNumber;
211
+ const duration = common_utils_1.performance.now() - this.timePaused;
212
+ this.batchCount++;
213
+ if (this.batchCount % 1000 === 1) {
214
+ this.logger.sendTelemetryEvent({
215
+ eventName: "BatchStats",
216
+ sequenceNumber: endBatch,
217
+ length: endBatch - startBatch + 1,
218
+ msnDistance: endBatch - messageEndBatch.minimumSequenceNumber,
219
+ duration,
220
+ batchCount: this.batchCount,
221
+ interrupted: this.localPaused,
222
+ });
223
+ }
202
224
  // Return early if no change in value
203
225
  if (!this.localPaused) {
204
226
  return;
205
227
  }
206
228
  this.localPaused = false;
207
- const duration = common_utils_1.performance.now() - this.timePaused;
208
229
  // Random round number - we want to know when batch waiting paused op processing.
209
230
  if (duration > connectionTelemetry_1.latencyThreshold) {
210
231
  this.logger.sendErrorEvent({
@@ -265,7 +286,7 @@ class ScheduleManagerCore {
265
286
  else if (batchMetadata === false) {
266
287
  common_utils_1.assert(this.pauseSequenceNumber !== undefined, 0x2a0 /* "batch presence was validated above" */);
267
288
  // Batch is complete, we can process it!
268
- this.resumeQueue(this.pauseSequenceNumber, message.sequenceNumber);
289
+ this.resumeQueue(this.pauseSequenceNumber, message);
269
290
  this.pauseSequenceNumber = undefined;
270
291
  this.currentBatchClientId = undefined;
271
292
  }
@@ -297,7 +318,7 @@ class ScheduleManager {
297
318
  common_utils_1.assert(this.batchClientId === undefined, 0x2a2 /* "Batch is interrupted by other client op. Should be caught by trackPending()" */);
298
319
  // This could be the beginning of a new batch or an individual message.
299
320
  this.emitter.emit("batchBegin", message);
300
- this.deltaScheduler.batchBegin();
321
+ this.deltaScheduler.batchBegin(message);
301
322
  const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
302
323
  if (batch) {
303
324
  this.batchClientId = message.clientId;
@@ -317,7 +338,7 @@ class ScheduleManager {
317
338
  this.hitError = true;
318
339
  this.batchClientId = undefined;
319
340
  this.emitter.emit("batchEnd", error, message);
320
- this.deltaScheduler.batchEnd();
341
+ this.deltaScheduler.batchEnd(message);
321
342
  return;
322
343
  }
323
344
  const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
@@ -326,7 +347,7 @@ class ScheduleManager {
326
347
  if (this.batchClientId === undefined || batch === false) {
327
348
  this.batchClientId = undefined;
328
349
  this.emitter.emit("batchEnd", undefined, message);
329
- this.deltaScheduler.batchEnd();
350
+ this.deltaScheduler.batchEnd(message);
330
351
  return;
331
352
  }
332
353
  }
@@ -370,7 +391,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
370
391
  this._storage = _storage;
371
392
  this.defaultMaxConsecutiveReconnects = 15;
372
393
  this._orderSequentiallyCalls = 0;
373
- this._flushMode = runtime_definitions_1.FlushMode.TurnBased;
374
394
  this.needsFlush = false;
375
395
  this.flushTrigger = false;
376
396
  this.paused = false;
@@ -453,12 +473,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
453
473
  ((_c = runtimeOptions.useDataStoreAliasing) !== null && _c !== void 0 ? _c : false);
454
474
  this._maxOpSizeInBytes = ((_d = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _d !== void 0 ? _d : defaultMaxOpSizeInBytes);
455
475
  this.maxConsecutiveReconnects = (_e = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _e !== void 0 ? _e : this.defaultMaxConsecutiveReconnects;
456
- this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
457
- /**
458
- * Returns the timestamp of the last message seen by this client. This is used by garbage collector as
459
- * the current reference timestamp for tracking unreferenced objects.
460
- */
461
- () => { 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) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
476
+ this._flushMode = runtimeOptions.flushMode;
477
+ this.garbageCollector = garbageCollection_1.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) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
462
478
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
463
479
  this.summarizerNode = runtime_utils_1.createRootSummarizerNodeWithGC(telemetry_utils_1.ChildLogger.create(this.logger, "SummarizerNode"),
464
480
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
@@ -479,7 +495,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
479
495
  if (this.context.baseSnapshot) {
480
496
  this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
481
497
  }
482
- this.dataStores = new dataStores_1.DataStores(dataStores_1.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);
498
+ this.dataStores = new dataStores_1.DataStores(dataStores_1.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);
483
499
  this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
484
500
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
485
501
  this.deltaSender = this.deltaManager;
@@ -567,6 +583,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
567
583
  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 }));
568
584
  connectionTelemetry_1.ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
569
585
  batchTracker_1.BindBatchTracker(this, this.logger);
586
+ this.opTracker = new opTelemetry_1.OpTracker(this.deltaManager, this.mc.config.getBoolean(disableOpTrackingKey) === true);
570
587
  }
571
588
  get IContainerRuntime() { return this; }
572
589
  get IFluidRouter() { return this; }
@@ -589,7 +606,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
589
606
  runtimeVersion: packageVersion_1.pkgVersion,
590
607
  },
591
608
  });
592
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
609
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, } = runtimeOptions;
593
610
  // We pack at data store level only. If isolated channels are disabled,
594
611
  // then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
595
612
  const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
@@ -660,6 +677,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
660
677
  gcOptions,
661
678
  loadSequenceNumberVerification,
662
679
  useDataStoreAliasing,
680
+ flushMode,
663
681
  }, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
664
682
  return runtime;
665
683
  }
@@ -878,12 +896,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
878
896
  const electedSummarizerContent = JSON.stringify((_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.serialize());
879
897
  runtime_utils_1.addBlobToSummary(summaryTree, summaryFormat_1.electedSummarizerBlobName, electedSummarizerContent);
880
898
  }
881
- const snapshot = this.blobManager.snapshot();
899
+ const summary = this.blobManager.summarize();
882
900
  // Some storage (like git) doesn't allow empty tree, so we can omit it.
883
901
  // and the blob manager can handle the tree not existing when loading
884
- if (snapshot.entries.length !== 0) {
885
- const blobsTree = runtime_utils_1.convertToSummaryTree(snapshot, false);
886
- runtime_utils_1.addTreeToSummary(summaryTree, summaryFormat_1.blobsTreeName, blobsTree);
902
+ if (Object.keys(summary.summary.tree).length > 0) {
903
+ runtime_utils_1.addTreeToSummary(summaryTree, summaryFormat_1.blobsTreeName, summary);
887
904
  }
888
905
  if (this.garbageCollector.writeDataAtRoot) {
889
906
  const gcSummary = this.garbageCollector.summarize();
@@ -1072,6 +1089,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1072
1089
  if (mode === this._flushMode) {
1073
1090
  return;
1074
1091
  }
1092
+ this.mc.logger.sendTelemetryEvent({
1093
+ eventName: "FlushMode Updated",
1094
+ old: this._flushMode,
1095
+ new: mode,
1096
+ });
1075
1097
  // Flush any pending batches if switching to immediate
1076
1098
  if (mode === runtime_definitions_1.FlushMode.Immediate) {
1077
1099
  this.flush();
@@ -1144,7 +1166,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1144
1166
  */
1145
1167
  async createRootDataStoreLegacy(pkg, rootDataStoreId) {
1146
1168
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1147
- fluidDataStore.bindToContext();
1169
+ // back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel. For
1170
+ // older versions, we still have to call bindToContext.
1171
+ if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
1172
+ fluidDataStore.makeVisibleAndAttachGraph();
1173
+ }
1174
+ else {
1175
+ fluidDataStore.bindToContext();
1176
+ }
1148
1177
  return fluidDataStore;
1149
1178
  }
1150
1179
  async createRootDataStore(pkg, rootDataStoreId) {
@@ -1198,7 +1227,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1198
1227
  async _createDataStoreWithPropsLegacy(pkg, props, id = uuid_1.v4(), isRoot = false) {
1199
1228
  const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
1200
1229
  if (isRoot) {
1201
- fluidDataStore.bindToContext();
1230
+ // back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
1231
+ // For older versions, we still have to call bindToContext.
1232
+ if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
1233
+ fluidDataStore.makeVisibleAndAttachGraph();
1234
+ }
1235
+ else {
1236
+ fluidDataStore.bindToContext();
1237
+ }
1238
+ this.logger.sendTelemetryEvent({
1239
+ eventName: "Root datastore with props",
1240
+ hasProps: props !== undefined,
1241
+ });
1202
1242
  }
1203
1243
  return dataStore_1.channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
1204
1244
  }
@@ -1322,9 +1362,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1322
1362
  if (runGC) {
1323
1363
  gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1324
1364
  }
1325
- const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
1326
- common_utils_1.assert(summarizeResult.summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1327
- return Object.assign(Object.assign({}, summarizeResult), { gcStats });
1365
+ const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState);
1366
+ common_utils_1.assert(summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1367
+ return { stats, summary, gcStats };
1328
1368
  }
1329
1369
  /**
1330
1370
  * Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
@@ -1341,7 +1381,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1341
1381
  * @param fullGC - true to bypass optimizations and force full generation of GC data.
1342
1382
  */
1343
1383
  async getGCData(fullGC) {
1344
- return this.dataStores.getGCData(fullGC);
1384
+ const builder = new garbage_collector_1.GCDataBuilder();
1385
+ const dsGCData = await this.dataStores.getGCData(fullGC);
1386
+ builder.addNodes(dsGCData.gcNodes);
1387
+ const blobsGCData = this.blobManager.getGCData(fullGC);
1388
+ builder.addNodes(blobsGCData.gcNodes);
1389
+ return builder.getGCData();
1345
1390
  }
1346
1391
  /**
1347
1392
  * Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
@@ -1355,7 +1400,78 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1355
1400
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1356
1401
  // always referenced, so the used routes is only self-route (empty string).
1357
1402
  this.summarizerNode.updateUsedRoutes([""]);
1358
- return this.dataStores.updateUsedRoutes(usedRoutes, gcTimestamp);
1403
+ const dataStoreUsedRoutes = [];
1404
+ for (const route of usedRoutes) {
1405
+ if (route.split("/")[1] !== blobManager_1.BlobManager.basePath) {
1406
+ dataStoreUsedRoutes.push(route);
1407
+ }
1408
+ }
1409
+ return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes, gcTimestamp);
1410
+ }
1411
+ /**
1412
+ * When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
1413
+ * scenarios with accessing deleted content.
1414
+ * @param unusedRoutes - The routes that are unused in all data stores in this Container.
1415
+ */
1416
+ deleteUnusedRoutes(unusedRoutes) {
1417
+ const blobManagerUnusedRoutes = [];
1418
+ const dataStoreUnusedRoutes = [];
1419
+ for (const route of unusedRoutes) {
1420
+ if (this.isBlobPath(route)) {
1421
+ blobManagerUnusedRoutes.push(route);
1422
+ }
1423
+ else {
1424
+ dataStoreUnusedRoutes.push(route);
1425
+ }
1426
+ }
1427
+ this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
1428
+ this.dataStores.deleteUnusedRoutes(dataStoreUnusedRoutes);
1429
+ }
1430
+ /**
1431
+ * Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
1432
+ */
1433
+ getCurrentReferenceTimestampMs() {
1434
+ var _a, _b, _c;
1435
+ // Use the timestamp of the last message seen by this client as that is server generated. If no messages have
1436
+ // been processed, use the timestamp of the message from the last summary.
1437
+ 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;
1438
+ }
1439
+ /**
1440
+ * Returns the type of the GC node. Currently, there are nodes that belong to data store and nodes that belong
1441
+ * to the blob manager.
1442
+ */
1443
+ getNodeType(nodePath) {
1444
+ if (this.isBlobPath(nodePath)) {
1445
+ return garbageCollection_1.GCNodeType.Blob;
1446
+ }
1447
+ if (this.dataStores.isDataStoreNode(nodePath)) {
1448
+ return garbageCollection_1.GCNodeType.DataStore;
1449
+ }
1450
+ // Root node ("/") and DDS nodes belong to "Other" node types.
1451
+ return garbageCollection_1.GCNodeType.Other;
1452
+ }
1453
+ /**
1454
+ * Called by GC to retrieve the package path of the node with the given path. The node should belong to a
1455
+ * data store or an attachment blob.
1456
+ */
1457
+ getGCNodePackagePath(nodePath) {
1458
+ // If the node is a blob, return "_blobs" as the package path.
1459
+ if (this.isBlobPath(nodePath)) {
1460
+ return ["_blobs"];
1461
+ }
1462
+ const dataStorePkgPath = this.dataStores.getDataStorePackagePath(nodePath);
1463
+ common_utils_1.assert(dataStorePkgPath !== undefined, 0x2d6 /* "Package path requested for unknown node type." */);
1464
+ return dataStorePkgPath;
1465
+ }
1466
+ /**
1467
+ * Returns whether a given path is for attachment blobs that are in the format - "/BlobManager.basePath/...".
1468
+ */
1469
+ isBlobPath(path) {
1470
+ const pathParts = path.split("/");
1471
+ if (pathParts.length < 2 || pathParts[1] !== blobManager_1.BlobManager.basePath) {
1472
+ return false;
1473
+ }
1474
+ return true;
1359
1475
  }
1360
1476
  /**
1361
1477
  * Runs garbage collection and updates the reference / used state of the nodes in the container.
@@ -1399,6 +1515,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1399
1515
  try {
1400
1516
  await this.deltaManager.inbound.pause();
1401
1517
  const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
1518
+ const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
1402
1519
  const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
1403
1520
  // We should be here is we haven't processed be here. If we are of if the last message's sequence number
1404
1521
  // doesn't match the last processed sequence number, log an error.
@@ -1438,7 +1555,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1438
1555
  };
1439
1556
  let continueResult = checkContinue();
1440
1557
  if (!continueResult.continue) {
1441
- return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error: continueResult.error };
1558
+ return {
1559
+ stage: "base",
1560
+ referenceSequenceNumber: summaryRefSeqNum,
1561
+ minimumSequenceNumber,
1562
+ error: continueResult.error,
1563
+ };
1442
1564
  }
1443
1565
  // increment summary count
1444
1566
  if (this.summaryCount !== undefined) {
@@ -1461,7 +1583,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1461
1583
  });
1462
1584
  }
1463
1585
  catch (error) {
1464
- return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error };
1586
+ return {
1587
+ stage: "base",
1588
+ referenceSequenceNumber: summaryRefSeqNum,
1589
+ minimumSequenceNumber,
1590
+ error,
1591
+ };
1465
1592
  }
1466
1593
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
1467
1594
  // Now that we have generated the summary, update the message at last summary to the last message processed.
@@ -1472,9 +1599,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1472
1599
  const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[runtime_definitions_1.channelsTreeName];
1473
1600
  common_utils_1.assert(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1474
1601
  const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
1475
- 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);
1602
+ const gcSummaryTreeStats = summaryTree.tree[garbageCollection_1.gcTreeKey]
1603
+ ? runtime_utils_1.calculateStats(summaryTree.tree[garbageCollection_1.gcTreeKey])
1604
+ : undefined;
1605
+ 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);
1476
1606
  const generateSummaryData = {
1477
1607
  referenceSequenceNumber: summaryRefSeqNum,
1608
+ minimumSequenceNumber,
1478
1609
  summaryTree,
1479
1610
  summaryStats,
1480
1611
  generateDuration: trace.trace().duration,
@@ -1525,6 +1656,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1525
1656
  }
1526
1657
  const submitData = Object.assign(Object.assign({ stage: "submit" }, uploadData), { clientSequenceNumber, submitOpDuration: trace.trace().duration });
1527
1658
  this.summarizerNode.completeSummary(handle);
1659
+ this.opTracker.reset();
1528
1660
  return submitData;
1529
1661
  }
1530
1662
  finally {