@fluidframework/container-runtime 2.0.0-internal.1.0.0.83139 → 2.0.0-internal.1.1.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 (124) hide show
  1. package/.mocharc.js +12 -0
  2. package/dist/batchTracker.js +1 -1
  3. package/dist/batchTracker.js.map +1 -1
  4. package/dist/blobManager.d.ts +7 -1
  5. package/dist/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager.js +34 -17
  7. package/dist/blobManager.js.map +1 -1
  8. package/dist/containerRuntime.d.ts +3 -104
  9. package/dist/containerRuntime.d.ts.map +1 -1
  10. package/dist/containerRuntime.js +83 -395
  11. package/dist/containerRuntime.js.map +1 -1
  12. package/dist/dataStore.d.ts +1 -1
  13. package/dist/dataStore.d.ts.map +1 -1
  14. package/dist/dataStore.js +2 -3
  15. package/dist/dataStore.js.map +1 -1
  16. package/dist/dataStoreContext.d.ts +3 -5
  17. package/dist/dataStoreContext.d.ts.map +1 -1
  18. package/dist/dataStoreContext.js +13 -23
  19. package/dist/dataStoreContext.js.map +1 -1
  20. package/dist/dataStores.d.ts +1 -1
  21. package/dist/dataStores.d.ts.map +1 -1
  22. package/dist/dataStores.js +3 -8
  23. package/dist/dataStores.js.map +1 -1
  24. package/dist/garbageCollection.d.ts +37 -6
  25. package/dist/garbageCollection.d.ts.map +1 -1
  26. package/dist/garbageCollection.js +61 -65
  27. package/dist/garbageCollection.js.map +1 -1
  28. package/dist/index.d.ts +2 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/packageVersion.d.ts +1 -1
  33. package/dist/packageVersion.d.ts.map +1 -1
  34. package/dist/packageVersion.js +1 -1
  35. package/dist/packageVersion.js.map +1 -1
  36. package/dist/pendingStateManager.d.ts.map +1 -1
  37. package/dist/pendingStateManager.js +15 -2
  38. package/dist/pendingStateManager.js.map +1 -1
  39. package/dist/runningSummarizer.js +1 -1
  40. package/dist/runningSummarizer.js.map +1 -1
  41. package/dist/scheduleManager.d.ts +28 -0
  42. package/dist/scheduleManager.d.ts.map +1 -0
  43. package/dist/scheduleManager.js +235 -0
  44. package/dist/scheduleManager.js.map +1 -0
  45. package/dist/summarizer.d.ts.map +1 -1
  46. package/dist/summarizer.js +20 -1
  47. package/dist/summarizer.js.map +1 -1
  48. package/dist/summaryCollection.js +1 -1
  49. package/dist/summaryCollection.js.map +1 -1
  50. package/dist/summaryGenerator.js +1 -1
  51. package/dist/summaryGenerator.js.map +1 -1
  52. package/dist/summaryManager.d.ts.map +1 -1
  53. package/dist/summaryManager.js +20 -5
  54. package/dist/summaryManager.js.map +1 -1
  55. package/lib/batchTracker.js +1 -1
  56. package/lib/batchTracker.js.map +1 -1
  57. package/lib/blobManager.d.ts +7 -1
  58. package/lib/blobManager.d.ts.map +1 -1
  59. package/lib/blobManager.js +35 -18
  60. package/lib/blobManager.js.map +1 -1
  61. package/lib/containerRuntime.d.ts +3 -104
  62. package/lib/containerRuntime.d.ts.map +1 -1
  63. package/lib/containerRuntime.js +84 -395
  64. package/lib/containerRuntime.js.map +1 -1
  65. package/lib/dataStore.d.ts +1 -1
  66. package/lib/dataStore.d.ts.map +1 -1
  67. package/lib/dataStore.js +2 -3
  68. package/lib/dataStore.js.map +1 -1
  69. package/lib/dataStoreContext.d.ts +3 -5
  70. package/lib/dataStoreContext.d.ts.map +1 -1
  71. package/lib/dataStoreContext.js +13 -23
  72. package/lib/dataStoreContext.js.map +1 -1
  73. package/lib/dataStores.d.ts +1 -1
  74. package/lib/dataStores.d.ts.map +1 -1
  75. package/lib/dataStores.js +3 -8
  76. package/lib/dataStores.js.map +1 -1
  77. package/lib/garbageCollection.d.ts +37 -6
  78. package/lib/garbageCollection.d.ts.map +1 -1
  79. package/lib/garbageCollection.js +47 -52
  80. package/lib/garbageCollection.js.map +1 -1
  81. package/lib/index.d.ts +2 -1
  82. package/lib/index.d.ts.map +1 -1
  83. package/lib/index.js +2 -1
  84. package/lib/index.js.map +1 -1
  85. package/lib/packageVersion.d.ts +1 -1
  86. package/lib/packageVersion.d.ts.map +1 -1
  87. package/lib/packageVersion.js +1 -1
  88. package/lib/packageVersion.js.map +1 -1
  89. package/lib/pendingStateManager.d.ts.map +1 -1
  90. package/lib/pendingStateManager.js +15 -2
  91. package/lib/pendingStateManager.js.map +1 -1
  92. package/lib/runningSummarizer.js +1 -1
  93. package/lib/runningSummarizer.js.map +1 -1
  94. package/lib/scheduleManager.d.ts +28 -0
  95. package/lib/scheduleManager.d.ts.map +1 -0
  96. package/lib/scheduleManager.js +231 -0
  97. package/lib/scheduleManager.js.map +1 -0
  98. package/lib/summarizer.d.ts.map +1 -1
  99. package/lib/summarizer.js +22 -3
  100. package/lib/summarizer.js.map +1 -1
  101. package/lib/summaryCollection.js +1 -1
  102. package/lib/summaryCollection.js.map +1 -1
  103. package/lib/summaryGenerator.js +1 -1
  104. package/lib/summaryGenerator.js.map +1 -1
  105. package/lib/summaryManager.d.ts.map +1 -1
  106. package/lib/summaryManager.js +20 -5
  107. package/lib/summaryManager.js.map +1 -1
  108. package/package.json +32 -19
  109. package/src/batchTracker.ts +1 -1
  110. package/src/blobManager.ts +43 -17
  111. package/src/containerRuntime.ts +113 -547
  112. package/src/dataStore.ts +1 -4
  113. package/src/dataStoreContext.ts +10 -25
  114. package/src/dataStores.ts +13 -19
  115. package/src/garbageCollection.ts +64 -69
  116. package/src/index.ts +1 -2
  117. package/src/packageVersion.ts +1 -1
  118. package/src/pendingStateManager.ts +18 -2
  119. package/src/runningSummarizer.ts +1 -1
  120. package/src/scheduleManager.ts +294 -0
  121. package/src/summarizer.ts +28 -3
  122. package/src/summaryCollection.ts +1 -1
  123. package/src/summaryGenerator.ts +1 -1
  124. package/src/summaryManager.ts +20 -5
