@fluidframework/container-runtime 2.0.0-internal.2.2.1 → 2.0.0-internal.2.3.1

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 +45 -34
  6. package/dist/blobManager.d.ts.map +1 -1
  7. package/dist/blobManager.js +135 -102
  8. package/dist/blobManager.js.map +1 -1
  9. package/dist/containerRuntime.d.ts +54 -8
  10. package/dist/containerRuntime.d.ts.map +1 -1
  11. package/dist/containerRuntime.js +143 -72
  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 +6 -8
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/dataStores.d.ts +12 -9
  18. package/dist/dataStores.d.ts.map +1 -1
  19. package/dist/dataStores.js +41 -35
  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 -150
  24. package/dist/garbageCollection.js.map +1 -1
  25. package/dist/garbageCollectionConstants.d.ts +7 -3
  26. package/dist/garbageCollectionConstants.d.ts.map +1 -1
  27. package/dist/garbageCollectionConstants.js +10 -8
  28. package/dist/garbageCollectionConstants.js.map +1 -1
  29. package/dist/garbageCollectionTombstoneUtils.d.ts +14 -0
  30. package/dist/garbageCollectionTombstoneUtils.d.ts.map +1 -0
  31. package/dist/garbageCollectionTombstoneUtils.js +23 -0
  32. package/dist/garbageCollectionTombstoneUtils.js.map +1 -0
  33. package/dist/index.d.ts +1 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +3 -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 +45 -34
  91. package/lib/blobManager.d.ts.map +1 -1
  92. package/lib/blobManager.js +137 -104
  93. package/lib/blobManager.js.map +1 -1
  94. package/lib/containerRuntime.d.ts +54 -8
  95. package/lib/containerRuntime.d.ts.map +1 -1
  96. package/lib/containerRuntime.js +140 -69
  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 +7 -9
  101. package/lib/dataStoreContext.js.map +1 -1
  102. package/lib/dataStores.d.ts +12 -9
  103. package/lib/dataStores.d.ts.map +1 -1
  104. package/lib/dataStores.js +44 -38
  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 -146
  109. package/lib/garbageCollection.js.map +1 -1
  110. package/lib/garbageCollectionConstants.d.ts +7 -3
  111. package/lib/garbageCollectionConstants.d.ts.map +1 -1
  112. package/lib/garbageCollectionConstants.js +9 -7
  113. package/lib/garbageCollectionConstants.js.map +1 -1
  114. package/lib/garbageCollectionTombstoneUtils.d.ts +14 -0
  115. package/lib/garbageCollectionTombstoneUtils.d.ts.map +1 -0
  116. package/lib/garbageCollectionTombstoneUtils.js +19 -0
  117. package/lib/garbageCollectionTombstoneUtils.js.map +1 -0
  118. package/lib/index.d.ts +1 -2
  119. package/lib/index.d.ts.map +1 -1
  120. package/lib/index.js +1 -2
  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 +20 -19
  173. package/src/batchTracker.ts +1 -1
  174. package/src/blobManager.ts +159 -111
  175. package/src/containerRuntime.ts +202 -73
  176. package/src/dataStoreContext.ts +15 -16
  177. package/src/dataStores.ts +61 -45
  178. package/src/garbageCollection.ts +258 -183
  179. package/src/garbageCollectionConstants.ts +10 -7
  180. package/src/garbageCollectionTombstoneUtils.ts +28 -0
  181. package/src/index.ts +2 -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";
@@ -76,6 +75,17 @@ export var RuntimeHeaders;
76
75
  /** True if the request is coming from an IFluidHandle. */
77
76
  RuntimeHeaders["viaHandle"] = "viaHandle";
78
77
  })(RuntimeHeaders || (RuntimeHeaders = {}));
