@fluidframework/container-runtime 2.0.0-internal.2.1.1 → 2.0.0-internal.2.2.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 (161) hide show
  1. package/.eslintrc.js +1 -1
  2. package/dist/blobManager.d.ts +20 -5
  3. package/dist/blobManager.d.ts.map +1 -1
  4. package/dist/blobManager.js +57 -15
  5. package/dist/blobManager.js.map +1 -1
  6. package/dist/containerRuntime.d.ts +39 -40
  7. package/dist/containerRuntime.d.ts.map +1 -1
  8. package/dist/containerRuntime.js +115 -278
  9. package/dist/containerRuntime.js.map +1 -1
  10. package/dist/dataStoreContext.d.ts +3 -1
  11. package/dist/dataStoreContext.d.ts.map +1 -1
  12. package/dist/dataStoreContext.js +21 -3
  13. package/dist/dataStoreContext.js.map +1 -1
  14. package/dist/dataStores.d.ts +8 -5
  15. package/dist/dataStores.d.ts.map +1 -1
  16. package/dist/dataStores.js +26 -13
  17. package/dist/dataStores.js.map +1 -1
  18. package/dist/garbageCollection.d.ts +15 -17
  19. package/dist/garbageCollection.d.ts.map +1 -1
  20. package/dist/garbageCollection.js +92 -106
  21. package/dist/garbageCollection.js.map +1 -1
  22. package/dist/garbageCollectionConstants.d.ts +19 -0
  23. package/dist/garbageCollectionConstants.d.ts.map +1 -0
  24. package/dist/garbageCollectionConstants.js +34 -0
  25. package/dist/garbageCollectionConstants.js.map +1 -0
  26. package/dist/gcSweepReadyUsageDetection.js +2 -2
  27. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  28. package/dist/index.d.ts +4 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +8 -6
  31. package/dist/index.js.map +1 -1
  32. package/dist/opLifecycle/batchManager.d.ts +30 -0
  33. package/dist/opLifecycle/batchManager.d.ts.map +1 -0
  34. package/dist/{batchManager.js → opLifecycle/batchManager.js} +25 -10
  35. package/dist/opLifecycle/batchManager.js.map +1 -0
  36. package/dist/opLifecycle/definitions.d.ts +40 -0
  37. package/dist/opLifecycle/definitions.d.ts.map +1 -0
  38. package/dist/opLifecycle/definitions.js +7 -0
  39. package/dist/opLifecycle/definitions.js.map +1 -0
  40. package/dist/opLifecycle/index.d.ts +12 -0
  41. package/dist/opLifecycle/index.d.ts.map +1 -0
  42. package/dist/opLifecycle/index.js +21 -0
  43. package/dist/opLifecycle/index.js.map +1 -0
  44. package/dist/opLifecycle/opCompressor.d.ts +18 -0
  45. package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
  46. package/dist/opLifecycle/opCompressor.js +53 -0
  47. package/dist/opLifecycle/opCompressor.js.map +1 -0
  48. package/dist/opLifecycle/opDecompressor.d.ts +20 -0
  49. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
  50. package/dist/opLifecycle/opDecompressor.js +72 -0
  51. package/dist/opLifecycle/opDecompressor.js.map +1 -0
  52. package/dist/opLifecycle/opSplitter.d.ts +17 -0
  53. package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
  54. package/dist/opLifecycle/opSplitter.js +61 -0
  55. package/dist/opLifecycle/opSplitter.js.map +1 -0
  56. package/dist/opLifecycle/outbox.d.ts +47 -0
  57. package/dist/opLifecycle/outbox.d.ts.map +1 -0
  58. package/dist/opLifecycle/outbox.js +153 -0
  59. package/dist/opLifecycle/outbox.js.map +1 -0
  60. package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  61. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  62. package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
  63. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
  64. package/dist/packageVersion.d.ts +1 -1
  65. package/dist/packageVersion.js +1 -1
  66. package/dist/packageVersion.js.map +1 -1
  67. package/dist/summaryFormat.js +2 -2
  68. package/dist/summaryFormat.js.map +1 -1
  69. package/lib/blobManager.d.ts +20 -5
  70. package/lib/blobManager.d.ts.map +1 -1
  71. package/lib/blobManager.js +59 -17
  72. package/lib/blobManager.js.map +1 -1
  73. package/lib/containerRuntime.d.ts +39 -40
  74. package/lib/containerRuntime.d.ts.map +1 -1
  75. package/lib/containerRuntime.js +113 -275
  76. package/lib/containerRuntime.js.map +1 -1
  77. package/lib/dataStoreContext.d.ts +3 -1
  78. package/lib/dataStoreContext.d.ts.map +1 -1
  79. package/lib/dataStoreContext.js +23 -5
  80. package/lib/dataStoreContext.js.map +1 -1
  81. package/lib/dataStores.d.ts +8 -5
  82. package/lib/dataStores.d.ts.map +1 -1
  83. package/lib/dataStores.js +28 -15
  84. package/lib/dataStores.js.map +1 -1
  85. package/lib/garbageCollection.d.ts +15 -17
  86. package/lib/garbageCollection.d.ts.map +1 -1
  87. package/lib/garbageCollection.js +72 -86
  88. package/lib/garbageCollection.js.map +1 -1
  89. package/lib/garbageCollectionConstants.d.ts +19 -0
  90. package/lib/garbageCollectionConstants.d.ts.map +1 -0
  91. package/lib/garbageCollectionConstants.js +31 -0
  92. package/lib/garbageCollectionConstants.js.map +1 -0
  93. package/lib/gcSweepReadyUsageDetection.js +1 -1
  94. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  95. package/lib/index.d.ts +4 -2
  96. package/lib/index.d.ts.map +1 -1
  97. package/lib/index.js +3 -2
  98. package/lib/index.js.map +1 -1
  99. package/lib/opLifecycle/batchManager.d.ts +30 -0
  100. package/lib/opLifecycle/batchManager.d.ts.map +1 -0
  101. package/lib/{batchManager.js → opLifecycle/batchManager.js} +25 -10
  102. package/lib/opLifecycle/batchManager.js.map +1 -0
  103. package/lib/opLifecycle/definitions.d.ts +40 -0
  104. package/lib/opLifecycle/definitions.d.ts.map +1 -0
  105. package/lib/opLifecycle/definitions.js +6 -0
  106. package/lib/opLifecycle/definitions.js.map +1 -0
  107. package/lib/opLifecycle/index.d.ts +12 -0
  108. package/lib/opLifecycle/index.d.ts.map +1 -0
  109. package/lib/opLifecycle/index.js +11 -0
  110. package/lib/opLifecycle/index.js.map +1 -0
  111. package/lib/opLifecycle/opCompressor.d.ts +18 -0
  112. package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
  113. package/lib/opLifecycle/opCompressor.js +49 -0
  114. package/lib/opLifecycle/opCompressor.js.map +1 -0
  115. package/lib/opLifecycle/opDecompressor.d.ts +20 -0
  116. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
  117. package/lib/opLifecycle/opDecompressor.js +68 -0
  118. package/lib/opLifecycle/opDecompressor.js.map +1 -0
  119. package/lib/opLifecycle/opSplitter.d.ts +17 -0
  120. package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
  121. package/lib/opLifecycle/opSplitter.js +57 -0
  122. package/lib/opLifecycle/opSplitter.js.map +1 -0
  123. package/lib/opLifecycle/outbox.d.ts +47 -0
  124. package/lib/opLifecycle/outbox.d.ts.map +1 -0
  125. package/lib/opLifecycle/outbox.js +149 -0
  126. package/lib/opLifecycle/outbox.js.map +1 -0
  127. package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  128. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  129. package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
  130. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
  131. package/lib/packageVersion.d.ts +1 -1
  132. package/lib/packageVersion.js +1 -1
  133. package/lib/packageVersion.js.map +1 -1
  134. package/lib/summaryFormat.js +1 -1
  135. package/lib/summaryFormat.js.map +1 -1
  136. package/package.json +35 -21
  137. package/prettier.config.cjs +8 -0
  138. package/src/blobManager.ts +74 -19
  139. package/src/containerRuntime.ts +144 -341
  140. package/src/dataStoreContext.ts +33 -5
  141. package/src/dataStores.ts +32 -16
  142. package/src/garbageCollection.ts +106 -82
  143. package/src/garbageCollectionConstants.ts +35 -0
  144. package/src/gcSweepReadyUsageDetection.ts +1 -1
  145. package/src/index.ts +6 -4
  146. package/src/{batchManager.ts → opLifecycle/batchManager.ts} +41 -23
  147. package/src/opLifecycle/definitions.ts +44 -0
  148. package/src/opLifecycle/index.ts +17 -0
  149. package/src/opLifecycle/opCompressor.ts +64 -0
  150. package/src/opLifecycle/opDecompressor.ts +84 -0
  151. package/src/opLifecycle/opSplitter.ts +78 -0
  152. package/src/opLifecycle/outbox.ts +204 -0
  153. package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
  154. package/src/packageVersion.ts +1 -1
  155. package/src/summaryFormat.ts +1 -1
  156. package/dist/batchManager.d.ts +0 -36
  157. package/dist/batchManager.d.ts.map +0 -1
  158. package/dist/batchManager.js.map +0 -1
  159. package/lib/batchManager.d.ts +0 -36
  160. package/lib/batchManager.d.ts.map +0 -1
  161. package/lib/batchManager.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