@@ -1,9 +1,9 @@
1
1
  import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
2
- import { assert, Trace, TypedEventEmitter, unreachableCase, performance, } from "@fluidframework/common-utils";
2
+ import { assert, Trace, TypedEventEmitter, unreachableCase, } from "@fluidframework/common-utils";
3
3
  import { ChildLogger, raiseConnectedEvent, PerformanceEvent, TaggedLoggerAdapter, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
4
4
  import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
5
5
  import { readAndParse, isUnpackedRuntimeMessage } from "@fluidframework/driver-utils";
6
- import { DataCorruptionError, DataProcessingError, GenericError, UsageError, extractSafePropertiesFromMessage, } from "@fluidframework/container-utils";
6
+ import { DataCorruptionError, DataProcessingError, GenericError, UsageError, } from "@fluidframework/container-utils";
7
7
  import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
8
8
  import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
9
9
  import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, TelemetryContext, } from "@fluidframework/runtime-utils";
@@ -13,8 +13,7 @@ import { ContainerFluidHandleContext } from "./containerHandleContext";
13
13
  import { FluidDataStoreRegistry } from "./dataStoreRegistry";
14
14
  import { Summarizer } from "./summarizer";
15
15
  import { SummaryManager } from "./summaryManager";
16
- import { DeltaScheduler } from "./deltaScheduler";
17
- import { ReportOpPerfTelemetry, latencyThreshold, } from "./connectionTelemetry";
16
+ import { ReportOpPerfTelemetry, } from "./connectionTelemetry";
18
17
  import { PendingStateManager } from "./pendingStateManager";
19
18
  import { pkgVersion } from "./packageVersion";
20
19
  import { BlobManager } from "./blobManager";
@@ -29,6 +28,7 @@ import { GarbageCollector, GCNodeType, gcTreeKey, } from "./garbageCollection";
29
28
  import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
30
29
  import { BindBatchTracker } from "./batchTracker";
31
30
  import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
31
+ import { ScheduleManager } from "./scheduleManager";
32
32
  export var ContainerMessageType;
33
33
  (function (ContainerMessageType) {
34
34
  // An op to be delivered to store
@@ -74,12 +74,7 @@ export var RuntimeHeaders;
74
74
  /** True if the request is coming from an IFluidHandle. */
75
75
  RuntimeHeaders["viaHandle"] = "viaHandle";
76
76
  })(RuntimeHeaders || (RuntimeHeaders = {}));
77
- const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
78
77
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
79
- // Feature gate for the max op size. If the value is negative, chunking is enabled
80
- // and all ops over 16k would be chunked. If the value is positive, all ops with
81
- // a size strictly larger will be rejected and the container closed with an error.
82
- const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
83
78
  // By default, we should reject any op larger than 768KB,
84
79
  // in order to account for some extra overhead from serialization
85
80
  // to not reach the 1MB limits in socket.io and Kafka.
@@ -123,229 +118,6 @@ export function unpackRuntimeMessage(message) {
123
118
  }
124
119
  return message;
125
120
  }