78
+ /** True if a tombstoned object should be returned without erroring */
79
+ export const AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
80
+ /** Tombstone error responses will have this header set to true */
81
+ export const TombstoneResponseHeaderKey = "isTombstoned";
82
+ /** Default values for Runtime Headers */
83
+ export const defaultRuntimeHeaderData = {
84
+ wait: true,
85
+ externalRequest: false,
86
+ viaHandle: false,
87
+ allowTombstone: false,
88
+ };
79
89
  /**
80
90
  * Available compression algorithms for op compression.
81
91
  */
@@ -213,12 +223,17 @@ export class ContainerRuntime extends TypedEventEmitter {
213
223
  this.nextSummaryNumber = loadSummaryNumber + 1;
214
224
  this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
215
225
  this._connected = this.context.connected;
216
- this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
217
- this.handleContext = new ContainerFluidHandleContext("", this);
218
226
  this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
227
+ const opSplitter = new OpSplitter(chunks, this.context.submitBatchFn, this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompressionChunking") === true ?
228
+ Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
229
+ this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor());
230
+ this.handleContext = new ContainerFluidHandleContext("", this);
219
231
  if (this.summaryConfiguration.state === "enabled") {
220
232
  this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
221
233
  }
234
+ this.enableOpReentryCheck = runtimeOptions.enableOpReentryCheck === true
235
+ // Allow for a break-glass config to override the options
236
+ && this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
222
237
  this.summariesDisabled = this.isSummariesDisabled();
223
238
  this.heuristicsDisabled = this.isHeuristicsDisabled();
224
239
  this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
@@ -267,16 +282,20 @@ export class ContainerRuntime extends TypedEventEmitter {
267
282
  throwOnFailure: true,
268
283
  // If GC should not run, let the summarizer node know so that it does not track GC state.
269
284
  gcDisabled: !this.garbageCollector.shouldRunGC,
270
- });
285
+ },
286
+ // Function to get GC data if needed. This will always be called by the root summarizer node to get GC data.
287
+ async (fullGC) => this.getGCDataInternal(fullGC),
288
+ // Function to get the GC details from the base snapshot we loaded from.
289
+ async () => this.garbageCollector.getBaseGCDetails());
271
290
  if (baseSnapshot) {
272
291
  this.summarizerNode.updateBaseSummaryState(baseSnapshot);
273
292
  }
274
293
  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) => {
294
+ this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
276
295
  if (!this.disposed) {
277
- this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
296
+ this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { localId, blobId });
278
297
  }