2
- import { assert, Trace, TypedEventEmitter, unreachableCase, IsoBuffer, } from "@fluidframework/common-utils";
2
+ import { assert, Trace, TypedEventEmitter, unreachableCase, } from "@fluidframework/common-utils";
3
3
  import { ChildLogger, raiseConnectedEvent, PerformanceEvent, TaggedLoggerAdapter, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
4
4
  import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
5
5
  import { readAndParse } from "@fluidframework/driver-utils";
@@ -9,14 +9,12 @@ import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definition
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";
12
- import { compress, decompress } from "lz4js";
13
12
  import { ContainerFluidHandleContext } from "./containerHandleContext";
14
13
  import { FluidDataStoreRegistry } from "./dataStoreRegistry";
15
14
  import { Summarizer } from "./summarizer";
16
15
  import { SummaryManager } from "./summaryManager";
17
16
  import { ReportOpPerfTelemetry, } from "./connectionTelemetry";
18
17
  import { PendingStateManager, } from "./pendingStateManager";
19
- import { BatchManager } from "./batchManager";
20
18
  import { pkgVersion } from "./packageVersion";
21
19
  import { BlobManager } from "./blobManager";
22
20
  import { DataStores, getSummaryForDatastores } from "./dataStores";
@@ -26,11 +24,13 @@ import { OrderedClientCollection, OrderedClientElection } from "./orderedClientE
26
24
  import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
27
25
  import { formExponentialFn, Throttler } from "./throttler";
28
26
  import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
29
- import { GarbageCollector, GCNodeType, gcTreeKey, testTombstoneKey, } from "./garbageCollection";
27
+ import { GarbageCollector, GCNodeType, } from "./garbageCollection";
28
+ import { gcTreeKey, } from "./garbageCollectionConstants";
30
29
  import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
31
30
  import { BindBatchTracker } from "./batchTracker";
32
31
  import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
33
32
  import { ScheduleManager } from "./scheduleManager";
33
+ import { OpCompressor, OpDecompressor, Outbox, OpSplitter, RemoteMessageProcessor, } from "./opLifecycle";
34
34
  export var ContainerMessageType;
35
35
  (function (ContainerMessageType) {
36
36
  // An op to be delivered to store
@@ -76,6 +76,13 @@ export var RuntimeHeaders;
76
76
  /** True if the request is coming from an IFluidHandle. */
77
77
  RuntimeHeaders["viaHandle"] = "viaHandle";
78
78
  })(RuntimeHeaders || (RuntimeHeaders = {}));
79
+ /**
80
+ * Available compression algorithms for op compression.
81
+ */
82
+ export var CompressionAlgorithms;
83
+ (function (CompressionAlgorithms) {
84
+ CompressionAlgorithms["lz4"] = "lz4";
85
+ })(CompressionAlgorithms || (CompressionAlgorithms = {}));
79
86
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
80
87
  const defaultFlushMode = FlushMode.TurnBased;
81
88
  // The actual limit is 1Mb (socket.io and Kafka limits)
@@ -102,47 +109,6 @@ export var RuntimeMessage;
102
109
  export function isRuntimeMessage(message) {
103
110
  return Object.values(RuntimeMessage).includes(message.type);
104
111
  }
105
- /**
106
- * Unpacks runtime messages
107
- *
108
- * @remarks This API makes no promises regarding backward-compatibility. This is internal API.
109
- * @param message - message (as it observed in storage / service)
110
- * @returns unpacked runtime message
111
- *
112
- * @internal
113
- */
114
- export function unpackRuntimeMessage(message) {
115
- var _a;
116
- if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
117
- const contents = IsoBuffer.from(message.contents.contents, "base64");
118
- const decompressedMessage = decompress(contents);
119
- const intoString = new TextDecoder().decode(decompressedMessage);
120
- const asObj = JSON.parse(intoString);
121
- message.contents.contents = asObj;
122
- message.metadata.compressed = false;
123
- }
124
- if (message.type === MessageType.Operation) {
125
- // legacy op format?
126
- if (message.contents.address !== undefined && message.contents.type === undefined) {
127
- message.type = ContainerMessageType.FluidDataStoreOp;
128
- }
129
- else {
130
- // new format
131
- const innerContents = message.contents;
132
- assert(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
133
- message.type = innerContents.type;
134
- message.contents = innerContents.contents;
135
- }
136
- return true;
137
- }
138
- else {
139
- // Legacy format, but it's already "unpacked",
140
- // i.e. message.type is actually ContainerMessageType.
141
- // Or it's non-runtime message.
142
- // Nothing to do in such case.
143
- return false;
144
- }
145
- }
146
112
  /**
147
113
  * Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
148
114
  * special-case for document dirty state. Ultimately we should have no special-cases from the
@@ -168,6 +134,9 @@ export function getDeviceSpec() {
168
134
  * It will define the store level mappings.
169
135
  */
170
136
  export class ContainerRuntime extends TypedEventEmitter {
137
+ /**
138
+ * @internal
139
+ */
171
140
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
172
141
  var _a, _b, _c, _d, _e, _f;
173
142
  if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
@@ -185,7 +154,6 @@ export class ContainerRuntime extends TypedEventEmitter {
185
154
  this.flushMicroTaskExists = false;
186
155
  this.savedOps = [];
187
156
  this.consecutiveReconnects = 0;
188
- this.compressedOpCount = 0;
189
157
  this._disposed = false;
190
158
  this.emitDirtyDocumentEvent = true;
191
159
  this.defaultTelemetrySignalSampleCount = 100;
@@ -223,9 +191,29 @@ export class ContainerRuntime extends TypedEventEmitter {
223
191
  throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
224
192
  }
225
193
  };
194
+ let loadSummaryNumber;
195
+ // Get the container creation metadata. For new container, we initialize these. For existing containers,
196
+ // get the values from the metadata blob.
197
+ if (existing) {
198
+ this.createContainerMetadata = {
199
+ createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
200
+ createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
201
+ };
202
+ // summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
203
+ // the count is reset to 0.
204
+ loadSummaryNumber = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _b !== void 0 ? _b : 0;
205
+ }
206
+ else {
207
+ this.createContainerMetadata = {
208
+ createContainerRuntimeVersion: pkgVersion,
209
+ createContainerTimestamp: Date.now(),
210
+ };
211
+ loadSummaryNumber = 0;
212
+ }
213
+ this.nextSummaryNumber = loadSummaryNumber + 1;
226
214
  this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
227
215
  this._connected = this.context.connected;
228
- this.chunkMap = new Map(chunks);
216
+ this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
229
217
  this.handleContext = new ContainerFluidHandleContext("", this);
230
218
  this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
231
219
  if (this.summaryConfiguration.state === "enabled") {
@@ -237,18 +225,11 @@ export class ContainerRuntime extends TypedEventEmitter {
237
225
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
238
226
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
239
227
  this.maxConsecutiveReconnects =
240
- (_b = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _b !== void 0 ? _b : this.defaultMaxConsecutiveReconnects;
228
+ (_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
241
229
  this._flushMode = runtimeOptions.flushMode;
242
- // Provide lower soft limit - we want to have some number of ops to get efficiency in compression
243
- // & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
244
- // latency of processing a batch.
245
- // So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
246
- // payloads. That number represents final (compressed) bits (once compression is implemented).
247
- this.pendingAttachBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes, 64 * 1024);
248
- this.pendingBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes);
249
230
  const pendingRuntimeState = context.pendingLocalState;
250
- const baseSnapshot = (_c = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _c !== void 0 ? _c : context.baseSnapshot;
251
- const maxSnapshotCacheDurationMs = (_e = (_d = this._storage) === null || _d === void 0 ? void 0 : _d.policies) === null || _e === void 0 ? void 0 : _e.maximumCacheDurationMs;
231
+ const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
232
+ const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
252
233
  if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
253
234
  // This is a runtime enforcement of what's already explicit in the policy's type itself,
254
235
  // which dictates the value is either undefined or exactly 5 days in ms.
@@ -262,6 +243,7 @@ export class ContainerRuntime extends TypedEventEmitter {
262
243
  baseLogger: this.mc.logger,
263
244
  existing,
264
245
  metadata,
246
+ createContainerMetadata: this.createContainerMetadata,
265
247
  isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
266
248
  getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
267
249
  getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
@@ -296,7 +278,6 @@ export class ContainerRuntime extends TypedEventEmitter {
296
278
  }
297
279
  }, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
298
280
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
299
- this.deltaSender = this.deltaManager;
300
281
  this.pendingStateManager = new PendingStateManager({
301
282
  applyStashedOp: this.applyStashedOp.bind(this),
302
283
  clientId: () => this.clientId,
@@ -307,8 +288,18 @@ export class ContainerRuntime extends TypedEventEmitter {
307
288
  rollback: this.rollback.bind(this),
308
289
  orderSequentially: this.orderSequentially.bind(this),
309
290
  }, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
291
+ this.outbox = new Outbox({
292
+ shouldSend: () => this.canSendOps(),
293
+ pendingStateManager: this.pendingStateManager,
294
+ containerContext: this.context,
295
+ compressor: new OpCompressor(this.mc.logger),
296
+ config: {
297
+ compressionOptions: runtimeOptions.compressionOptions,
298
+ maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
299
+ },
300
+ });
310
301
  this.context.quorum.on("removeMember", (clientId) => {
311
- this.clearPartialChunks(clientId);
302
+ this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
312
303
  });
313
304
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
314
305
  this.dirtyContainer = this.context.attachState !== AttachState.Attached
@@ -376,26 +367,6 @@ export class ContainerRuntime extends TypedEventEmitter {
376
367
  });
377
368
  // logging hardware telemetry
378
369
  logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
379
- let loadSummaryNumber;
380
- // Get the container creation metadata. For new container, we initialize these. For existing containers,
381
- // get the values from the metadata blob.
382
- if (existing) {
383
- this.createContainerMetadata = {
384
- createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
385
- createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
386
- };
387
- // summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
388
- // the count is reset to 0.
389
- loadSummaryNumber = (_f = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _f !== void 0 ? _f : 0;
390
- }
391
- else {
392
- this.createContainerMetadata = {
393
- createContainerRuntimeVersion: pkgVersion,
394
- createContainerTimestamp: Date.now(),
395
- };
396
- loadSummaryNumber = 0;
397
- }
398
- this.nextSummaryNumber = loadSummaryNumber + 1;
399
370
  this.logger.sendTelemetryEvent(Object.assign(Object.assign(Object.assign({ eventName: "ContainerLoadStats" }, this.createContainerMetadata), this.dataStores.containerLoadStats), { summaryNumber: loadSummaryNumber, summaryFormatVersion: metadata === null || metadata === void 0 ? void 0 : metadata.summaryFormatVersion, disableIsolatedChannels: metadata === null || metadata === void 0 ? void 0 : metadata.disableIsolatedChannels, gcVersion: metadata === null || metadata === void 0 ? void 0 : metadata.gcFeature }));
400
371
  ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
401
372
  BindBatchTracker(this, this.logger);
@@ -409,8 +380,10 @@ export class ContainerRuntime extends TypedEventEmitter {
409
380
  * @param requestHandler - Request handlers for the container runtime
410
381
  * @param runtimeOptions - Additional options to be passed to the runtime
411
382
  * @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
383
+ * @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
384
+ * allows mixin classes to leverage this method to define their own async initializer.
412
385
  */
413
- static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing) {
386
+ static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
414
387
  var _a, _b, _c;
415
388
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
416
389
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
@@ -421,7 +394,10 @@ export class ContainerRuntime extends TypedEventEmitter {
421
394
  runtimeVersion: pkgVersion,
422
395
  },
423
396
  });
424
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
397
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
398
+ minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
399
+ compressionAlgorithm: CompressionAlgorithms.lz4
400
+ }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
425
401
  const pendingRuntimeState = context.pendingLocalState;
426
402
  const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
427
403
  const storage = !pendingRuntimeState ?
@@ -470,7 +446,7 @@ export class ContainerRuntime extends TypedEventEmitter {
470
446
  }
471
447
  }
472
448
  }
473
- const runtime = new ContainerRuntime(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
449
+ const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
474
450
  summaryOptions,
475
451
  gcOptions,
476
452
  loadSequenceNumberVerification,
@@ -484,7 +460,8 @@ export class ContainerRuntime extends TypedEventEmitter {
484
460
  // delete these once runtime has seen them to save space
485
461
  pendingRuntimeState.savedOps = [];
486
462
  }
487
- await runtime.getSnapshotBlobs();
463
+ // Initialize the base state of the runtime before it's returned.
464
+ await runtime.initializeBaseState();
488
465
  return runtime;
489
466
  }
490
467
  get options() {
@@ -533,9 +510,6 @@ export class ContainerRuntime extends TypedEventEmitter {
533
510
  return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
534
511
  }
535
512
  get disposed() { return this._disposed; }
536
- get emptyBatch() {
537
- return this.pendingBatch.empty && this.pendingAttachBatch.empty;
538
- }
539
513
  get summarizer() {
540
514
  assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
541
515
  return this._summarizer;
@@ -591,6 +565,13 @@ export class ContainerRuntime extends TypedEventEmitter {
591
565
  ? this.summaryConfiguration.initialSummarizerDelayMs
592
566
  : 0;
593
567
  }
568
+ /**
569
+ * Initializes the state from the base snapshot this container runtime loaded from.
570
+ */
571
+ async initializeBaseState() {
572
+ await this.initializeBaseSnapshotBlobs();
573
+ await this.garbageCollector.initializeBaseState();
574
+ }
594
575
  dispose(error) {
595
576
  var _a;
596
577
  if (this._disposed) {
@@ -736,8 +717,8 @@ export class ContainerRuntime extends TypedEventEmitter {
736
717
  addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
737
718
  var _a;
738
719
  this.addMetadataToSummary(summaryTree);
739
- if (this.chunkMap.size > 0) {
740
- const content = JSON.stringify([...this.chunkMap]);
720
+ if (this.remoteMessageProcessor.partialMessages.size > 0) {
721
+ const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
741
722
  addBlobToSummary(summaryTree, chunksBlobName, content);
742
723
  }
743
724
  const dataStoreAliases = this.dataStores.aliases;
@@ -901,31 +882,21 @@ export class ContainerRuntime extends TypedEventEmitter {
901
882
  process(messageArg, local) {
902
883
  var _a;
903
884
  this.verifyNotClosed();
904
- // Do shallow copy of message, as methods below will modify it.
905
- // There might be multiple container instances receiving same message
906
- // We do not need to make deep copy, as each layer will just replace message.content itself,
907
- // but would not modify contents details
908
- let message = Object.assign({}, messageArg);
909
- // back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
910
- // System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
911
- // Old ops may contain empty string (I assume noops).
912
- if (typeof message.contents === "string" && message.contents !== "") {
913
- message.contents = JSON.parse(message.contents);
914
- }
915
- // Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
916
- // This format was not shipped to production workflows.
917
- const runtimeMessage = unpackRuntimeMessage(message);
918
885
  if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
919
886
  this.savedOps.push(messageArg);
920
887
  }
888
+ // Whether or not the message is actually a runtime message.
889
+ // It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
890
+ // or something different, like a system message.
891
+ const runtimeMessage = messageArg.type === MessageType.Operation;
892
+ // Do shallow copy of message, as the processing flow will modify it.
893
+ const messageCopy = Object.assign({}, messageArg);
894
+ const message = this.remoteMessageProcessor.process(messageCopy);
921
895
  // Surround the actual processing of the operation with messages to the schedule manager indicating
922
896
  // the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
923
897
  // messages once a batch has been fully processed.
924
898
  this.scheduleManager.beforeOpProcessing(message);
925
899
  try {
926
- // Chunk processing must come first given that we will transform the message to the unchunked version
927
- // once all pieces are available
928
- message = this.processRemoteChunkedMessage(message);
929
900
  let localOpMetadata;
930
901
  if (local && runtimeMessage) {
931
902
  localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
@@ -1039,83 +1010,8 @@ export class ContainerRuntime extends TypedEventEmitter {
1039
1010
  */
1040
1011
  flush() {
1041
1012
  assert(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
1042
- this.flushBatch(this.pendingAttachBatch.popBatch());
1043
- this.flushBatch(this.pendingBatch.popBatch());
1044
- assert(this.emptyBatch, 0x3cf /* reentrancy */);
1045
- }
1046
- flushBatch(batch) {
1047
- const length = batch.length;
1048
- if (length > 1) {
1049
- batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
1050
- batch[length - 1].metadata = Object.assign(Object.assign({}, batch[length - 1].metadata), { batch: false });
1051
- // This assert fires for the following reason (there might be more cases like that):
1052
- // AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
1053
- // i.e. in the middle of op processing!
1054
- // Sending ops while processing ops is not good idea - it's not defined when
1055
- // referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
1056
- // If we send ops in response to processing multiple ops, then we for sure hit this assert!
1057
- // Tracked via ADO #1834
1058
- // assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
1059
- // "Batch should be generated synchronously, without processing ops in the middle!");
1060
- }
1061
- let clientSequenceNumber = -1;
1062
- // Did we disconnect in the middle of turn-based batch?
1063
- // If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
1064
- if (this.canSendOps()) {
1065
- if (this.context.submitBatchFn !== undefined) {
1066
- const batchToSend = [];
1067
- for (const message of batch) {
1068
- let contents = message.contents;
1069
- let metadata = message.metadata;
1070
- if (this.runtimeOptions.compressionOptions.minimumSize &&
1071
- this.runtimeOptions.compressionOptions.minimumSize < message.contents.length) {
1072
- this.compressedOpCount++;
1073
- const copiedMessage = Object.assign({}, message.deserializedContent);
1074
- const compressionStart = Date.now();
1075
- const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(copiedMessage.contents));
1076
- const compressedContents = compress(contentsAsBuffer);
1077
- const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
1078
- const duration = Date.now() - compressionStart;
1079
- if (this.compressedOpCount % 100) {
1080
- this.mc.logger.sendPerformanceEvent({
1081
- eventName: "compressedOp",
1082
- duration,
1083
- sizeBeforeCompression: message.contents.length,
1084
- sizeAfterCompression: compressedContent.length,
1085
- });
1086
- }
1087
- copiedMessage.contents = compressedContent;
1088
- const stringifiedContents = JSON.stringify(copiedMessage);
1089
- if (stringifiedContents.length < message.contents.length) {
1090
- contents = JSON.stringify(copiedMessage);
1091
- metadata = Object.assign(Object.assign({}, message.metadata), { compressed: true });
1092
- }
1093
- }
1094
- batchToSend.push({ contents, metadata });
1095
- }
1096
- // returns clientSequenceNumber of last message in a batch
1097
- clientSequenceNumber = this.context.submitBatchFn(batchToSend);
1098
- }
1099
- else {
1100
- // Legacy path - supporting old loader versions. Can be removed only when LTS moves above
1101
- // version that has support for batches (submitBatchFn)
1102
- for (const message of batch) {
1103
- clientSequenceNumber = this.context.submitFn(MessageType.Operation, message.deserializedContent, true, // batch
1104
- message.metadata);
1105
- }
1106
- this.deltaSender.flush();
1107
- }
1108
- // Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
1109
- clientSequenceNumber -= batch.length - 1;
1110
- assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
1111
- }
1112
- // Let the PendingStateManager know that a message was submitted.
1113
- // In future, need to shift toward keeping batch as a whole!
1114
- for (const message of batch) {
1115
- this.pendingStateManager.onSubmitMessage(message.deserializedContent.type, clientSequenceNumber, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
1116
- clientSequenceNumber++;
1117
- }
1118
- this.pendingStateManager.onFlush();
1013
+ this.outbox.flush();
1014
+ assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
1119
1015
  }
1120
1016
  orderSequentially(callback) {
1121
1017
  let checkpoint;
@@ -1123,7 +1019,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1123
1019
  // Note: we are not touching this.pendingAttachBatch here, for two reasons:
1124
1020
  // 1. It would not help, as we flush attach ops as they become available.
1125
1021
  // 2. There is no way to undo process of data store creation.
1126
- checkpoint = this.pendingBatch.checkpoint();
1022
+ checkpoint = this.outbox.checkpoint().mainBatch;
1127
1023
  }
1128
1024
  try {
1129
1025
  this._orderSequentiallyCalls++;
@@ -1343,33 +1239,27 @@ export class ContainerRuntime extends TypedEventEmitter {
1343
1239
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1344
1240
  // always referenced, so the used routes is only self-route (empty string).
1345
1241
  this.summarizerNode.updateUsedRoutes([""]);
1242
+ const blobManagerUsedRoutes = [];
1346
1243
  const dataStoreUsedRoutes = [];
1347
1244
  for (const route of usedRoutes) {
1348
- if (route.split("/")[1] !== BlobManager.basePath) {
1245
+ if (this.isBlobPath(route)) {
1246
+ blobManagerUsedRoutes.push(route);
1247
+ }
1248
+ else {
1349
1249
  dataStoreUsedRoutes.push(route);
1350
1250
  }
1351
1251
  }
1352
- return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
1252
+ this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
1253
+ this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
1353
1254
  }
1354
1255
  /**
1355
- * When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
1356
- * scenarios with accessing deleted content.
1357
- * @param unusedRoutes - The routes that are unused in all data stores and blobs in this Container.
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.
1358
1261
  */
1359
- deleteUnusedRoutes(unusedRoutes) {
1360
- var _a;
1361
- /**
1362
- * When running GC in tombstone mode, this is called to tombstone datastore routes that are unused. This
1363
- * enables testing scenarios without actually deleting content. The content acts as if it's deleted to the
1364
- * external user, but the internal runtime does not delete it in summarizes, etc.
1365
- */
1366
- const tombstone = (_a = this.mc.config.getBoolean(testTombstoneKey)) !== null && _a !== void 0 ? _a : false;
1367
- // TODO: add blobs
1368
- if (tombstone) {
1369
- // If blob routes are passed in here, tombstone will fail and hit an assert
1370
- this.dataStores.deleteUnusedRoutes(unusedRoutes, tombstone);
1371
- return;
1372
- }
1262
+ updateUnusedRoutes(unusedRoutes, tombstone) {
1373
1263
  const blobManagerUnusedRoutes = [];
1374
1264
  const dataStoreUnusedRoutes = [];
1375
1265
  for (const route of unusedRoutes) {
@@ -1380,8 +1270,8 @@ export class ContainerRuntime extends TypedEventEmitter {
1380
1270
  dataStoreUnusedRoutes.push(route);
1381
1271
  }
1382
1272
  }
1383
- this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
1384
- this.dataStores.deleteUnusedRoutes(dataStoreUnusedRoutes);
1273
+ this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
1274
+ this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
1385
1275
  }
1386
1276
  /**
1387
1277
  * Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
@@ -1461,7 +1351,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1461
1351
  const summaryNumberLogger = ChildLogger.create(summaryLogger, undefined, {
1462
1352
  all: { summaryNumber },
1463
1353
  });
1464
- assert(this.emptyBatch, 0x3d1 /* Can't trigger summary in the middle of a batch */);
1354
+ assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
1465
1355
  let latestSnapshotVersionId;
1466
1356
  if (refreshLatestAck) {
1467
1357
  const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
@@ -1634,40 +1524,8 @@ export class ContainerRuntime extends TypedEventEmitter {
1634
1524
  this.deltaManager.inbound.resume();
1635
1525
  }
1636
1526
  }
1637
- processRemoteChunkedMessage(message) {
1638
- if (message.type !== ContainerMessageType.ChunkedOp) {
1639
- return message;
1640
- }
1641
- const clientId = message.clientId;
1642
- const chunkedContent = message.contents;
1643
- this.addChunk(clientId, chunkedContent);
1644
- if (chunkedContent.chunkId === chunkedContent.totalChunks) {
1645
- const newMessage = Object.assign({}, message);
1646
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1647
- const serializedContent = this.chunkMap.get(clientId).join("");
1648
- newMessage.contents = JSON.parse(serializedContent);
1649
- newMessage.type = chunkedContent.originalType;
1650
- this.clearPartialChunks(clientId);
1651
- return newMessage;
1652
- }
1653
- return message;
1654
- }
1655
- addChunk(clientId, chunkedContent) {
1656
- let map = this.chunkMap.get(clientId);
1657
- if (map === undefined) {
1658
- map = [];
1659
- this.chunkMap.set(clientId, map);
1660
- }
1661
- assert(chunkedContent.chunkId === map.length + 1, 0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
1662
- map.push(chunkedContent.contents);
1663
- }
1664
- clearPartialChunks(clientId) {
1665
- if (this.chunkMap.has(clientId)) {
1666
- this.chunkMap.delete(clientId);
1667
- }
1668
- }
1669
1527
  hasPendingMessages() {
1670
- return this.pendingStateManager.hasPendingMessages() || !this.emptyBatch;
1528
+ return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
1671
1529
  }
1672
1530
  updateDocumentDirtyState(dirty) {
1673
1531
  if (this.attachState !== AttachState.Attached) {
@@ -1711,7 +1569,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1711
1569
  const deserializedContent = { type, contents };
1712
1570
  const serializedContent = JSON.stringify(deserializedContent);
1713
1571
  if (this.deltaManager.readOnlyInfo.readonly) {
1714
- this.logger.sendErrorEvent({ eventName: "SubmitOpInReadonly" });
1572
+ this.logger.sendTelemetryEvent({ eventName: "SubmitOpInReadonly", connected: this.connected });
1715
1573
  }
1716
1574
  const message = {
1717
1575
  contents: serializedContent,
@@ -1742,43 +1600,23 @@ export class ContainerRuntime extends TypedEventEmitter {
1742
1600
  // Please note that this does not change file format, so it can be disabled in the future if this
1743
1601
  // optimization no longer makes sense (for example, batch compression may make it less appealing).
1744
1602
  if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
1745
- this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
1746
- if (!this.pendingAttachBatch.push(message)) {
1747
- // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
1748
- // when queue is not empty.
1749
- // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
1750
- this.flushBatch(this.pendingAttachBatch.popBatch());
1751
- if (!this.pendingAttachBatch.push(message)) {
1752
- throw new GenericError("BatchTooLarge",
1753
- /* error */ undefined, {
1754
- opSize: message.contents.length,
1755
- count: this.pendingAttachBatch.length,
1756
- limit: this.pendingAttachBatch.limit,
1757
- });
1758
- }
1759
- }
1603
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
1604
+ this.outbox.submitAttach(message);
1760
1605
  }
1761
1606
  else {
1762
- if (!this.pendingBatch.push(message)) {
1763
- throw new GenericError("BatchTooLarge",
1764
- /* error */ undefined, {
1765
- opSize: message.contents.length,
1766
- count: this.pendingBatch.length,
1767
- limit: this.pendingBatch.limit,
1768
- });
1769
- }
1770
- if (!this.currentlyBatching()) {
1607
+ this.outbox.submit(message);
1608
+ }
1609
+ if (!this.currentlyBatching()) {
1610
+ this.flush();
1611
+ }
1612
+ else if (!this.flushMicroTaskExists) {
1613
+ this.flushMicroTaskExists = true;
1614
+ // 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
+ Promise.resolve().then(() => {
1617
+ this.flushMicroTaskExists = false;
1771
1618
  this.flush();
1772
- }
1773
- else if (!this.flushMicroTaskExists) {
1774
- this.flushMicroTaskExists = true;
1775
- // Queue a microtask to detect the end of the turn and force a flush.
1776
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1777
- Promise.resolve().then(() => {
1778
- this.flushMicroTaskExists = false;
1779
- this.flush();
1780
- });
1781
- }
1619
+ }).catch((error) => { this.closeFn(error); });
1782
1620
  }
1783
1621
  }
1784
1622
  catch (error) {
@@ -1793,7 +1631,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1793
1631
  this.verifyNotClosed();
1794
1632
  assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
1795
1633
  // System message should not be sent in the middle of the batch.
1796
- assert(this.emptyBatch, 0x3d4 /* System op in the middle of a batch */);
1634
+ assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
1797
1635
  // back-compat: ADO #1385: Make this call unconditional in the future
1798
1636
  return this.context.submitSummaryFn !== undefined
1799
1637
  ? this.context.submitSummaryFn(contents)
@@ -1927,7 +1765,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1927
1765
  this.baseSnapshotBlobs = SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
1928
1766
  }
1929
1767
  }
1930
- async getSnapshotBlobs() {
1768
+ async initializeBaseSnapshotBlobs() {
1931
1769
  var _a;
1932
1770
  if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
1933
1771
  this.attachState !== AttachState.Attached || this.context.pendingLocalState) {