@fluidframework/container-runtime 2.33.0-333010 → 2.33.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 (180) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/api-report/container-runtime.legacy.alpha.api.md +71 -67
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/blobManager/blobManager.d.ts +7 -4
  5. package/dist/blobManager/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager/blobManager.js +38 -12
  7. package/dist/blobManager/blobManager.js.map +1 -1
  8. package/dist/channelCollection.d.ts +4 -0
  9. package/dist/channelCollection.d.ts.map +1 -1
  10. package/dist/channelCollection.js +24 -0
  11. package/dist/channelCollection.js.map +1 -1
  12. package/dist/compatUtils.d.ts +74 -0
  13. package/dist/compatUtils.d.ts.map +1 -0
  14. package/dist/compatUtils.js +151 -0
  15. package/dist/compatUtils.js.map +1 -0
  16. package/dist/compressionDefinitions.d.ts +39 -0
  17. package/dist/compressionDefinitions.d.ts.map +1 -0
  18. package/dist/compressionDefinitions.js +30 -0
  19. package/dist/compressionDefinitions.js.map +1 -0
  20. package/dist/containerRuntime.d.ts +78 -52
  21. package/dist/containerRuntime.d.ts.map +1 -1
  22. package/dist/containerRuntime.js +141 -54
  23. package/dist/containerRuntime.js.map +1 -1
  24. package/dist/dataStoreContext.d.ts +3 -0
  25. package/dist/dataStoreContext.d.ts.map +1 -1
  26. package/dist/dataStoreContext.js +122 -66
  27. package/dist/dataStoreContext.js.map +1 -1
  28. package/dist/deltaManagerProxies.d.ts +55 -12
  29. package/dist/deltaManagerProxies.d.ts.map +1 -1
  30. package/dist/deltaManagerProxies.js +63 -55
  31. package/dist/deltaManagerProxies.js.map +1 -1
  32. package/dist/gc/gcDefinitions.d.ts +2 -0
  33. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  34. package/dist/gc/gcDefinitions.js.map +1 -1
  35. package/dist/index.d.ts +4 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +3 -2
  38. package/dist/index.js.map +1 -1
  39. package/dist/legacy.d.ts +1 -0
  40. package/dist/opLifecycle/batchManager.d.ts +1 -1
  41. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  42. package/dist/opLifecycle/batchManager.js +4 -1
  43. package/dist/opLifecycle/batchManager.js.map +1 -1
  44. package/dist/opLifecycle/definitions.d.ts +35 -4
  45. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  46. package/dist/opLifecycle/definitions.js.map +1 -1
  47. package/dist/opLifecycle/index.d.ts +1 -1
  48. package/dist/opLifecycle/index.d.ts.map +1 -1
  49. package/dist/opLifecycle/index.js.map +1 -1
  50. package/dist/opLifecycle/opCompressor.js +2 -2
  51. package/dist/opLifecycle/opCompressor.js.map +1 -1
  52. package/dist/opLifecycle/opDecompressor.js +3 -3
  53. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  54. package/dist/opLifecycle/opGroupingManager.d.ts +2 -2
  55. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  56. package/dist/opLifecycle/opGroupingManager.js +1 -2
  57. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  58. package/dist/opLifecycle/opSerialization.d.ts +3 -1
  59. package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
  60. package/dist/opLifecycle/opSerialization.js +4 -2
  61. package/dist/opLifecycle/opSerialization.js.map +1 -1
  62. package/dist/opLifecycle/outbox.d.ts +6 -3
  63. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  64. package/dist/opLifecycle/outbox.js +46 -20
  65. package/dist/opLifecycle/outbox.js.map +1 -1
  66. package/dist/packageVersion.d.ts +1 -1
  67. package/dist/packageVersion.d.ts.map +1 -1
  68. package/dist/packageVersion.js +1 -1
  69. package/dist/packageVersion.js.map +1 -1
  70. package/dist/pendingStateManager.d.ts +36 -7
  71. package/dist/pendingStateManager.d.ts.map +1 -1
  72. package/dist/pendingStateManager.js +83 -16
  73. package/dist/pendingStateManager.js.map +1 -1
  74. package/dist/runtimeLayerCompatState.d.ts.map +1 -1
  75. package/dist/runtimeLayerCompatState.js +1 -1
  76. package/dist/runtimeLayerCompatState.js.map +1 -1
  77. package/dist/summary/documentSchema.d.ts +1 -0
  78. package/dist/summary/documentSchema.d.ts.map +1 -1
  79. package/dist/summary/documentSchema.js +2 -0
  80. package/dist/summary/documentSchema.js.map +1 -1
  81. package/lib/blobManager/blobManager.d.ts +7 -4
  82. package/lib/blobManager/blobManager.d.ts.map +1 -1
  83. package/lib/blobManager/blobManager.js +38 -12
  84. package/lib/blobManager/blobManager.js.map +1 -1
  85. package/lib/channelCollection.d.ts +4 -0
  86. package/lib/channelCollection.d.ts.map +1 -1
  87. package/lib/channelCollection.js +24 -0
  88. package/lib/channelCollection.js.map +1 -1
  89. package/lib/compatUtils.d.ts +74 -0
  90. package/lib/compatUtils.d.ts.map +1 -0
  91. package/lib/compatUtils.js +142 -0
  92. package/lib/compatUtils.js.map +1 -0
  93. package/lib/compressionDefinitions.d.ts +39 -0
  94. package/lib/compressionDefinitions.d.ts.map +1 -0
  95. package/lib/compressionDefinitions.js +27 -0
  96. package/lib/compressionDefinitions.js.map +1 -0
  97. package/lib/containerRuntime.d.ts +78 -52
  98. package/lib/containerRuntime.d.ts.map +1 -1
  99. package/lib/containerRuntime.js +143 -56
  100. package/lib/containerRuntime.js.map +1 -1
  101. package/lib/dataStoreContext.d.ts +3 -0
  102. package/lib/dataStoreContext.d.ts.map +1 -1
  103. package/lib/dataStoreContext.js +57 -1
  104. package/lib/dataStoreContext.js.map +1 -1
  105. package/lib/deltaManagerProxies.d.ts +55 -12
  106. package/lib/deltaManagerProxies.d.ts.map +1 -1
  107. package/lib/deltaManagerProxies.js +63 -55
  108. package/lib/deltaManagerProxies.js.map +1 -1
  109. package/lib/gc/gcDefinitions.d.ts +2 -0
  110. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  111. package/lib/gc/gcDefinitions.js.map +1 -1
  112. package/lib/index.d.ts +4 -2
  113. package/lib/index.d.ts.map +1 -1
  114. package/lib/index.js +2 -1
  115. package/lib/index.js.map +1 -1
  116. package/lib/legacy.d.ts +1 -0
  117. package/lib/opLifecycle/batchManager.d.ts +1 -1
  118. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  119. package/lib/opLifecycle/batchManager.js +4 -1
  120. package/lib/opLifecycle/batchManager.js.map +1 -1
  121. package/lib/opLifecycle/definitions.d.ts +35 -4
  122. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  123. package/lib/opLifecycle/definitions.js.map +1 -1
  124. package/lib/opLifecycle/index.d.ts +1 -1
  125. package/lib/opLifecycle/index.d.ts.map +1 -1
  126. package/lib/opLifecycle/index.js.map +1 -1
  127. package/lib/opLifecycle/opCompressor.js +1 -1
  128. package/lib/opLifecycle/opCompressor.js.map +1 -1
  129. package/lib/opLifecycle/opDecompressor.js +1 -1
  130. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  131. package/lib/opLifecycle/opGroupingManager.d.ts +2 -2
  132. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  133. package/lib/opLifecycle/opGroupingManager.js +1 -2
  134. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  135. package/lib/opLifecycle/opSerialization.d.ts +3 -1
  136. package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
  137. package/lib/opLifecycle/opSerialization.js +4 -2
  138. package/lib/opLifecycle/opSerialization.js.map +1 -1
  139. package/lib/opLifecycle/outbox.d.ts +6 -3
  140. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  141. package/lib/opLifecycle/outbox.js +46 -20
  142. package/lib/opLifecycle/outbox.js.map +1 -1
  143. package/lib/packageVersion.d.ts +1 -1
  144. package/lib/packageVersion.d.ts.map +1 -1
  145. package/lib/packageVersion.js +1 -1
  146. package/lib/packageVersion.js.map +1 -1
  147. package/lib/pendingStateManager.d.ts +36 -7
  148. package/lib/pendingStateManager.d.ts.map +1 -1
  149. package/lib/pendingStateManager.js +84 -17
  150. package/lib/pendingStateManager.js.map +1 -1
  151. package/lib/runtimeLayerCompatState.d.ts.map +1 -1
  152. package/lib/runtimeLayerCompatState.js +2 -2
  153. package/lib/runtimeLayerCompatState.js.map +1 -1
  154. package/lib/summary/documentSchema.d.ts +1 -0
  155. package/lib/summary/documentSchema.d.ts.map +1 -1
  156. package/lib/summary/documentSchema.js +2 -0
  157. package/lib/summary/documentSchema.js.map +1 -1
  158. package/lib/tsdoc-metadata.json +1 -1
  159. package/package.json +21 -20
  160. package/src/blobManager/blobManager.ts +48 -15
  161. package/src/channelCollection.ts +27 -0
  162. package/src/compatUtils.ts +211 -0
  163. package/src/compressionDefinitions.ts +47 -0
  164. package/src/containerRuntime.ts +259 -108
  165. package/src/dataStoreContext.ts +82 -2
  166. package/src/deltaManagerProxies.ts +132 -70
  167. package/src/gc/gcDefinitions.ts +2 -0
  168. package/src/index.ts +5 -3
  169. package/src/opLifecycle/batchManager.ts +5 -4
  170. package/src/opLifecycle/definitions.ts +34 -4
  171. package/src/opLifecycle/index.ts +1 -0
  172. package/src/opLifecycle/opCompressor.ts +1 -1
  173. package/src/opLifecycle/opDecompressor.ts +1 -1
  174. package/src/opLifecycle/opGroupingManager.ts +7 -5
  175. package/src/opLifecycle/opSerialization.ts +6 -2
  176. package/src/opLifecycle/outbox.ts +65 -30
  177. package/src/packageVersion.ts +1 -1
  178. package/src/pendingStateManager.ts +135 -21
  179. package/src/runtimeLayerCompatState.ts +5 -2
  180. package/src/summary/documentSchema.ts +3 -0
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.createNewSignalEnvelope = exports.ContainerRuntime = exports.loadContainerRuntime = exports.getSingleUseLegacyLogCallback = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isUnpackedRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.disabledCompressionConfig = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DeletedResponseHeaderKey = void 0;
7
+ exports.createNewSignalEnvelope = exports.ContainerRuntime = exports.loadContainerRuntime = exports.getSingleUseLegacyLogCallback = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isUnpackedRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DeletedResponseHeaderKey = void 0;
8
8
  const client_utils_1 = require("@fluid-internal/client-utils");