279
- }, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
298
+ }, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (fromPath, toPath) => this.garbageCollector.addedOutboundReference(fromPath, toPath), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
280
299
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
281
300
  this.pendingStateManager = new PendingStateManager({
282
301
  applyStashedOp: this.applyStashedOp.bind(this),
@@ -288,15 +307,23 @@ export class ContainerRuntime extends TypedEventEmitter {
288
307
  rollback: this.rollback.bind(this),
289
308
  orderSequentially: this.orderSequentially.bind(this),
290
309
  }, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
310
+ const compressionOptions = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompression") === true ?
311
+ {
312
+ minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
313
+ compressionAlgorithm: CompressionAlgorithms.lz4
314
+ } : runtimeOptions.compressionOptions;
291
315
  this.outbox = new Outbox({
292
316
  shouldSend: () => this.canSendOps(),
293
317
  pendingStateManager: this.pendingStateManager,
294
318
  containerContext: this.context,
295
319
  compressor: new OpCompressor(this.mc.logger),
320
+ splitter: opSplitter,
296
321
  config: {
297
- compressionOptions: runtimeOptions.compressionOptions,
322
+ compressionOptions,
298
323
  maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
324
+ enableOpReentryCheck: this.enableOpReentryCheck,
299
325
  },
326
+ logger: this.mc.logger,
300
327
  });
301
328
  this.context.quorum.on("removeMember", (clientId) => {
302
329
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
@@ -384,7 +411,7 @@ export class ContainerRuntime extends TypedEventEmitter {
384
411
  * allows mixin classes to leverage this method to define their own async initializer.
385
412
  */
386
413
  static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
387
- var _a, _b, _c;
414
+ var _a, _b, _c, _d;
388
415
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
389
416
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
390
417
  const backCompatContext = context;
@@ -397,7 +424,7 @@ export class ContainerRuntime extends TypedEventEmitter {
397
424
  const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
398
425
  minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
399
426
  compressionAlgorithm: CompressionAlgorithms.lz4
400
- }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
427
+ }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, chunkSizeInBytes = Number.POSITIVE_INFINITY, enableOpReentryCheck = false, } = runtimeOptions;
401
428
  const pendingRuntimeState = context.pendingLocalState;
402
429
  const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
403
430
  const storage = !pendingRuntimeState ?
@@ -442,7 +469,9 @@ export class ContainerRuntime extends TypedEventEmitter {
442
469
  logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
443
470
  }
444
471
  else {
472
+ // Call both close and dispose as close implementation will no longer dispose runtime in future (2.0.0-internal.3.0.0)
445
473
  context.closeFn(error);
474
+ (_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
446
475
  }
447
476
  }
448
477
  }
@@ -454,6 +483,8 @@ export class ContainerRuntime extends TypedEventEmitter {
454
483
  enableOfflineLoad,
455
484
  compressionOptions,
456
485
  maxBatchSizeInBytes,
486
+ chunkSizeInBytes,
487
+ enableOpReentryCheck,
457
488
  }, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
458
489
  if (pendingRuntimeState) {
459
490
  await runtime.processSavedOps(pendingRuntimeState);
@@ -483,8 +514,18 @@ export class ContainerRuntime extends TypedEventEmitter {
483
514
  // eslint-disable-next-line @typescript-eslint/unbound-method
484
515
  return this.reSubmit;
485
516
  }
517
+ get disposeFn() {
518
+ var _a;
519
+ // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
520
+ return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
521
+ }
486
522
  get closeFn() {
487
- return this.context.closeFn;
523
+ // Also call disposeFn to retain functionality of runtime being disposed on close
524
+ return (error) => {
525
+ var _a, _b;
526
+ this.context.closeFn(error);
527
+ (_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
528
+ };
488
529
  }
489
530
  get flushMode() {
490
531
  return this._flushMode;
@@ -670,16 +711,20 @@ export class ContainerRuntime extends TypedEventEmitter {
670
711
  return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
671
712
  }
672
713
  async getDataStoreFromRequest(id, request) {
673
- var _a, _b, _c, _d, _e;
674
- const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
675
- ? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait]
676
- : true;
677
- const viaHandle = typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.viaHandle]) === "boolean"
678
- ? (_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.viaHandle]
679
- : false;
714
+ var _a, _b, _c, _d;
715
+ const headerData = {};
716
+ if (typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean") {
717
+ headerData.wait = request.headers[RuntimeHeaders.wait];
718
+ }
719
+ if (typeof ((_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.viaHandle]) === "boolean") {
720
+ headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
721
+ }
722
+ if (typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[AllowTombstoneRequestHeaderKey]) === "boolean") {
723
+ headerData.allowTombstone = request.headers[AllowTombstoneRequestHeaderKey];
724
+ }
680
725
  await this.dataStores.waitIfPendingAlias(id);
681
726
  const internalId = this.internalId(id);
682
- const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
727
+ const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
683
728
  /**
684
729
  * If GC should run and this an external app request with "externalRequest" header, we need to return
685
730
  * an error if the data store being requested is marked as unreferenced as per the data store's base
@@ -688,7 +733,7 @@ export class ContainerRuntime extends TypedEventEmitter {
688
733
  * This is a workaround to handle scenarios where a data store shared with an external app is deleted
689
734
  * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
690
735
  */
691
- if (((_e = request.headers) === null || _e === void 0 ? void 0 : _e[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
736
+ if (((_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
692
737
  // The data store is referenced if used routes in the base summary has a route to self.
693
738
  // Older documents may not have used routes in the summary. They are considered referenced.
694
739
  const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
@@ -862,9 +907,7 @@ export class ContainerRuntime extends TypedEventEmitter {
862
907
  if (reconnection) {
863
908
  this.consecutiveReconnects++;
864
909
  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, {
910
+ 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
911
  dataLoss: 1,
869
912
  attempts: this.consecutiveReconnects,
870
913
  pendingMessages: this.pendingStateManager.pendingMessagesCount,
@@ -898,7 +941,7 @@ export class ContainerRuntime extends TypedEventEmitter {
898
941
  this.scheduleManager.beforeOpProcessing(message);
899
942
  try {
900
943
  let localOpMetadata;
901
- if (local && runtimeMessage) {
944
+ if (local && runtimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
902
945
  localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
903
946
  }
904
947
  // If there are no more pending messages after processing a local message,
@@ -1000,7 +1043,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1000
1043
  async getRootDataStoreChannel(id, wait = true) {
1001
1044
  await this.dataStores.waitIfPendingAlias(id);
1002
1045
  const internalId = this.internalId(id);
1003
- const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
1046
+ const context = await this.dataStores.getDataStore(internalId, { wait });
1004
1047
  assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
1005
1048
  return context.realize();
1006
1049
  }
@@ -1015,6 +1058,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1015
1058
  }
1016
1059
  orderSequentially(callback) {
1017
1060
  let checkpoint;
1061
+ let result;
1018
1062
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1019
1063
  // Note: we are not touching this.pendingAttachBatch here, for two reasons:
1020
1064
  // 1. It would not help, as we flush attach ops as they become available.
@@ -1023,7 +1067,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1023
1067
  }
1024
1068
  try {
1025
1069
  this._orderSequentiallyCalls++;
1026
- callback();
1070
+ result = callback();
1027
1071
  }
1028
1072
  catch (error) {
1029
1073
  if (checkpoint) {
@@ -1051,6 +1095,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1051
1095
  if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
1052
1096
  this.flush();
1053
1097
  }
1098
+ return result;
1054
1099
  }
1055
1100
  async createDataStore(pkg) {
1056
1101
  const internalId = uuid();
@@ -1216,6 +1261,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1216
1261
  async updateStateBeforeGC() {
1217
1262
  return this.dataStores.updateStateBeforeGC();
1218
1263
  }
1264
+ async getGCDataInternal(fullGC) {
1265
+ return this.dataStores.getGCData(fullGC);
1266
+ }
1219
1267
  /**
1220
1268
  * Implementation of IGarbageCollectionRuntime::getGCData.
1221
1269
  * Generates and returns the GC data for this container.
@@ -1223,7 +1271,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1223
1271
  */
1224
1272
  async getGCData(fullGC) {
1225
1273
  const builder = new GCDataBuilder();
1226
- const dsGCData = await this.dataStores.getGCData(fullGC);
1274
+ const dsGCData = await this.summarizerNode.getGCData(fullGC);
1227
1275
  builder.addNodes(dsGCData.gcNodes);
1228
1276
  const blobsGCData = this.blobManager.getGCData(fullGC);
1229
1277
  builder.addNodes(blobsGCData.gcNodes);
@@ -1239,39 +1287,26 @@ export class ContainerRuntime extends TypedEventEmitter {
1239
1287
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1240
1288
  // always referenced, so the used routes is only self-route (empty string).
1241
1289
  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);
1290
+ const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
1291
+ this.dataStores.updateUsedRoutes(dataStoreRoutes);
1254
1292
  }
1255
1293
  /**
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.
1294
+ * This is called to update objects whose routes are unused.
1295
+ * @param unusedRoutes - Data store and attachment blob routes that are unused in this Container.
1261
1296
  */
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);
1297
+ updateUnusedRoutes(unusedRoutes) {
1298
+ const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(unusedRoutes);
1299
+ this.blobManager.updateUnusedRoutes(blobManagerRoutes);
1300
+ this.dataStores.updateUnusedRoutes(dataStoreRoutes);
1301
+ }
1302
+ /**
1303
+ * This is called to update objects that are tombstones.
1304
+ * @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
1305
+ */
1306
+ updateTombstonedRoutes(tombstonedRoutes) {
1307
+ const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(tombstonedRoutes);
1308
+ this.blobManager.updateTombstonedRoutes(blobManagerRoutes);
1309
+ this.dataStores.updateTombstonedRoutes(dataStoreRoutes);
1275
1310
  }
1276
1311
  /**
1277
1312
  * Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
@@ -1300,7 +1335,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1300
1335
  async getGCNodePackagePath(nodePath) {
1301
1336
  switch (this.getNodeType(nodePath)) {
1302
1337
  case GCNodeType.Blob:
1303
- return ["_blobs"];
1338
+ return [BlobManager.basePath];
1304
1339
  case GCNodeType.DataStore:
1305
1340
  case GCNodeType.SubDataStore:
1306
1341
  return this.dataStores.getDataStorePackagePath(nodePath);
@@ -1318,6 +1353,25 @@ export class ContainerRuntime extends TypedEventEmitter {
1318
1353
  }
1319
1354
  return true;
1320
1355
  }
1356
+ /**
1357
+ * From a given list of routes, separate and return routes that belong to blob manager and data stores.
1358
+ * @param routes - A list of routes that can belong to data stores or blob manager.
1359
+ * @returns - Two route lists - One that contains routes for blob manager and another one that contains routes
1360
+ * for data stores.
1361
+ */
1362
+ getDataStoreAndBlobManagerRoutes(routes) {
1363
+ const blobManagerRoutes = [];
1364
+ const dataStoreRoutes = [];
1365
+ for (const route of routes) {
1366
+ if (this.isBlobPath(route)) {
1367
+ blobManagerRoutes.push(route);
1368
+ }
1369
+ else {
1370
+ dataStoreRoutes.push(route);
1371
+ }
1372
+ }
1373
+ return { blobManagerRoutes, dataStoreRoutes };
1374
+ }
1321
1375
  /**
1322
1376
  * Runs garbage collection and updates the reference / used state of the nodes in the container.
1323
1377
  * @returns the statistics of the garbage collection run; undefined if GC did not run.
@@ -1389,7 +1443,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1389
1443
  if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
1390
1444
  return {
1391
1445
  continue: false,
1392
- // eslint-disable-next-line max-len
1393
1446
  error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
1394
1447
  };
1395
1448
  }
@@ -1397,7 +1450,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1397
1450
  if (lastAck !== this.summaryCollection.latestAck) {
1398
1451
  return {
1399
1452
  continue: false,
1400
- // eslint-disable-next-line max-len
1401
1453
  error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
1402
1454
  };
1403
1455
  }
@@ -1612,7 +1664,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1612
1664
  else if (!this.flushMicroTaskExists) {
1613
1665
  this.flushMicroTaskExists = true;
1614
1666
  // 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
1667
  Promise.resolve().then(() => {
1617
1668
  this.flushMicroTaskExists = false;
1618
1669
  this.flush();
@@ -1705,13 +1756,32 @@ export class ContainerRuntime extends TypedEventEmitter {
1705
1756
  // It should only be done by the summarizerNode, if required.
1706
1757
  // When fetching from storage we will always get the latest version and do not use the ackHandle.
1707
1758
  const snapshotTreeFetcher = async () => {
1708
- const fetchResult = await this.fetchSnapshotFromStorage(null, summaryLogger, {
1759
+ const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
1709
1760
  eventName: "RefreshLatestSummaryGetSnapshot",
1710
1761
  ackHandle,
1711
1762
  summaryRefSeq,
1712
1763
  fetchLatest: true,
1713
1764
  });
1714
1765
  const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
1766
+ /**
1767
+ * If the fetched snapshot is older than the one for which the ack was received, close the container.
1768
+ * This should never happen because an ack should be sent after the latest summary is updated in the server.
1769
+ * However, there are couple of scenarios where it's possible:
1770
+ * 1. A file was modified externally resulting in modifying the snapshot's sequence number. This can lead to
1771
+ * the document being unusable and we should not proceed.
1772
+ * 2. The server DB failed after the ack was sent which may delete the corresponding snapshot. Ideally, in
1773
+ * such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
1774
+ * state.
1775
+ */
1776
+ if (latestSnapshotRefSeq < summaryRefSeq) {
1777
+ const error = DataProcessingError.create("Fetched snapshot is older than the received ack", "RefreshLatestSummaryAck", undefined /* sequencedMessage */, {
1778
+ ackHandle,
1779
+ summaryRefSeq,
1780
+ latestSnapshotRefSeq,
1781
+ });
1782
+ this.closeFn(error);
1783
+ throw error;
1784
+ }
1715
1785
  summaryLogger.sendTelemetryEvent({
1716
1786
  eventName: "LatestSummaryRetrieved",
1717
1787
  ackHandle,
@@ -1725,7 +1795,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1725
1795
  };
1726
1796
  const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
1727
1797
  // Notify the garbage collector so it can update its latest summary state.
1728
- await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
1798
+ await this.garbageCollector.refreshLatestSummary(result, proposalHandle, summaryRefSeq, readAndParseBlob);
1729
1799
  }
1730
1800
  /**
1731
1801
  * Fetches the latest snapshot from storage and uses it to refresh SummarizerNode's
@@ -1734,22 +1804,22 @@ export class ContainerRuntime extends TypedEventEmitter {
1734
1804
  * @returns downloaded snapshot's reference sequence number
1735
1805
  */
1736
1806
  async refreshLatestSummaryAckFromServer(summaryLogger) {
1737
- const { snapshotTree, versionId } = await this.fetchSnapshotFromStorage(null, summaryLogger, {
1807
+ const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
1738
1808
  eventName: "RefreshLatestSummaryGetSnapshot",
1739
1809
  fetchLatest: true,
1740
- }, FetchSource.noCache);
1810
+ });
1741
1811
  const readAndParseBlob = async (id) => readAndParse(this.storage, id);
1742
1812
  const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
1743
1813
  const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
1744
1814
  // Notify the garbage collector so it can update its latest summary state.
1745
- await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
1815
+ await this.garbageCollector.refreshLatestSummary(result, undefined, latestSnapshotRefSeq, readAndParseBlob);
1746
1816
  return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
1747
1817
  }
1748
- async fetchSnapshotFromStorage(versionId, logger, event, fetchSource) {
1818
+ async fetchLatestSnapshotFromStorage(logger, event) {
1749
1819
  return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
1750
1820
  const stats = {};
1751
1821
  const trace = Trace.start();
1752
- const versions = await this.storage.getVersions(versionId, 1, "refreshLatestSummaryAckFromServer", fetchSource);
1822
+ const versions = await this.storage.getVersions(null, 1, "refreshLatestSummaryAckFromServer", FetchSource.noCache);
1753
1823
  assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
1754
1824
  stats.getVersionDuration = trace.trace().duration;
1755
1825
  const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
@@ -1865,6 +1935,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1865
1935
  const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
1866
1936
  // TODO: remove cast to any when actual event is determined
1867
1937
  deltaManager.on("closed", reject);
1938
+ deltaManager.on("disposed", reject);
1868
1939
  // If we already reached target sequence number, simply resolve the promise.
1869
1940
  if (deltaManager.lastSequenceNumber >= targetSeq) {
1870
1941
  resolve();