@fluidframework/container-runtime 2.0.0-internal.1.0.0.82693 → 2.0.0-internal.1.1.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.
- package/.mocharc.js +12 -0
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +7 -1
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +34 -17
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +3 -104
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +83 -395
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +2 -3
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -5
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +13 -23
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +3 -8
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +37 -6
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +61 -65
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +15 -2
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +14 -0
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +25 -0
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.d.ts +28 -0
- package/dist/scheduleManager.d.ts.map +1 -0
- package/dist/scheduleManager.js +235 -0
- package/dist/scheduleManager.js.map +1 -0
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +33 -3
- package/dist/summarizer.js.map +1 -1
- package/dist/summaryCollection.js +1 -1
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryGenerator.js +1 -1
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +20 -5
- package/dist/summaryManager.js.map +1 -1
- package/lib/batchTracker.js +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +7 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +35 -18
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +3 -104
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +84 -395
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +2 -3
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -5
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +13 -23
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +3 -8
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +37 -6
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +47 -52
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +15 -2
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +14 -0
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +25 -0
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.d.ts +28 -0
- package/lib/scheduleManager.d.ts.map +1 -0
- package/lib/scheduleManager.js +231 -0
- package/lib/scheduleManager.js.map +1 -0
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +35 -5
- package/lib/summarizer.js.map +1 -1
- package/lib/summaryCollection.js +1 -1
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryGenerator.js +1 -1
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +20 -5
- package/lib/summaryManager.js.map +1 -1
- package/package.json +32 -19
- package/src/batchTracker.ts +1 -1
- package/src/blobManager.ts +43 -17
- package/src/containerRuntime.ts +113 -547
- package/src/dataStore.ts +1 -4
- package/src/dataStoreContext.ts +10 -25
- package/src/dataStores.ts +13 -19
- package/src/garbageCollection.ts +64 -69
- package/src/index.ts +1 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +18 -2
- package/src/runningSummarizer.ts +33 -1
- package/src/scheduleManager.ts +294 -0
- package/src/summarizer.ts +46 -10
- package/src/summaryCollection.ts +1 -1
- package/src/summaryGenerator.ts +1 -1
- package/src/summaryManager.ts +20 -5
package/dist/containerRuntime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.
|
|
3
|
+
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
4
4
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
5
5
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
6
6
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
@@ -16,7 +16,6 @@ const containerHandleContext_1 = require("./containerHandleContext");
|
|
|
16
16
|
const dataStoreRegistry_1 = require("./dataStoreRegistry");
|
|
17
17
|
const summarizer_1 = require("./summarizer");
|
|
18
18
|
const summaryManager_1 = require("./summaryManager");
|
|
19
|
-
const deltaScheduler_1 = require("./deltaScheduler");
|
|
20
19
|
const connectionTelemetry_1 = require("./connectionTelemetry");
|
|
21
20
|
const pendingStateManager_1 = require("./pendingStateManager");
|
|
22
21
|
const packageVersion_1 = require("./packageVersion");
|
|
@@ -32,6 +31,7 @@ const garbageCollection_1 = require("./garbageCollection");
|
|
|
32
31
|
const dataStore_1 = require("./dataStore");
|
|
33
32
|
const batchTracker_1 = require("./batchTracker");
|
|
34
33
|
const serializedSnapshotStorage_1 = require("./serializedSnapshotStorage");
|
|
34
|
+
const scheduleManager_1 = require("./scheduleManager");
|
|
35
35
|
var ContainerMessageType;
|
|
36
36
|
(function (ContainerMessageType) {
|
|
37
37
|
// An op to be delivered to store
|
|
@@ -77,12 +77,7 @@ var RuntimeHeaders;
|
|
|
77
77
|
/** True if the request is coming from an IFluidHandle. */
|
|
78
78
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
79
79
|
})(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
|
|
80
|
-
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
81
80
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
82
|
-
// Feature gate for the max op size. If the value is negative, chunking is enabled
|
|
83
|
-
// and all ops over 16k would be chunked. If the value is positive, all ops with
|
|
84
|
-
// a size strictly larger will be rejected and the container closed with an error.
|
|
85
|
-
const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
|
|
86
81
|
// By default, we should reject any op larger than 768KB,
|
|
87
82
|
// in order to account for some extra overhead from serialization
|
|
88
83
|
// to not reach the 1MB limits in socket.io and Kafka.
|
|
@@ -128,230 +123,6 @@ function unpackRuntimeMessage(message) {
|
|
|
128
123
|
return message;
|
|
129
124
|
}
|
|
130
125
|
exports.unpackRuntimeMessage = unpackRuntimeMessage;
|
|
131
|
-
/**
|
|
132
|
-
* This class controls pausing and resuming of inbound queue to ensure that we never
|
|
133
|
-
* start processing ops in a batch IF we do not have all ops in the batch.
|
|
134
|
-
*/
|
|
135
|
-
class ScheduleManagerCore {
|
|
136
|
-
constructor(deltaManager, logger) {
|
|
137
|
-
this.deltaManager = deltaManager;
|
|
138
|
-
this.logger = logger;
|
|
139
|
-
this.localPaused = false;
|
|
140
|
-
this.timePaused = 0;
|
|
141
|
-
this.batchCount = 0;
|
|
142
|
-
// Listen for delta manager sends and add batch metadata to messages
|
|
143
|
-
this.deltaManager.on("prepareSend", (messages) => {
|
|
144
|
-
if (messages.length === 0) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
// First message will have the batch flag set to true if doing a batched send
|
|
148
|
-
const firstMessageMetadata = messages[0].metadata;
|
|
149
|
-
if (!(firstMessageMetadata === null || firstMessageMetadata === void 0 ? void 0 : firstMessageMetadata.batch)) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
// If the batch contains only a single op, clear the batch flag.
|
|
153
|
-
if (messages.length === 1) {
|
|
154
|
-
delete firstMessageMetadata.batch;
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
// Set the batch flag to false on the last message to indicate the end of the send batch
|
|
158
|
-
const lastMessage = messages[messages.length - 1];
|
|
159
|
-
lastMessage.metadata = Object.assign(Object.assign({}, lastMessage.metadata), { batch: false });
|
|
160
|
-
});
|
|
161
|
-
// Listen for updates and peek at the inbound
|
|
162
|
-
this.deltaManager.inbound.on("push", (message) => {
|
|
163
|
-
this.trackPending(message);
|
|
164
|
-
});
|
|
165
|
-
// Start with baseline - empty inbound queue.
|
|
166
|
-
(0, common_utils_1.assert)(!this.localPaused, 0x293 /* "initial state" */);
|
|
167
|
-
const allPending = this.deltaManager.inbound.toArray();
|
|
168
|
-
for (const pending of allPending) {
|
|
169
|
-
this.trackPending(pending);
|
|
170
|
-
}
|
|
171
|
-
// We are intentionally directly listening to the "op" to inspect system ops as well.
|
|
172
|
-
// If we do not observe system ops, we are likely to hit 0x296 assert when system ops
|
|
173
|
-
// precedes start of incomplete batch.
|
|
174
|
-
this.deltaManager.on("op", (message) => this.afterOpProcessing(message.sequenceNumber));
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* The only public function in this class - called when we processed an op,
|
|
178
|
-
* to make decision if op processing should be paused or not afer that.
|
|
179
|
-
*/
|
|
180
|
-
afterOpProcessing(sequenceNumber) {
|
|
181
|
-
(0, common_utils_1.assert)(!this.localPaused, 0x294 /* "can't have op processing paused if we are processing an op" */);
|
|
182
|
-
// If the inbound queue is ever empty, nothing to do!
|
|
183
|
-
if (this.deltaManager.inbound.length === 0) {
|
|
184
|
-
(0, common_utils_1.assert)(this.pauseSequenceNumber === undefined, 0x295 /* "there should be no pending batch if we have no ops" */);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
// The queue is
|
|
188
|
-
// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:
|
|
189
|
-
// - here (processing ops until reaching start of incomplete batch)
|
|
190
|
-
// - in trackPending(), when queue was empty and start of batch showed up.
|
|
191
|
-
// 2. resumed when batch end comes in (in trackPending())
|
|
192
|
-
// do we have incomplete batch to worry about?
|
|
193
|
-
if (this.pauseSequenceNumber !== undefined) {
|
|
194
|
-
(0, common_utils_1.assert)(sequenceNumber < this.pauseSequenceNumber, 0x296 /* "we should never start processing incomplete batch!" */);
|
|
195
|
-
// If the next op is the start of incomplete batch, then we can't process it until it's fully in - pause!
|
|
196
|
-
if (sequenceNumber + 1 === this.pauseSequenceNumber) {
|
|
197
|
-
this.pauseQueue();
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
pauseQueue() {
|
|
202
|
-
(0, common_utils_1.assert)(!this.localPaused, 0x297 /* "always called from resumed state" */);
|
|
203
|
-
this.localPaused = true;
|
|
204
|
-
this.timePaused = common_utils_1.performance.now();
|
|
205
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
206
|
-
this.deltaManager.inbound.pause();
|
|
207
|
-
}
|
|
208
|
-
resumeQueue(startBatch, messageEndBatch) {
|
|
209
|
-
const endBatch = messageEndBatch.sequenceNumber;
|
|
210
|
-
const duration = this.localPaused ? (common_utils_1.performance.now() - this.timePaused) : undefined;
|
|
211
|
-
this.batchCount++;
|
|
212
|
-
if (this.batchCount % 1000 === 1) {
|
|
213
|
-
this.logger.sendTelemetryEvent({
|
|
214
|
-
eventName: "BatchStats",
|
|
215
|
-
sequenceNumber: endBatch,
|
|
216
|
-
length: endBatch - startBatch + 1,
|
|
217
|
-
msnDistance: endBatch - messageEndBatch.minimumSequenceNumber,
|
|
218
|
-
duration,
|
|
219
|
-
batchCount: this.batchCount,
|
|
220
|
-
interrupted: this.localPaused,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
// Return early if no change in value
|
|
224
|
-
if (!this.localPaused) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
this.localPaused = false;
|
|
228
|
-
// Random round number - we want to know when batch waiting paused op processing.
|
|
229
|
-
if (duration !== undefined && duration > connectionTelemetry_1.latencyThreshold) {
|
|
230
|
-
this.logger.sendErrorEvent({
|
|
231
|
-
eventName: "MaxBatchWaitTimeExceeded",
|
|
232
|
-
duration,
|
|
233
|
-
sequenceNumber: endBatch,
|
|
234
|
-
length: endBatch - startBatch,
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
this.deltaManager.inbound.resume();
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Called for each incoming op (i.e. inbound "push" notification)
|
|
241
|
-
*/
|
|
242
|
-
trackPending(message) {
|
|
243
|
-
(0, common_utils_1.assert)(this.deltaManager.inbound.length !== 0, 0x298 /* "we have something in the queue that generates this event" */);
|
|
244
|
-
(0, common_utils_1.assert)((this.currentBatchClientId === undefined) === (this.pauseSequenceNumber === undefined), 0x299 /* "non-synchronized state" */);
|
|
245
|
-
const metadata = message.metadata;
|
|
246
|
-
const batchMetadata = metadata === null || metadata === void 0 ? void 0 : metadata.batch;
|
|
247
|
-
// Protocol messages are never part of a runtime batch of messages
|
|
248
|
-
if (!(0, driver_utils_1.isUnpackedRuntimeMessage)(message)) {
|
|
249
|
-
// Protocol messages should never show up in the middle of the batch!
|
|
250
|
-
(0, common_utils_1.assert)(this.currentBatchClientId === undefined, 0x29a /* "System message in the middle of batch!" */);
|
|
251
|
-
(0, common_utils_1.assert)(batchMetadata === undefined, 0x29b /* "system op in a batch?" */);
|
|
252
|
-
(0, common_utils_1.assert)(!this.localPaused, 0x29c /* "we should be processing ops when there is no active batch" */);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
if (this.currentBatchClientId === undefined && batchMetadata === undefined) {
|
|
256
|
-
(0, common_utils_1.assert)(!this.localPaused, 0x29d /* "we should be processing ops when there is no active batch" */);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
// If the client ID changes then we can move the pause point. If it stayed the same then we need to check.
|
|
260
|
-
// If batchMetadata is not undefined then if it's true we've begun a new batch - if false we've ended
|
|
261
|
-
// the previous one
|
|
262
|
-
if (this.currentBatchClientId !== undefined || batchMetadata === false) {
|
|
263
|
-
if (this.currentBatchClientId !== message.clientId) {
|
|
264
|
-
// "Batch not closed, yet message from another client!"
|
|
265
|
-
throw new container_utils_1.DataCorruptionError("OpBatchIncomplete", Object.assign({ runtimeVersion: packageVersion_1.pkgVersion, batchClientId: this.currentBatchClientId }, (0, container_utils_1.extractSafePropertiesFromMessage)(message)));
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
// The queue is
|
|
269
|
-
// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:
|
|
270
|
-
// - in afterOpProcessing() - processing ops until reaching start of incomplete batch
|
|
271
|
-
// - here (batchMetadata == false below), when queue was empty and start of batch showed up.
|
|
272
|
-
// 2. resumed when batch end comes in (batchMetadata === true case below)
|
|
273
|
-
if (batchMetadata) {
|
|
274
|
-
(0, common_utils_1.assert)(this.currentBatchClientId === undefined, 0x29e /* "there can't be active batch" */);
|
|
275
|
-
(0, common_utils_1.assert)(!this.localPaused, 0x29f /* "we should be processing ops when there is no active batch" */);
|
|
276
|
-
this.pauseSequenceNumber = message.sequenceNumber;
|
|
277
|
-
this.currentBatchClientId = message.clientId;
|
|
278
|
-
// Start of the batch
|
|
279
|
-
// Only pause processing if queue has no other ops!
|
|
280
|
-
// If there are any other ops in the queue, processing will be stopped when they are processed!
|
|
281
|
-
if (this.deltaManager.inbound.length === 1) {
|
|
282
|
-
this.pauseQueue();
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
else if (batchMetadata === false) {
|
|
286
|
-
(0, common_utils_1.assert)(this.pauseSequenceNumber !== undefined, 0x2a0 /* "batch presence was validated above" */);
|
|
287
|
-
// Batch is complete, we can process it!
|
|
288
|
-
this.resumeQueue(this.pauseSequenceNumber, message);
|
|
289
|
-
this.pauseSequenceNumber = undefined;
|
|
290
|
-
this.currentBatchClientId = undefined;
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
// Continuation of current batch. Do nothing
|
|
294
|
-
(0, common_utils_1.assert)(this.currentBatchClientId !== undefined, 0x2a1 /* "logic error" */);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* This class has the following responsibilities:
|
|
300
|
-
* 1. It tracks batches as we process ops and raises "batchBegin" and "batchEnd" events.
|
|
301
|
-
* As part of it, it validates batch correctness (i.e. no system ops in the middle of batch)
|
|
302
|
-
* 2. It creates instance of ScheduleManagerCore that ensures we never start processing ops from batch
|
|
303
|
-
* unless all ops of the batch are in.
|
|
304
|
-
*/
|
|
305
|
-
class ScheduleManager {
|
|
306
|
-
constructor(deltaManager, emitter, logger) {
|
|
307
|
-
this.deltaManager = deltaManager;
|
|
308
|
-
this.emitter = emitter;
|
|
309
|
-
this.logger = logger;
|
|
310
|
-
this.hitError = false;
|
|
311
|
-
this.deltaScheduler = new deltaScheduler_1.DeltaScheduler(this.deltaManager, telemetry_utils_1.ChildLogger.create(this.logger, "DeltaScheduler"));
|
|
312
|
-
void new ScheduleManagerCore(deltaManager, logger);
|
|
313
|
-
}
|
|
314
|
-
beforeOpProcessing(message) {
|
|
315
|
-
var _a;
|
|
316
|
-
if (this.batchClientId !== message.clientId) {
|
|
317
|
-
(0, common_utils_1.assert)(this.batchClientId === undefined, 0x2a2 /* "Batch is interrupted by other client op. Should be caught by trackPending()" */);
|
|
318
|
-
// This could be the beginning of a new batch or an individual message.
|
|
319
|
-
this.emitter.emit("batchBegin", message);
|
|
320
|
-
this.deltaScheduler.batchBegin(message);
|
|
321
|
-
const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
|
|
322
|
-
if (batch) {
|
|
323
|
-
this.batchClientId = message.clientId;
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
this.batchClientId = undefined;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
afterOpProcessing(error, message) {
|
|
331
|
-
var _a;
|
|
332
|
-
// If this is no longer true, we need to revisit what we do where we set this.hitError.
|
|
333
|
-
(0, common_utils_1.assert)(!this.hitError, 0x2a3 /* "container should be closed on any error" */);
|
|
334
|
-
if (error) {
|
|
335
|
-
// We assume here that loader will close container and stop processing all future ops.
|
|
336
|
-
// This is implicit dependency. If this flow changes, this code might no longer be correct.
|
|
337
|
-
this.hitError = true;
|
|
338
|
-
this.batchClientId = undefined;
|
|
339
|
-
this.emitter.emit("batchEnd", error, message);
|
|
340
|
-
this.deltaScheduler.batchEnd(message);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
|
|
344
|
-
// If no batchClientId has been set then we're in an individual batch. Else, if we get
|
|
345
|
-
// batch end metadata, this is end of the current batch.
|
|
346
|
-
if (this.batchClientId === undefined || batch === false) {
|
|
347
|
-
this.batchClientId = undefined;
|
|
348
|
-
this.emitter.emit("batchEnd", undefined, message);
|
|
349
|
-
this.deltaScheduler.batchEnd(message);
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
exports.ScheduleManager = ScheduleManager;
|
|
355
126
|
/**
|
|
356
127
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
357
128
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
@@ -379,7 +150,7 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
379
150
|
*/
|
|
380
151
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
381
152
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
382
|
-
var _a, _b, _c, _d
|
|
153
|
+
var _a, _b, _c, _d;
|
|
383
154
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
384
155
|
super();
|
|
385
156
|
this.context = context;
|
|
@@ -390,7 +161,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
390
161
|
this._storage = _storage;
|
|
391
162
|
this.requestHandler = requestHandler;
|
|
392
163
|
this.summaryConfiguration = summaryConfiguration;
|
|
393
|
-
this.defaultMaxConsecutiveReconnects =
|
|
164
|
+
this.defaultMaxConsecutiveReconnects = 7;
|
|
394
165
|
this._orderSequentiallyCalls = 0;
|
|
395
166
|
this.needsFlush = false;
|
|
396
167
|
this.flushTrigger = false;
|
|
@@ -434,8 +205,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
434
205
|
}
|
|
435
206
|
};
|
|
436
207
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
437
|
-
// Default to false (enabled).
|
|
438
|
-
this.disableIsolatedChannels = (_b = this.runtimeOptions.summaryOptions.disableIsolatedChannels) !== null && _b !== void 0 ? _b : false;
|
|
439
208
|
this._connected = this.context.connected;
|
|
440
209
|
this.chunkMap = new Map(chunks);
|
|
441
210
|
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
@@ -448,15 +217,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
448
217
|
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
449
218
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
450
219
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
451
|
-
this._aliasingEnabled =
|
|
452
|
-
((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
|
|
453
|
-
((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
|
|
454
|
-
this._maxOpSizeInBytes = ((_e = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _e !== void 0 ? _e : defaultMaxOpSizeInBytes);
|
|
455
220
|
this.maxConsecutiveReconnects =
|
|
456
|
-
(
|
|
221
|
+
(_b = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _b !== void 0 ? _b : this.defaultMaxConsecutiveReconnects;
|
|
457
222
|
this._flushMode = runtimeOptions.flushMode;
|
|
458
223
|
const pendingRuntimeState = context.pendingLocalState;
|
|
459
|
-
const baseSnapshot = (
|
|
224
|
+
const baseSnapshot = (_c = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _c !== void 0 ? _c : context.baseSnapshot;
|
|
460
225
|
this.garbageCollector = garbageCollection_1.GarbageCollector.create({
|
|
461
226
|
runtime: this,
|
|
462
227
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -490,8 +255,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
490
255
|
this.summarizerNode.loadBaseSummaryWithoutDifferential(baseSnapshot);
|
|
491
256
|
}
|
|
492
257
|
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.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);
|
|
493
|
-
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) =>
|
|
494
|
-
|
|
258
|
+
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
259
|
+
if (!this.disposed) {
|
|
260
|
+
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
261
|
+
}
|
|
262
|
+
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
263
|
+
this.scheduleManager = new scheduleManager_1.ScheduleManager(context.deltaManager, this, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
495
264
|
this.deltaSender = this.deltaManager;
|
|
496
265
|
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
497
266
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
@@ -583,7 +352,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
583
352
|
};
|
|
584
353
|
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
585
354
|
// the count is reset to 0.
|
|
586
|
-
loadSummaryNumber = (
|
|
355
|
+
loadSummaryNumber = (_d = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _d !== void 0 ? _d : 0;
|
|
587
356
|
}
|
|
588
357
|
else {
|
|
589
358
|
this.createContainerMetadata = {
|
|
@@ -618,7 +387,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
618
387
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
619
388
|
},
|
|
620
389
|
});
|
|
621
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close",
|
|
390
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
|
|
622
391
|
const pendingRuntimeState = context.pendingLocalState;
|
|
623
392
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
624
393
|
const storage = !pendingRuntimeState ?
|
|
@@ -671,7 +440,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
671
440
|
summaryOptions,
|
|
672
441
|
gcOptions,
|
|
673
442
|
loadSequenceNumberVerification,
|
|
674
|
-
useDataStoreAliasing,
|
|
675
443
|
flushMode,
|
|
676
444
|
enableOfflineLoad,
|
|
677
445
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
@@ -930,7 +698,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
930
698
|
var _a;
|
|
931
699
|
const metadata = Object.assign(Object.assign(Object.assign(Object.assign({}, this.createContainerMetadata), {
|
|
932
700
|
// Increment the summary number for the next summary that will be generated.
|
|
933
|
-
summaryNumber: this.nextSummaryNumber++, summaryFormatVersion: 1
|
|
701
|
+
summaryNumber: this.nextSummaryNumber++, summaryFormatVersion: 1 }), this.garbageCollector.getMetadata()), {
|
|
934
702
|
// The last message processed at the time of summary. If there are no new messages, use the message from the
|
|
935
703
|
// last summary.
|
|
936
704
|
message: (_a = (0, summaryFormat_1.extractSummaryMetadataMessage)(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary });
|
|
@@ -1053,8 +821,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1053
821
|
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1054
822
|
const connecting = connected && !this._connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1055
823
|
if (connecting && this.blobManager.hasPendingOfflineUploads) {
|
|
1056
|
-
(0, common_utils_1.assert)(!this.delayConnectClientId,
|
|
1057
|
-
(0, common_utils_1.assert)(!!clientId,
|
|
824
|
+
(0, common_utils_1.assert)(!this.delayConnectClientId, 0x392 /* Connect event delay must be canceled before subsequent connect event */);
|
|
825
|
+
(0, common_utils_1.assert)(!!clientId, 0x393 /* Must have clientId when connecting */);
|
|
1058
826
|
this.delayConnectClientId = clientId;
|
|
1059
827
|
this.blobManager.onConnected().then(() => {
|
|
1060
828
|
// make sure we didn't reconnect before the promise resolved
|
|
@@ -1068,17 +836,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1068
836
|
this.setConnectionStateCore(connected, clientId);
|
|
1069
837
|
}
|
|
1070
838
|
setConnectionStateCore(connected, clientId) {
|
|
1071
|
-
(0, common_utils_1.assert)(!this.delayConnectClientId,
|
|
839
|
+
(0, common_utils_1.assert)(!this.delayConnectClientId, 0x394 /* connect event delay must be cleared before propagating connect event */);
|
|
1072
840
|
this.verifyNotClosed();
|
|
1073
841
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
1074
842
|
const changeOfState = this._connected !== connected;
|
|
1075
|
-
const reconnection = changeOfState && connected;
|
|
843
|
+
const reconnection = changeOfState && !connected;
|
|
1076
844
|
this._connected = connected;
|
|
1077
845
|
if (!connected) {
|
|
1078
846
|
this._perfSignalData.signalsLost = 0;
|
|
1079
847
|
this._perfSignalData.signalTimestamp = 0;
|
|
1080
848
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1081
849
|
}
|
|
850
|
+
// Fail while disconnected
|
|
1082
851
|
if (reconnection) {
|
|
1083
852
|
this.consecutiveReconnects++;
|
|
1084
853
|
if (!this.shouldContinueReconnecting()) {
|
|
@@ -1310,59 +1079,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1310
1079
|
}
|
|
1311
1080
|
async createDataStore(pkg) {
|
|
1312
1081
|
const internalId = (0, uuid_1.v4)();
|
|
1313
|
-
return (0, dataStore_1.channelToDataStore)(await this._createDataStore(pkg,
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* Creates a root datastore directly with a user generated id and attaches it to storage.
|
|
1317
|
-
* It is vulnerable to name collisions and should not be used.
|
|
1318
|
-
*
|
|
1319
|
-
* This method will be removed. See #6465.
|
|
1320
|
-
*/
|
|
1321
|
-
async createRootDataStoreLegacy(pkg, rootDataStoreId) {
|
|
1322
|
-
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1323
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
1324
|
-
return fluidDataStore;
|
|
1325
|
-
}
|
|
1326
|
-
/**
|
|
1327
|
-
* @deprecated - will be removed in an upcoming release. See #9660.
|
|
1328
|
-
*/
|
|
1329
|
-
async createRootDataStore(pkg, rootDataStoreId) {
|
|
1330
|
-
if (rootDataStoreId.includes("/")) {
|
|
1331
|
-
throw new container_utils_1.UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
1332
|
-
}
|
|
1333
|
-
return this._aliasingEnabled === true ?
|
|
1334
|
-
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
1335
|
-
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
1336
|
-
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Creates a data store then attempts to alias it.
|
|
1339
|
-
* If aliasing fails, it will raise an exception.
|
|
1340
|
-
*
|
|
1341
|
-
* This method will be removed. See #6465.
|
|
1342
|
-
*
|
|
1343
|
-
* @param pkg - Package name of the data store
|
|
1344
|
-
* @param alias - Alias to be assigned to the data store
|
|
1345
|
-
* @param props - Properties for the data store
|
|
1346
|
-
* @returns - An aliased data store which can can be found / loaded by alias.
|
|
1347
|
-
*/
|
|
1348
|
-
async createAndAliasDataStore(pkg, alias, props) {
|
|
1349
|
-
const internalId = (0, uuid_1.v4)();
|
|
1350
|
-
try {
|
|
1351
|
-
// A similar call may have been initiated by the same client, so we should try to get
|
|
1352
|
-
// a possible existing aliased datastore first.
|
|
1353
|
-
const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
|
|
1354
|
-
return (0, dataStore_1.channelToDataStore)(existingDataStore, internalId, this, this.dataStores, this.mc.logger, true);
|
|
1355
|
-
}
|
|
1356
|
-
catch (err) {
|
|
1357
|
-
const newChannel = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
|
|
1358
|
-
const newDataStore = (0, dataStore_1.channelToDataStore)(newChannel, internalId, this, this.dataStores, this.mc.logger);
|
|
1359
|
-
const aliasResult = await newDataStore.trySetAlias(alias);
|
|
1360
|
-
if (aliasResult === "Success") {
|
|
1361
|
-
return newDataStore;
|
|
1362
|
-
}
|
|
1363
|
-
const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
|
|
1364
|
-
return (0, dataStore_1.channelToDataStore)(existingDataStore, internalId, this, this.dataStores, this.mc.logger, true);
|
|
1365
|
-
}
|
|
1082
|
+
return (0, dataStore_1.channelToDataStore)(await this._createDataStore(pkg, internalId), internalId, this, this.dataStores, this.mc.logger);
|
|
1366
1083
|
}
|
|
1367
1084
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1368
1085
|
if (rootDataStoreId.includes("/")) {
|
|
@@ -1373,31 +1090,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1373
1090
|
createDetachedDataStore(pkg) {
|
|
1374
1091
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1375
1092
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
* It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
|
|
1379
|
-
*
|
|
1380
|
-
* This method will be removed. See #6465.
|
|
1381
|
-
*/
|
|
1382
|
-
async _createDataStoreWithPropsLegacy(pkg, props, id = (0, uuid_1.v4)(), isRoot = false) {
|
|
1383
|
-
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1384
|
-
if (isRoot) {
|
|
1385
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
1386
|
-
this.logger.sendTelemetryEvent({
|
|
1387
|
-
eventName: "Root datastore with props",
|
|
1388
|
-
hasProps: props !== undefined,
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1093
|
+
async _createDataStoreWithProps(pkg, props, id = (0, uuid_1.v4)()) {
|
|
1094
|
+
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props).realize();
|
|
1391
1095
|
return (0, dataStore_1.channelToDataStore)(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
1392
1096
|
}
|
|
1393
|
-
async
|
|
1394
|
-
return this._aliasingEnabled === true && isRoot ?
|
|
1395
|
-
this.createAndAliasDataStore(pkg, id, props) :
|
|
1396
|
-
this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
|
|
1397
|
-
}
|
|
1398
|
-
async _createDataStore(pkg, isRoot, id = (0, uuid_1.v4)(), props) {
|
|
1097
|
+
async _createDataStore(pkg, id = (0, uuid_1.v4)(), props) {
|
|
1399
1098
|
return this.dataStores
|
|
1400
|
-
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id,
|
|
1099
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
1401
1100
|
.realize();
|
|
1402
1101
|
}
|
|
1403
1102
|
canSendOps() {
|
|
@@ -1489,10 +1188,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1489
1188
|
this.blobManager.setRedirectTable(blobRedirectTable);
|
|
1490
1189
|
}
|
|
1491
1190
|
const summarizeResult = this.dataStores.createSummary(telemetryContext);
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
(0, summaryFormat_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1495
|
-
}
|
|
1191
|
+
// Wrap data store summaries in .channels subtree.
|
|
1192
|
+
(0, summaryFormat_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1496
1193
|
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
|
|
1497
1194
|
return summarizeResult.summary;
|
|
1498
1195
|
}
|
|
@@ -1507,12 +1204,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1507
1204
|
}
|
|
1508
1205
|
async summarizeInternal(fullTree, trackState, telemetryContext) {
|
|
1509
1206
|
const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
(0, summaryFormat_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1514
|
-
pathPartsForChildren = [runtime_definitions_1.channelsTreeName];
|
|
1515
|
-
}
|
|
1207
|
+
// Wrap data store summaries in .channels subtree.
|
|
1208
|
+
(0, summaryFormat_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1209
|
+
const pathPartsForChildren = [runtime_definitions_1.channelsTreeName];
|
|
1516
1210
|
this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
|
|
1517
1211
|
return Object.assign(Object.assign({}, summarizeResult), { id: "", pathPartsForChildren });
|
|
1518
1212
|
}
|
|
@@ -1671,16 +1365,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1671
1365
|
const summaryNumberLogger = telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, {
|
|
1672
1366
|
all: { summaryNumber },
|
|
1673
1367
|
});
|
|
1368
|
+
let latestSnapshotVersionId;
|
|
1674
1369
|
if (refreshLatestAck) {
|
|
1675
|
-
const
|
|
1676
|
-
|
|
1370
|
+
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
1371
|
+
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1372
|
+
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1373
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
1677
1374
|
// We need to catch up to the latest summary's reference sequence number before pausing.
|
|
1678
1375
|
await telemetry_utils_1.PerformanceEvent.timedExecAsync(summaryNumberLogger, {
|
|
1679
1376
|
eventName: "WaitingForSeq",
|
|
1680
1377
|
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1681
|
-
targetSequenceNumber:
|
|
1378
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1682
1379
|
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1683
|
-
}, async () => waitForSeq(this.deltaManager,
|
|
1380
|
+
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1684
1381
|
}
|
|
1685
1382
|
}
|
|
1686
1383
|
try {
|
|
@@ -1716,7 +1413,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1716
1413
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1717
1414
|
};
|
|
1718
1415
|
}
|
|
1719
|
-
(0, common_utils_1.assert)(summaryRefSeqNum === ((_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.sequenceNumber),
|
|
1416
|
+
(0, common_utils_1.assert)(summaryRefSeqNum === ((_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.sequenceNumber), 0x395 /* it's one and the same thing */);
|
|
1720
1417
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1721
1418
|
return {
|
|
1722
1419
|
continue: false,
|
|
@@ -1762,7 +1459,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1762
1459
|
// Counting dataStores and handles
|
|
1763
1460
|
// Because handles are unchanged dataStores in the current logic,
|
|
1764
1461
|
// summarized dataStore count is total dataStore count minus handle count
|
|
1765
|
-
const dataStoreTree =
|
|
1462
|
+
const dataStoreTree = summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1766
1463
|
(0, common_utils_1.assert)(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1767
1464
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1768
1465
|
const gcSummaryTreeStats = summaryTree.tree[garbageCollection_1.gcTreeKey]
|
|
@@ -1781,17 +1478,34 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1781
1478
|
if (!continueResult.continue) {
|
|
1782
1479
|
return Object.assign(Object.assign({ stage: "generate" }, generateSummaryData), { error: continueResult.error });
|
|
1783
1480
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1481
|
+
// It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
|
|
1482
|
+
// summary. So if the previous summarizer closes just after submitting the summary and before
|
|
1483
|
+
// submitting the summaryOp then we can't rely on summaryAck. So in case we have
|
|
1484
|
+
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
1485
|
+
// the one fetched from storage as parent as that is the latest.
|
|
1486
|
+
let summaryContext;
|
|
1487
|
+
if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId
|
|
1488
|
+
&& latestSnapshotVersionId !== undefined) {
|
|
1489
|
+
summaryContext = {
|
|
1490
|
+
proposalHandle: undefined,
|
|
1491
|
+
ackHandle: latestSnapshotVersionId,
|
|
1492
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
else if (lastAck === undefined) {
|
|
1496
|
+
summaryContext = {
|
|
1786
1497
|
proposalHandle: undefined,
|
|
1787
1498
|
ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
|
|
1788
1499
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
else {
|
|
1503
|
+
summaryContext = {
|
|
1791
1504
|
proposalHandle: lastAck.summaryOp.contents.handle,
|
|
1792
1505
|
ackHandle: lastAck.summaryAck.contents.handle,
|
|
1793
1506
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1794
1507
|
};
|
|
1508
|
+
}
|
|
1795
1509
|
let handle;
|
|
1796
1510
|
try {
|
|
1797
1511
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
@@ -1898,7 +1612,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1898
1612
|
let opMetadataInternal = opMetadata;
|
|
1899
1613
|
if (this.canSendOps()) {
|
|
1900
1614
|
const serializedContent = JSON.stringify(content);
|
|
1901
|
-
const maxOpSize = this.context.deltaManager.maxMessageSize;
|
|
1902
1615
|
// If in TurnBased flush mode we will trigger a flush at the next turn break
|
|
1903
1616
|
if (this.flushMode === runtime_definitions_1.FlushMode.TurnBased && !this.needsFlush) {
|
|
1904
1617
|
opMetadataInternal = Object.assign(Object.assign({}, opMetadata), { batch: true });
|
|
@@ -1912,7 +1625,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1912
1625
|
});
|
|
1913
1626
|
}
|
|
1914
1627
|
}
|
|
1915
|
-
|
|
1628
|
+
if (!serializedContent || serializedContent.length <= defaultMaxOpSizeInBytes) {
|
|
1629
|
+
clientSequenceNumber = this.submitRuntimeMessage(type, content, this._flushMode === runtime_definitions_1.FlushMode.TurnBased /* batch */, opMetadataInternal);
|
|
1630
|
+
}
|
|
1631
|
+
else {
|
|
1632
|
+
// If the content length is larger than the client configured message size
|
|
1633
|
+
// instead of splitting the content, we will fail by explicitly closing the container
|
|
1634
|
+
this.closeFn(new container_utils_1.GenericError("OpTooLarge",
|
|
1635
|
+
/* error */ undefined, {
|
|
1636
|
+
length: serializedContent.length,
|
|
1637
|
+
limit: defaultMaxOpSizeInBytes,
|
|
1638
|
+
}));
|
|
1639
|
+
clientSequenceNumber = -1;
|
|
1640
|
+
}
|
|
1916
1641
|
}
|
|
1917
1642
|
// Let the PendingStateManager know that a message was submitted.
|
|
1918
1643
|
this.pendingStateManager.onSubmitMessage(type, clientSequenceNumber, this.deltaManager.lastSequenceNumber, content, localOpMetadata, opMetadataInternal);
|
|
@@ -1920,46 +1645,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1920
1645
|
this.updateDocumentDirtyState(true);
|
|
1921
1646
|
}
|
|
1922
1647
|
}
|
|
1923
|
-
submitMaybeChunkedMessages(type, content, serializedContent, serverMaxOpSize, batch, opMetadataInternal = undefined) {
|
|
1924
|
-
if (this._maxOpSizeInBytes >= 0) {
|
|
1925
|
-
// Chunking disabled
|
|
1926
|
-
if (!serializedContent || serializedContent.length <= this._maxOpSizeInBytes) {
|
|
1927
|
-
return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
|
|
1928
|
-
}
|
|
1929
|
-
// When chunking is disabled, we ignore the server max message size
|
|
1930
|
-
// and if the content length is larger than the client configured message size
|
|
1931
|
-
// instead of splitting the content, we will fail by explicitly close the container
|
|
1932
|
-
this.closeFn(new container_utils_1.GenericError("OpTooLarge",
|
|
1933
|
-
/* error */ undefined, {
|
|
1934
|
-
length: serializedContent.length,
|
|
1935
|
-
limit: this._maxOpSizeInBytes,
|
|
1936
|
-
}));
|
|
1937
|
-
return -1;
|
|
1938
|
-
}
|
|
1939
|
-
// Chunking enabled, fallback on the server's max message size
|
|
1940
|
-
// and split the content accordingly
|
|
1941
|
-
if (!serializedContent || serializedContent.length <= serverMaxOpSize) {
|
|
1942
|
-
return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
|
|
1943
|
-
}
|
|
1944
|
-
return this.submitChunkedMessage(type, serializedContent, serverMaxOpSize);
|
|
1945
|
-
}
|
|
1946
|
-
submitChunkedMessage(type, content, maxOpSize) {
|
|
1947
|
-
const contentLength = content.length;
|
|
1948
|
-
const chunkN = Math.floor((contentLength - 1) / maxOpSize) + 1;
|
|
1949
|
-
let offset = 0;
|
|
1950
|
-
let clientSequenceNumber = 0;
|
|
1951
|
-
for (let i = 1; i <= chunkN; i = i + 1) {
|
|
1952
|
-
const chunkedOp = {
|
|
1953
|
-
chunkId: i,
|
|
1954
|
-
contents: content.substr(offset, maxOpSize),
|
|
1955
|
-
originalType: type,
|
|
1956
|
-
totalChunks: chunkN,
|
|
1957
|
-
};
|
|
1958
|
-
offset += maxOpSize;
|
|
1959
|
-
clientSequenceNumber = this.submitRuntimeMessage(ContainerMessageType.ChunkedOp, chunkedOp, false);
|
|
1960
|
-
}
|
|
1961
|
-
return clientSequenceNumber;
|
|
1962
|
-
}
|
|
1963
1648
|
submitSystemMessage(type, contents) {
|
|
1964
1649
|
this.verifyNotClosed();
|
|
1965
1650
|
(0, common_utils_1.assert)(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
@@ -2030,12 +1715,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2030
1715
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2031
1716
|
async refreshLatestSummaryAck(proposalHandle, ackHandle, summaryRefSeq, summaryLogger) {
|
|
2032
1717
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
2033
|
-
const
|
|
1718
|
+
const { snapshotTree } = await this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
|
|
2034
1719
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2035
1720
|
ackHandle,
|
|
2036
1721
|
summaryRefSeq,
|
|
2037
1722
|
fetchLatest: false,
|
|
2038
|
-
})
|
|
1723
|
+
});
|
|
1724
|
+
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
|
|
2039
1725
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2040
1726
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
2041
1727
|
}
|
|
@@ -2046,16 +1732,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2046
1732
|
* @returns downloaded snapshot's reference sequence number
|
|
2047
1733
|
*/
|
|
2048
1734
|
async refreshLatestSummaryAckFromServer(summaryLogger) {
|
|
2049
|
-
const
|
|
1735
|
+
const { snapshotTree, versionId } = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
2050
1736
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2051
1737
|
fetchLatest: true,
|
|
2052
1738
|
}, driver_definitions_1.FetchSource.noCache);
|
|
2053
1739
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
2054
|
-
const
|
|
2055
|
-
const result = await this.summarizerNode.refreshLatestSummary(undefined,
|
|
1740
|
+
const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(snapshotTree, readAndParseBlob);
|
|
1741
|
+
const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
|
|
2056
1742
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2057
1743
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
2058
|
-
return
|
|
1744
|
+
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
2059
1745
|
}
|
|
2060
1746
|
async fetchSnapshotFromStorage(versionId, logger, event, fetchSource) {
|
|
2061
1747
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
@@ -2068,7 +1754,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2068
1754
|
(0, common_utils_1.assert)(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
|
|
2069
1755
|
stats.getSnapshotDuration = trace.trace().duration;
|
|
2070
1756
|
perfEvent.end(stats);
|
|
2071
|
-
return maybeSnapshot;
|
|
1757
|
+
return { snapshotTree: maybeSnapshot, versionId: versions[0].id };
|
|
2072
1758
|
});
|
|
2073
1759
|
}
|
|
2074
1760
|
notifyAttaching(snapshot) {
|
|
@@ -2095,6 +1781,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2095
1781
|
if (previousPendingState) {
|
|
2096
1782
|
return {
|
|
2097
1783
|
pending: this.pendingStateManager.getLocalState(),
|
|
1784
|
+
pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
|
|
2098
1785
|
snapshotBlobs: previousPendingState.snapshotBlobs,
|
|
2099
1786
|
baseSnapshot: previousPendingState.baseSnapshot,
|
|
2100
1787
|
savedOps: this.savedOps,
|
|
@@ -2104,6 +1791,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2104
1791
|
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x2e7 /* "Must serialize base snapshot blobs before getting runtime state" */);
|
|
2105
1792
|
return {
|
|
2106
1793
|
pending: this.pendingStateManager.getLocalState(),
|
|
1794
|
+
pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
|
|
2107
1795
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
2108
1796
|
baseSnapshot: this.context.baseSnapshot,
|
|
2109
1797
|
savedOps: this.savedOps,
|