9
9
  const container_definitions_1 = require("@fluidframework/container-definitions");
10
10
  const internal_1 = require("@fluidframework/container-definitions/internal");
@@ -19,6 +19,8 @@ const uuid_1 = require("uuid");
19
19
  const batchTracker_js_1 = require("./batchTracker.js");
20
20
  const index_js_1 = require("./blobManager/index.js");
21
21
  const channelCollection_js_1 = require("./channelCollection.js");
22
+ const compatUtils_js_1 = require("./compatUtils.js");
23
+ const compressionDefinitions_js_1 = require("./compressionDefinitions.js");
22
24
  const connectionTelemetry_js_1 = require("./connectionTelemetry.js");
23
25
  const containerHandleContext_js_1 = require("./containerHandleContext.js");
24
26
  const dataStore_js_1 = require("./dataStore.js");
@@ -83,35 +85,12 @@ exports.defaultRuntimeHeaderData = {
83
85
  viaHandle: false,
84
86
  allowTombstone: false,
85
87
  };
86
- /**
87
- * Available compression algorithms for op compression.
88
- * @legacy
89
- * @alpha
90
- */
91
- var CompressionAlgorithms;
92
- (function (CompressionAlgorithms) {
93
- CompressionAlgorithms["lz4"] = "lz4";
94
- })(CompressionAlgorithms || (exports.CompressionAlgorithms = CompressionAlgorithms = {}));
95
- /**
96
- * @legacy
97
- * @alpha
98
- */
99
- exports.disabledCompressionConfig = {
100
- minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
101
- compressionAlgorithm: CompressionAlgorithms.lz4,
102
- };
103
88
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
104
- const defaultFlushMode = internal_5.FlushMode.TurnBased;
105
89
  // The actual limit is 1Mb (socket.io and Kafka limits)