126
- /**
127
- * This class controls pausing and resuming of inbound queue to ensure that we never
128
- * start processing ops in a batch IF we do not have all ops in the batch.
129
- */
130
- class ScheduleManagerCore {
131
- constructor(deltaManager, logger) {
132
- this.deltaManager = deltaManager;
133
- this.logger = logger;
134
- this.localPaused = false;
135
- this.timePaused = 0;
136
- this.batchCount = 0;
137
- // Listen for delta manager sends and add batch metadata to messages
138
- this.deltaManager.on("prepareSend", (messages) => {
139
- if (messages.length === 0) {
140
- return;
141
- }
142
- // First message will have the batch flag set to true if doing a batched send
143
- const firstMessageMetadata = messages[0].metadata;
144
- if (!(firstMessageMetadata === null || firstMessageMetadata === void 0 ? void 0 : firstMessageMetadata.batch)) {
145
- return;
146
- }
147
- // If the batch contains only a single op, clear the batch flag.
148
- if (messages.length === 1) {
149
- delete firstMessageMetadata.batch;
150
- return;
151
- }
152
- // Set the batch flag to false on the last message to indicate the end of the send batch
153
- const lastMessage = messages[messages.length - 1];
154
- lastMessage.metadata = Object.assign(Object.assign({}, lastMessage.metadata), { batch: false });
155
- });
156
- // Listen for updates and peek at the inbound
157
- this.deltaManager.inbound.on("push", (message) => {
158
- this.trackPending(message);
159
- });
160
- // Start with baseline - empty inbound queue.
161
- assert(!this.localPaused, 0x293 /* "initial state" */);
162
- const allPending = this.deltaManager.inbound.toArray();
163
- for (const pending of allPending) {
164
- this.trackPending(pending);
165
- }
166
- // We are intentionally directly listening to the "op" to inspect system ops as well.
167
- // If we do not observe system ops, we are likely to hit 0x296 assert when system ops
168
- // precedes start of incomplete batch.
169
- this.deltaManager.on("op", (message) => this.afterOpProcessing(message.sequenceNumber));
170
- }
171
- /**
172
- * The only public function in this class - called when we processed an op,
173
- * to make decision if op processing should be paused or not afer that.
174
- */
175
- afterOpProcessing(sequenceNumber) {
176
- assert(!this.localPaused, 0x294 /* "can't have op processing paused if we are processing an op" */);
177
- // If the inbound queue is ever empty, nothing to do!
178
- if (this.deltaManager.inbound.length === 0) {
179
- assert(this.pauseSequenceNumber === undefined, 0x295 /* "there should be no pending batch if we have no ops" */);
180
- return;
181
- }
182
- // The queue is
183
- // 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:
184
- // - here (processing ops until reaching start of incomplete batch)
185
- // - in trackPending(), when queue was empty and start of batch showed up.
186
- // 2. resumed when batch end comes in (in trackPending())
187
- // do we have incomplete batch to worry about?
188
- if (this.pauseSequenceNumber !== undefined) {
189
- assert(sequenceNumber < this.pauseSequenceNumber, 0x296 /* "we should never start processing incomplete batch!" */);
190
- // If the next op is the start of incomplete batch, then we can't process it until it's fully in - pause!
191
- if (sequenceNumber + 1 === this.pauseSequenceNumber) {
192
- this.pauseQueue();
193
- }
194
- }
195
- }
196
- pauseQueue() {
197
- assert(!this.localPaused, 0x297 /* "always called from resumed state" */);
198
- this.localPaused = true;
199
- this.timePaused = performance.now();
200
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
201
- this.deltaManager.inbound.pause();
202
- }
203
- resumeQueue(startBatch, messageEndBatch) {
204
- const endBatch = messageEndBatch.sequenceNumber;
205
- const duration = this.localPaused ? (performance.now() - this.timePaused) : undefined;
206
- this.batchCount++;
207
- if (this.batchCount % 1000 === 1) {
208
- this.logger.sendTelemetryEvent({
209
- eventName: "BatchStats",
210
- sequenceNumber: endBatch,
211
- length: endBatch - startBatch + 1,
212
- msnDistance: endBatch - messageEndBatch.minimumSequenceNumber,
213
- duration,
214
- batchCount: this.batchCount,
215
- interrupted: this.localPaused,
216
- });
217
- }
218
- // Return early if no change in value
219
- if (!this.localPaused) {
220
- return;
221
- }
222
- this.localPaused = false;
223
- // Random round number - we want to know when batch waiting paused op processing.
224
- if (duration !== undefined && duration > latencyThreshold) {
225
- this.logger.sendErrorEvent({
226
- eventName: "MaxBatchWaitTimeExceeded",
227
- duration,
228
- sequenceNumber: endBatch,
229
- length: endBatch - startBatch,
230
- });
231
- }
232
- this.deltaManager.inbound.resume();
233
- }
234
- /**
235
- * Called for each incoming op (i.e. inbound "push" notification)
236
- */
237
- trackPending(message) {
238
- assert(this.deltaManager.inbound.length !== 0, 0x298 /* "we have something in the queue that generates this event" */);
239
- assert((this.currentBatchClientId === undefined) === (this.pauseSequenceNumber === undefined), 0x299 /* "non-synchronized state" */);
240
- const metadata = message.metadata;
241
- const batchMetadata = metadata === null || metadata === void 0 ? void 0 : metadata.batch;
242
- // Protocol messages are never part of a runtime batch of messages
243
- if (!isUnpackedRuntimeMessage(message)) {
244
- // Protocol messages should never show up in the middle of the batch!
245
- assert(this.currentBatchClientId === undefined, 0x29a /* "System message in the middle of batch!" */);
246
- assert(batchMetadata === undefined, 0x29b /* "system op in a batch?" */);
247
- assert(!this.localPaused, 0x29c /* "we should be processing ops when there is no active batch" */);
248
- return;
249
- }
250
- if (this.currentBatchClientId === undefined && batchMetadata === undefined) {
251
- assert(!this.localPaused, 0x29d /* "we should be processing ops when there is no active batch" */);
252
- return;
253
- }
254
- // If the client ID changes then we can move the pause point. If it stayed the same then we need to check.
255
- // If batchMetadata is not undefined then if it's true we've begun a new batch - if false we've ended
256
- // the previous one
257
- if (this.currentBatchClientId !== undefined || batchMetadata === false) {
258
- if (this.currentBatchClientId !== message.clientId) {
259
- // "Batch not closed, yet message from another client!"
260
- throw new DataCorruptionError("OpBatchIncomplete", Object.assign({ runtimeVersion: pkgVersion, batchClientId: this.currentBatchClientId }, extractSafePropertiesFromMessage(message)));
261
- }
262
- }
263
- // The queue is
264
- // 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:
265
- // - in afterOpProcessing() - processing ops until reaching start of incomplete batch
266
- // - here (batchMetadata == false below), when queue was empty and start of batch showed up.
267
- // 2. resumed when batch end comes in (batchMetadata === true case below)
268
- if (batchMetadata) {
269
- assert(this.currentBatchClientId === undefined, 0x29e /* "there can't be active batch" */);
270
- assert(!this.localPaused, 0x29f /* "we should be processing ops when there is no active batch" */);
271
- this.pauseSequenceNumber = message.sequenceNumber;
272
- this.currentBatchClientId = message.clientId;
273
- // Start of the batch
274
- // Only pause processing if queue has no other ops!
275
- // If there are any other ops in the queue, processing will be stopped when they are processed!
276
- if (this.deltaManager.inbound.length === 1) {
277
- this.pauseQueue();
278
- }
279
- }
280
- else if (batchMetadata === false) {
281
- assert(this.pauseSequenceNumber !== undefined, 0x2a0 /* "batch presence was validated above" */);
282
- // Batch is complete, we can process it!
283
- this.resumeQueue(this.pauseSequenceNumber, message);
284
- this.pauseSequenceNumber = undefined;
285
- this.currentBatchClientId = undefined;
286
- }
287
- else {
288
- // Continuation of current batch. Do nothing
289
- assert(this.currentBatchClientId !== undefined, 0x2a1 /* "logic error" */);
290
- }
291
- }
292
- }
293
- /**
294
- * This class has the following responsibilities:
295
- * 1. It tracks batches as we process ops and raises "batchBegin" and "batchEnd" events.
296
- * As part of it, it validates batch correctness (i.e. no system ops in the middle of batch)
297
- * 2. It creates instance of ScheduleManagerCore that ensures we never start processing ops from batch
298
- * unless all ops of the batch are in.
299
- */
300
- export class ScheduleManager {
301
- constructor(deltaManager, emitter, logger) {
302
- this.deltaManager = deltaManager;
303
- this.emitter = emitter;
304
- this.logger = logger;
305
- this.hitError = false;
306
- this.deltaScheduler = new DeltaScheduler(this.deltaManager, ChildLogger.create(this.logger, "DeltaScheduler"));
307
- void new ScheduleManagerCore(deltaManager, logger);
308
- }
309
- beforeOpProcessing(message) {
310
- var _a;
311
- if (this.batchClientId !== message.clientId) {
312
- assert(this.batchClientId === undefined, 0x2a2 /* "Batch is interrupted by other client op. Should be caught by trackPending()" */);
313
- // This could be the beginning of a new batch or an individual message.
314
- this.emitter.emit("batchBegin", message);
315
- this.deltaScheduler.batchBegin(message);
316
- const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
317
- if (batch) {
318
- this.batchClientId = message.clientId;
319
- }
320
- else {
321
- this.batchClientId = undefined;
322
- }
323
- }
324
- }
325
- afterOpProcessing(error, message) {
326
- var _a;
327
- // If this is no longer true, we need to revisit what we do where we set this.hitError.
328
- assert(!this.hitError, 0x2a3 /* "container should be closed on any error" */);
329
- if (error) {
330
- // We assume here that loader will close container and stop processing all future ops.
331
- // This is implicit dependency. If this flow changes, this code might no longer be correct.
332
- this.hitError = true;
333
- this.batchClientId = undefined;
334
- this.emitter.emit("batchEnd", error, message);
335
- this.deltaScheduler.batchEnd(message);
336
- return;
337
- }
338
- const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
339
- // If no batchClientId has been set then we're in an individual batch. Else, if we get
340
- // batch end metadata, this is end of the current batch.
341
- if (this.batchClientId === undefined || batch === false) {
342
- this.batchClientId = undefined;
343
- this.emitter.emit("batchEnd", undefined, message);
344
- this.deltaScheduler.batchEnd(message);
345
- return;
346
- }
347
- }
348
- }
349
121
  /**
350
122
  * Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
351
123
  * special-case for document dirty state. Ultimately we should have no special-cases from the
@@ -372,7 +144,7 @@ export function getDeviceSpec() {
372
144
  */
