@fluidframework/container-loader 2.0.0-internal.5.4.0 → 2.0.0-internal.6.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/CHANGELOG.md +85 -0
- package/dist/connectionManager.d.ts +4 -4
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +57 -49
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +15 -14
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +26 -28
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +10 -5
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +183 -134
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -12
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +1 -20
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.js +3 -5
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +20 -7
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +3 -3
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.js +2 -3
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +19 -6
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +67 -28
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +1 -2
- package/dist/deltaQueue.js.map +1 -1
- package/dist/loader.d.ts +12 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +57 -42
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +4 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +25 -4
- package/dist/protocol.js.map +1 -1
- package/dist/utils.d.ts +8 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -6
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts +4 -4
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +58 -50
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +15 -14
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +26 -28
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +10 -5
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +182 -133
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -12
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +1 -20
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.js +3 -5
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +20 -7
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +3 -3
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.js +2 -3
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +19 -6
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +67 -28
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +1 -2
- package/lib/deltaQueue.js.map +1 -1
- package/lib/loader.d.ts +12 -0
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +57 -42
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +4 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +25 -4
- package/lib/protocol.js.map +1 -1
- package/lib/utils.d.ts +8 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +22 -5
- package/lib/utils.js.map +1 -1
- package/package.json +15 -19
- package/src/connectionManager.ts +53 -34
- package/src/connectionStateHandler.ts +30 -37
- package/src/container.ts +156 -76
- package/src/containerContext.ts +0 -24
- package/src/contracts.ts +27 -10
- package/src/deltaManager.ts +41 -18
- package/src/loader.ts +37 -23
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +33 -2
- package/src/utils.ts +29 -0
package/dist/container.js
CHANGED
|
@@ -111,7 +111,7 @@ async function waitContainerToCatchUp(container) {
|
|
|
111
111
|
exports.waitContainerToCatchUp = waitContainerToCatchUp;
|
|
112
112
|
const getCodeProposal =
|
|
113
113
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
114
|
-
(quorum) =>
|
|
114
|
+
(quorum) => quorum.get("code") ?? quorum.get("code2");
|
|
115
115
|
/**
|
|
116
116
|
* Helper function to report to telemetry cases where operation takes longer than expected (200ms)
|
|
117
117
|
* @param logger - logger to use
|
|
@@ -132,7 +132,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
132
132
|
* @internal
|
|
133
133
|
*/
|
|
134
134
|
constructor(createProps, loadProps) {
|
|
135
|
-
var _a;
|
|
136
135
|
super((name, error) => {
|
|
137
136
|
this.mc.logger.sendErrorEvent({
|
|
138
137
|
eventName: "ContainerEventHandlerException",
|
|
@@ -182,8 +181,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
182
181
|
};
|
|
183
182
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
184
183
|
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
|
|
185
|
-
const pendingLocalState = loadProps
|
|
186
|
-
this._canReconnect = canReconnect
|
|
184
|
+
const pendingLocalState = loadProps?.pendingLocalState;
|
|
185
|
+
this._canReconnect = canReconnect ?? true;
|
|
187
186
|
this.clientDetailsOverride = clientDetailsOverride;
|
|
188
187
|
this.urlResolver = urlResolver;
|
|
189
188
|
this.serviceFactory = documentServiceFactory;
|
|
@@ -191,14 +190,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
191
190
|
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
192
191
|
// all clients that were loaded from the same loader (including summarizer clients).
|
|
193
192
|
// Tracking alternative ways to handle this in AB#4129.
|
|
194
|
-
this.options =
|
|
193
|
+
this.options = { ...options };
|
|
195
194
|
this.scope = scope;
|
|
196
195
|
this.detachedBlobStorage = detachedBlobStorage;
|
|
197
196
|
this.protocolHandlerBuilder =
|
|
198
|
-
protocolHandlerBuilder
|
|
197
|
+
protocolHandlerBuilder ??
|
|
198
|
+
((attributes, quorumSnapshot, sendProposal) => new protocol_1.ProtocolHandler(attributes, quorumSnapshot, sendProposal, new audience_1.Audience(), (clientId) => this.clientsWhoShouldHaveLeft.has(clientId)));
|
|
199
199
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
200
200
|
this.clone = async (_loadProps, createParamOverrides) => {
|
|
201
|
-
return Container.load(_loadProps,
|
|
201
|
+
return Container.load(_loadProps, {
|
|
202
|
+
...createProps,
|
|
203
|
+
...createParamOverrides,
|
|
204
|
+
});
|
|
202
205
|
};
|
|
203
206
|
// Create logger for data stores to use
|
|
204
207
|
const type = this.client.details.type;
|
|
@@ -212,7 +215,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
212
215
|
all: {
|
|
213
216
|
clientType,
|
|
214
217
|
containerId: (0, uuid_1.v4)(),
|
|
215
|
-
docId: () =>
|
|
218
|
+
docId: () => this.resolvedUrl?.id,
|
|
216
219
|
containerAttachState: () => this._attachState,
|
|
217
220
|
containerLifecycleState: () => this._lifecycleState,
|
|
218
221
|
containerConnectionState: () => connectionState_1.ConnectionState[this.connectionState],
|
|
@@ -223,22 +226,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
223
226
|
// specific error or class of errors
|
|
224
227
|
error: {
|
|
225
228
|
// load information to associate errors with the specific load point
|
|
226
|
-
dmInitialSeqNumber: () =>
|
|
227
|
-
dmLastProcessedSeqNumber: () =>
|
|
228
|
-
dmLastKnownSeqNumber: () =>
|
|
229
|
-
containerLoadedFromVersionId: () =>
|
|
230
|
-
containerLoadedFromVersionDate: () =>
|
|
229
|
+
dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
|
|
230
|
+
dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
|
|
231
|
+
dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
|
|
232
|
+
containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
|
|
233
|
+
containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
|
|
231
234
|
// message information to associate errors with the specific execution state
|
|
232
235
|
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
233
|
-
dmLastMsqSeqNumber: () =>
|
|
234
|
-
dmLastMsqSeqTimestamp: () =>
|
|
235
|
-
dmLastMsqSeqClientId: () =>
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
: (_d = (_c = this.deltaManager) === null || _c === void 0 ? void 0 : _c.lastMessage) === null || _d === void 0 ? void 0 : _d.clientId;
|
|
240
|
-
},
|
|
241
|
-
dmLastMsgClientSeq: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientSequenceNumber; },
|
|
236
|
+
dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
|
|
237
|
+
dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
|
|
238
|
+
dmLastMsqSeqClientId: () => this.deltaManager?.lastMessage?.clientId === null
|
|
239
|
+
? "null"
|
|
240
|
+
: this.deltaManager?.lastMessage?.clientId,
|
|
241
|
+
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
242
242
|
connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
243
243
|
},
|
|
244
244
|
},
|
|
@@ -248,11 +248,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
248
248
|
this._deltaManager = this.createDeltaManager();
|
|
249
249
|
this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
|
|
250
250
|
logger: this.mc.logger,
|
|
251
|
-
connectionStateChanged: (value, oldState, reason
|
|
251
|
+
connectionStateChanged: (value, oldState, reason) => {
|
|
252
252
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
253
253
|
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
254
254
|
}
|
|
255
|
-
this.logConnectionStateChangeTelemetry(value, oldState, reason
|
|
255
|
+
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
256
256
|
if (this._lifecycleState === "loaded") {
|
|
257
257
|
this.propagateConnectionState(false /* initial transition */, value === connectionState_1.ConnectionState.Disconnected
|
|
258
258
|
? reason
|
|
@@ -268,9 +268,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
268
268
|
// Report issues only if we already loaded container - op processing is paused while container is loading,
|
|
269
269
|
// so we always time-out processing of join op in cases where fetching snapshot takes a minute.
|
|
270
270
|
// It's not a problem with op processing itself - such issues should be tracked as part of boot perf monitoring instead.
|
|
271
|
-
this._deltaManager.logConnectionIssue(
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
this._deltaManager.logConnectionIssue({
|
|
272
|
+
eventName,
|
|
273
|
+
mode,
|
|
274
|
+
category: this._lifecycleState === "loading" ? "generic" : category,
|
|
275
|
+
duration: common_utils_1.performance.now() -
|
|
276
|
+
this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp],
|
|
277
|
+
...(details === undefined ? {} : { details: JSON.stringify(details) }),
|
|
278
|
+
});
|
|
274
279
|
// If this is "write" connection, it took too long to receive join op. But in most cases that's due
|
|
275
280
|
// to very slow op fetches and we will eventually get there.
|
|
276
281
|
// For "read" connections, we get here due to self join signal not arriving on time. We will need to
|
|
@@ -280,14 +285,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
280
285
|
// Other possible recovery path - move to connected state (i.e. ConnectionStateHandler.joinOpTimer
|
|
281
286
|
// to call this.applyForConnectedState("addMemberEvent") for "read" connections)
|
|
282
287
|
if (mode === "read") {
|
|
283
|
-
|
|
284
|
-
this.
|
|
288
|
+
const reason = { text: "NoJoinSignal" };
|
|
289
|
+
this.disconnectInternal(reason);
|
|
290
|
+
this.connectInternal({ reason, fetchOpsFromStorage: false });
|
|
285
291
|
}
|
|
286
292
|
},
|
|
287
293
|
clientShouldHaveLeft: (clientId) => {
|
|
288
294
|
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
289
295
|
},
|
|
290
|
-
}, this.deltaManager, pendingLocalState
|
|
296
|
+
}, this.deltaManager, pendingLocalState?.clientId);
|
|
291
297
|
this.on(savedContainerEvent, () => {
|
|
292
298
|
this.connectionStateHandler.containerSaved();
|
|
293
299
|
});
|
|
@@ -296,11 +302,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
296
302
|
// using this callback and fix them up.
|
|
297
303
|
const addProtocolSummaryIfMissing = (summaryTree) => (0, driver_utils_1.isCombinedAppAndProtocolSummary)(summaryTree) === true
|
|
298
304
|
? summaryTree
|
|
299
|
-
: (0,
|
|
305
|
+
: (0, utils_1.combineAppAndProtocolSummary)(summaryTree, this.captureProtocolSummary());
|
|
300
306
|
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
301
307
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
302
|
-
const forceEnableSummarizeProtocolTree =
|
|
303
|
-
|
|
308
|
+
const forceEnableSummarizeProtocolTree = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
309
|
+
options.summarizeProtocolTree;
|
|
310
|
+
this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
304
311
|
const isDomAvailable = typeof document === "object" &&
|
|
305
312
|
document !== null &&
|
|
306
313
|
typeof document.addEventListener === "function" &&
|
|
@@ -327,7 +334,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
327
334
|
* @internal
|
|
328
335
|
*/
|
|
329
336
|
static async load(loadProps, createProps) {
|
|
330
|
-
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
337
|
+
const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } = loadProps;
|
|
331
338
|
const container = new Container(createProps, loadProps);
|
|
332
339
|
const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
|
|
333
340
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
@@ -335,19 +342,20 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
335
342
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
336
343
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
337
344
|
const mode = pendingLocalState
|
|
338
|
-
?
|
|
345
|
+
? { ...(loadMode ?? defaultMode), opsBeforeReturn: undefined }
|
|
346
|
+
: loadMode ?? defaultMode;
|
|
339
347
|
const onClosed = (err) => {
|
|
340
348
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
341
|
-
reject(err
|
|
349
|
+
reject(err ?? new container_utils_1.GenericError("Container closed without error during load"));
|
|
342
350
|
};
|
|
343
351
|
container.on("closed", onClosed);
|
|
344
352
|
container
|
|
345
|
-
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
353
|
+
.load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
|
|
346
354
|
.finally(() => {
|
|
347
355
|
container.removeListener("closed", onClosed);
|
|
348
356
|
})
|
|
349
357
|
.then((props) => {
|
|
350
|
-
event.end(
|
|
358
|
+
event.end({ ...props, ...loadMode });
|
|
351
359
|
resolve(container);
|
|
352
360
|
}, (error) => {
|
|
353
361
|
const err = (0, telemetry_utils_1.normalizeError)(error);
|
|
@@ -415,7 +423,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
415
423
|
return this;
|
|
416
424
|
}
|
|
417
425
|
get resolvedUrl() {
|
|
418
|
-
var _a;
|
|
419
426
|
/**
|
|
420
427
|
* All attached containers will have a document service,
|
|
421
428
|
* this is required, as attached containers are attached to
|
|
@@ -427,7 +434,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
427
434
|
* is always the same as the containers, as we had to
|
|
428
435
|
* obtain the resolved url, and then create the service from it.
|
|
429
436
|
*/
|
|
430
|
-
return
|
|
437
|
+
return this.service?.resolvedUrl;
|
|
431
438
|
}
|
|
432
439
|
get readOnlyInfo() {
|
|
433
440
|
return this._deltaManager.readOnlyInfo;
|
|
@@ -455,8 +462,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
455
462
|
return this._clientId;
|
|
456
463
|
}
|
|
457
464
|
get offlineLoadEnabled() {
|
|
458
|
-
|
|
459
|
-
|
|
465
|
+
const enabled = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ??
|
|
466
|
+
this.options?.enableOfflineLoad === true;
|
|
460
467
|
// summarizer will not have any pending state we want to save
|
|
461
468
|
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
462
469
|
}
|
|
@@ -493,18 +500,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
493
500
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
494
501
|
*/
|
|
495
502
|
async getEntryPoint() {
|
|
496
|
-
var _a, _b;
|
|
497
503
|
if (this._disposed) {
|
|
498
504
|
throw new container_utils_1.UsageError("The context is already disposed");
|
|
499
505
|
}
|
|
500
506
|
if (this._runtime !== undefined) {
|
|
501
|
-
return
|
|
507
|
+
return this._runtime.getEntryPoint?.();
|
|
502
508
|
}
|
|
503
509
|
return new Promise((resolve, reject) => {
|
|
504
510
|
const runtimeInstantiatedHandler = () => {
|
|
505
|
-
var _a, _b;
|
|
506
511
|
(0, common_utils_1.assert)(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
|
|
507
|
-
resolve(
|
|
512
|
+
resolve(this._runtime.getEntryPoint?.());
|
|
508
513
|
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
509
514
|
};
|
|
510
515
|
const disposedHandler = () => {
|
|
@@ -538,7 +543,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
538
543
|
(0, common_utils_1.assert)(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
|
|
539
544
|
}
|
|
540
545
|
closeCore(error) {
|
|
541
|
-
var _a;
|
|
542
546
|
(0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
543
547
|
try {
|
|
544
548
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -554,7 +558,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
554
558
|
: "generic",
|
|
555
559
|
}, error);
|
|
556
560
|
this._lifecycleState = "closing";
|
|
557
|
-
|
|
561
|
+
this._protocolHandler?.close();
|
|
558
562
|
this.connectionStateHandler.dispose();
|
|
559
563
|
}
|
|
560
564
|
catch (exception) {
|
|
@@ -574,7 +578,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
574
578
|
}
|
|
575
579
|
}
|
|
576
580
|
disposeCore(error) {
|
|
577
|
-
var _a, _b, _c;
|
|
578
581
|
(0, common_utils_1.assert)(!this._disposed, 0x54c /* Container already disposed */);
|
|
579
582
|
this._disposed = true;
|
|
580
583
|
try {
|
|
@@ -591,15 +594,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
591
594
|
if (this._lifecycleState !== "closed") {
|
|
592
595
|
this._lifecycleState = "disposing";
|
|
593
596
|
}
|
|
594
|
-
|
|
597
|
+
this._protocolHandler?.close();
|
|
595
598
|
this.connectionStateHandler.dispose();
|
|
596
599
|
const maybeError = error !== undefined ? new Error(error.message) : undefined;
|
|
597
|
-
|
|
600
|
+
this._runtime?.dispose(maybeError);
|
|
598
601
|
this.storageAdapter.dispose();
|
|
599
602
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
600
603
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
601
604
|
// Driver need to ensure all caches are cleared on critical errors
|
|
602
|
-
|
|
605
|
+
this.service?.dispose(error);
|
|
603
606
|
}
|
|
604
607
|
catch (exception) {
|
|
605
608
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerDisposeException" }, exception);
|
|
@@ -615,15 +618,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
615
618
|
this._lifecycleEvents.emit("disposed");
|
|
616
619
|
}
|
|
617
620
|
}
|
|
618
|
-
closeAndGetPendingLocalState() {
|
|
621
|
+
async closeAndGetPendingLocalState() {
|
|
619
622
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
620
623
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
621
624
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
622
|
-
|
|
625
|
+
this.disconnectInternal({ text: "closeAndGetPendingLocalState" }); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
626
|
+
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
623
627
|
this.close();
|
|
624
628
|
return pendingState;
|
|
625
629
|
}
|
|
626
|
-
getPendingLocalState() {
|
|
630
|
+
async getPendingLocalState() {
|
|
631
|
+
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
632
|
+
}
|
|
633
|
+
async getPendingLocalStateCore(props) {
|
|
627
634
|
if (!this.offlineLoadEnabled) {
|
|
628
635
|
throw new container_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
|
|
629
636
|
}
|
|
@@ -634,8 +641,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
634
641
|
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
635
642
|
(0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
636
643
|
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
644
|
+
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
637
645
|
const pendingState = {
|
|
638
|
-
pendingRuntimeState
|
|
646
|
+
pendingRuntimeState,
|
|
639
647
|
baseSnapshot: this.baseSnapshot,
|
|
640
648
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
641
649
|
savedOps: this.savedOps,
|
|
@@ -653,7 +661,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
653
661
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
|
|
654
662
|
const appSummary = this.runtime.createSummary();
|
|
655
663
|
const protocolSummary = this.captureProtocolSummary();
|
|
656
|
-
const combinedSummary = (0,
|
|
664
|
+
const combinedSummary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
657
665
|
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
658
666
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
659
667
|
type: protocol_definitions_1.SummaryType.Blob,
|
|
@@ -664,7 +672,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
664
672
|
}
|
|
665
673
|
async attach(request) {
|
|
666
674
|
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
667
|
-
var _a;
|
|
668
675
|
if (this._lifecycleState !== "loaded") {
|
|
669
676
|
// pre-0.58 error message: containerNotValidForAttach
|
|
670
677
|
throw new container_utils_1.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
|
|
@@ -682,7 +689,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
682
689
|
// semantics around what the attach means as far as async code goes.
|
|
683
690
|
const appSummary = this.runtime.createSummary();
|
|
684
691
|
const protocolSummary = this.captureProtocolSummary();
|
|
685
|
-
summary = (0,
|
|
692
|
+
summary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
686
693
|
// Set the state as attaching as we are starting the process of attaching container.
|
|
687
694
|
// This should be fired after taking the summary because it is the place where we are
|
|
688
695
|
// starting to attach the container to storage.
|
|
@@ -727,7 +734,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
727
734
|
// take summary and upload
|
|
728
735
|
const appSummary = this.runtime.createSummary(redirectTable);
|
|
729
736
|
const protocolSummary = this.captureProtocolSummary();
|
|
730
|
-
summary = (0,
|
|
737
|
+
summary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
731
738
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
732
739
|
this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
|
|
733
740
|
this.emit("attaching");
|
|
@@ -749,14 +756,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
749
756
|
if (!this.closed) {
|
|
750
757
|
this.resumeInternal({
|
|
751
758
|
fetchOpsFromStorage: false,
|
|
752
|
-
reason: "createDetached",
|
|
759
|
+
reason: { text: "createDetached" },
|
|
753
760
|
});
|
|
754
761
|
}
|
|
755
762
|
}
|
|
756
763
|
catch (error) {
|
|
757
764
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
758
765
|
const newError = (0, telemetry_utils_1.normalizeError)(error);
|
|
759
|
-
newError.addTelemetryProperties({ resolvedUrl:
|
|
766
|
+
newError.addTelemetryProperties({ resolvedUrl: this.resolvedUrl?.url });
|
|
760
767
|
this.close(newError);
|
|
761
768
|
throw newError;
|
|
762
769
|
}
|
|
@@ -765,7 +772,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
765
772
|
async request(path) {
|
|
766
773
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
|
|
767
774
|
}
|
|
768
|
-
setAutoReconnectInternal(mode) {
|
|
775
|
+
setAutoReconnectInternal(mode, reason) {
|
|
769
776
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
770
777
|
if (currentMode === mode) {
|
|
771
778
|
return;
|
|
@@ -779,7 +786,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
779
786
|
connectionState: connectionState_1.ConnectionState[this.connectionState],
|
|
780
787
|
duration,
|
|
781
788
|
});
|
|
782
|
-
this._deltaManager.connectionManager.setAutoReconnect(mode);
|
|
789
|
+
this._deltaManager.connectionManager.setAutoReconnect(mode, reason);
|
|
783
790
|
}
|
|
784
791
|
connect() {
|
|
785
792
|
if (this.closed) {
|
|
@@ -792,7 +799,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
792
799
|
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
793
800
|
// If there is gap, we will learn about it once connected, but the gap should be small (if any),
|
|
794
801
|
// assuming that connect() is called quickly after initial container boot.
|
|
795
|
-
this.connectInternal({
|
|
802
|
+
this.connectInternal({
|
|
803
|
+
reason: { text: "DocumentConnect" },
|
|
804
|
+
fetchOpsFromStorage: false,
|
|
805
|
+
});
|
|
796
806
|
}
|
|
797
807
|
}
|
|
798
808
|
connectInternal(args) {
|
|
@@ -802,21 +812,21 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
802
812
|
this.resumeInternal(args);
|
|
803
813
|
// Set Auto Reconnect Mode
|
|
804
814
|
const mode = contracts_1.ReconnectMode.Enabled;
|
|
805
|
-
this.setAutoReconnectInternal(mode);
|
|
815
|
+
this.setAutoReconnectInternal(mode, args.reason);
|
|
806
816
|
}
|
|
807
817
|
disconnect() {
|
|
808
818
|
if (this.closed) {
|
|
809
819
|
throw new container_utils_1.UsageError(`The Container is closed and cannot be disconnected`);
|
|
810
820
|
}
|
|
811
821
|
else {
|
|
812
|
-
this.disconnectInternal();
|
|
822
|
+
this.disconnectInternal({ text: "DocumentDisconnect" });
|
|
813
823
|
}
|
|
814
824
|
}
|
|
815
|
-
disconnectInternal() {
|
|
825
|
+
disconnectInternal(reason) {
|
|
816
826
|
(0, common_utils_1.assert)(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
|
|
817
827
|
// Set Auto Reconnect Mode
|
|
818
828
|
const mode = contracts_1.ReconnectMode.Disabled;
|
|
819
|
-
this.setAutoReconnectInternal(mode);
|
|
829
|
+
this.setAutoReconnectInternal(mode, reason);
|
|
820
830
|
}
|
|
821
831
|
resumeInternal(args) {
|
|
822
832
|
(0, common_utils_1.assert)(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
|
|
@@ -863,7 +873,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
863
873
|
* Determines if the currently loaded module satisfies the incoming constraint code details
|
|
864
874
|
*/
|
|
865
875
|
async satisfies(constraintCodeDetails) {
|
|
866
|
-
var _a, _b;
|
|
867
876
|
// If we have no module, it can't satisfy anything.
|
|
868
877
|
if (this._loadedModule === undefined) {
|
|
869
878
|
return false;
|
|
@@ -873,8 +882,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
873
882
|
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
874
883
|
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
875
884
|
}
|
|
876
|
-
const maybeCompareExport =
|
|
877
|
-
if (
|
|
885
|
+
const maybeCompareExport = this._loadedModule?.module.fluidExport;
|
|
886
|
+
if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
|
|
878
887
|
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
879
888
|
}
|
|
880
889
|
// If there are no comparers, then it's impossible to know if the currently loaded package satisfies
|
|
@@ -884,7 +893,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
884
893
|
return false;
|
|
885
894
|
}
|
|
886
895
|
for (const comparer of comparers) {
|
|
887
|
-
const satisfies = await comparer.satisfies(
|
|
896
|
+
const satisfies = await comparer.satisfies(this._loadedModule?.details, constraintCodeDetails);
|
|
888
897
|
if (satisfies === false) {
|
|
889
898
|
return false;
|
|
890
899
|
}
|
|
@@ -907,8 +916,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
907
916
|
*
|
|
908
917
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
909
918
|
*/
|
|
910
|
-
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
911
|
-
var _a, _b, _c;
|
|
919
|
+
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState, loadToSequenceNumber) {
|
|
912
920
|
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
913
921
|
// Ideally we always connect as "read" by default.
|
|
914
922
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
@@ -920,7 +928,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
920
928
|
// A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
|
|
921
929
|
// B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
|
|
922
930
|
const connectionArgs = {
|
|
923
|
-
reason: "DocumentOpen",
|
|
931
|
+
reason: { text: "DocumentOpen" },
|
|
924
932
|
mode: "write",
|
|
925
933
|
fetchOpsFromStorage: false,
|
|
926
934
|
};
|
|
@@ -957,9 +965,51 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
957
965
|
}
|
|
958
966
|
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
959
967
|
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
960
|
-
const sequenceNumber =
|
|
961
|
-
const dmAttributes = sequenceNumber !== undefined ?
|
|
968
|
+
const sequenceNumber = pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber;
|
|
969
|
+
const dmAttributes = sequenceNumber !== undefined ? { ...attributes, sequenceNumber } : attributes;
|
|
962
970
|
let opsBeforeReturnP;
|
|
971
|
+
if (loadMode.pauseAfterLoad === true) {
|
|
972
|
+
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
973
|
+
if (loadMode.opsBeforeReturn === "sequenceNumber") {
|
|
974
|
+
(0, common_utils_1.assert)(loadToSequenceNumber !== undefined, 0x727 /* sequenceNumber should be defined */);
|
|
975
|
+
// Note: It is possible that we think the latest snapshot is newer than the specified sequence number
|
|
976
|
+
// due to saved ops that may be replayed after the snapshot.
|
|
977
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
|
|
978
|
+
if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
|
|
979
|
+
throw new Error("Cannot satisfy request to pause the container at the specified sequence number. Most recent snapshot is newer than the specified sequence number.");
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
// Force readonly mode - this will ensure we don't receive an error for the lack of join op
|
|
983
|
+
this.forceReadonly(true);
|
|
984
|
+
// We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
|
|
985
|
+
const opHandler = () => {
|
|
986
|
+
if (loadToSequenceNumber === undefined) {
|
|
987
|
+
// If there is no specified sequence number, pause after the inbound queue is empty.
|
|
988
|
+
if (this.deltaManager.inbound.length !== 0) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
// If there is a specified sequence number, keep processing until we reach it.
|
|
994
|
+
if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
// Pause op processing once we have processed the desired number of ops.
|
|
999
|
+
void this.deltaManager.inbound.pause();
|
|
1000
|
+
void this.deltaManager.outbound.pause();
|
|
1001
|
+
this.off("op", opHandler);
|
|
1002
|
+
};
|
|
1003
|
+
if ((loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
|
|
1004
|
+
this.deltaManager.lastSequenceNumber === loadToSequenceNumber) {
|
|
1005
|
+
// If we have already reached the desired sequence number, call opHandler() to pause immediately.
|
|
1006
|
+
opHandler();
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
// If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
|
|
1010
|
+
this.on("op", opHandler);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
963
1013
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
964
1014
|
// Kick off any ops fetching if required.
|
|
965
1015
|
switch (loadMode.opsBeforeReturn) {
|
|
@@ -968,6 +1018,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
968
1018
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
969
1019
|
this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
|
|
970
1020
|
break;
|
|
1021
|
+
case "sequenceNumber":
|
|
1022
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
|
|
1023
|
+
break;
|
|
971
1024
|
case "cached":
|
|
972
1025
|
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
973
1026
|
break;
|
|
@@ -981,18 +1034,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
981
1034
|
// Initialize the protocol handler
|
|
982
1035
|
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
983
1036
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
984
|
-
await this.instantiateRuntime(codeDetails, snapshot, pendingLocalState
|
|
1037
|
+
await this.instantiateRuntime(codeDetails, snapshot, pendingLocalState?.pendingRuntimeState);
|
|
985
1038
|
// replay saved ops
|
|
986
1039
|
if (pendingLocalState) {
|
|
987
1040
|
for (const message of pendingLocalState.savedOps) {
|
|
988
1041
|
this.processRemoteMessage(message);
|
|
989
1042
|
// allow runtime to apply stashed ops at this op's sequence number
|
|
990
|
-
await
|
|
1043
|
+
await this.runtime.notifyOpReplay?.(message);
|
|
991
1044
|
}
|
|
992
1045
|
pendingLocalState.savedOps = [];
|
|
993
1046
|
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
994
1047
|
(0, common_utils_1.assert)(this.clientId === undefined, 0x5d6 /* Unexpected clientId when setting stashed clientId */);
|
|
995
|
-
this._clientId = pendingLocalState
|
|
1048
|
+
this._clientId = pendingLocalState?.clientId;
|
|
996
1049
|
}
|
|
997
1050
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
998
1051
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
@@ -1023,6 +1076,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1023
1076
|
(0, common_utils_1.unreachableCase)(loadMode.deltaConnection);
|
|
1024
1077
|
}
|
|
1025
1078
|
}
|
|
1079
|
+
// If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
|
|
1080
|
+
if (loadToSequenceNumber !== undefined &&
|
|
1081
|
+
this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
1082
|
+
await new Promise((resolve, reject) => {
|
|
1083
|
+
const opHandler = (message) => {
|
|
1084
|
+
if (message.sequenceNumber >= loadToSequenceNumber) {
|
|
1085
|
+
resolve();
|
|
1086
|
+
this.off("op", opHandler);
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
this.on("op", opHandler);
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1026
1092
|
// Safety net: static version of Container.load() should have learned about it through "closed" handler.
|
|
1027
1093
|
// But if that did not happen for some reason, fail load for sure.
|
|
1028
1094
|
// Otherwise we can get into situations where container is closed and does not try to connect to ordering
|
|
@@ -1177,8 +1243,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1177
1243
|
return pkg;
|
|
1178
1244
|
}
|
|
1179
1245
|
get client() {
|
|
1180
|
-
|
|
1181
|
-
const client = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.client) !== undefined
|
|
1246
|
+
const client = this.options?.client !== undefined
|
|
1182
1247
|
? this.options.client
|
|
1183
1248
|
: {
|
|
1184
1249
|
details: {
|
|
@@ -1225,11 +1290,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1225
1290
|
deltaManager.on("cancelEstablishingConnection", (reason) => {
|
|
1226
1291
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1227
1292
|
});
|
|
1228
|
-
deltaManager.on("disconnect", (reason
|
|
1229
|
-
|
|
1230
|
-
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
|
|
1293
|
+
deltaManager.on("disconnect", (reason) => {
|
|
1294
|
+
this.noopHeuristic?.notifyDisconnect();
|
|
1231
1295
|
if (!this.closed) {
|
|
1232
|
-
this.connectionStateHandler.receivedDisconnectEvent(reason
|
|
1296
|
+
this.connectionStateHandler.receivedDisconnectEvent(reason);
|
|
1233
1297
|
}
|
|
1234
1298
|
});
|
|
1235
1299
|
deltaManager.on("throttled", (warning) => {
|
|
@@ -1261,8 +1325,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1261
1325
|
},
|
|
1262
1326
|
}, prefetchType);
|
|
1263
1327
|
}
|
|
1264
|
-
logConnectionStateChangeTelemetry(value, oldState, reason
|
|
1265
|
-
var _a;
|
|
1328
|
+
logConnectionStateChangeTelemetry(value, oldState, reason) {
|
|
1266
1329
|
// Log actual event
|
|
1267
1330
|
const time = common_utils_1.performance.now();
|
|
1268
1331
|
this.connectionTransitionTimes[value] = time;
|
|
@@ -1292,19 +1355,31 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1292
1355
|
}
|
|
1293
1356
|
connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
|
|
1294
1357
|
}
|
|
1295
|
-
this.mc.logger.sendPerformanceEvent(
|
|
1358
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1359
|
+
eventName: `ConnectionStateChange_${connectionState_1.ConnectionState[value]}`,
|
|
1360
|
+
from: connectionState_1.ConnectionState[oldState],
|
|
1361
|
+
duration,
|
|
1296
1362
|
durationFromDisconnected,
|
|
1297
|
-
reason,
|
|
1298
|
-
connectionInitiationReason,
|
|
1299
|
-
|
|
1363
|
+
reason: reason?.text,
|
|
1364
|
+
connectionInitiationReason,
|
|
1365
|
+
pendingClientId: this.connectionStateHandler.pendingClientId,
|
|
1366
|
+
clientId: this.clientId,
|
|
1367
|
+
autoReconnect,
|
|
1368
|
+
opsBehind,
|
|
1369
|
+
online: driver_utils_1.OnlineStatus[(0, driver_utils_1.isOnline)()],
|
|
1370
|
+
lastVisible: this.lastVisible !== undefined
|
|
1300
1371
|
? common_utils_1.performance.now() - this.lastVisible
|
|
1301
|
-
: undefined,
|
|
1372
|
+
: undefined,
|
|
1373
|
+
checkpointSequenceNumber,
|
|
1374
|
+
quorumSize: this._protocolHandler?.quorum.getMembers().size,
|
|
1375
|
+
isDirty: this.isDirty,
|
|
1376
|
+
...this._deltaManager.connectionProps,
|
|
1377
|
+
}, reason?.error);
|
|
1302
1378
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
1303
1379
|
this.firstConnection = false;
|
|
1304
1380
|
}
|
|
1305
1381
|
}
|
|
1306
1382
|
propagateConnectionState(initialTransition, disconnectedReason) {
|
|
1307
|
-
var _a;
|
|
1308
1383
|
// When container loaded, we want to propagate initial connection state.
|
|
1309
1384
|
// After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
1310
1385
|
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
@@ -1315,9 +1390,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1315
1390
|
}
|
|
1316
1391
|
const state = this.connectionState === connectionState_1.ConnectionState.Connected;
|
|
1317
1392
|
// Both protocol and context should not be undefined if we got so far.
|
|
1318
|
-
this.setContextConnectedState(state,
|
|
1393
|
+
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
1319
1394
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1320
|
-
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
1395
|
+
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId, disconnectedReason?.text);
|
|
1321
1396
|
}
|
|
1322
1397
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1323
1398
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
@@ -1355,12 +1430,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1355
1430
|
return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch */, undefined /* metadata */, undefined /* compression */, referenceSequenceNumber);
|
|
1356
1431
|
}
|
|
1357
1432
|
submitMessage(type, contents, batch, metadata, compression, referenceSequenceNumber) {
|
|
1358
|
-
var _a;
|
|
1359
1433
|
if (this.connectionState !== connectionState_1.ConnectionState.Connected) {
|
|
1360
1434
|
this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
|
|
1361
1435
|
return -1;
|
|
1362
1436
|
}
|
|
1363
|
-
|
|
1437
|
+
this.noopHeuristic?.notifyMessageSent();
|
|
1364
1438
|
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1365
1439
|
}
|
|
1366
1440
|
processRemoteMessage(message) {
|
|
@@ -1368,23 +1442,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1368
1442
|
this.savedOps.push(message);
|
|
1369
1443
|
}
|
|
1370
1444
|
const local = this.clientId === message.clientId;
|
|
1371
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
1372
|
-
// flagged should have left, or from a client that's not in the quorum but should be
|
|
1373
|
-
if (message.clientId != null) {
|
|
1374
|
-
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
1375
|
-
if (client === undefined && message.type !== protocol_definitions_1.MessageType.ClientJoin) {
|
|
1376
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1377
|
-
throw new Error("Remote message's clientId is missing from the quorum");
|
|
1378
|
-
}
|
|
1379
|
-
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
1380
|
-
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
1381
|
-
// document we don't need to blow up aggressively.
|
|
1382
|
-
if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
1383
|
-
!(0, driver_utils_1.canBeCoalescedByService)(message)) {
|
|
1384
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1385
|
-
throw new Error("Remote message's clientId already should have left");
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
1445
|
// Allow the protocol handler to process the message
|
|
1389
1446
|
const result = this.protocolHandler.processMessage(message, local);
|
|
1390
1447
|
// Forward messages to the loaded runtime for processing
|
|
@@ -1435,8 +1492,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1435
1492
|
* @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
|
|
1436
1493
|
*/
|
|
1437
1494
|
async fetchSnapshotTree(specifiedVersion) {
|
|
1438
|
-
|
|
1439
|
-
const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : null);
|
|
1495
|
+
const version = await this.getVersion(specifiedVersion ?? null);
|
|
1440
1496
|
if (version === undefined && specifiedVersion !== undefined) {
|
|
1441
1497
|
// We should have a defined version to load from if specified version requested
|
|
1442
1498
|
this.mc.logger.sendErrorEvent({
|
|
@@ -1445,15 +1501,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1445
1501
|
});
|
|
1446
1502
|
}
|
|
1447
1503
|
this._loadedFromVersion = version;
|
|
1448
|
-
const snapshot = (
|
|
1504
|
+
const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
|
|
1449
1505
|
if (snapshot === undefined && version !== undefined) {
|
|
1450
1506
|
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
1451
1507
|
}
|
|
1452
|
-
return { snapshot, versionId: version
|
|
1508
|
+
return { snapshot, versionId: version?.id };
|
|
1453
1509
|
}
|
|
1454
1510
|
async instantiateRuntime(codeDetails, snapshot, pendingLocalState) {
|
|
1455
|
-
|
|
1456
|
-
(0, common_utils_1.assert)(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1511
|
+
(0, common_utils_1.assert)(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1457
1512
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1458
1513
|
// are set. Global requests will still go directly to the loader
|
|
1459
1514
|
const maybeLoader = this.scope;
|
|
@@ -1464,22 +1519,17 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1464
1519
|
// An older interface ICodeLoader could return an IFluidModule which didn't have details.
|
|
1465
1520
|
// If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
|
|
1466
1521
|
// TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
|
|
1467
|
-
details:
|
|
1522
|
+
details: loadCodeResult.details ?? codeDetails,
|
|
1468
1523
|
};
|
|
1469
1524
|
const fluidExport = this._loadedModule.module.fluidExport;
|
|
1470
|
-
const runtimeFactory = fluidExport
|
|
1525
|
+
const runtimeFactory = fluidExport?.IRuntimeFactory;
|
|
1471
1526
|
if (runtimeFactory === undefined) {
|
|
1472
1527
|
throw new Error(packageNotFactoryError);
|
|
1473
1528
|
}
|
|
1474
|
-
const getSpecifiedCodeDetails = () =>
|
|
1475
|
-
|
|
1476
|
-
return ((_a = this.protocolHandler.quorum.get("code")) !== null && _a !== void 0 ? _a : this.protocolHandler.quorum.get("code2"));
|
|
1477
|
-
};
|
|
1529
|
+
const getSpecifiedCodeDetails = () => (this.protocolHandler.quorum.get("code") ??
|
|
1530
|
+
this.protocolHandler.quorum.get("code2"));
|
|
1478
1531
|
const existing = snapshot !== undefined;
|
|
1479
|
-
const context = new containerContext_1.ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () =>
|
|
1480
|
-
this._lifecycleEvents.once("disposed", () => {
|
|
1481
|
-
context.dispose();
|
|
1482
|
-
});
|
|
1532
|
+
const context = new containerContext_1.ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1483
1533
|
this._runtime = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1484
1534
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1485
1535
|
this._loadedCodeDetails = codeDetails;
|
|
@@ -1491,8 +1541,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1491
1541
|
* @param readonly - Is the container in readonly mode?
|
|
1492
1542
|
*/
|
|
1493
1543
|
setContextConnectedState(state, readonly) {
|
|
1494
|
-
|
|
1495
|
-
if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1544
|
+
if (this._runtime?.disposed === false) {
|
|
1496
1545
|
/**
|
|
1497
1546
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1498
1547
|
* ops getting through to the DeltaManager.
|