106
90
  // We can't estimate it fully, as we
107
91
  // - do not know what properties relay service will add
108
92
  // - we do not stringify final op, thus we do not know how much escaping will be added.
109
93
  const defaultMaxBatchSizeInBytes = 700 * 1024;
110
- const defaultCompressionConfig = {
111
- // Batches with content size exceeding this value will be compressed
112
- minimumBatchSizeInBytes: 614400,
113
- compressionAlgorithm: CompressionAlgorithms.lz4,
114
- };
115
94
  const defaultChunkSizeInBytes = 204800;
116
95
  /**
117
96
  * The default time to wait for pending ops to be processed during summarization
@@ -255,9 +234,42 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
255
234
  },
256
235
  });
257
236
  const mc = (0, internal_7.loggerToMonitoringContext)(logger);
258
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = runtimeOptions.enableGroupedBatching === false
259
- ? exports.disabledCompressionConfig // Compression must be disabled if Grouping is disabled
260
- : defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor, chunkSizeInBytes = defaultChunkSizeInBytes, enableGroupedBatching = true, explicitSchemaControl = false, } = runtimeOptions;
237
+ // Some options require a minimum version of the FF runtime to operate, so the default configs will be generated
238
+ // based on the compatibility mode.
239
+ // For example, if compatibility mode is set to "1.0.0", the default configs will ensure compatibility with FF runtime
240
+ // 1.0.0 or later. If the compatibility mode is set to "2.10.0", the default values will be generated to ensure compatibility
241
+ // with FF runtime 2.10.0 or later.
242
+ // TODO: We will add in a way for users to pass in compatibilityVersion in a follow up PR.
243
+ const compatibilityVersion = compatUtils_js_1.defaultCompatibilityVersion;
244
+ if (!(0, compatUtils_js_1.isValidCompatVersion)(compatibilityVersion)) {
245
+ throw new internal_7.UsageError(`Invalid compatibility version: ${compatibilityVersion}. It must be an existing FF version (i.e. 2.22.1).`);
246
+ }
247
+ const defaultVersionDependentConfigs = (0, compatUtils_js_1.getCompatibilityVersionDefaults)(compatibilityVersion);
248
+ // The following are the default values for the options that do not affect the DocumentSchema.
249
+ const defaultConfigsNonVersionDependent = {
250
+ summaryOptions: {},
251
+ loadSequenceNumberVerification: "close",
252
+ maxBatchSizeInBytes: defaultMaxBatchSizeInBytes,
253
+ chunkSizeInBytes: defaultChunkSizeInBytes,
254
+ };
255
+ const defaultConfigs = {
256
+ ...defaultVersionDependentConfigs,
257
+ ...defaultConfigsNonVersionDependent,
258
+ };
259
+ // Here we set each option to its corresponding default config value if it's not provided in runtimeOptions.
260
+ // Note: We cannot do a simple object merge of defaultConfigs/runtimeOptions because in most cases we don't want
261
+ // a option that is undefined in runtimeOptions to override the default value (except for idCompressor, see below).
262
+ const { summaryOptions = defaultConfigs.summaryOptions, gcOptions = defaultConfigs.gcOptions, loadSequenceNumberVerification = defaultConfigs.loadSequenceNumberVerification, maxBatchSizeInBytes = defaultConfigs.maxBatchSizeInBytes, chunkSizeInBytes = defaultConfigs.chunkSizeInBytes, explicitSchemaControl = defaultConfigs.explicitSchemaControl, enableGroupedBatching = defaultConfigs.enableGroupedBatching, flushMode = defaultConfigs.flushMode,
263
+ // If batching is disabled then we should disable compression as well. If batching is disabled and compression
264
+ // is enabled via runtimeOptions, we will throw an error later.
265
+ compressionOptions = enableGroupedBatching === false
266
+ ? compressionDefinitions_js_1.disabledCompressionConfig
267
+ : defaultConfigs.compressionOptions, createBlobPayloadPending = defaultConfigs.createBlobPayloadPending, } = runtimeOptions;
268
+ // The logic for enableRuntimeIdCompressor is a bit different. Since `undefined` represents a logical state (off)
269
+ // we need to check it's explicitly set in runtimeOptions. If so, we should use that value even if it's undefined.
270
+ const enableRuntimeIdCompressor = "enableRuntimeIdCompressor" in runtimeOptions
271
+ ? runtimeOptions.enableRuntimeIdCompressor
272
+ : defaultConfigs.enableRuntimeIdCompressor;
261
273
  const registry = new dataStoreRegistry_js_1.FluidDataStoreRegistry(registryEntries);
262
274
  const tryFetchBlob = async (blobName) => {
263
275
  const blobId = context.baseSnapshot?.blobs[blobName];
@@ -386,6 +398,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
386
398
  compressionLz4,
387
399
  idCompressorMode,
388
400
  opGroupingEnabled: enableGroupedBatching,
401
+ createBlobPayloadPending,
389
402
  disallowedVersions: [],
390
403
  }, (schema) => {
391
404
  runtime.onSchemaChange(schema);
@@ -403,10 +416,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
403
416
  compressionOptions,
404
417
  maxBatchSizeInBytes,
405
418
  chunkSizeInBytes,
406
- // Requires<> drops undefined from IdCompressorType
407
- enableRuntimeIdCompressor: enableRuntimeIdCompressor,
419
+ enableRuntimeIdCompressor,
408
420
  enableGroupedBatching,
409
421
  explicitSchemaControl,
422
+ createBlobPayloadPending,
410
423
  };
411
424
  const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], internalRuntimeOptions, containerScope, logger, existing, blobManagerLoadInfo, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined, // summaryConfiguration
412
425
  recentBatchInfo);
@@ -556,6 +569,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
556
569
  this.requestHandler = requestHandler;
557
570
  this.summaryConfiguration = summaryConfiguration;
558
571
  this.imminentClosure = false;
572
+ this.isReadOnly = () => this.deltaManager.readOnlyInfo.readonly === true;
559
573
  // We accumulate Id compressor Ops while Id compressor is not loaded yet (only for "delayed" mode)
560
574
  // Once it loads, it will process all such ops and we will stop accumulating further ops - ops will be processes as they come in.
561
575
  this.pendingIdCompressorOps = [];
@@ -573,6 +587,51 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
573
587
  this.snapshotCacheForLoadingGroupIds = new internal_2.PromiseCache({
574
588
  expiry: { policy: "absolute", durationMs: 60000 },
575
589
  });
590
+ this.notifyReadOnlyState = (readonly) => this.channelCollection.notifyReadOnlyState(readonly);
591
+ /**
592
+ * Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
593
+ * To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
594
+ *
595
+ * @returns StageControlsExperimental - Controls for exiting Staging Mode.
596
+ */
597
+ // eslint-disable-next-line import/no-deprecated
598
+ this.enterStagingMode = () => {
599
+ if (this.stageControls !== undefined) {
600
+ throw new Error("already in staging mode");
601
+ }
602
+ // Make sure all BatchManagers are empty before entering staging mode,
603
+ // since we mark whole batches as "staged" or not to indicate whether to submit them.
604
+ this.outbox.flush();
605
+ const exitStagingMode = (discardOrCommit) => () => {
606
+ // Final flush of any last staged changes
607
+ this.outbox.flush(undefined, true /* staged */);
608
+ this.stageControls = undefined;
609
+ discardOrCommit();
610
+ };
611
+ const stageControls = {
612
+ discardChanges: exitStagingMode(() => {
613
+ // Pop all staged batches from the PSM and roll them back in LIFO order
614
+ this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
615
+ (0, internal_2.assert)(runtimeOp !== undefined, 0xb82 /* Staged batches expected to have runtimeOp defined */);
616
+ this.rollback(runtimeOp, localOpMetadata);
617
+ });
618
+ if (this.attachState === container_definitions_1.AttachState.Attached) {
619
+ this.updateDocumentDirtyState(this.pendingMessagesCount !== 0);
620
+ }
621
+ }),
622
+ commitChanges: exitStagingMode(() => {
623
+ // All staged changes are in the PSM, so just replay them (ignore pre-staging batches)
624
+ // FUTURE: Have this do squash-rebase instead of resubmitting all intermediate changes
625
+ if (this.connected) {
626
+ this.pendingStateManager.replayPendingStates(true /* onlyStagedBatched */);
627
+ }
628
+ else {
629
+ this.pendingStateManager.clearStagingFlags();
630
+ }
631
+ }),
632
+ };
633
+ return (this.stageControls = stageControls);
634
+ };
576
635
  this.readAndParseBlob = async (id) => (0, internal_4.readAndParse)(this.storage, id);