373
145
  export class ContainerRuntime extends TypedEventEmitter {
374
146
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
375
- var _a, _b, _c, _d, _e, _f, _g, _h;
147
+ var _a, _b, _c, _d;
376
148
  if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
377
149
  super();
378
150
  this.context = context;
@@ -383,7 +155,7 @@ export class ContainerRuntime extends TypedEventEmitter {
383
155
  this._storage = _storage;
384
156
  this.requestHandler = requestHandler;
385
157
  this.summaryConfiguration = summaryConfiguration;
386
- this.defaultMaxConsecutiveReconnects = 15;
158
+ this.defaultMaxConsecutiveReconnects = 7;
387
159
  this._orderSequentiallyCalls = 0;
388
160
  this.needsFlush = false;
389
161
  this.flushTrigger = false;
@@ -427,8 +199,6 @@ export class ContainerRuntime extends TypedEventEmitter {
427
199
  }
428
200
  };
429
201
  this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
430
- // Default to false (enabled).
431
- this.disableIsolatedChannels = (_b = this.runtimeOptions.summaryOptions.disableIsolatedChannels) !== null && _b !== void 0 ? _b : false;
432
202
  this._connected = this.context.connected;
433
203
  this.chunkMap = new Map(chunks);
434
204
  this.handleContext = new ContainerFluidHandleContext("", this);
@@ -441,15 +211,11 @@ export class ContainerRuntime extends TypedEventEmitter {
441
211
  this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
442
212
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
443
213
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
444
- this._aliasingEnabled =
445
- ((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
446
- ((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
447
- this._maxOpSizeInBytes = ((_e = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _e !== void 0 ? _e : defaultMaxOpSizeInBytes);
448
214
  this.maxConsecutiveReconnects =
449
- (_f = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _f !== void 0 ? _f : this.defaultMaxConsecutiveReconnects;
215
+ (_b = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _b !== void 0 ? _b : this.defaultMaxConsecutiveReconnects;
450
216
  this._flushMode = runtimeOptions.flushMode;
451
217
  const pendingRuntimeState = context.pendingLocalState;
452
- const baseSnapshot = (_g = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _g !== void 0 ? _g : context.baseSnapshot;
218
+ const baseSnapshot = (_c = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _c !== void 0 ? _c : context.baseSnapshot;
453
219
  this.garbageCollector = GarbageCollector.create({
454
220
  runtime: this,
455
221
  gcOptions: this.runtimeOptions.gcOptions,
@@ -483,7 +249,11 @@ export class ContainerRuntime extends TypedEventEmitter {
483
249
  this.summarizerNode.loadBaseSummaryWithoutDifferential(baseSnapshot);
484
250
  }
485
251
  this.dataStores = new DataStores(getSummaryForDatastores(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
486
- this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId }), (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this);
252
+ this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
253
+ if (!this.disposed) {
254
+ this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
255
+ }
256
+ }, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
487
257
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
488
258
  this.deltaSender = this.deltaManager;
489
259
  this.pendingStateManager = new PendingStateManager({
@@ -576,7 +346,7 @@ export class ContainerRuntime extends TypedEventEmitter {
576
346
  };
577
347
  // summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
578
348
  // the count is reset to 0.
579
- loadSummaryNumber = (_h = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _h !== void 0 ? _h : 0;
349
+ loadSummaryNumber = (_d = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _d !== void 0 ? _d : 0;
580
350
  }
581
351
  else {
582
352
  this.createContainerMetadata = {
@@ -611,7 +381,7 @@ export class ContainerRuntime extends TypedEventEmitter {
611
381
  runtimeVersion: pkgVersion,
612
382
  },
613
383
  });
614
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
384
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
615
385
  const pendingRuntimeState = context.pendingLocalState;
616
386
  const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
617
387
  const storage = !pendingRuntimeState ?
@@ -664,7 +434,6 @@ export class ContainerRuntime extends TypedEventEmitter {
664
434
  summaryOptions,
665
435
  gcOptions,
666
436
  loadSequenceNumberVerification,
667
- useDataStoreAliasing,
668
437
  flushMode,
669
438
  enableOfflineLoad,
670
439
  }, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
@@ -923,7 +692,7 @@ export class ContainerRuntime extends TypedEventEmitter {
923
692
  var _a;
924
693
  const metadata = Object.assign(Object.assign(Object.assign(Object.assign({}, this.createContainerMetadata), {
925
694
  // Increment the summary number for the next summary that will be generated.
926
- summaryNumber: this.nextSummaryNumber++, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined }), this.garbageCollector.getMetadata()), {
695
+ summaryNumber: this.nextSummaryNumber++, summaryFormatVersion: 1 }), this.garbageCollector.getMetadata()), {
927
696
  // The last message processed at the time of summary. If there are no new messages, use the message from the
928
697
  // last summary.
929
698
  message: (_a = extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary });
@@ -1046,8 +815,8 @@ export class ContainerRuntime extends TypedEventEmitter {
1046
815
  // ensure we don't submit ops referencing a blob that has not been uploaded
1047
816
  const connecting = connected && !this._connected && !this.deltaManager.readOnlyInfo.readonly;
1048
817
  if (connecting && this.blobManager.hasPendingOfflineUploads) {
1049
- assert(!this.delayConnectClientId, "Connect event delay must be canceled before subsequent connect event");
1050
- assert(!!clientId, "Must have clientId when connecting");
818
+ assert(!this.delayConnectClientId, 0x392 /* Connect event delay must be canceled before subsequent connect event */);
819
+ assert(!!clientId, 0x393 /* Must have clientId when connecting */);
1051
820
  this.delayConnectClientId = clientId;
1052
821
  this.blobManager.onConnected().then(() => {
1053
822
  // make sure we didn't reconnect before the promise resolved
@@ -1061,17 +830,18 @@ export class ContainerRuntime extends TypedEventEmitter {
1061
830
  this.setConnectionStateCore(connected, clientId);
1062
831
  }
1063
832
  setConnectionStateCore(connected, clientId) {
1064
- assert(!this.delayConnectClientId, "connect event delay must be cleared before propagating connect event");
833
+ assert(!this.delayConnectClientId, 0x394 /* connect event delay must be cleared before propagating connect event */);
1065
834
  this.verifyNotClosed();
1066
835
  // There might be no change of state due to Container calling this API after loading runtime.
1067
836
  const changeOfState = this._connected !== connected;
1068
- const reconnection = changeOfState && connected;
837
+ const reconnection = changeOfState && !connected;
1069
838
  this._connected = connected;
1070
839
  if (!connected) {
1071
840
  this._perfSignalData.signalsLost = 0;
1072
841
  this._perfSignalData.signalTimestamp = 0;
1073
842
  this._perfSignalData.trackingSignalSequenceNumber = undefined;
1074
843
  }
844
+ // Fail while disconnected
1075
845
  if (reconnection) {
1076
846
  this.consecutiveReconnects++;
1077
847
  if (!this.shouldContinueReconnecting()) {
@@ -1303,59 +1073,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1303
1073
  }
1304
1074
  async createDataStore(pkg) {
1305
1075
  const internalId = uuid();
1306
- return channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
1307
- }
1308
- /**
1309
- * Creates a root datastore directly with a user generated id and attaches it to storage.
1310
- * It is vulnerable to name collisions and should not be used.
1311
- *
1312
- * This method will be removed. See #6465.
1313
- */
1314
- async createRootDataStoreLegacy(pkg, rootDataStoreId) {
1315
- const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1316
- fluidDataStore.makeVisibleAndAttachGraph();
1317
- return fluidDataStore;
1318
- }
1319
- /**
1320
- * @deprecated - will be removed in an upcoming release. See #9660.
1321
- */
1322
- async createRootDataStore(pkg, rootDataStoreId) {
1323
- if (rootDataStoreId.includes("/")) {
1324
- throw new UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
1325
- }
1326
- return this._aliasingEnabled === true ?
1327
- this.createAndAliasDataStore(pkg, rootDataStoreId) :
1328
- this.createRootDataStoreLegacy(pkg, rootDataStoreId);
1329
- }
1330
- /**
1331
- * Creates a data store then attempts to alias it.
1332
- * If aliasing fails, it will raise an exception.
1333
- *
1334
- * This method will be removed. See #6465.
1335
- *
1336
- * @param pkg - Package name of the data store
1337
- * @param alias - Alias to be assigned to the data store
1338
- * @param props - Properties for the data store
1339
- * @returns - An aliased data store which can can be found / loaded by alias.
1340
- */
1341
- async createAndAliasDataStore(pkg, alias, props) {
1342
- const internalId = uuid();
1343
- try {
1344
- // A similar call may have been initiated by the same client, so we should try to get
1345
- // a possible existing aliased datastore first.
1346
- const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
1347
- return channelToDataStore(existingDataStore, internalId, this, this.dataStores, this.mc.logger, true);
1348
- }
1349
- catch (err) {
1350
- const newChannel = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
1351
- const newDataStore = channelToDataStore(newChannel, internalId, this, this.dataStores, this.mc.logger);
1352
- const aliasResult = await newDataStore.trySetAlias(alias);
1353
- if (aliasResult === "Success") {
1354
- return newDataStore;
1355
- }
1356
- const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
1357
- return channelToDataStore(existingDataStore, internalId, this, this.dataStores, this.mc.logger, true);
1358
- }
1076
+ return channelToDataStore(await this._createDataStore(pkg, internalId), internalId, this, this.dataStores, this.mc.logger);
1359
1077
  }
1360
1078
  createDetachedRootDataStore(pkg, rootDataStoreId) {
1361
1079
  if (rootDataStoreId.includes("/")) {
@@ -1366,31 +1084,13 @@ export class ContainerRuntime extends TypedEventEmitter {
1366
1084
  createDetachedDataStore(pkg) {
1367
1085
  return this.dataStores.createDetachedDataStoreCore(pkg, false);
1368
1086
  }
1369
- /**
1370
- * Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
1371
- * It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
1372
- *
1373
- * This method will be removed. See #6465.
1374
- */
1375
- async _createDataStoreWithPropsLegacy(pkg, props, id = uuid(), isRoot = false) {
1376
- const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
1377
- if (isRoot) {
1378
- fluidDataStore.makeVisibleAndAttachGraph();
1379
- this.logger.sendTelemetryEvent({
1380
- eventName: "Root datastore with props",
1381
- hasProps: props !== undefined,
1382
- });
1383
- }
1087
+ async _createDataStoreWithProps(pkg, props, id = uuid()) {
1088
+ const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props).realize();
1384
1089
  return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
1385
1090
  }
1386
- async _createDataStoreWithProps(pkg, props, id = uuid(), isRoot = false) {
1387
- return this._aliasingEnabled === true && isRoot ?
1388
- this.createAndAliasDataStore(pkg, id, props) :
1389
- this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
1390
- }
1391
- async _createDataStore(pkg, isRoot, id = uuid(), props) {
1091
+ async _createDataStore(pkg, id = uuid(), props) {
1392
1092
  return this.dataStores
1393
- ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
1093
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
1394
1094
  .realize();
1395
1095
  }
1396
1096
  canSendOps() {
@@ -1482,10 +1182,8 @@ export class ContainerRuntime extends TypedEventEmitter {
1482
1182
  this.blobManager.setRedirectTable(blobRedirectTable);
1483
1183
  }
1484
1184
  const summarizeResult = this.dataStores.createSummary(telemetryContext);
1485
- if (!this.disableIsolatedChannels) {
1486
- // Wrap data store summaries in .channels subtree.
1487
- wrapSummaryInChannelsTree(summarizeResult);
1488
- }
1185
+ // Wrap data store summaries in .channels subtree.
1186
+ wrapSummaryInChannelsTree(summarizeResult);
1489
1187
  this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
1490
1188
  return summarizeResult.summary;
1491
1189
  }
@@ -1500,12 +1198,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1500
1198
  }
1501
1199
  async summarizeInternal(fullTree, trackState, telemetryContext) {
1502
1200
  const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
1503
- let pathPartsForChildren;
1504
- if (!this.disableIsolatedChannels) {
1505
- // Wrap data store summaries in .channels subtree.
1506
- wrapSummaryInChannelsTree(summarizeResult);
1507
- pathPartsForChildren = [channelsTreeName];
1508
- }
1201
+ // Wrap data store summaries in .channels subtree.
1202
+ wrapSummaryInChannelsTree(summarizeResult);
1203
+ const pathPartsForChildren = [channelsTreeName];
1509
1204
  this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
1510
1205
  return Object.assign(Object.assign({}, summarizeResult), { id: "", pathPartsForChildren });
1511
1206
  }
@@ -1664,16 +1359,19 @@ export class ContainerRuntime extends TypedEventEmitter {
1664
1359
  const summaryNumberLogger = ChildLogger.create(summaryLogger, undefined, {
1665
1360
  all: { summaryNumber },
1666
1361
  });
1362
+ let latestSnapshotVersionId;
1667
1363
  if (refreshLatestAck) {
1668
- const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
1669
- if (latestSummaryRefSeq > this.deltaManager.lastSequenceNumber) {
1364
+ const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
1365
+ const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
1366
+ latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
1367
+ if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
1670
1368
  // We need to catch up to the latest summary's reference sequence number before pausing.
1671
1369
  await PerformanceEvent.timedExecAsync(summaryNumberLogger, {
1672
1370
  eventName: "WaitingForSeq",
1673
1371
  lastSequenceNumber: this.deltaManager.lastSequenceNumber,
1674
- targetSequenceNumber: latestSummaryRefSeq,
1372
+ targetSequenceNumber: latestSnapshotRefSeq,
1675
1373
  lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
1676
- }, async () => waitForSeq(this.deltaManager, latestSummaryRefSeq), { start: true, end: true, cancel: "error" });
1374
+ }, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
1677
1375
  }
1678
1376
  }
1679
1377
  try {
@@ -1709,7 +1407,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1709
1407
  error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
1710
1408
  };
1711
1409
  }
1712
- assert(summaryRefSeqNum === ((_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.sequenceNumber), "it's one and the same thing");
1410
+ assert(summaryRefSeqNum === ((_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.sequenceNumber), 0x395 /* it's one and the same thing */);
1713
1411
  if (lastAck !== this.summaryCollection.latestAck) {
1714
1412
  return {
1715
1413
  continue: false,
@@ -1755,7 +1453,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1755
1453
  // Counting dataStores and handles
1756
1454
  // Because handles are unchanged dataStores in the current logic,
1757
1455
  // summarized dataStore count is total dataStore count minus handle count
1758
- const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[channelsTreeName];
1456
+ const dataStoreTree = summaryTree.tree[channelsTreeName];
1759
1457
  assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1760
1458
  const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === SummaryType.Handle).length;
1761
1459
  const gcSummaryTreeStats = summaryTree.tree[gcTreeKey]
@@ -1774,17 +1472,34 @@ export class ContainerRuntime extends TypedEventEmitter {
1774
1472
  if (!continueResult.continue) {
1775
1473
  return Object.assign(Object.assign({ stage: "generate" }, generateSummaryData), { error: continueResult.error });
1776
1474
  }
1777
- const summaryContext = lastAck === undefined
1778
- ? {
1475
+ // It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
1476
+ // summary. So if the previous summarizer closes just after submitting the summary and before
1477
+ // submitting the summaryOp then we can't rely on summaryAck. So in case we have
1478
+ // latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
1479
+ // the one fetched from storage as parent as that is the latest.
1480
+ let summaryContext;
1481
+ if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId
1482
+ && latestSnapshotVersionId !== undefined) {
1483
+ summaryContext = {
1484
+ proposalHandle: undefined,
1485
+ ackHandle: latestSnapshotVersionId,
1486
+ referenceSequenceNumber: summaryRefSeqNum,
1487
+ };
1488
+ }
1489
+ else if (lastAck === undefined) {
1490
+ summaryContext = {
1779
1491
  proposalHandle: undefined,
1780
1492
  ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
1781
1493
  referenceSequenceNumber: summaryRefSeqNum,
1782
- }
1783
- : {
1494
+ };
1495
+ }
1496
+ else {
1497
+ summaryContext = {
1784
1498
  proposalHandle: lastAck.summaryOp.contents.handle,
1785
1499
  ackHandle: lastAck.summaryAck.contents.handle,
1786
1500
  referenceSequenceNumber: summaryRefSeqNum,
1787
1501
  };
1502
+ }
1788
1503
  let handle;
1789
1504
  try {
1790
1505
  handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
@@ -1891,7 +1606,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1891
1606
  let opMetadataInternal = opMetadata;
1892
1607
  if (this.canSendOps()) {
1893
1608
  const serializedContent = JSON.stringify(content);
1894
- const maxOpSize = this.context.deltaManager.maxMessageSize;
1895
1609
  // If in TurnBased flush mode we will trigger a flush at the next turn break
1896
1610
  if (this.flushMode === FlushMode.TurnBased && !this.needsFlush) {
1897
1611
  opMetadataInternal = Object.assign(Object.assign({}, opMetadata), { batch: true });
@@ -1905,7 +1619,19 @@ export class ContainerRuntime extends TypedEventEmitter {
1905
1619
  });
1906
1620
  }
1907
1621
  }
1908
- clientSequenceNumber = this.submitMaybeChunkedMessages(type, content, serializedContent, maxOpSize, this._flushMode === FlushMode.TurnBased, opMetadataInternal);
1622
+ if (!serializedContent || serializedContent.length <= defaultMaxOpSizeInBytes) {
1623
+ clientSequenceNumber = this.submitRuntimeMessage(type, content, this._flushMode === FlushMode.TurnBased /* batch */, opMetadataInternal);
1624
+ }
1625
+ else {
1626
+ // If the content length is larger than the client configured message size
1627
+ // instead of splitting the content, we will fail by explicitly closing the container
1628
+ this.closeFn(new GenericError("OpTooLarge",
1629
+ /* error */ undefined, {
1630
+ length: serializedContent.length,
1631
+ limit: defaultMaxOpSizeInBytes,
1632
+ }));
1633
+ clientSequenceNumber = -1;
1634
+ }
1909
1635
  }
1910
1636
  // Let the PendingStateManager know that a message was submitted.
1911
1637
  this.pendingStateManager.onSubmitMessage(type, clientSequenceNumber, this.deltaManager.lastSequenceNumber, content, localOpMetadata, opMetadataInternal);
@@ -1913,46 +1639,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1913
1639
  this.updateDocumentDirtyState(true);
1914
1640
  }
1915
1641
  }
1916
- submitMaybeChunkedMessages(type, content, serializedContent, serverMaxOpSize, batch, opMetadataInternal = undefined) {
1917
- if (this._maxOpSizeInBytes >= 0) {
1918
- // Chunking disabled
1919
- if (!serializedContent || serializedContent.length <= this._maxOpSizeInBytes) {
1920
- return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
1921
- }
1922
- // When chunking is disabled, we ignore the server max message size
1923
- // and if the content length is larger than the client configured message size
1924
- // instead of splitting the content, we will fail by explicitly close the container
1925
- this.closeFn(new GenericError("OpTooLarge",
1926
- /* error */ undefined, {
1927
- length: serializedContent.length,
1928
- limit: this._maxOpSizeInBytes,
1929
- }));
1930
- return -1;
1931
- }
1932
- // Chunking enabled, fallback on the server's max message size
1933
- // and split the content accordingly
1934
- if (!serializedContent || serializedContent.length <= serverMaxOpSize) {
1935
- return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
1936
- }
1937
- return this.submitChunkedMessage(type, serializedContent, serverMaxOpSize);
1938
- }
1939
- submitChunkedMessage(type, content, maxOpSize) {
1940
- const contentLength = content.length;
1941
- const chunkN = Math.floor((contentLength - 1) / maxOpSize) + 1;
1942
- let offset = 0;
1943
- let clientSequenceNumber = 0;
1944
- for (let i = 1; i <= chunkN; i = i + 1) {
1945
- const chunkedOp = {
1946
- chunkId: i,
1947
- contents: content.substr(offset, maxOpSize),
1948
- originalType: type,
1949
- totalChunks: chunkN,
1950
- };
1951
- offset += maxOpSize;
1952
- clientSequenceNumber = this.submitRuntimeMessage(ContainerMessageType.ChunkedOp, chunkedOp, false);
1953
- }
1954
- return clientSequenceNumber;
1955
- }
1956
1642
  submitSystemMessage(type, contents) {
1957
1643
  this.verifyNotClosed();
1958
1644
  assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
@@ -2023,12 +1709,13 @@ export class ContainerRuntime extends TypedEventEmitter {
2023
1709
  /** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
2024
1710
  async refreshLatestSummaryAck(proposalHandle, ackHandle, summaryRefSeq, summaryLogger) {
2025
1711
  const readAndParseBlob = async (id) => readAndParse(this.storage, id);
2026
- const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, async () => this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
1712
+ const { snapshotTree } = await this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
2027
1713
  eventName: "RefreshLatestSummaryGetSnapshot",
2028
1714
  ackHandle,
2029
1715
  summaryRefSeq,
2030
1716
  fetchLatest: false,
2031
- }), readAndParseBlob, summaryLogger);
1717
+ });
1718
+ const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
2032
1719
  // Notify the garbage collector so it can update its latest summary state.
2033
1720
  await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
2034
1721
  }
@@ -2039,16 +1726,16 @@ export class ContainerRuntime extends TypedEventEmitter {
2039
1726
  * @returns downloaded snapshot's reference sequence number
2040
1727
  */
2041
1728
  async refreshLatestSummaryAckFromServer(summaryLogger) {
2042
- const snapshot = await this.fetchSnapshotFromStorage(null, summaryLogger, {
1729
+ const { snapshotTree, versionId } = await this.fetchSnapshotFromStorage(null, summaryLogger, {
2043
1730
  eventName: "RefreshLatestSummaryGetSnapshot",
2044
1731
  fetchLatest: true,
2045
1732
  }, FetchSource.noCache);
2046
1733
  const readAndParseBlob = async (id) => readAndParse(this.storage, id);
2047
- const snapshotRefSeq = await seqFromTree(snapshot, readAndParseBlob);
2048
- const result = await this.summarizerNode.refreshLatestSummary(undefined, snapshotRefSeq, async () => snapshot, readAndParseBlob, summaryLogger);
1734
+ const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
1735
+ const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
2049
1736
  // Notify the garbage collector so it can update its latest summary state.
2050
1737
  await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
2051
- return snapshotRefSeq;
1738
+ return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
2052
1739
  }
2053
1740
  async fetchSnapshotFromStorage(versionId, logger, event, fetchSource) {
2054
1741
  return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
@@ -2061,7 +1748,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2061
1748
  assert(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
2062
1749
  stats.getSnapshotDuration = trace.trace().duration;
2063
1750
  perfEvent.end(stats);
2064
- return maybeSnapshot;
1751
+ return { snapshotTree: maybeSnapshot, versionId: versions[0].id };
2065
1752
  });
2066
1753
  }
2067
1754
  notifyAttaching(snapshot) {
@@ -2088,6 +1775,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2088
1775
  if (previousPendingState) {
2089
1776
  return {
2090
1777
  pending: this.pendingStateManager.getLocalState(),
1778
+ pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
2091
1779
  snapshotBlobs: previousPendingState.snapshotBlobs,
2092
1780
  baseSnapshot: previousPendingState.baseSnapshot,
2093
1781
  savedOps: this.savedOps,
@@ -2097,6 +1785,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2097
1785
  assert(!!this.baseSnapshotBlobs, 0x2e7 /* "Must serialize base snapshot blobs before getting runtime state" */);
2098
1786
  return {
2099
1787
  pending: this.pendingStateManager.getLocalState(),
1788
+ pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
2100
1789
  snapshotBlobs: this.baseSnapshotBlobs,
2101
1790
  baseSnapshot: this.context.baseSnapshot,
2102
1791
  savedOps: this.savedOps,