@fluidframework/container-runtime 2.40.0-336023 → 2.40.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 (92) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/api-report/container-runtime.legacy.alpha.api.md +4 -0
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/blobManager/blobManager.d.ts +31 -8
  5. package/dist/blobManager/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager/blobManager.js +90 -17
  7. package/dist/blobManager/blobManager.js.map +1 -1
  8. package/dist/channelCollection.d.ts +8 -2
  9. package/dist/channelCollection.d.ts.map +1 -1
  10. package/dist/channelCollection.js +29 -6
  11. package/dist/channelCollection.js.map +1 -1
  12. package/dist/compatUtils.d.ts +19 -10
  13. package/dist/compatUtils.d.ts.map +1 -1
  14. package/dist/compatUtils.js +39 -32
  15. package/dist/compatUtils.js.map +1 -1
  16. package/dist/containerRuntime.d.ts +29 -13
  17. package/dist/containerRuntime.d.ts.map +1 -1
  18. package/dist/containerRuntime.js +139 -149
  19. package/dist/containerRuntime.js.map +1 -1
  20. package/dist/dataStoreContext.d.ts +12 -4
  21. package/dist/dataStoreContext.d.ts.map +1 -1
  22. package/dist/dataStoreContext.js +37 -18
  23. package/dist/dataStoreContext.js.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/legacy.d.ts +1 -0
  28. package/dist/opLifecycle/index.d.ts +1 -1
  29. package/dist/opLifecycle/index.d.ts.map +1 -1
  30. package/dist/opLifecycle/index.js.map +1 -1
  31. package/dist/opLifecycle/outbox.d.ts +20 -7
  32. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  33. package/dist/opLifecycle/outbox.js +16 -20
  34. package/dist/opLifecycle/outbox.js.map +1 -1
  35. package/dist/packageVersion.d.ts +1 -1
  36. package/dist/packageVersion.d.ts.map +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/pendingStateManager.d.ts +22 -8
  40. package/dist/pendingStateManager.d.ts.map +1 -1
  41. package/dist/pendingStateManager.js +11 -16
  42. package/dist/pendingStateManager.js.map +1 -1
  43. package/lib/blobManager/blobManager.d.ts +31 -8
  44. package/lib/blobManager/blobManager.d.ts.map +1 -1
  45. package/lib/blobManager/blobManager.js +91 -18
  46. package/lib/blobManager/blobManager.js.map +1 -1
  47. package/lib/channelCollection.d.ts +8 -2
  48. package/lib/channelCollection.d.ts.map +1 -1
  49. package/lib/channelCollection.js +29 -6
  50. package/lib/channelCollection.js.map +1 -1
  51. package/lib/compatUtils.d.ts +19 -10
  52. package/lib/compatUtils.d.ts.map +1 -1
  53. package/lib/compatUtils.js +36 -29
  54. package/lib/compatUtils.js.map +1 -1
  55. package/lib/containerRuntime.d.ts +29 -13
  56. package/lib/containerRuntime.d.ts.map +1 -1
  57. package/lib/containerRuntime.js +60 -70
  58. package/lib/containerRuntime.js.map +1 -1
  59. package/lib/dataStoreContext.d.ts +12 -4
  60. package/lib/dataStoreContext.d.ts.map +1 -1
  61. package/lib/dataStoreContext.js +38 -19
  62. package/lib/dataStoreContext.js.map +1 -1
  63. package/lib/index.d.ts +1 -0
  64. package/lib/index.d.ts.map +1 -1
  65. package/lib/index.js.map +1 -1
  66. package/lib/legacy.d.ts +1 -0
  67. package/lib/opLifecycle/index.d.ts +1 -1
  68. package/lib/opLifecycle/index.d.ts.map +1 -1
  69. package/lib/opLifecycle/index.js.map +1 -1
  70. package/lib/opLifecycle/outbox.d.ts +20 -7
  71. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  72. package/lib/opLifecycle/outbox.js +16 -20
  73. package/lib/opLifecycle/outbox.js.map +1 -1
  74. package/lib/packageVersion.d.ts +1 -1
  75. package/lib/packageVersion.d.ts.map +1 -1
  76. package/lib/packageVersion.js +1 -1
  77. package/lib/packageVersion.js.map +1 -1
  78. package/lib/pendingStateManager.d.ts +22 -8
  79. package/lib/pendingStateManager.d.ts.map +1 -1
  80. package/lib/pendingStateManager.js +11 -16
  81. package/lib/pendingStateManager.js.map +1 -1
  82. package/package.json +18 -18
  83. package/src/blobManager/blobManager.ts +141 -33
  84. package/src/channelCollection.ts +42 -6
  85. package/src/compatUtils.ts +53 -30
  86. package/src/containerRuntime.ts +102 -81
  87. package/src/dataStoreContext.ts +44 -25
  88. package/src/index.ts +1 -0
  89. package/src/opLifecycle/index.ts +1 -0
  90. package/src/opLifecycle/outbox.ts +42 -33
  91. package/src/packageVersion.ts +1 -1
  92. package/src/pendingStateManager.ts +37 -20