577
636
  const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, pendingLocalState, supportedFeatures, snapshotWithContents, } = context;
578
637
  // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
@@ -592,7 +651,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
592
651
  minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
593
652
  ? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
594
653
  : Number.POSITIVE_INFINITY,
595
- compressionAlgorithm: CompressionAlgorithms.lz4,
654
+ compressionAlgorithm: compressionDefinitions_js_1.CompressionAlgorithms.lz4,
596
655
  };
597
656
  (0, internal_2.assert)((0, internal_1.isIDeltaManagerFull)(deltaManager), 0xa80 /* Invalid delta manager */);
598
657
  this.innerDeltaManager = deltaManager;
@@ -692,19 +751,22 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
692
751
  isActiveConnection: () => this.innerDeltaManager.active,
693
752
  isAttached: () => this.attachState !== container_definitions_1.AttachState.Detached,
694
753
  }, pendingRuntimeState?.pending, this.baseLogger);
695
- let outerDeltaManager;
754
+ let outerDeltaManager = this.innerDeltaManager;
696
755
  this.useDeltaManagerOpsProxy =
697
756
  this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") === true;
698
757
  // The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
699
- const summarizerDeltaManagerProxy = new deltaManagerProxies_js_1.DeltaManagerSummarizerProxy(this.innerDeltaManager);
700
- outerDeltaManager = summarizerDeltaManagerProxy;
758
+ outerDeltaManager = deltaManagerProxies_js_1.DeltaManagerSummarizerProxy.wrapIfSummarizer(outerDeltaManager);
701
759
  // The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
