@fluidframework/container-runtime 2.0.0-internal.2.2.0 → 2.0.0-internal.2.3.0

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 (195) hide show
  1. package/.eslintrc.js +19 -8
  2. package/dist/batchTracker.d.ts +1 -2
  3. package/dist/batchTracker.d.ts.map +1 -1
  4. package/dist/batchTracker.js.map +1 -1
  5. package/dist/blobManager.d.ts +44 -33
  6. package/dist/blobManager.d.ts.map +1 -1
  7. package/dist/blobManager.js +130 -97
  8. package/dist/blobManager.js.map +1 -1
  9. package/dist/containerRuntime.d.ts +39 -8
  10. package/dist/containerRuntime.d.ts.map +1 -1
  11. package/dist/containerRuntime.js +117 -61
  12. package/dist/containerRuntime.js.map +1 -1
  13. package/dist/dataStoreContext.d.ts +1 -1
  14. package/dist/dataStoreContext.d.ts.map +1 -1
  15. package/dist/dataStoreContext.js +4 -3
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/dataStores.d.ts +9 -6
  18. package/dist/dataStores.d.ts.map +1 -1
  19. package/dist/dataStores.js +30 -24
  20. package/dist/dataStores.js.map +1 -1
  21. package/dist/garbageCollection.d.ts +41 -20
  22. package/dist/garbageCollection.d.ts.map +1 -1
  23. package/dist/garbageCollection.js +205 -151
  24. package/dist/garbageCollection.js.map +1 -1
  25. package/dist/garbageCollectionConstants.d.ts +6 -3
  26. package/dist/garbageCollectionConstants.d.ts.map +1 -1
  27. package/dist/garbageCollectionConstants.js +7 -7
  28. package/dist/garbageCollectionConstants.js.map +1 -1
  29. package/dist/garbageCollectionTombstoneUtils.d.ts +13 -0
  30. package/dist/garbageCollectionTombstoneUtils.d.ts.map +1 -0
  31. package/dist/garbageCollectionTombstoneUtils.js +28 -0
  32. package/dist/garbageCollectionTombstoneUtils.js.map +1 -0
  33. package/dist/index.d.ts +0 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +1 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/opLifecycle/batchManager.d.ts +13 -1
  38. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  39. package/dist/opLifecycle/batchManager.js +35 -1
  40. package/dist/opLifecycle/batchManager.js.map +1 -1
  41. package/dist/opLifecycle/definitions.d.ts +25 -1
  42. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  43. package/dist/opLifecycle/definitions.js.map +1 -1
  44. package/dist/opLifecycle/index.d.ts +2 -2
  45. package/dist/opLifecycle/index.d.ts.map +1 -1
  46. package/dist/opLifecycle/index.js +2 -1
  47. package/dist/opLifecycle/index.js.map +1 -1
  48. package/dist/opLifecycle/opCompressor.d.ts +1 -1
  49. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  50. package/dist/opLifecycle/opCompressor.js +24 -10
  51. package/dist/opLifecycle/opCompressor.js.map +1 -1
  52. package/dist/opLifecycle/opDecompressor.d.ts +2 -1
  53. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  54. package/dist/opLifecycle/opDecompressor.js +30 -17
  55. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  56. package/dist/opLifecycle/opSplitter.d.ts +34 -2
  57. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  58. package/dist/opLifecycle/opSplitter.js +114 -5
  59. package/dist/opLifecycle/opSplitter.js.map +1 -1
  60. package/dist/opLifecycle/outbox.d.ts +5 -0
  61. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  62. package/dist/opLifecycle/outbox.js +24 -14
  63. package/dist/opLifecycle/outbox.js.map +1 -1
  64. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  65. package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
  66. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  67. package/dist/packageVersion.d.ts +1 -1
  68. package/dist/packageVersion.js +1 -1
  69. package/dist/packageVersion.js.map +1 -1
  70. package/dist/runningSummarizer.d.ts.map +1 -1
  71. package/dist/runningSummarizer.js +0 -1
  72. package/dist/runningSummarizer.js.map +1 -1
  73. package/dist/scheduleManager.d.ts +0 -1
  74. package/dist/scheduleManager.d.ts.map +1 -1
  75. package/dist/scheduleManager.js +9 -20
  76. package/dist/scheduleManager.js.map +1 -1
  77. package/dist/summarizer.d.ts +0 -1
  78. package/dist/summarizer.d.ts.map +1 -1
  79. package/dist/summarizer.js +2 -1
  80. package/dist/summarizer.js.map +1 -1
  81. package/dist/summarizerTypes.d.ts +1 -0
  82. package/dist/summarizerTypes.d.ts.map +1 -1
  83. package/dist/summarizerTypes.js.map +1 -1
  84. package/dist/summaryFormat.d.ts.map +1 -1
  85. package/dist/summaryFormat.js +1 -2
  86. package/dist/summaryFormat.js.map +1 -1
  87. package/lib/batchTracker.d.ts +1 -2
  88. package/lib/batchTracker.d.ts.map +1 -1
  89. package/lib/batchTracker.js.map +1 -1
  90. package/lib/blobManager.d.ts +44 -33
  91. package/lib/blobManager.d.ts.map +1 -1
  92. package/lib/blobManager.js +131 -98
  93. package/lib/blobManager.js.map +1 -1
  94. package/lib/containerRuntime.d.ts +39 -8
  95. package/lib/containerRuntime.d.ts.map +1 -1
  96. package/lib/containerRuntime.js +115 -59
  97. package/lib/containerRuntime.js.map +1 -1
  98. package/lib/dataStoreContext.d.ts +1 -1
  99. package/lib/dataStoreContext.d.ts.map +1 -1
  100. package/lib/dataStoreContext.js +5 -4
  101. package/lib/dataStoreContext.js.map +1 -1
  102. package/lib/dataStores.d.ts +9 -6
  103. package/lib/dataStores.d.ts.map +1 -1
  104. package/lib/dataStores.js +32 -26
  105. package/lib/dataStores.js.map +1 -1
  106. package/lib/garbageCollection.d.ts +41 -20
  107. package/lib/garbageCollection.d.ts.map +1 -1
  108. package/lib/garbageCollection.js +201 -147
  109. package/lib/garbageCollection.js.map +1 -1
  110. package/lib/garbageCollectionConstants.d.ts +6 -3
  111. package/lib/garbageCollectionConstants.d.ts.map +1 -1
  112. package/lib/garbageCollectionConstants.js +6 -6
  113. package/lib/garbageCollectionConstants.js.map +1 -1
  114. package/lib/garbageCollectionTombstoneUtils.d.ts +13 -0
  115. package/lib/garbageCollectionTombstoneUtils.d.ts.map +1 -0
  116. package/lib/garbageCollectionTombstoneUtils.js +24 -0
  117. package/lib/garbageCollectionTombstoneUtils.js.map +1 -0
  118. package/lib/index.d.ts +0 -1
  119. package/lib/index.d.ts.map +1 -1
  120. package/lib/index.js +0 -1
  121. package/lib/index.js.map +1 -1
  122. package/lib/opLifecycle/batchManager.d.ts +13 -1
  123. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  124. package/lib/opLifecycle/batchManager.js +35 -1
  125. package/lib/opLifecycle/batchManager.js.map +1 -1
  126. package/lib/opLifecycle/definitions.d.ts +25 -1
  127. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  128. package/lib/opLifecycle/definitions.js.map +1 -1
  129. package/lib/opLifecycle/index.d.ts +2 -2
  130. package/lib/opLifecycle/index.d.ts.map +1 -1
  131. package/lib/opLifecycle/index.js +1 -1
  132. package/lib/opLifecycle/index.js.map +1 -1
  133. package/lib/opLifecycle/opCompressor.d.ts +1 -1
  134. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  135. package/lib/opLifecycle/opCompressor.js +24 -10
  136. package/lib/opLifecycle/opCompressor.js.map +1 -1
  137. package/lib/opLifecycle/opDecompressor.d.ts +2 -1
  138. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  139. package/lib/opLifecycle/opDecompressor.js +30 -17
  140. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  141. package/lib/opLifecycle/opSplitter.d.ts +34 -2
  142. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  143. package/lib/opLifecycle/opSplitter.js +112 -4
  144. package/lib/opLifecycle/opSplitter.js.map +1 -1
  145. package/lib/opLifecycle/outbox.d.ts +5 -0
  146. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  147. package/lib/opLifecycle/outbox.js +24 -14
  148. package/lib/opLifecycle/outbox.js.map +1 -1
  149. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  150. package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
  151. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  152. package/lib/packageVersion.d.ts +1 -1
  153. package/lib/packageVersion.js +1 -1
  154. package/lib/packageVersion.js.map +1 -1
  155. package/lib/runningSummarizer.d.ts.map +1 -1
  156. package/lib/runningSummarizer.js +0 -1
  157. package/lib/runningSummarizer.js.map +1 -1
  158. package/lib/scheduleManager.d.ts +0 -1
  159. package/lib/scheduleManager.d.ts.map +1 -1
  160. package/lib/scheduleManager.js +9 -20
  161. package/lib/scheduleManager.js.map +1 -1
  162. package/lib/summarizer.d.ts +0 -1
  163. package/lib/summarizer.d.ts.map +1 -1
  164. package/lib/summarizer.js +2 -1
  165. package/lib/summarizer.js.map +1 -1
  166. package/lib/summarizerTypes.d.ts +1 -0
  167. package/lib/summarizerTypes.d.ts.map +1 -1
  168. package/lib/summarizerTypes.js.map +1 -1
  169. package/lib/summaryFormat.d.ts.map +1 -1
  170. package/lib/summaryFormat.js +1 -2
  171. package/lib/summaryFormat.js.map +1 -1
  172. package/package.json +31 -27
  173. package/src/batchTracker.ts +1 -1
  174. package/src/blobManager.ts +146 -103
  175. package/src/containerRuntime.ts +166 -65
  176. package/src/dataStoreContext.ts +5 -5
  177. package/src/dataStores.ts +40 -30
  178. package/src/garbageCollection.ts +254 -183
  179. package/src/garbageCollectionConstants.ts +7 -6
  180. package/src/garbageCollectionTombstoneUtils.ts +31 -0
  181. package/src/index.ts +0 -5
  182. package/src/opLifecycle/batchManager.ts +59 -1
  183. package/src/opLifecycle/definitions.ts +27 -1
  184. package/src/opLifecycle/index.ts +2 -1
  185. package/src/opLifecycle/opCompressor.ts +29 -12
  186. package/src/opLifecycle/opDecompressor.ts +39 -18
  187. package/src/opLifecycle/opSplitter.ts +141 -7
  188. package/src/opLifecycle/outbox.ts +32 -16
  189. package/src/opLifecycle/remoteMessageProcessor.ts +19 -3
  190. package/src/packageVersion.ts +1 -1
  191. package/src/runningSummarizer.ts +0 -1
  192. package/src/scheduleManager.ts +19 -30
  193. package/src/summarizer.ts +1 -1
  194. package/src/summarizerTypes.ts +1 -0
  195. package/src/summaryFormat.ts +1 -2