@@ -9,6 +9,7 @@ import { assert, Deferred, LazyPromise, PromiseCache, delay, } from "@fluidframe
9
9
  import { SummaryType } from "@fluidframework/driver-definitions";
10
10
  import { FetchSource, MessageType } from "@fluidframework/driver-definitions/internal";
11
11
  import { readAndParse } from "@fluidframework/driver-utils/internal";
12
+ import { createIdCompressor, createSessionId, deserializeIdCompressor, } from "@fluidframework/id-compressor/internal";
12
13
  import { FlushMode, FlushModeExperimental, channelsTreeName, gcTreeKey, } from "@fluidframework/runtime-definitions/internal";
13
14
  import { GCDataBuilder, RequestParser, RuntimeHeaders, TelemetryContext, addBlobToSummary, addSummarizeResultToSummary, calculateStats, create404Response, exceptionToResponse, seqFromTree, } from "@fluidframework/runtime-utils/internal";
14
15
  import { DataCorruptionError, DataProcessingError, extractSafePropertiesFromMessage, GenericError, LoggingError, PerformanceEvent,
@@ -18,7 +19,7 @@ import { v4 as uuid } from "uuid";
18
19
  import { BindBatchTracker } from "./batchTracker.js";
19
20
  import { BlobManager, blobManagerBasePath, blobsTreeName, isBlobPath, loadBlobManagerLoadInfo, } from "./blobManager/index.js";
20
21
  import { ChannelCollection, getSummaryForDatastores, wrapContext, } from "./channelCollection.js";
21
- import { defaultCompatibilityVersion, getCompatibilityVersionDefaults, isValidCompatVersion, } from "./compatUtils.js";
22
+ import { defaultMinVersionForCollab, getMinVersionForCollabDefaults, isValidMinVersionForCollab, } from "./compatUtils.js";
22
23
  import { CompressionAlgorithms, disabledCompressionConfig } from "./compressionDefinitions.js";
23
24
  import { ReportOpPerfTelemetry } from "./connectionTelemetry.js";
24
25
  import { ContainerFluidHandleContext } from "./containerHandleContext.js";
@@ -84,6 +85,7 @@ export const defaultRuntimeHeaderData = {
84
85
  viaHandle: false,
85
86
  allowTombstone: false,
86
87
  };
88
+ const defaultStagingCommitOptions = { squash: false };
87
89
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
88
90
  // The actual limit is 1Mb (socket.io and Kafka limits)
89
91
  // We can't estimate it fully, as we
@@ -209,10 +211,11 @@ export class ContainerRuntime extends TypedEventEmitter {
209
211
  * - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
210
212
  * This allows mixin classes to leverage this method to define their own async initializer.
211
213
  * - provideEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
214
+ * - minVersionForCollab - Minimum version of the FF runtime that this runtime supports collaboration with.
212
215
  * This object should provide all the functionality that the Container is expected to provide to the loader layer.
213
216
  */
214
217
  static async loadRuntime(params) {
215
- const { context, registryEntries, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
218
+ const { context, registryEntries, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, minVersionForCollab = defaultMinVersionForCollab, } = params;
216
219
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
217
220
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
218
221
  const backCompatContext = context;
@@ -229,26 +232,24 @@ export class ContainerRuntime extends TypedEventEmitter {
229
232
  });
230
233
  const mc = loggerToMonitoringContext(logger);
231
234
  // Some options require a minimum version of the FF runtime to operate, so the default configs will be generated
232
- // based on the compatibility mode.
233
- // For example, if compatibility mode is set to "1.0.0", the default configs will ensure compatibility with FF runtime
234
- // 1.0.0 or later. If the compatibility mode is set to "2.10.0", the default values will be generated to ensure compatibility
235
+ // based on the minVersionForCollab.
236
+ // For example, if minVersionForCollab is set to "1.0.0", the default configs will ensure compatibility with FF runtime
237
+ // 1.0.0 or later. If the minVersionForCollab is set to "2.10.0", the default values will be generated to ensure compatibility
235
238
  // with FF runtime 2.10.0 or later.
236
- // TODO: We will add in a way for users to pass in compatibilityVersion in a follow up PR.
237
- const compatibilityVersion = defaultCompatibilityVersion;
238
- if (!isValidCompatVersion(compatibilityVersion)) {
239
- throw new UsageError(`Invalid compatibility version: ${compatibilityVersion}. It must be an existing FF version (i.e. 2.22.1).`);
239
+ if (!isValidMinVersionForCollab(minVersionForCollab)) {
240
+ throw new UsageError(`Invalid minVersionForCollab: ${minVersionForCollab}. It must be an existing FF version (i.e. 2.22.1).`);
240
241
  }
241
- const defaultVersionDependentConfigs = getCompatibilityVersionDefaults(compatibilityVersion);
242
+ const defaultsAffectingDocSchema = getMinVersionForCollabDefaults(minVersionForCollab);
242
243
  // The following are the default values for the options that do not affect the DocumentSchema.
243
- const defaultConfigsNonVersionDependent = {
244
+ const defaultsNotAffectingDocSchema = {
244
245
  summaryOptions: {},
245
246
  loadSequenceNumberVerification: "close",
246
247
  maxBatchSizeInBytes: defaultMaxBatchSizeInBytes,
247
248
  chunkSizeInBytes: defaultChunkSizeInBytes,
248
249
  };
249
250
  const defaultConfigs = {
250
- ...defaultVersionDependentConfigs,
251
- ...defaultConfigsNonVersionDependent,
251
+ ...defaultsAffectingDocSchema,
252
+ ...defaultsNotAffectingDocSchema,
252
253
  };
253
254
  // Here we set each option to its corresponding default config value if it's not provided in runtimeOptions.
254
255
  // Note: We cannot do a simple object merge of defaultConfigs/runtimeOptions because in most cases we don't want
@@ -359,8 +360,7 @@ export class ContainerRuntime extends TypedEventEmitter {
359
360
  else {
360
361
  idCompressorMode = desiredIdCompressorMode;
361
362
  }
362
- const createIdCompressorFn = async () => {
363
- const { createIdCompressor, deserializeIdCompressor, createSessionId } = await import("@fluidframework/id-compressor/internal");
363
+ const createIdCompressorFn = () => {
364
364
  /**
365
365
  * Because the IdCompressor emits so much telemetry, this function is used to sample
366
366
  * approximately 5% of all clients. Only the given percentage of sessions will emit telemetry.
@@ -415,7 +415,7 @@ export class ContainerRuntime extends TypedEventEmitter {
415
415
  explicitSchemaControl,
416
416
  createBlobPayloadPending,
417
417
  };
418
- const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], internalRuntimeOptions, containerScope, logger, existing, blobManagerLoadInfo, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined, // summaryConfiguration
418
+ const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], internalRuntimeOptions, containerScope, logger, existing, blobManagerLoadInfo, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, minVersionForCollab, requestHandler, undefined, // summaryConfiguration
419
419
  recentBatchInfo);
420
420
  runtime.blobManager.stashedBlobsUploadP.then(() => {
421
421
  // make sure we didn't reconnect before the promise resolved
@@ -542,7 +542,7 @@ export class ContainerRuntime extends TypedEventEmitter {
542
542
  /***/
543
543
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope,
544
544
  // Create a custom ITelemetryBaseLogger to output telemetry events.
545
- baseLogger, existing, blobManagerLoadInfo, _storage, createIdCompressor, documentsSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler,
545
+ baseLogger, existing, blobManagerLoadInfo, _storage, createIdCompressorFn, documentsSchemaController, featureGatesForTelemetry, provideEntryPoint, minVersionForCollab, requestHandler,
546
546
  // // eslint-disable-next-line unicorn/no-object-as-default-parameter
547
547
  summaryConfiguration = {
548
548
  // the defaults
@@ -558,8 +558,9 @@ export class ContainerRuntime extends TypedEventEmitter {
558
558
  this.containerScope = containerScope;
559
559
  this.baseLogger = baseLogger;
560
560
  this._storage = _storage;
561
- this.createIdCompressor = createIdCompressor;
561
+ this.createIdCompressorFn = createIdCompressorFn;
562
562
  this.documentsSchemaController = documentsSchemaController;
563
+ this.minVersionForCollab = minVersionForCollab;
563
564
  this.requestHandler = requestHandler;
564
565
  this.summaryConfiguration = summaryConfiguration;
565
566
  this.imminentClosure = false;
@@ -598,10 +599,12 @@ export class ContainerRuntime extends TypedEventEmitter {
598
599
  this.outbox.flush();
599
600
  const exitStagingMode = (discardOrCommit) => () => {
600
601
  // Final flush of any last staged changes
601
- this.outbox.flush(undefined, true /* staged */);
602
+ this.outbox.flush();
602
603
  this.stageControls = undefined;
603
604
  discardOrCommit();
605
+ this.channelCollection.notifyStagingMode(false);
604
606
  };
607
+ // eslint-disable-next-line import/no-deprecated
605
608
  const stageControls = {
606
609
  discardChanges: exitStagingMode(() => {
607
610
  // Pop all staged batches from the PSM and roll them back in LIFO order
@@ -613,18 +616,19 @@ export class ContainerRuntime extends TypedEventEmitter {
613
616
  this.updateDocumentDirtyState(this.pendingMessagesCount !== 0);
614
617
  }
615
618
  }),
616
- commitChanges: exitStagingMode(() => {
617
- // All staged changes are in the PSM, so just replay them (ignore pre-staging batches)
618
- // FUTURE: Have this do squash-rebase instead of resubmitting all intermediate changes
619
- if (this.connected) {
620
- this.pendingStateManager.replayPendingStates(true /* onlyStagedBatched */);
621
- }
622
- else {
623
- this.pendingStateManager.clearStagingFlags();
624
- }
625
- }),
619
+ commitChanges: (optionsParam) => {
620
+ const options = { ...defaultStagingCommitOptions, ...optionsParam };
621
+ return exitStagingMode(() => {
622
+ this.pendingStateManager.replayPendingStates({
623
+ onlyStagedBatches: true,
624
+ squash: options.squash ?? false,
625
+ });
626
+ })();
627
+ },
626
628
  };
627
- return (this.stageControls = stageControls);
629
+ this.stageControls = stageControls;
630
+ this.channelCollection.notifyStagingMode(true);
631
+ return this.stageControls;
628
632
  };
629
633
  this.readAndParseBlob = async (id) => readAndParse(this.storage, id);
630
634
  const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, pendingLocalState, supportedFeatures, snapshotWithContents, } = context;
@@ -988,6 +992,7 @@ export class ContainerRuntime extends TypedEventEmitter {
988
992
  telemetryDocumentId: this.telemetryDocumentId,
989
993
  groupedBatchingEnabled: this.groupedBatchingEnabled,
990
994
  initialSequenceNumber: this.deltaManager.initialSequenceNumber,
995
+ minVersionForCollab: this.minVersionForCollab,
991
996
  });
992
997
  ReportOpPerfTelemetry(this.clientId, this._deltaManager, this, this.baseLogger);
993
998
  BindBatchTracker(this, this.baseLogger);
@@ -1015,7 +1020,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1015
1020
  // As it's implemented right now (with async initialization), this will only work for "off" -> "delayed" transitions.
1016
1021
  // Anything else is too risky, and requires ability to initialize ID compressor synchronously!
1017
1022
  if (schema.runtime.idCompressorMode !== undefined) {
1018
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1019
1023
  this.loadIdCompressor();
1020
1024
  }
1021
1025
  }
@@ -1040,7 +1044,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1040
1044
  await this.initializeSummarizer(loader);
1041
1045
  if (this.sessionSchema.idCompressorMode === "on" ||
1042
1046
  (this.sessionSchema.idCompressorMode === "delayed" && this.connected)) {
1043
- this._idCompressor = await this.createIdCompressor();
1047
+ this._idCompressor = this.createIdCompressorFn();
1044
1048
  // This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
1045
1049
  assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
1046
1050
  }
@@ -1496,27 +1500,18 @@ export class ContainerRuntime extends TypedEventEmitter {
1496
1500
  }
1497
1501
  }
1498
1502
  }
1499
- async loadIdCompressor() {
1503
+ loadIdCompressor() {
1500
1504
  if (this._idCompressor === undefined &&
1501
- this.sessionSchema.idCompressorMode !== undefined &&
1502
- this._loadIdCompressor === undefined) {
1503
- this._loadIdCompressor = this.createIdCompressor()
1504
- .then((compressor) => {
1505
- // Finalize any ranges we received while the compressor was turned off.
1506
- const ops = this.pendingIdCompressorOps;
1507
- this.pendingIdCompressorOps = [];
1508
- for (const range of ops) {
1509
- compressor.finalizeCreationRange(range);
1510
- }
1511
- assert(this.pendingIdCompressorOps.length === 0, 0x976 /* No new ops added */);
1512
- this._idCompressor = compressor;
1513
- })
1514
- .catch((error) => {
1515
- this.mc.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
1516
- throw error;
1517
- });
1505
+ this.sessionSchema.idCompressorMode !== undefined) {
1506
+ this._idCompressor = this.createIdCompressorFn();
1507
+ // Finalize any ranges we received while the compressor was turned off.
1508
+ const ops = this.pendingIdCompressorOps;
1509
+ this.pendingIdCompressorOps = [];
1510
+ for (const range of ops) {
1511
+ this._idCompressor.finalizeCreationRange(range);
1512
+ }
1513
+ assert(this.pendingIdCompressorOps.length === 0, 0x976 /* No new ops added */);
1518
1514
  }
1519
- return this._loadIdCompressor;
1520
1515
  }
1521
1516
  setConnectionState(connected, clientId) {
1522
1517
  // Validate we have consistent state
@@ -1524,7 +1519,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1524
1519
  assert(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
1525
1520
  assert(this.clientId === currentClientId, 0x978 /* this.clientId does not match Audience */);
1526
1521
  if (connected && this.sessionSchema.idCompressorMode === "delayed") {
1527
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1528
1522
  this.loadIdCompressor();
1529
1523
  }
1530
1524
  if (connected === false && this.delayConnectClientId !== undefined) {
@@ -1930,19 +1924,16 @@ export class ContainerRuntime extends TypedEventEmitter {
1930
1924
  this.channelCollection.processSignal(transformed, local);
1931
1925
  }
1932
1926
  /**
1933
- * Flush the pending ops manually.
1934
- * This method is expected to be called at the end of a batch.
1927
+ * Flush the current batch of ops to the ordering service for sequencing
1928
+ * This method is not expected to be called in the middle of a batch.
1935
1929
  * @remarks - If it throws (e.g. if the batch is too large to send), the container will be closed.
1936
1930
  *
1937
- * @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
1938
- * with the given Batch ID, which must be preserved
1939
- * @param resubmittingStagedBatch - If defined, indicates this is a resubmission of a batch that is staged,
1940
- * meaning it should not be sent to the ordering service yet.
1931
+ * @param resubmitInfo - If defined, indicates this is a resubmission of a batch with the given Batch info needed for resubmit.
1941
1932
  */
1942
- flush(resubmittingBatchId, resubmittingStagedBatch) {
1933
+ flush(resubmitInfo) {
1943
1934
  try {
1944
1935
  assert(!this.batchRunner.running, 0x24c /* "Cannot call `flush()` while manually accumulating a batch (e.g. under orderSequentially) */);
1945
- this.outbox.flush(resubmittingBatchId, resubmittingStagedBatch);
1936
+ this.outbox.flush(resubmitInfo);
1946
1937
  assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
1947
1938
  }
1948
1939
  catch (error) {
@@ -2004,7 +1995,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2004
1995
  throw error; // throw the original error for the consumer of the runtime
2005
1996
  }
2006
1997
  });
2007
- stageControls?.commitChanges();
1998
+ stageControls?.commitChanges({ squash: false });
2008
1999
  // We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
2009
2000
  if (this.flushMode !== FlushMode.TurnBased && !this.batchRunner.running) {
2010
2001
  this.flush();
@@ -2181,8 +2172,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2181
2172
  // Wrap data store summaries in .channels subtree.
2182
2173
  wrapSummaryInChannelsTree(summarizeResult);
2183
2174
  const pathPartsForChildren = [channelsTreeName];
2184
- // Ensure that ID compressor had a chance to load, if we are using delayed mode.
2185
- await this.loadIdCompressor();
2175
+ this.loadIdCompressor();
2186
2176
  this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
2187
2177
  return {
2188
2178
  ...summarizeResult,
@@ -2893,18 +2883,18 @@ export class ContainerRuntime extends TypedEventEmitter {
2893
2883
  * @remarks - If the "Offline Load" feature is enabled, the batchId is included in the resubmitted messages,
2894
2884
  * for correlation to detect container forking.
2895
2885
  */
2896
- reSubmitBatch(batch, batchId, staged) {
2886
+ reSubmitBatch(batch, { batchId, staged, squash }) {
2897
2887
  this.batchRunner.run(() => {
2898
2888
  for (const message of batch) {
2899
- this.reSubmit(message);
2889
+ this.reSubmit(message, squash);
2900
2890
  }
2901
2891
  });
2902
2892
  // Only include Batch ID if "Offline Load" feature is enabled
2903
2893
  // It's only needed to identify batches across container forks arising from misuse of offline load.
2904
- this.flush(this.offlineEnabled ? batchId : undefined, staged);
2894
+ this.flush({ batchId: this.offlineEnabled ? batchId : undefined, staged });
2905
2895
  }
2906
- reSubmit(message) {
2907
- this.reSubmitCore(message.runtimeOp, message.localOpMetadata, message.opMetadata);
2896
+ reSubmit(message, squash) {
2897
+ this.reSubmitCore(message.runtimeOp, message.localOpMetadata, message.opMetadata, squash);
2908
2898
  }
2909
2899
  /**
2910
2900
  * Finds the right store and asks it to resubmit the message. This typically happens when we
@@ -2913,7 +2903,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2913
2903
  * @param message - The original LocalContainerRuntimeMessage.
2914
2904
  * @param localOpMetadata - The local metadata associated with the original message.
2915
2905
  */
2916
- reSubmitCore(message, localOpMetadata, opMetadata) {
2906
+ reSubmitCore(message, localOpMetadata, opMetadata, squash) {
2917
2907
  assert(this._summarizer === undefined, 0x8f2 /* Summarizer never reconnects so should never resubmit */);
2918
2908
  switch (message.type) {
2919
2909
  case ContainerMessageType.FluidDataStoreOp:
@@ -2921,7 +2911,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2921
2911
  case ContainerMessageType.Alias: {
2922
2912
  // For Operations, call resubmitDataStoreOp which will find the right store
2923
2913
  // and trigger resubmission on it.
2924
- this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata);
2914
+ this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata, squash);
2925
2915
  break;
2926
2916
  }
2927
2917
  case ContainerMessageType.IdAllocation: {