702
760
  // It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
703
761
  if (this.useDeltaManagerOpsProxy) {
704
- const pendingOpsDeltaManagerProxy = new deltaManagerProxies_js_1.DeltaManagerPendingOpsProxy(summarizerDeltaManagerProxy, this.pendingStateManager);
762
+ const pendingOpsDeltaManagerProxy = new deltaManagerProxies_js_1.DeltaManagerPendingOpsProxy(outerDeltaManager, this.pendingStateManager);
705
763
  outerDeltaManager = pendingOpsDeltaManagerProxy;
706
764
  }
707
- this._deltaManager = outerDeltaManager;
765
+ // always wrap the exposed delta manager in at least on layer of proxying
766
+ this._deltaManager =
767
+ outerDeltaManager instanceof deltaManagerProxies_js_1.BaseDeltaManagerProxy
768
+ ? outerDeltaManager
769
+ : new deltaManagerProxies_js_1.BaseDeltaManagerProxy(outerDeltaManager);
708
770
  this.handleContext = new containerHandleContext_js_1.ContainerFluidHandleContext("", this);
709
771
  if (this.summaryConfiguration.state === "enabled") {
710
772
  (0, index_js_4.validateSummaryHeuristicConfiguration)(this.summaryConfiguration);
@@ -815,6 +877,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
815
877
  ...props,
816
878
  timestampMs: props.timestampMs ?? this.getCurrentReferenceTimestampMs(),
817
879
  }), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