@@ -5,7 +5,7 @@ import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
5
5
  import { readAndParse } from "@fluidframework/driver-utils";
6
6
  import { DataCorruptionError, DataProcessingError, GenericError, UsageError, } from "@fluidframework/container-utils";
7
7
  import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
8
- import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
8
+ import { FlushMode, gcTreeKey, channelsTreeName, } from "@fluidframework/runtime-definitions";
9
9
  import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, TelemetryContext, } from "@fluidframework/runtime-utils";
10
10
  import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
11
11
  import { v4 as uuid } from "uuid";
@@ -25,7 +25,6 @@ import { SummarizerClientElection, summarizerClientType } from "./summarizerClie
25
25
  import { formExponentialFn, Throttler } from "./throttler";
26
26
  import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
27
27
  import { GarbageCollector, GCNodeType, } from "./garbageCollection";
28
- import { gcTreeKey, } from "./garbageCollectionConstants";
29
28
  import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
30
29
  import { BindBatchTracker } from "./batchTracker";
31
30
  import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
@@ -213,12 +212,17 @@ export class ContainerRuntime extends TypedEventEmitter {
213
212
  this.nextSummaryNumber = loadSummaryNumber + 1;
214
213
  this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
215
214
  this._connected = this.context.connected;
216
- this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
217
- this.handleContext = new ContainerFluidHandleContext("", this);
218
215
  this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
216
+ const opSplitter = new OpSplitter(chunks, this.context.submitBatchFn, this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompressionChunking") === true ?
217
+ Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
218
+ this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor());
219
+ this.handleContext = new ContainerFluidHandleContext("", this);
219
220
  if (this.summaryConfiguration.state === "enabled") {
220
221
  this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
221
222
  }
223
+ this.enableOpReentryCheck = runtimeOptions.enableOpReentryCheck === true
224
+ // Allow for a break-glass config to override the options
225
+ && this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
222
226
  this.summariesDisabled = this.isSummariesDisabled();
223
227
  this.heuristicsDisabled = this.isHeuristicsDisabled();
224
228
  this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
@@ -267,16 +271,20 @@ export class ContainerRuntime extends TypedEventEmitter {
267
271
  throwOnFailure: true,
268
272
  // If GC should not run, let the summarizer node know so that it does not track GC state.
269
273
  gcDisabled: !this.garbageCollector.shouldRunGC,
270
- });
274
+ },
275
+ // Function to get GC data if needed. This will always be called by the root summarizer node to get GC data.
276
+ async (fullGC) => this.getGCDataInternal(fullGC),
277
+ // Function to get the GC details from the base snapshot we loaded from.
278
+ async () => this.garbageCollector.getBaseGCDetails());
271
279
  if (baseSnapshot) {
272
280
  this.summarizerNode.updateBaseSummaryState(baseSnapshot);
273
281
  }
