@fluidframework/container-loader 0.59.4001 → 1.1.0-75972
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/.eslintrc.js +1 -1
- package/README.md +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +3 -1
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.d.ts +15 -3
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionState.js +15 -3
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +47 -11
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +108 -38
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +20 -28
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +97 -153
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +6 -4
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +8 -7
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +4 -5
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +4 -7
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +9 -1
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts +0 -1
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaManagerProxy.js +0 -3
- package/dist/deltaManagerProxy.js.map +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +8 -3
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +1 -13
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +2 -3
- package/dist/loader.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/protocolTreeDocumentStorageService.d.ts +2 -3
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +0 -1
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +3 -4
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -7
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -2
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +4 -2
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionState.d.ts +15 -3
- package/lib/connectionState.d.ts.map +1 -1
- package/lib/connectionState.js +15 -3
- package/lib/connectionState.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +47 -11
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +108 -38
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +20 -28
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +98 -154
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +6 -4
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +8 -7
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +4 -5
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +4 -7
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +1 -1
- package/lib/contracts.js +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +9 -1
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts +0 -1
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaManagerProxy.js +0 -3
- package/lib/deltaManagerProxy.js.map +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +8 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +1 -13
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +2 -3
- package/lib/loader.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/protocolTreeDocumentStorageService.d.ts +2 -3
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js +0 -1
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +3 -4
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +4 -7
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +3 -2
- package/lib/utils.js.map +1 -1
- package/package.json +14 -27
- package/src/connectionManager.ts +4 -6
- package/src/connectionState.ts +20 -6
- package/src/connectionStateHandler.ts +136 -56
- package/src/container.ts +139 -185
- package/src/containerContext.ts +10 -10
- package/src/containerStorageAdapter.ts +5 -10
- package/src/contracts.ts +1 -1
- package/src/deltaManager.ts +8 -2
- package/src/deltaManagerProxy.ts +0 -4
- package/src/deltaQueue.ts +7 -3
- package/src/index.ts +1 -0
- package/src/loader.ts +4 -21
- package/src/packageVersion.ts +1 -1
- package/src/protocolTreeDocumentStorageService.ts +0 -1
- package/src/retriableDocumentStorageService.ts +4 -12
- package/src/utils.ts +3 -2
package/dist/container.js
CHANGED
|
@@ -42,7 +42,7 @@ const savedContainerEvent = "saved";
|
|
|
42
42
|
* Useful when resolving URIs and hitting 404, due to container being loaded from (stale) snapshot and not being
|
|
43
43
|
* up to date. Host may chose to wait in such case and retry resolving URI.
|
|
44
44
|
* Warning: Will wait infinitely for connection to establish if there is no connection.
|
|
45
|
-
* May result in deadlock if Container.
|
|
45
|
+
* May result in deadlock if Container.disconnect() is called and never followed by a call to Container.connect().
|
|
46
46
|
* @returns true: container is up to date, it processed all the ops that were know at the time of first connection
|
|
47
47
|
* false: storage does not provide indication of how far the client is. Container processed
|
|
48
48
|
* all the ops known to it, but it maybe still behind.
|
|
@@ -64,7 +64,8 @@ async function waitContainerToCatchUp(container) {
|
|
|
64
64
|
};
|
|
65
65
|
container.on("closed", closedCallback);
|
|
66
66
|
const waitForOps = () => {
|
|
67
|
-
(0, common_utils_1.assert)(container.connectionState
|
|
67
|
+
(0, common_utils_1.assert)(container.connectionState === connectionState_1.ConnectionState.CatchingUp
|
|
68
|
+
|| container.connectionState === connectionState_1.ConnectionState.Connected, 0x0cd /* "Container disconnected while waiting for ops!" */);
|
|
68
69
|
const hasCheckpointSequenceNumber = deltaManager.hasCheckpointSequenceNumber;
|
|
69
70
|
const connectionOpSeqNumber = deltaManager.lastKnownSeqNumber;
|
|
70
71
|
(0, common_utils_1.assert)(deltaManager.lastSequenceNumber <= connectionOpSeqNumber, 0x266 /* "lastKnownSeqNumber should never be below last processed sequence number" */);
|
|
@@ -95,9 +96,7 @@ async function waitContainerToCatchUp(container) {
|
|
|
95
96
|
waitForOps();
|
|
96
97
|
};
|
|
97
98
|
container.on(telemetry_utils_1.connectedEventName, callback);
|
|
98
|
-
|
|
99
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
100
|
-
container.resume();
|
|
99
|
+
container.connect();
|
|
101
100
|
});
|
|
102
101
|
}
|
|
103
102
|
exports.waitContainerToCatchUp = waitContainerToCatchUp;
|
|
@@ -107,7 +106,7 @@ const getCodeProposal =
|
|
|
107
106
|
const summarizerClientType = "summarizer";
|
|
108
107
|
class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
109
108
|
constructor(loader, config) {
|
|
110
|
-
var _a;
|
|
109
|
+
var _a, _b;
|
|
111
110
|
super((name, error) => {
|
|
112
111
|
this.mc.logger.sendErrorEvent({
|
|
113
112
|
eventName: "ContainerEventHandlerException",
|
|
@@ -118,7 +117,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
118
117
|
// Tells if container can reconnect on losing fist connection
|
|
119
118
|
// If false, container gets closed on loss of connection.
|
|
120
119
|
this._canReconnect = true;
|
|
121
|
-
this._lifecycleState = "
|
|
120
|
+
this._lifecycleState = "loading";
|
|
122
121
|
this._attachState = container_definitions_1.AttachState.Detached;
|
|
123
122
|
this.resumedOpProcessingAfterLoad = false;
|
|
124
123
|
this.firstConnection = true;
|
|
@@ -174,20 +173,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
174
173
|
logConnectionStateChangeTelemetry: (value, oldState, reason) => this.logConnectionStateChangeTelemetry(value, oldState, reason),
|
|
175
174
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
176
175
|
maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
|
|
177
|
-
logConnectionIssue: (eventName) => {
|
|
176
|
+
logConnectionIssue: (eventName, details) => {
|
|
178
177
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
179
178
|
// its own join op. Attempt recovery option.
|
|
180
|
-
this._deltaManager.logConnectionIssue({
|
|
181
|
-
eventName,
|
|
182
|
-
duration: common_utils_1.performance.now() - this.connectionTransitionTimes[connectionState_1.ConnectionState.Connecting],
|
|
183
|
-
});
|
|
179
|
+
this._deltaManager.logConnectionIssue(Object.assign({ eventName, duration: common_utils_1.performance.now() - this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
|
|
184
180
|
},
|
|
185
181
|
connectionStateChanged: () => {
|
|
186
|
-
if
|
|
182
|
+
// Fire events only if container is fully loaded and not closed
|
|
183
|
+
if (this._lifecycleState === "loaded") {
|
|
187
184
|
this.propagateConnectionState();
|
|
188
185
|
}
|
|
189
186
|
},
|
|
190
|
-
}, this.mc.logger);
|
|
187
|
+
}, this.mc.logger, (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId);
|
|
191
188
|
this.on(savedContainerEvent, () => {
|
|
192
189
|
this.connectionStateHandler.containerSaved();
|
|
193
190
|
});
|
|
@@ -265,16 +262,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
265
262
|
clientDetailsOverride: loadOptions.clientDetailsOverride,
|
|
266
263
|
resolvedUrl: loadOptions.resolvedUrl,
|
|
267
264
|
canReconnect: loadOptions.canReconnect,
|
|
265
|
+
serializedContainerState: pendingLocalState,
|
|
268
266
|
});
|
|
269
267
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
270
|
-
var _a;
|
|
271
|
-
container._lifecycleState = "loading";
|
|
268
|
+
var _a, _b;
|
|
272
269
|
const version = loadOptions.version;
|
|
273
|
-
// always load unpaused with pending ops!
|
|
274
|
-
// It is also default mode in general.
|
|
275
270
|
const defaultMode = { opsBeforeReturn: "cached" };
|
|
276
|
-
|
|
277
|
-
|
|
271
|
+
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
272
|
+
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
273
|
+
const mode = pendingLocalState
|
|
274
|
+
? Object.assign(Object.assign({}, ((_a = loadOptions.loadMode) !== null && _a !== void 0 ? _a : defaultMode)), { opsBeforeReturn: undefined }) : (_b = loadOptions.loadMode) !== null && _b !== void 0 ? _b : defaultMode;
|
|
278
275
|
const onClosed = (err) => {
|
|
279
276
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
280
277
|
reject(err !== null && err !== void 0 ? err : new container_utils_1.GenericError("Container closed without error during load"));
|
|
@@ -303,7 +300,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
303
300
|
static async createDetached(loader, codeDetails) {
|
|
304
301
|
const container = new Container(loader, {});
|
|
305
302
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
306
|
-
container._lifecycleState = "loading";
|
|
307
303
|
await container.createDetached(codeDetails);
|
|
308
304
|
return container;
|
|
309
305
|
}, { start: true, end: true, cancel: "generic" });
|
|
@@ -316,20 +312,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
316
312
|
const container = new Container(loader, {});
|
|
317
313
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
318
314
|
const deserializedSummary = JSON.parse(snapshot);
|
|
319
|
-
container._lifecycleState = "loading";
|
|
320
315
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
321
316
|
return container;
|
|
322
317
|
}, { start: true, end: true, cancel: "generic" });
|
|
323
318
|
}
|
|
324
|
-
|
|
325
|
-
return (this._lifecycleState !== "created" && this._lifecycleState !== "loading");
|
|
326
|
-
}
|
|
327
|
-
set loaded(t) {
|
|
328
|
-
(0, common_utils_1.assert)(t, 0x27d /* "Setting loaded state to false is not supported" */);
|
|
329
|
-
(0, common_utils_1.assert)(this._lifecycleState !== "created", 0x27e /* "Must go through loading state before loaded" */);
|
|
319
|
+
setLoaded() {
|
|
330
320
|
// It's conceivable the container could be closed when this is called
|
|
331
321
|
// Only transition states if currently loading
|
|
332
322
|
if (this._lifecycleState === "loading") {
|
|
323
|
+
// Propagate current connection state through the system.
|
|
324
|
+
this.propagateConnectionState();
|
|
333
325
|
this._lifecycleState = "loaded";
|
|
334
326
|
}
|
|
335
327
|
}
|
|
@@ -451,19 +443,30 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
451
443
|
return this.protocolHandler.quorum;
|
|
452
444
|
}
|
|
453
445
|
close(error) {
|
|
446
|
+
// 1. Ensure that close sequence is exactly the same no matter if it's initiated by host or by DeltaManager
|
|
447
|
+
// 2. We need to ensure that we deliver disconnect event to runtime properly. See connectionStateChanged
|
|
448
|
+
// handler. We only deliver events if container fully loaded. Transitioning from "loading" ->
|
|
449
|
+
// "closing" will lose that info (can also solve by tracking extra state).
|
|
450
|
+
this._deltaManager.close(error);
|
|
451
|
+
(0, common_utils_1.assert)(this.connectionState === connectionState_1.ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
452
|
+
(0, common_utils_1.assert)(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
|
|
453
|
+
}
|
|
454
|
+
closeCore(error) {
|
|
454
455
|
var _a, _b, _c, _d;
|
|
455
|
-
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
456
|
+
(0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
458
457
|
try {
|
|
459
|
-
this._lifecycleState = "closing";
|
|
460
458
|
// Ensure that we raise all key events even if one of these throws
|
|
461
459
|
try {
|
|
462
|
-
|
|
460
|
+
// Raise event first, to ensure we capture _lifecycleState before transition.
|
|
461
|
+
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
462
|
+
this.mc.logger.sendTelemetryEvent({
|
|
463
|
+
eventName: "ContainerClose",
|
|
464
|
+
category: error === undefined ? "generic" : "error",
|
|
465
|
+
}, error);
|
|
466
|
+
this._lifecycleState = "closing";
|
|
463
467
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
464
468
|
this.connectionStateHandler.dispose();
|
|
465
469
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
466
|
-
(0, common_utils_1.assert)(this.connectionState === connectionState_1.ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
467
470
|
(_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
|
|
468
471
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
469
472
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
@@ -473,10 +476,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
473
476
|
catch (exception) {
|
|
474
477
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
475
478
|
}
|
|
476
|
-
this.mc.logger.sendTelemetryEvent({
|
|
477
|
-
eventName: "ContainerClose",
|
|
478
|
-
category: error === undefined ? "generic" : "error",
|
|
479
|
-
}, error);
|
|
480
479
|
this.emit("closed", error);
|
|
481
480
|
this.removeAllListeners();
|
|
482
481
|
if (this.visibilityEventHandler !== undefined) {
|
|
@@ -493,9 +492,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
493
492
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
494
493
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
495
494
|
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
495
|
+
(0, common_utils_1.assert)(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
|
|
496
|
+
(0, common_utils_1.assert)(this._protocolHandler.attributes.term !== undefined, 0x30b /* Must have a valid protocol handler instance */);
|
|
496
497
|
const pendingState = {
|
|
497
498
|
pendingRuntimeState: this.context.getPendingLocalState(),
|
|
498
499
|
url: this.resolvedUrl.url,
|
|
500
|
+
protocol: this.protocolHandler.getProtocolState(),
|
|
501
|
+
term: this._protocolHandler.attributes.term,
|
|
502
|
+
clientId: this.clientId,
|
|
499
503
|
};
|
|
500
504
|
this.close();
|
|
501
505
|
return JSON.stringify(pendingState);
|
|
@@ -539,7 +543,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
539
543
|
// starting to attach the container to storage.
|
|
540
544
|
// Also, this should only be fired in detached container.
|
|
541
545
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
542
|
-
this.context.notifyAttaching();
|
|
546
|
+
this.context.notifyAttaching((0, utils_1.getSnapshotTreeFromSerializedContainer)(summary));
|
|
543
547
|
}
|
|
544
548
|
// Actually go and create the resolved document
|
|
545
549
|
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
@@ -574,7 +578,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
574
578
|
const protocolSummary = this.captureProtocolSummary();
|
|
575
579
|
summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
576
580
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
577
|
-
this.context.notifyAttaching();
|
|
581
|
+
this.context.notifyAttaching((0, utils_1.getSnapshotTreeFromSerializedContainer)(summary));
|
|
578
582
|
await this.storageService.uploadSummaryWithContext(summary, {
|
|
579
583
|
referenceSequenceNumber: 0,
|
|
580
584
|
ackHandle: undefined,
|
|
@@ -604,27 +608,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
604
608
|
async request(path) {
|
|
605
609
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
|
|
606
610
|
}
|
|
607
|
-
/**
|
|
608
|
-
* Dictates whether or not the current container will automatically attempt to reconnect to the delta stream
|
|
609
|
-
* after receiving a disconnect event
|
|
610
|
-
* @param reconnect - Boolean indicating if reconnect should automatically occur
|
|
611
|
-
* @deprecated - 0.58, This API will be removed in 1.0
|
|
612
|
-
* Use `connect()` and `disconnect()` instead of `setAutoReconnect(true)` and `setAutoReconnect(false)` respectively
|
|
613
|
-
* See https://github.com/microsoft/FluidFramework/issues/9167 for context
|
|
614
|
-
*/
|
|
615
|
-
setAutoReconnect(reconnect) {
|
|
616
|
-
if (this.closed) {
|
|
617
|
-
throw new Error("Attempting to setAutoReconnect() a closed Container");
|
|
618
|
-
}
|
|
619
|
-
const mode = reconnect ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Disabled;
|
|
620
|
-
this.setAutoReconnectInternal(mode);
|
|
621
|
-
// If container state is not attached and resumed, then don't connect to delta stream. Also don't set the
|
|
622
|
-
// manual reconnection flag to true as we haven't made the initial connection yet.
|
|
623
|
-
if (reconnect && this._attachState === container_definitions_1.AttachState.Attached && this.resumedOpProcessingAfterLoad) {
|
|
624
|
-
// Ensure connection to web socket
|
|
625
|
-
this.connectToDeltaStream({ reason: "autoReconnect" });
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
611
|
setAutoReconnectInternal(mode) {
|
|
629
612
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
630
613
|
if (currentMode === mode) {
|
|
@@ -678,22 +661,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
678
661
|
const mode = contracts_1.ReconnectMode.Disabled;
|
|
679
662
|
this.setAutoReconnectInternal(mode);
|
|
680
663
|
}
|
|
681
|
-
/**
|
|
682
|
-
* Have the container attempt to resume processing ops
|
|
683
|
-
* @deprecated - 0.58, This API will be removed in 1.0
|
|
684
|
-
* Use `connect()` instead
|
|
685
|
-
* See https://github.com/microsoft/FluidFramework/issues/9167 for context
|
|
686
|
-
*/
|
|
687
|
-
resume() {
|
|
688
|
-
if (!this.closed) {
|
|
689
|
-
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
690
|
-
// If there is gap, we will learn about it once connected, but the gap should be small (if any),
|
|
691
|
-
// assuming that resume() is called quickly after initial container boot.
|
|
692
|
-
this.resumeInternal({ reason: "DocumentOpenResume", fetchOpsFromStorage: false });
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
664
|
resumeInternal(args) {
|
|
696
|
-
(0, common_utils_1.assert)(!this.closed, 0x0d9 /* "Attempting to
|
|
665
|
+
(0, common_utils_1.assert)(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
|
|
697
666
|
// Resume processing ops
|
|
698
667
|
if (!this.resumedOpProcessingAfterLoad) {
|
|
699
668
|
this.resumedOpProcessingAfterLoad = true;
|
|
@@ -782,12 +751,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
782
751
|
if (loadMode.deltaConnection === undefined) {
|
|
783
752
|
this.connectToDeltaStream(connectionArgs);
|
|
784
753
|
}
|
|
785
|
-
|
|
754
|
+
if (!pendingLocalState) {
|
|
755
|
+
await this.connectStorageService();
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
759
|
+
this.connectStorageService().catch((error) => this.close(error));
|
|
760
|
+
}
|
|
786
761
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
787
762
|
// Fetch specified snapshot.
|
|
788
|
-
const { snapshot, versionId } =
|
|
789
|
-
|
|
790
|
-
|
|
763
|
+
const { snapshot, versionId } = pendingLocalState === undefined
|
|
764
|
+
? await this.fetchSnapshotTree(specifiedVersion)
|
|
765
|
+
: { snapshot: undefined, versionId: undefined };
|
|
766
|
+
(0, common_utils_1.assert)(snapshot !== undefined || pendingLocalState !== undefined, 0x237 /* "Snapshot should exist" */);
|
|
767
|
+
const attributes = pendingLocalState === undefined
|
|
768
|
+
? await this.getDocumentAttributes(this.storageService, snapshot)
|
|
769
|
+
: {
|
|
770
|
+
sequenceNumber: pendingLocalState.protocol.sequenceNumber,
|
|
771
|
+
minimumSequenceNumber: pendingLocalState.protocol.minimumSequenceNumber,
|
|
772
|
+
term: pendingLocalState.term,
|
|
773
|
+
};
|
|
791
774
|
let opsBeforeReturnP;
|
|
792
775
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
793
776
|
// Kick off any ops fetching if required.
|
|
@@ -808,15 +791,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
808
791
|
}
|
|
809
792
|
// ...load in the existing quorum
|
|
810
793
|
// Initialize the protocol handler
|
|
811
|
-
this._protocolHandler =
|
|
812
|
-
await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot)
|
|
794
|
+
this._protocolHandler = pendingLocalState === undefined
|
|
795
|
+
? await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot)
|
|
796
|
+
: await this.initializeProtocolState(attributes, pendingLocalState.protocol.members, pendingLocalState.protocol.proposals, pendingLocalState.protocol.values);
|
|
813
797
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
814
798
|
await this.instantiateContext(true, // existing
|
|
815
|
-
codeDetails, snapshot, pendingLocalState);
|
|
816
|
-
// Propagate current connection state through the system.
|
|
817
|
-
this.propagateConnectionState();
|
|
799
|
+
codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
|
|
818
800
|
// Internal context is fully loaded at this point
|
|
819
|
-
this.
|
|
801
|
+
this.setLoaded();
|
|
820
802
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
821
803
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
822
804
|
if (!this.closed) {
|
|
@@ -829,7 +811,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
829
811
|
}
|
|
830
812
|
switch (loadMode.deltaConnection) {
|
|
831
813
|
case undefined:
|
|
832
|
-
|
|
814
|
+
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
815
|
+
// If there is gap, we will learn about it once connected, but the gap should be small (if any),
|
|
816
|
+
// assuming that resumeInternal() is called quickly after initial container boot.
|
|
817
|
+
this.resumeInternal({ reason: "DocumentLoad", fetchOpsFromStorage: false });
|
|
833
818
|
break;
|
|
834
819
|
case "delayed":
|
|
835
820
|
this.resumedOpProcessingAfterLoad = true;
|
|
@@ -869,8 +854,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
869
854
|
qValues);
|
|
870
855
|
// The load context - given we seeded the quorum - will be great
|
|
871
856
|
await this.instantiateContextDetached(false);
|
|
872
|
-
this.
|
|
873
|
-
this.loaded = true;
|
|
857
|
+
this.setLoaded();
|
|
874
858
|
}
|
|
875
859
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
876
860
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
@@ -891,8 +875,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
891
875
|
codeDetails !== undefined ? (0, quorum_1.initQuorumValuesFromCodeDetails)(codeDetails) : []);
|
|
892
876
|
await this.instantiateContextDetached(true, // existing
|
|
893
877
|
snapshotTree);
|
|
894
|
-
this.
|
|
895
|
-
this.propagateConnectionState();
|
|
878
|
+
this.setLoaded();
|
|
896
879
|
}
|
|
897
880
|
async connectStorageService() {
|
|
898
881
|
var _a, _b;
|
|
@@ -946,18 +929,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
946
929
|
return protocolHandler;
|
|
947
930
|
}
|
|
948
931
|
async initializeProtocolState(attributes, members, proposals, values) {
|
|
949
|
-
const protocol = new protocol_base_1.
|
|
932
|
+
const protocol = new protocol_base_1.ProtocolOpHandlerWithClientValidation(attributes.minimumSequenceNumber, attributes.sequenceNumber, attributes.term, members, proposals, values, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, { key, value }));
|
|
950
933
|
const protocolLogger = telemetry_utils_1.ChildLogger.create(this.subLogger, "ProtocolHandler");
|
|
951
934
|
protocol.quorum.on("error", (error) => {
|
|
952
935
|
protocolLogger.sendErrorEvent(error);
|
|
953
936
|
});
|
|
954
937
|
// Track membership changes and update connection state accordingly
|
|
955
|
-
|
|
956
|
-
this.connectionStateHandler.receivedAddMemberEvent(clientId);
|
|
957
|
-
});
|
|
958
|
-
protocol.quorum.on("removeMember", (clientId) => {
|
|
959
|
-
this.connectionStateHandler.receivedRemoveMemberEvent(clientId);
|
|
960
|
-
});
|
|
938
|
+
this.connectionStateHandler.initProtocol(protocol);
|
|
961
939
|
protocol.quorum.on("addProposal", (proposal) => {
|
|
962
940
|
if (proposal.key === "code" || proposal.key === "code2") {
|
|
963
941
|
this.emit("codeDetailsProposed", proposal.value, proposal);
|
|
@@ -979,17 +957,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
979
957
|
return protocol;
|
|
980
958
|
}
|
|
981
959
|
captureProtocolSummary() {
|
|
982
|
-
const quorumSnapshot = this.protocolHandler.
|
|
983
|
-
// Save attributes for the document
|
|
984
|
-
const documentAttributes = {
|
|
985
|
-
minimumSequenceNumber: this.protocolHandler.minimumSequenceNumber,
|
|
986
|
-
sequenceNumber: this.protocolHandler.sequenceNumber,
|
|
987
|
-
term: this.protocolHandler.term,
|
|
988
|
-
};
|
|
960
|
+
const quorumSnapshot = this.protocolHandler.snapshot();
|
|
989
961
|
const summary = {
|
|
990
962
|
tree: {
|
|
991
963
|
attributes: {
|
|
992
|
-
content: JSON.stringify(
|
|
964
|
+
content: JSON.stringify(this.protocolHandler.attributes),
|
|
993
965
|
type: protocol_definitions_1.SummaryType.Blob,
|
|
994
966
|
},
|
|
995
967
|
quorumMembers: {
|
|
@@ -1078,7 +1050,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1078
1050
|
this.emit("readonly", readonly);
|
|
1079
1051
|
});
|
|
1080
1052
|
deltaManager.on("closed", (error) => {
|
|
1081
|
-
this.
|
|
1053
|
+
this.closeCore(error);
|
|
1082
1054
|
});
|
|
1083
1055
|
return deltaManager;
|
|
1084
1056
|
}
|
|
@@ -1092,6 +1064,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1092
1064
|
}, prefetchType);
|
|
1093
1065
|
}
|
|
1094
1066
|
logConnectionStateChangeTelemetry(value, oldState, reason) {
|
|
1067
|
+
var _a;
|
|
1095
1068
|
// Log actual event
|
|
1096
1069
|
const time = common_utils_1.performance.now();
|
|
1097
1070
|
this.connectionTransitionTimes[value] = time;
|
|
@@ -1127,12 +1100,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1127
1100
|
durationFromDisconnected,
|
|
1128
1101
|
reason,
|
|
1129
1102
|
connectionInitiationReason, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
|
|
1130
|
-
opsBehind, online: driver_utils_1.OnlineStatus[(0, driver_utils_1.isOnline)()], lastVisible: this.lastVisible !== undefined ? common_utils_1.performance.now() - this.lastVisible : undefined, checkpointSequenceNumber }, this._deltaManager.connectionProps));
|
|
1103
|
+
opsBehind, online: driver_utils_1.OnlineStatus[(0, driver_utils_1.isOnline)()], lastVisible: this.lastVisible !== undefined ? common_utils_1.performance.now() - this.lastVisible : undefined, checkpointSequenceNumber, quorumSize: (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.quorum.getMembers().size }, this._deltaManager.connectionProps));
|
|
1131
1104
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
1132
1105
|
this.firstConnection = false;
|
|
1133
1106
|
}
|
|
1134
1107
|
}
|
|
1135
1108
|
propagateConnectionState() {
|
|
1109
|
+
var _a;
|
|
1136
1110
|
const logOpsOnReconnect = this.connectionState === connectionState_1.ConnectionState.Connected &&
|
|
1137
1111
|
!this.firstConnection &&
|
|
1138
1112
|
this.connectionMode === "write";
|
|
@@ -1140,11 +1114,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1140
1114
|
this.messageCountAfterDisconnection = 0;
|
|
1141
1115
|
}
|
|
1142
1116
|
const state = this.connectionState === connectionState_1.ConnectionState.Connected;
|
|
1143
|
-
if
|
|
1117
|
+
// Both protocol and context should not be undefined if we got so far.
|
|
1118
|
+
if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1144
1119
|
this.context.setConnectionState(state, this.clientId);
|
|
1145
1120
|
}
|
|
1146
|
-
(
|
|
1147
|
-
this.protocolHandler.quorum.setConnectionState(state, this.clientId);
|
|
1121
|
+
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1148
1122
|
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId);
|
|
1149
1123
|
if (logOpsOnReconnect) {
|
|
1150
1124
|
this.mc.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
|
|
@@ -1186,31 +1160,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1186
1160
|
return this._deltaManager.submit(type, contents, batch, metadata);
|
|
1187
1161
|
}
|
|
1188
1162
|
processRemoteMessage(message) {
|
|
1189
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
1190
|
-
// flagged as shouldHaveLeft, or from a client that's not in the quorum but should be
|
|
1191
|
-
if (message.clientId != null) {
|
|
1192
|
-
let errorMsg;
|
|
1193
|
-
const client = this.getQuorum().getMember(message.clientId);
|
|
1194
|
-
if (client === undefined && message.type !== protocol_definitions_1.MessageType.ClientJoin) {
|
|
1195
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1196
|
-
errorMsg = "Remote message's clientId is missing from the quorum";
|
|
1197
|
-
}
|
|
1198
|
-
else if ((client === null || client === void 0 ? void 0 : client.shouldHaveLeft) === true && message.type !== protocol_definitions_1.MessageType.NoOp) {
|
|
1199
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1200
|
-
errorMsg = "Remote message's clientId already should have left";
|
|
1201
|
-
}
|
|
1202
|
-
if (errorMsg !== undefined) {
|
|
1203
|
-
const error = new container_utils_1.DataCorruptionError(errorMsg, (0, container_utils_1.extractSafePropertiesFromMessage)(message));
|
|
1204
|
-
this.close((0, telemetry_utils_1.normalizeError)(error));
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
1163
|
const local = this.clientId === message.clientId;
|
|
1164
|
+
// Allow the protocol handler to process the message
|
|
1165
|
+
let result = { immediateNoOp: false };
|
|
1166
|
+
try {
|
|
1167
|
+
result = this.protocolHandler.processMessage(message, local);
|
|
1168
|
+
}
|
|
1169
|
+
catch (error) {
|
|
1170
|
+
this.close((0, telemetry_utils_1.wrapError)(error, (errorMessage) => new container_utils_1.DataCorruptionError(errorMessage, (0, container_utils_1.extractSafePropertiesFromMessage)(message))));
|
|
1171
|
+
}
|
|
1208
1172
|
// Forward non system messages to the loaded runtime for processing
|
|
1209
1173
|
if (!(0, protocol_base_1.isSystemMessage)(message)) {
|
|
1210
1174
|
this.context.process(message, local, undefined);
|
|
1211
1175
|
}
|
|
1212
|
-
// Allow the protocol handler to process the message
|
|
1213
|
-
const result = this.protocolHandler.processMessage(message, local);
|
|
1214
1176
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1215
1177
|
if (this.activeConnection()) {
|
|
1216
1178
|
if (this.collabWindowTracker === undefined) {
|
|
@@ -1218,35 +1180,17 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1218
1180
|
// That means that if relay service changes settings, such changes will impact only newly booted
|
|
1219
1181
|
// clients.
|
|
1220
1182
|
// All existing will continue to use settings they got earlier.
|
|
1221
|
-
|
|
1183
|
+
(0, common_utils_1.assert)(this.serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1222
1184
|
this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type, contents) => {
|
|
1223
1185
|
(0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
|
|
1224
1186
|
this.submitMessage(type, contents);
|
|
1225
|
-
}, noopTimeFrequency, noopCountFrequency);
|
|
1187
|
+
}, this.serviceConfiguration.noopTimeFrequency, this.serviceConfiguration.noopCountFrequency);
|
|
1226
1188
|
}
|
|
1227
1189
|
this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
|
|
1228
1190
|
}
|
|
1229
1191
|
this.emit("op", message);
|
|
1230
1192
|
return result;
|
|
1231
1193
|
}
|
|
1232
|
-
/**
|
|
1233
|
-
* #260 (ADO)
|
|
1234
|
-
* back-compat: noopTimeFrequency & noopCountFrequency properties were added to
|
|
1235
|
-
* IClientConfiguration in 0.59.3000. During the integration, we must read the
|
|
1236
|
-
* available configuration from the loader options.
|
|
1237
|
-
*/
|
|
1238
|
-
getNoopConfig() {
|
|
1239
|
-
var _a, _b;
|
|
1240
|
-
(0, common_utils_1.assert)(this.serviceConfiguration !== undefined, 0x2e2);
|
|
1241
|
-
if (this.serviceConfiguration.noopTimeFrequency !== undefined ||
|
|
1242
|
-
this.serviceConfiguration.noopCountFrequency !== undefined) {
|
|
1243
|
-
return [
|
|
1244
|
-
this.serviceConfiguration.noopTimeFrequency,
|
|
1245
|
-
this.serviceConfiguration.noopCountFrequency,
|
|
1246
|
-
];
|
|
1247
|
-
}
|
|
1248
|
-
return [(_a = this.loader.services.options) === null || _a === void 0 ? void 0 : _a.noopTimeFrequency, (_b = this.loader.services.options) === null || _b === void 0 ? void 0 : _b.noopCountFrequency];
|
|
1249
|
-
}
|
|
1250
1194
|
submitSignal(message) {
|
|
1251
1195
|
this._deltaManager.submitSignal(JSON.stringify(message));
|
|
1252
1196
|
}
|
|
@@ -1287,12 +1231,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1287
1231
|
}
|
|
1288
1232
|
return { snapshot, versionId: version === null || version === void 0 ? void 0 : version.id };
|
|
1289
1233
|
}
|
|
1290
|
-
async instantiateContextDetached(existing, snapshot
|
|
1234
|
+
async instantiateContextDetached(existing, snapshot) {
|
|
1291
1235
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1292
1236
|
if (codeDetails === undefined) {
|
|
1293
1237
|
throw new Error("pkg should be provided in create flow!!");
|
|
1294
1238
|
}
|
|
1295
|
-
await this.instantiateContext(existing, codeDetails, snapshot
|
|
1239
|
+
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
1296
1240
|
}
|
|
1297
1241
|
async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
|
|
1298
1242
|
var _a;
|