880
+ this._deltaManager.on("readonly", this.notifyReadOnlyState);
818
881
  this.blobManager = new index_js_1.BlobManager({
819
882
  routeContext: this.handleContext,
820
883
  blobManagerLoadInfo,
@@ -835,6 +898,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
835
898
  isBlobDeleted: (blobPath) => this.garbageCollector.isNodeDeleted(blobPath),
836
899
  runtime: this,
837
900
  stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
901
+ createBlobPayloadPending: this.sessionSchema.createBlobPayloadPending === true,
838
902
  });
839
903
  this.deltaScheduler = new deltaScheduler_js_1.DeltaScheduler(this.innerDeltaManager, this, (0, internal_7.createChildLogger)({ logger: this.baseLogger, namespace: "DeltaScheduler" }));
840
904
  this.inboundBatchAggregator = new inboundBatchAggregator_js_1.InboundBatchAggregator(this.innerDeltaManager, () => this.clientId, (0, internal_7.createChildLogger)({ logger: this.baseLogger, namespace: "InboundBatchAggregator" }));
@@ -1086,6 +1150,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1086
1150
  this.pendingStateManager.dispose();
1087
1151
  this.inboundBatchAggregator.dispose();
1088
1152
  this.deltaScheduler.dispose();
1153
+ this._deltaManager.dispose();
1089
1154
  this.emit("dispose");