274
282
  this.dataStores = new DataStores(getSummaryForDatastores(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));
275
- this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
283
+ this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
276
284
  if (!this.disposed) {
277
- this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
285
+ this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { localId, blobId });
278
286
  }
279
- }, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
287
+ }, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (fromPath, toPath) => this.garbageCollector.addedOutboundReference(fromPath, toPath), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
280
288
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
281
289
  this.pendingStateManager = new PendingStateManager({
282
290
  applyStashedOp: this.applyStashedOp.bind(this),
@@ -288,15 +296,23 @@ export class ContainerRuntime extends TypedEventEmitter {
288
296
  rollback: this.rollback.bind(this),
289
297
  orderSequentially: this.orderSequentially.bind(this),
290
298
  }, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
299
+ const compressionOptions = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompression") === true ?
300
+ {
301
+ minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
302
+ compressionAlgorithm: CompressionAlgorithms.lz4
303
+ } : runtimeOptions.compressionOptions;
291
304
  this.outbox = new Outbox({
292
305
  shouldSend: () => this.canSendOps(),
293
306
  pendingStateManager: this.pendingStateManager,
294
307
  containerContext: this.context,
295
308
  compressor: new OpCompressor(this.mc.logger),
309
+ splitter: opSplitter,
296
310
  config: {
297
- compressionOptions: runtimeOptions.compressionOptions,
311
+ compressionOptions,
298
312
  maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
313
+ enableOpReentryCheck: this.enableOpReentryCheck,
299
314
  },
315
+ logger: this.mc.logger,
300
316
  });
301
317
  this.context.quorum.on("removeMember", (clientId) => {
302
318
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
@@ -384,7 +400,7 @@ export class ContainerRuntime extends TypedEventEmitter {
384
400
  * allows mixin classes to leverage this method to define their own async initializer.
385
401
  */
386
402
  static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
387
- var _a, _b, _c;
403
+ var _a, _b, _c, _d;
388
404
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
389
405
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
390
406
  const backCompatContext = context;
@@ -397,7 +413,7 @@ export class ContainerRuntime extends TypedEventEmitter {
397
413
  const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
398
414
  minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
399
415
  compressionAlgorithm: CompressionAlgorithms.lz4
400
- }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
416
+ }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, chunkSizeInBytes = Number.POSITIVE_INFINITY, enableOpReentryCheck = false, } = runtimeOptions;
401
417
  const pendingRuntimeState = context.pendingLocalState;
402
418
  const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
403
419
  const storage = !pendingRuntimeState ?
@@ -442,7 +458,9 @@ export class ContainerRuntime extends TypedEventEmitter {
442
458
  logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
443
459
  }
444
460
  else {
461
+ // Call both close and dispose as close implementation will no longer dispose runtime in future (2.0.0-internal.3.0.0)
445
462
  context.closeFn(error);
463
+ (_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
446
464
  }
447
465
  }
448
466
  }
@@ -454,6 +472,8 @@ export class ContainerRuntime extends TypedEventEmitter {
454
472
  enableOfflineLoad,
455
473
  compressionOptions,
456
474
  maxBatchSizeInBytes,
475
+ chunkSizeInBytes,
476
+ enableOpReentryCheck,
457
477
  }, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
458
478
  if (pendingRuntimeState) {
459
479
  await runtime.processSavedOps(pendingRuntimeState);
@@ -483,8 +503,18 @@ export class ContainerRuntime extends TypedEventEmitter {
483
503
  // eslint-disable-next-line @typescript-eslint/unbound-method
484
504
  return this.reSubmit;
485
505
  }
506
+ get disposeFn() {
507
+ var _a;
508
+ // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
509
+ return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
510
+ }
486
511
  get closeFn() {
487
- return this.context.closeFn;
512
+ // Also call disposeFn to retain functionality of runtime being disposed on close
513
+ return (error) => {
514
+ var _a, _b;
515
+ this.context.closeFn(error);
516
+ (_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
517
+ };
488
518
  }
489
519
  get flushMode() {
490
520
  return this._flushMode;
@@ -862,9 +892,7 @@ export class ContainerRuntime extends TypedEventEmitter {
862
892
  if (reconnection) {
863
893
  this.consecutiveReconnects++;
864
894
  if (!this.shouldContinueReconnecting()) {
865
- this.closeFn(DataProcessingError.create(
866
- // eslint-disable-next-line max-len
867
- "Runtime detected too many reconnects with no progress syncing local ops. Batch of ops is likely too large (over 1Mb)", "setConnectionState", undefined, {
895
+ this.closeFn(DataProcessingError.create("Runtime detected too many reconnects with no progress syncing local ops. Batch of ops is likely too large (over 1Mb)", "setConnectionState", undefined, {
868
896
  dataLoss: 1,
869
897
  attempts: this.consecutiveReconnects,
870
898
  pendingMessages: this.pendingStateManager.pendingMessagesCount,
@@ -898,7 +926,7 @@ export class ContainerRuntime extends TypedEventEmitter {
898
926
  this.scheduleManager.beforeOpProcessing(message);
899
927
  try {
900
928
  let localOpMetadata;
901
- if (local && runtimeMessage) {
929
+ if (local && runtimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
902
930
  localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
903
931
  }
904
932
  // If there are no more pending messages after processing a local message,
@@ -1015,6 +1043,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1015
1043
  }
1016
1044
  orderSequentially(callback) {
1017
1045
  let checkpoint;
1046
+ let result;
1018
1047
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1019
1048
  // Note: we are not touching this.pendingAttachBatch here, for two reasons:
1020
1049
  // 1. It would not help, as we flush attach ops as they become available.
@@ -1023,7 +1052,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1023
1052
  }
1024
1053
  try {
1025
1054
  this._orderSequentiallyCalls++;
1026
- callback();
1055
+ result = callback();
1027
1056
  }
1028
1057
  catch (error) {
1029
1058
  if (checkpoint) {
@@ -1051,6 +1080,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1051
1080
  if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
1052
1081
  this.flush();
1053
1082
  }
1083
+ return result;
1054
1084
  }
1055
1085
  async createDataStore(pkg) {
1056
1086
  const internalId = uuid();
@@ -1216,6 +1246,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1216
1246
  async updateStateBeforeGC() {
1217
1247
  return this.dataStores.updateStateBeforeGC();
1218
1248
  }
1249
+ async getGCDataInternal(fullGC) {
1250
+ return this.dataStores.getGCData(fullGC);
1251
+ }
1219
1252
  /**
1220
1253
  * Implementation of IGarbageCollectionRuntime::getGCData.
1221
1254
  * Generates and returns the GC data for this container.
@@ -1223,7 +1256,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1223
1256
  */
1224
1257
  async getGCData(fullGC) {
1225
1258
  const builder = new GCDataBuilder();
1226
- const dsGCData = await this.dataStores.getGCData(fullGC);
1259
+ const dsGCData = await this.summarizerNode.getGCData(fullGC);
1227
1260
  builder.addNodes(dsGCData.gcNodes);
1228
1261
  const blobsGCData = this.blobManager.getGCData(fullGC);
1229
1262
  builder.addNodes(blobsGCData.gcNodes);
@@ -1239,39 +1272,26 @@ export class ContainerRuntime extends TypedEventEmitter {
1239
1272
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1240
1273
  // always referenced, so the used routes is only self-route (empty string).
1241
1274
  this.summarizerNode.updateUsedRoutes([""]);
1242
- const blobManagerUsedRoutes = [];
1243
- const dataStoreUsedRoutes = [];
1244
- for (const route of usedRoutes) {
1245
- if (this.isBlobPath(route)) {
1246
- blobManagerUsedRoutes.push(route);
1247
- }
1248
- else {
1249
- dataStoreUsedRoutes.push(route);
1250
- }
1251
- }
1252
- this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
1253
- this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
1275
+ const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
1276
+ this.dataStores.updateUsedRoutes(dataStoreRoutes);
1254
1277
  }
1255
1278
  /**
1256
- * This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
1257
- * tombstones.
1258
- * @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
1259
- * @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
1260
- * are deleted.
1279
+ * This is called to update objects whose routes are unused.
1280
+ * @param unusedRoutes - Data store and attachment blob routes that are unused in this Container.
1261
1281
  */
1262
- updateUnusedRoutes(unusedRoutes, tombstone) {
1263
- const blobManagerUnusedRoutes = [];
1264
- const dataStoreUnusedRoutes = [];
1265
- for (const route of unusedRoutes) {
1266
- if (this.isBlobPath(route)) {
1267
- blobManagerUnusedRoutes.push(route);
1268
- }
1269
- else {
1270
- dataStoreUnusedRoutes.push(route);
1271
- }
1272
- }
1273
- this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
1274
- this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
1282
+ updateUnusedRoutes(unusedRoutes) {
1283
+ const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(unusedRoutes);
1284
+ this.blobManager.updateUnusedRoutes(blobManagerRoutes);
1285
+ this.dataStores.updateUnusedRoutes(dataStoreRoutes);
1286
+ }
1287
+ /**
1288
+ * This is called to update objects that are tombstones.
1289
+ * @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
1290
+ */
1291
+ updateTombstonedRoutes(tombstonedRoutes) {
1292
+ const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(tombstonedRoutes);
1293
+ this.blobManager.updateTombstonedRoutes(blobManagerRoutes);
1294
+ this.dataStores.updateTombstonedRoutes(dataStoreRoutes);
1275
1295
  }
1276
1296
  /**
1277
1297
  * Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
@@ -1300,7 +1320,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1300
1320
  async getGCNodePackagePath(nodePath) {
1301
1321
  switch (this.getNodeType(nodePath)) {
1302
1322
  case GCNodeType.Blob:
1303
- return ["_blobs"];
1323
+ return [BlobManager.basePath];
1304
1324
  case GCNodeType.DataStore:
1305
1325
  case GCNodeType.SubDataStore:
1306
1326
  return this.dataStores.getDataStorePackagePath(nodePath);
@@ -1318,6 +1338,25 @@ export class ContainerRuntime extends TypedEventEmitter {
1318
1338
  }
1319
1339
  return true;
1320
1340
  }
1341
+ /**
1342
+ * From a given list of routes, separate and return routes that belong to blob manager and data stores.
1343
+ * @param routes - A list of routes that can belong to data stores or blob manager.
1344
+ * @returns - Two route lists - One that contains routes for blob manager and another one that contains routes
1345
+ * for data stores.
1346
+ */
1347
+ getDataStoreAndBlobManagerRoutes(routes) {
1348
+ const blobManagerRoutes = [];
1349
+ const dataStoreRoutes = [];
1350
+ for (const route of routes) {
1351
+ if (this.isBlobPath(route)) {
1352
+ blobManagerRoutes.push(route);
1353
+ }
1354
+ else {
1355
+ dataStoreRoutes.push(route);
1356
+ }
1357
+ }
1358
+ return { blobManagerRoutes, dataStoreRoutes };
1359
+ }
1321
1360
  /**
1322
1361
  * Runs garbage collection and updates the reference / used state of the nodes in the container.
1323
1362
  * @returns the statistics of the garbage collection run; undefined if GC did not run.
@@ -1389,7 +1428,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1389
1428
  if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
1390
1429
  return {
1391
1430
  continue: false,
1392
- // eslint-disable-next-line max-len
1393
1431
  error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
1394
1432
  };
1395
1433
  }
@@ -1397,7 +1435,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1397
1435
  if (lastAck !== this.summaryCollection.latestAck) {
1398
1436
  return {
1399
1437
  continue: false,
1400
- // eslint-disable-next-line max-len
1401
1438
  error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
1402
1439
  };
1403
1440
  }
@@ -1612,7 +1649,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1612
1649
  else if (!this.flushMicroTaskExists) {
1613
1650
  this.flushMicroTaskExists = true;
1614
1651
  // Queue a microtask to detect the end of the turn and force a flush.
1615
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1616
1652
  Promise.resolve().then(() => {
1617
1653
  this.flushMicroTaskExists = false;
1618
1654
  this.flush();
@@ -1705,13 +1741,32 @@ export class ContainerRuntime extends TypedEventEmitter {
1705
1741
  // It should only be done by the summarizerNode, if required.
1706
1742
  // When fetching from storage we will always get the latest version and do not use the ackHandle.
1707
1743
  const snapshotTreeFetcher = async () => {
1708
- const fetchResult = await this.fetchSnapshotFromStorage(null, summaryLogger, {
1744
+ const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
1709
1745
  eventName: "RefreshLatestSummaryGetSnapshot",
1710
1746
  ackHandle,
1711
1747
  summaryRefSeq,
1712
1748
  fetchLatest: true,
1713
1749
  });
1714
1750
  const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
1751
+ /**
1752
+ * If the fetched snapshot is older than the one for which the ack was received, close the container.
1753
+ * This should never happen because an ack should be sent after the latest summary is updated in the server.
1754
+ * However, there are couple of scenarios where it's possible:
1755
+ * 1. A file was modified externally resulting in modifying the snapshot's sequence number. This can lead to
1756
+ * the document being unusable and we should not proceed.
1757
+ * 2. The server DB failed after the ack was sent which may delete the corresponding snapshot. Ideally, in
1758
+ * such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
1759
+ * state.
1760
+ */
1761
+ if (latestSnapshotRefSeq < summaryRefSeq) {
1762
+ const error = DataProcessingError.create("Fetched snapshot is older than the received ack", "RefreshLatestSummaryAck", undefined /* sequencedMessage */, {
1763
+ ackHandle,
1764
+ summaryRefSeq,
1765
+ latestSnapshotRefSeq,
1766
+ });
1767
+ this.closeFn(error);
1768
+ throw error;
1769
+ }
1715
1770
  summaryLogger.sendTelemetryEvent({
1716
1771
  eventName: "LatestSummaryRetrieved",
1717
1772
  ackHandle,
@@ -1725,7 +1780,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1725
1780
  };
1726
1781
  const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
1727
1782
  // Notify the garbage collector so it can update its latest summary state.
1728
- await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
1783
+ await this.garbageCollector.refreshLatestSummary(result, proposalHandle, summaryRefSeq, readAndParseBlob);
1729
1784
  }
1730
1785
  /**
1731
1786
  * Fetches the latest snapshot from storage and uses it to refresh SummarizerNode's
@@ -1734,22 +1789,22 @@ export class ContainerRuntime extends TypedEventEmitter {
1734
1789
  * @returns downloaded snapshot's reference sequence number
1735
1790
  */
1736
1791
  async refreshLatestSummaryAckFromServer(summaryLogger) {
1737
- const { snapshotTree, versionId } = await this.fetchSnapshotFromStorage(null, summaryLogger, {
1792
+ const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
1738
1793
  eventName: "RefreshLatestSummaryGetSnapshot",
1739
1794
  fetchLatest: true,
1740
- }, FetchSource.noCache);
1795
+ });
1741
1796
  const readAndParseBlob = async (id) => readAndParse(this.storage, id);
1742
1797
  const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
1743
1798
  const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
1744
1799
  // Notify the garbage collector so it can update its latest summary state.
1745
- await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
1800
+ await this.garbageCollector.refreshLatestSummary(result, undefined, latestSnapshotRefSeq, readAndParseBlob);
1746
1801
  return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
1747
1802
  }
1748
- async fetchSnapshotFromStorage(versionId, logger, event, fetchSource) {
1803
+ async fetchLatestSnapshotFromStorage(logger, event) {
1749
1804
  return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
1750
1805
  const stats = {};
1751
1806
  const trace = Trace.start();
1752
- const versions = await this.storage.getVersions(versionId, 1, "refreshLatestSummaryAckFromServer", fetchSource);
1807
+ const versions = await this.storage.getVersions(null, 1, "refreshLatestSummaryAckFromServer", FetchSource.noCache);
1753
1808
  assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
1754
1809
  stats.getVersionDuration = trace.trace().duration;
1755
1810
  const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
@@ -1865,6 +1920,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1865
1920
  const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
1866
1921
  // TODO: remove cast to any when actual event is determined
1867
1922
  deltaManager.on("closed", reject);
1923
+ deltaManager.on("disposed", reject);
1868
1924
  // If we already reached target sequence number, simply resolve the promise.
1869
1925
  if (deltaManager.lastSequenceNumber >= targetSeq) {
1870
1926
  resolve();