1090
1155
  this.removeAllListeners();
1091
1156
  }
@@ -1231,7 +1296,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1231
1296
  return this.resolveHandle(requestParser.createSubRequest(1));
1232
1297
  }
1233
1298
  if (id === index_js_1.blobManagerBasePath && requestParser.isLeaf(2)) {
1234
- const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
1299
+ const localId = requestParser.pathParts[1];
1300
+ const payloadPending = requestParser.headers?.[internal_6.RuntimeHeaders.payloadPending] === true;
1301
+ if (!this.blobManager.hasBlob(localId) &&
1302
+ requestParser.headers?.[internal_6.RuntimeHeaders.wait] === false) {
1303
+ return (0, internal_6.create404Response)(request);
1304
+ }
1305
+ const blob = await this.blobManager.getBlob(localId, payloadPending);
1235
1306
  return {
1236
1307
  status: 200,
1237
1308
  mimeType: "fluid/object",
@@ -1871,11 +1942,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1871
1942
  *
1872
1943
  * @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
1873
1944
  * with the given Batch ID, which must be preserved
1945
+ * @param resubmittingStagedBatch - If defined, indicates this is a resubmission of a batch that is staged,
1946
+ * meaning it should not be sent to the ordering service yet.
1874
1947
  */
1875
- flush(resubmittingBatchId) {
1948
+ flush(resubmittingBatchId, resubmittingStagedBatch) {
1876
1949
  try {
1877
1950
  (0, internal_2.assert)(!this.batchRunner.running, 0x24c /* "Cannot call `flush()` while manually accumulating a batch (e.g. under orderSequentially) */);
1878
- this.outbox.flush(resubmittingBatchId);
1951
+ this.outbox.flush(resubmittingBatchId, resubmittingStagedBatch);
1879
1952
  (0, internal_2.assert)(this.outbox.isEmpty, 0x3cf /* reentrancy */);
1880
1953
  }
1881
1954
  catch (error) {
@@ -1894,7 +1967,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1894
1967
  orderSequentially(callback) {
1895
1968
  let checkpoint;
1896
1969
  const checkpointDirtyState = this.dirtyContainer;
1970
+ // eslint-disable-next-line import/no-deprecated
1971
+ let stageControls;
1897
1972
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1973
+ if (!this.batchRunner.running && !this.inStagingMode) {
1974
+ stageControls = this.enterStagingMode();
1975
+ }
1898
1976
  // Note: we are not touching any batches other than mainBatch here, for two reasons:
1899
1977
  // 1. It would not help, as other batches are flushed independently from main batch.
1900
1978
  // 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
@@ -1908,11 +1986,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1908
1986
  if (checkpoint) {
1909
1987
  // This will throw and close the container if rollback fails
1910
1988
  try {
1911
- checkpoint.rollback((message) => this.rollback(message.serializedOp, message.localOpMetadata));
1989
+ checkpoint.rollback((message) => this.rollback(message.runtimeOp, message.localOpMetadata));
1912
1990
  // reset the dirty state after rollback to what it was before to keep it consistent
1913
1991
  if (this.dirtyContainer !== checkpointDirtyState) {
1914
1992
  this.updateDocumentDirtyState(checkpointDirtyState);
1915
1993
  }
1994
+ stageControls?.discardChanges();
1995
+ stageControls = undefined;
1916
1996
  }
1917
1997
  catch (error_) {
1918
1998
  const error2 = (0, internal_7.wrapError)(error_, (message) => {
@@ -1930,12 +2010,21 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1930
2010
  throw error; // throw the original error for the consumer of the runtime
1931
2011
  }
1932
2012
  });
2013
+ stageControls?.commitChanges();
1933
2014
  // We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
1934
2015
  if (this.flushMode !== internal_5.FlushMode.TurnBased && !this.batchRunner.running) {
1935
2016
  this.flush();
1936
2017
  }
1937
2018
  return result;
1938
2019
  }
2020
+ /**
2021
+ * If true, the ContainerRuntime is not submitting any new ops to the ordering service.
2022
+ * Ops submitted to the ContainerRuntime while in Staging Mode will be queued in the PendingStateManager,
2023
+ * either to be discarded or committed later (via the Stage Controls returned from enterStagingMode).
2024
+ */
2025
+ get inStagingMode() {
2026
+ return this.stageControls !== undefined;
2027
+ }
1939
2028
  /**
1940
2029
  * Returns the aliased data store's entryPoint, given the alias.
1941
2030
  * @param alias - The alias for the data store.
@@ -2668,8 +2757,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2668
2757
  contents: idRange,
2669
2758
  };
2670
2759
  const idAllocationBatchMessage = {
2671
- serializedOp: (0, index_js_3.serializeOp)(idAllocationMessage),
2760
+ runtimeOp: idAllocationMessage,
2672
2761
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2762
+ // Note: For now, we will never stage ID Allocation messages.
2763
+ // They won't contain personal info and no harm in extra allocations in case of discarding the staged changes
2764
+ staged: false,
2673
2765
  };
2674
2766
  this.outbox.submitIdAllocation(idAllocationBatchMessage);
2675
2767
  }
@@ -2711,17 +2803,19 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2711
2803
  contents: schemaChangeMessage,
2712
2804
  };
2713
2805
  this.outbox.submit({
2714
- serializedOp: (0, index_js_3.serializeOp)(msg),
2806
+ runtimeOp: msg,
2715
2807
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2808
+ staged: this.inStagingMode,
2716
2809
  });
2717
2810
  }
2718
2811
  const message = {
2719
2812
  // This will encode any handles present in this op before serializing to string
2720
2813
  // Note: handles may already have been encoded by the DDS layer, but encoding handles is idempotent so there's no problem.
2721
- serializedOp: (0, index_js_3.serializeOp)(containerRuntimeMessage),
2814
+ runtimeOp: containerRuntimeMessage,
2722
2815
  metadata,
2723
2816
  localOpMetadata,
2724
2817
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2818
+ staged: this.inStagingMode,
2725
2819
  };
2726
2820
  if (type === messageTypes_js_1.ContainerMessageType.BlobAttach) {
2727
2821
  // BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
@@ -2805,7 +2899,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2805
2899
  * @remarks - If the "Offline Load" feature is enabled, the batchId is included in the resubmitted messages,
2806
2900
  * for correlation to detect container forking.
2807
2901
  */
2808
- reSubmitBatch(batch, batchId) {
2902
+ reSubmitBatch(batch, batchId, staged) {
2809
2903
  this.batchRunner.run(() => {
2810
2904
  for (const message of batch) {
2811
2905
  this.reSubmit(message);
@@ -2813,14 +2907,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2813
2907
  });
2814
2908
  // Only include Batch ID if "Offline Load" feature is enabled
2815
2909
  // It's only needed to identify batches across container forks arising from misuse of offline load.
2816
- this.flush(this.offlineEnabled ? batchId : undefined);
2910
+ this.flush(this.offlineEnabled ? batchId : undefined, staged);
2817
2911
  }
2818
2912
  reSubmit(message) {
2819
- // Messages in the PendingStateManager and Outbox (both call resubmit) have serialized contents, so parse it here.
2820
- // Note that roundtripping handles through string like this means this parsed contents
2821
- // contains RemoteFluidObjectHandles, not the original handle.
2822
- const containerRuntimeMessage = this.parseLocalOpContent(message.content);
2823
- this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
2913
+ this.reSubmitCore(message.runtimeOp, message.localOpMetadata, message.opMetadata);
2824
2914
  }
2825
2915
  /**
2826
2916
  * Finds the right store and asks it to resubmit the message. This typically happens when we
@@ -2875,11 +2965,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2875
2965
  }
2876
2966
  }
2877
2967
  }
2878
- rollback(content, localOpMetadata) {
2879
- // Messages in the Outbox (which calls rollback) have serialized contents, so parse it here.
2880
- // Note that roundtripping handles through string like this means this parsed contents
2881
- // contains RemoteFluidObjectHandles, not the original handle.
2882
- const { type, contents } = this.parseLocalOpContent(content);
2968
+ rollback(runtimeOp, localOpMetadata) {
2969
+ const { type, contents } = runtimeOp;
2883
2970
  switch (type) {
2884
2971
  case messageTypes_js_1.ContainerMessageType.FluidDataStoreOp: {
2885
2972
  // For operations, call rollbackDataStoreOp which will find the right store