@fluidframework/container-loader 2.0.0-rc.3.0.2 → 2.0.0-rc.4.0.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 +17 -0
- package/api-report/container-loader.api.md +5 -1
- package/dist/attachment.d.ts +3 -2
- package/dist/attachment.d.ts.map +1 -1
- package/dist/attachment.js +5 -5
- package/dist/attachment.js.map +1 -1
- package/dist/audience.d.ts +6 -4
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +18 -3
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +7 -3
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +57 -38
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +31 -10
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +49 -36
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +22 -13
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +145 -117
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +3 -3
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +12 -3
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +42 -4
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +2 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.d.ts +1 -2
- package/dist/debugLogger.d.ts.map +1 -1
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +5 -6
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +29 -24
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js.map +1 -1
- package/dist/error.d.ts +1 -2
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +2 -2
- package/dist/loadPaused.d.ts +35 -0
- package/dist/loadPaused.d.ts.map +1 -0
- package/dist/loadPaused.js +115 -0
- package/dist/loadPaused.js.map +1 -0
- package/dist/loader.d.ts +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +1 -14
- package/dist/loader.js.map +1 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +4 -4
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.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.map +1 -1
- package/dist/protocol.js +3 -0
- package/dist/protocol.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/retriableDocumentStorageService.d.ts +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/serializedStateManager.d.ts +89 -9
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +150 -34
- package/dist/serializedStateManager.js.map +1 -1
- package/dist/utils.d.ts +11 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +29 -14
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.ts +3 -2
- package/lib/attachment.d.ts.map +1 -1
- package/lib/attachment.js +5 -5
- package/lib/attachment.js.map +1 -1
- package/lib/audience.d.ts +6 -4
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +19 -4
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +7 -3
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +36 -17
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +31 -10
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +49 -36
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +22 -13
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +146 -118
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +3 -3
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +12 -3
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +42 -4
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +2 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.d.ts +1 -2
- package/lib/debugLogger.d.ts.map +1 -1
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +5 -6
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +10 -5
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js.map +1 -1
- package/lib/error.d.ts +1 -2
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +2 -2
- package/lib/loadPaused.d.ts +35 -0
- package/lib/loadPaused.d.ts.map +1 -0
- package/lib/loadPaused.js +111 -0
- package/lib/loadPaused.js.map +1 -0
- package/lib/loader.d.ts +1 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +3 -16
- package/lib/loader.js.map +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.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.map +1 -1
- package/lib/protocol.js +3 -0
- package/lib/protocol.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/retriableDocumentStorageService.d.ts +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/serializedStateManager.d.ts +89 -9
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +146 -30
- package/lib/serializedStateManager.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/utils.d.ts +11 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +15 -1
- package/lib/utils.js.map +1 -1
- package/package.json +24 -21
- package/src/attachment.ts +12 -13
- package/src/audience.ts +30 -9
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +45 -22
- package/src/connectionStateHandler.ts +78 -45
- package/src/container.ts +181 -160
- package/src/containerContext.ts +2 -2
- package/src/containerStorageAdapter.ts +61 -6
- package/src/contracts.ts +5 -4
- package/src/debugLogger.ts +1 -1
- package/src/deltaManager.ts +15 -8
- package/src/deltaQueue.ts +1 -1
- package/src/error.ts +1 -1
- package/src/index.ts +1 -0
- package/src/loadPaused.ts +140 -0
- package/src/loader.ts +6 -23
- package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +4 -0
- package/src/retriableDocumentStorageService.ts +5 -2
- package/src/serializedStateManager.ts +215 -48
- package/src/utils.ts +19 -1
package/dist/container.js
CHANGED
|
@@ -134,10 +134,9 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
134
134
|
* Load an existing container.
|
|
135
135
|
*/
|
|
136
136
|
static async load(loadProps, createProps) {
|
|
137
|
-
const { version, pendingLocalState, loadMode, resolvedUrl
|
|
137
|
+
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
138
138
|
const container = new Container(createProps, loadProps);
|
|
139
|
-
|
|
140
|
-
return internal_4.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
139
|
+
return internal_4.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load", ...loadMode }, async (event) => new Promise((resolve, reject) => {
|
|
141
140
|
const defaultMode = { opsBeforeReturn: "cached" };
|
|
142
141
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
143
142
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
@@ -150,12 +149,12 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
150
149
|
};
|
|
151
150
|
container.on("closed", onClosed);
|
|
152
151
|
container
|
|
153
|
-
.load(version, mode, resolvedUrl, pendingLocalState
|
|
152
|
+
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
154
153
|
.finally(() => {
|
|
155
154
|
container.removeListener("closed", onClosed);
|
|
156
155
|
})
|
|
157
156
|
.then((props) => {
|
|
158
|
-
event.end({ ...props
|
|
157
|
+
event.end({ ...props });
|
|
159
158
|
resolve(container);
|
|
160
159
|
}, (error) => {
|
|
161
160
|
const err = (0, internal_4.normalizeError)(error);
|
|
@@ -169,7 +168,7 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
169
168
|
container.dispose(err);
|
|
170
169
|
onClosed(err);
|
|
171
170
|
});
|
|
172
|
-
}), { start: true, end: true, cancel: "generic" }
|
|
171
|
+
}), { start: true, end: true, cancel: "generic" });
|
|
173
172
|
}
|
|
174
173
|
/**
|
|
175
174
|
* Create a new container in a detached state.
|
|
@@ -184,6 +183,8 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
184
183
|
/**
|
|
185
184
|
* Create a new container in a detached state that is initialized with a
|
|
186
185
|
* snapshot from a previous detached container.
|
|
186
|
+
* @param createProps - Config options for this new container instance
|
|
187
|
+
* @param snapshot - A stringified {@link IPendingDetachedContainerState}, e.g. generated via {@link serialize}
|
|
187
188
|
*/
|
|
188
189
|
static async rehydrateDetachedFromSnapshot(createProps, snapshot) {
|
|
189
190
|
const container = new Container(createProps);
|
|
@@ -197,14 +198,33 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
197
198
|
// It's conceivable the container could be closed when this is called
|
|
198
199
|
// Only transition states if currently loading
|
|
199
200
|
if (this._lifecycleState === "loading") {
|
|
200
|
-
// Propagate current connection state through the system.
|
|
201
|
-
this.propagateConnectionState(true /* initial transition */);
|
|
202
201
|
this._lifecycleState = "loaded";
|
|
202
|
+
// Connections transitions are delayed till we are loaded.
|
|
203
|
+
// This is done by holding ops and signals until the end of load sequence
|
|
204
|
+
// (calling this.handleDeltaConnectionArg() after setLoaded() call)
|
|
205
|
+
// If this assert fires, it means our logic managing connection flow is wrong, and the logic below is also wrong.
|
|
206
|
+
(0, internal_2.assert)(this.connectionState !== connectionState_js_1.ConnectionState.Connected, 0x969 /* not connected yet */);
|
|
207
|
+
// Propagate current connection state through the system.
|
|
208
|
+
const readonly = this.readOnlyInfo.readonly ?? false;
|
|
209
|
+
// This call does not look like needed any more, with delaying all connection-related events past loaded phase.
|
|
210
|
+
// Yet, there could be some customer code that would break if we do not deliver it.
|
|
211
|
+
// Will be removed in further PRs with proper changeset.
|
|
212
|
+
this.setContextConnectedState(false /* connected */, readonly);
|
|
213
|
+
// Deliver delayed calls to DeltaManager - we ignored "connect" events while loading.
|
|
214
|
+
const cm = this._deltaManager.connectionManager;
|
|
215
|
+
if (cm.connected) {
|
|
216
|
+
const details = cm.connectionDetails;
|
|
217
|
+
(0, internal_2.assert)(details !== undefined, 0x96a /* should have details if connected */);
|
|
218
|
+
this.connectionStateHandler.receivedConnectEvent(details);
|
|
219
|
+
}
|
|
203
220
|
}
|
|
204
221
|
}
|
|
205
222
|
get closed() {
|
|
206
223
|
return (this._lifecycleState === "closing" || this._lifecycleState === "closed" || this.disposed);
|
|
207
224
|
}
|
|
225
|
+
get loaded() {
|
|
226
|
+
return this._lifecycleState === "loaded";
|
|
227
|
+
}
|
|
208
228
|
get disposed() {
|
|
209
229
|
return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
|
|
210
230
|
}
|
|
@@ -273,11 +293,12 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
273
293
|
return this.connectionStateHandler.connectionState === connectionState_js_1.ConnectionState.Connected;
|
|
274
294
|
}
|
|
275
295
|
/**
|
|
276
|
-
*
|
|
277
|
-
*
|
|
296
|
+
* clientId of the latest connection. Changes only once client is connected, caught up and fully loaded.
|
|
297
|
+
* Changes to clientId are delayed through container loading sequence and delived once container is fully loaded.
|
|
298
|
+
* clientId does not reset on lost connection - old value persists until new connection is fully established.
|
|
278
299
|
*/
|
|
279
300
|
get clientId() {
|
|
280
|
-
return this.
|
|
301
|
+
return this.protocolHandler.audience.getSelf()?.clientId;
|
|
281
302
|
}
|
|
282
303
|
get isInteractiveClient() {
|
|
283
304
|
return this.deltaManager.clientDetails.capabilities.interactive;
|
|
@@ -341,6 +362,7 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
341
362
|
eventName: "ContainerEventHandlerException",
|
|
342
363
|
name: typeof name === "string" ? name : undefined,
|
|
343
364
|
}, error);
|
|
365
|
+
this.close((0, internal_4.normalizeError)(error));
|
|
344
366
|
});
|
|
345
367
|
/**
|
|
346
368
|
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
@@ -435,13 +457,14 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
435
457
|
throw normalizeErrorAndClose(error);
|
|
436
458
|
});
|
|
437
459
|
}
|
|
460
|
+
// If offline load is enabled, attachP will return the attach summary (in Snapshot format) so we can initialize SerializedStateManager
|
|
438
461
|
const snapshotWithBlobs = await attachP;
|
|
439
462
|
this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs);
|
|
440
463
|
if (!this.closed) {
|
|
441
|
-
this.handleDeltaConnectionArg({
|
|
464
|
+
this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
|
|
442
465
|
fetchOpsFromStorage: false,
|
|
443
466
|
reason: { text: "createDetached" },
|
|
444
|
-
}
|
|
467
|
+
});
|
|
445
468
|
}
|
|
446
469
|
}, { start: true, end: true, cancel: "generic" });
|
|
447
470
|
});
|
|
@@ -465,7 +488,6 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
465
488
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
466
489
|
this.connectionTransitionTimes[connectionState_js_1.ConnectionState.Disconnected] = client_utils_1.performance.now();
|
|
467
490
|
const pendingLocalState = loadProps?.pendingLocalState;
|
|
468
|
-
this._clientId = pendingLocalState?.clientId;
|
|
469
491
|
this._canReconnect = canReconnect ?? true;
|
|
470
492
|
this.clientDetailsOverride = clientDetailsOverride;
|
|
471
493
|
this.urlResolver = urlResolver;
|
|
@@ -535,12 +557,9 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
535
557
|
this.connectionStateHandler = (0, connectionStateHandler_js_1.createConnectionStateHandler)({
|
|
536
558
|
logger: this.mc.logger,
|
|
537
559
|
connectionStateChanged: (value, oldState, reason) => {
|
|
538
|
-
if (value === connectionState_js_1.ConnectionState.Connected) {
|
|
539
|
-
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
540
|
-
}
|
|
541
560
|
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
542
|
-
if (this.
|
|
543
|
-
this.propagateConnectionState(
|
|
561
|
+
if (this.loaded) {
|
|
562
|
+
this.propagateConnectionState(value === connectionState_js_1.ConnectionState.Disconnected
|
|
544
563
|
? reason
|
|
545
564
|
: undefined /* disconnectedReason */);
|
|
546
565
|
}
|
|
@@ -562,15 +581,22 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
562
581
|
this.connectionTransitionTimes[connectionState_js_1.ConnectionState.CatchingUp],
|
|
563
582
|
...(details === undefined ? {} : { details: JSON.stringify(details) }),
|
|
564
583
|
});
|
|
584
|
+
// This assert is important for many reasons:
|
|
585
|
+
// 1) Cosmetic / OCE burden: It's useless to raise NoJoinOp error events, if we are loading, as that's most
|
|
586
|
+
// likely to happen if snapshot loading takes too long. During this time we are not processing ops so there is no
|
|
587
|
+
// way to move to "connected" state, and thus "NoJoin" timer would fire (see
|
|
588
|
+
// IConnectionStateHandler.logConnectionIssue() callback and related code in ConnectStateHandler class implementation).
|
|
589
|
+
// But these events do not tell us anything about connectivity pipeline / op processing pipeline,
|
|
590
|
+
// only that boot is slow, and we have events for that.
|
|
591
|
+
// 2) Doing recovery below is useless in loading mode, for the reasons described above. At the same time we can't
|
|
592
|
+
// not do it, as maybe we lost JoinSignal for "self", and when loading is done, we never move to connected
|
|
593
|
+
// state. So we would have to do (in most cases) useless infinite reconnect loop while we are loading.
|
|
594
|
+
(0, internal_2.assert)(this.loaded, 0x96b /* connection issues can be raised only after container is loaded */);
|
|
565
595
|
// If this is "write" connection, it took too long to receive join op. But in most cases that's due
|
|
566
596
|
// to very slow op fetches and we will eventually get there.
|
|
567
|
-
// For "read" connections, we get here due to
|
|
568
|
-
//
|
|
569
|
-
|
|
570
|
-
// current state of audience.
|
|
571
|
-
// Other possible recovery path - move to connected state (i.e. ConnectionStateHandler.joinOpTimer
|
|
572
|
-
// to call this.applyForConnectedState("addMemberEvent") for "read" connections)
|
|
573
|
-
if (mode === "read") {
|
|
597
|
+
// For "read" connections, we get here due to join signal for "self" not arriving on time.
|
|
598
|
+
// Attempt to recover by reconnecting.
|
|
599
|
+
if (mode === "read" && category === "error") {
|
|
574
600
|
const reason = { text: "NoJoinSignal" };
|
|
575
601
|
this.disconnectInternal(reason);
|
|
576
602
|
this.connectInternal({ reason, fetchOpsFromStorage: false });
|
|
@@ -579,6 +605,9 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
579
605
|
clientShouldHaveLeft: (clientId) => {
|
|
580
606
|
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
581
607
|
},
|
|
608
|
+
onCriticalError: (error) => {
|
|
609
|
+
this.close((0, internal_4.normalizeError)(error));
|
|
610
|
+
},
|
|
582
611
|
}, this.deltaManager, pendingLocalState?.clientId);
|
|
583
612
|
this.on(savedContainerEvent, () => {
|
|
584
613
|
this.connectionStateHandler.containerSaved();
|
|
@@ -593,11 +622,11 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
593
622
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
594
623
|
const forceEnableSummarizeProtocolTree = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
595
624
|
options.summarizeProtocolTree;
|
|
596
|
-
this.storageAdapter = new containerStorageAdapter_js_1.ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
625
|
+
this.storageAdapter = new containerStorageAdapter_js_1.ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, pendingLocalState?.loadedGroupIdSnapshots, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
597
626
|
const offlineLoadEnabled = (this.isInteractiveClient &&
|
|
598
627
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) ??
|
|
599
628
|
options.enableOfflineLoad === true;
|
|
600
|
-
this.serializedStateManager = new serializedStateManager_js_1.SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled);
|
|
629
|
+
this.serializedStateManager = new serializedStateManager_js_1.SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled, this, () => this.isDirty);
|
|
601
630
|
const isDomAvailable = typeof document === "object" &&
|
|
602
631
|
document !== null &&
|
|
603
632
|
typeof document.addEventListener === "function" &&
|
|
@@ -732,6 +761,11 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
732
761
|
this.close();
|
|
733
762
|
return pendingState;
|
|
734
763
|
}
|
|
764
|
+
/**
|
|
765
|
+
* Serialize current container state required to rehydrate to the same position without dataloss.
|
|
766
|
+
* Note: The container must already be attached. For detached containers use {@link serialize}
|
|
767
|
+
* @returns stringified {@link IPendingContainerState} for the container
|
|
768
|
+
*/
|
|
735
769
|
async getPendingLocalState() {
|
|
736
770
|
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
737
771
|
}
|
|
@@ -741,12 +775,18 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
741
775
|
}
|
|
742
776
|
(0, internal_2.assert)(this.attachmentData.state === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
743
777
|
(0, internal_2.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
744
|
-
const pendingState = await this.serializedStateManager.
|
|
778
|
+
const pendingState = await this.serializedStateManager.getPendingLocalState(props, this.clientId, this.runtime, this.resolvedUrl);
|
|
745
779
|
return pendingState;
|
|
746
780
|
}
|
|
747
781
|
get attachState() {
|
|
748
782
|
return this.attachmentData.state;
|
|
749
783
|
}
|
|
784
|
+
/**
|
|
785
|
+
* Serialize current container state required to rehydrate to the same position without dataloss.
|
|
786
|
+
* Note: The container must be detached and not closed. For attached containers use
|
|
787
|
+
* {@link getPendingLocalState} or {@link closeAndGetPendingLocalState}
|
|
788
|
+
* @returns stringified {@link IPendingDetachedContainerState} for the container
|
|
789
|
+
*/
|
|
750
790
|
serialize() {
|
|
751
791
|
if (this.attachmentData.state === container_definitions_1.AttachState.Attached || this.closed) {
|
|
752
792
|
throw new internal_4.UsageError("Container must not be attached or closed.");
|
|
@@ -802,11 +842,11 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
802
842
|
connectInternal(args) {
|
|
803
843
|
(0, internal_2.assert)(!this.closed, 0x2c5 /* "Attempting to connect() a closed Container" */);
|
|
804
844
|
(0, internal_2.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x2c6 /* "Attempting to connect() a container that is not attached" */);
|
|
805
|
-
// Resume processing ops and connect to delta stream
|
|
806
|
-
this.resumeInternal(args);
|
|
807
845
|
// Set Auto Reconnect Mode
|
|
808
846
|
const mode = contracts_js_1.ReconnectMode.Enabled;
|
|
809
847
|
this.setAutoReconnectInternal(mode, args.reason);
|
|
848
|
+
// Resume processing ops and connect to delta stream
|
|
849
|
+
this.resumeInternal(args);
|
|
810
850
|
}
|
|
811
851
|
disconnect() {
|
|
812
852
|
if (this.closed) {
|
|
@@ -826,6 +866,11 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
826
866
|
(0, internal_2.assert)(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
|
|
827
867
|
// Resume processing ops
|
|
828
868
|
if (this.inboundQueuePausedFromInit) {
|
|
869
|
+
// This assert guards against possibility of ops/signals showing up too soon, while
|
|
870
|
+
// container is not ready yet to receive them. We can hit it only if some internal code call into here,
|
|
871
|
+
// as public API like Container.connect() can be only called when user got back container object, i.e.
|
|
872
|
+
// it is already fully loaded.
|
|
873
|
+
(0, internal_2.assert)(this.loaded, 0x96c /* connect() can be called only in fully loaded state */);
|
|
829
874
|
this.inboundQueuePausedFromInit = false;
|
|
830
875
|
this._deltaManager.inbound.resume();
|
|
831
876
|
this._deltaManager.inboundSignal.resume();
|
|
@@ -914,7 +959,7 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
914
959
|
*
|
|
915
960
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
916
961
|
*/
|
|
917
|
-
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState
|
|
962
|
+
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
918
963
|
const timings = { phase1: client_utils_1.performance.now() };
|
|
919
964
|
this.service = await this.createDocumentService(async () => this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType));
|
|
920
965
|
// Except in cases where it has stashed ops or requested by feature gate, the container will connect in "read" mode
|
|
@@ -929,7 +974,7 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
929
974
|
};
|
|
930
975
|
// Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
|
|
931
976
|
// DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
|
|
932
|
-
if (loadMode.deltaConnection === undefined
|
|
977
|
+
if (loadMode.deltaConnection === undefined) {
|
|
933
978
|
this.connectToDeltaStream(connectionArgs);
|
|
934
979
|
}
|
|
935
980
|
this.storageAdapter.connectToService(this.service);
|
|
@@ -941,54 +986,13 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
941
986
|
true && this.service?.policies?.supportGetSnapshotApi === true;
|
|
942
987
|
// Fetch specified snapshot.
|
|
943
988
|
const { baseSnapshot, version } = await this.serializedStateManager.fetchSnapshot(specifiedVersion, supportGetSnapshotApi);
|
|
989
|
+
const baseSnapshotTree = (0, internal_3.getSnapshotTree)(baseSnapshot);
|
|
944
990
|
this._loadedFromVersion = version;
|
|
945
|
-
const attributes = await (0, utils_js_1.getDocumentAttributes)(this.storageAdapter,
|
|
991
|
+
const attributes = await (0, utils_js_1.getDocumentAttributes)(this.storageAdapter, baseSnapshotTree);
|
|
946
992
|
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
947
993
|
const lastProcessedSequenceNumber = pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber ??
|
|
948
994
|
attributes.sequenceNumber;
|
|
949
995
|
let opsBeforeReturnP;
|
|
950
|
-
if (loadMode.pauseAfterLoad === true) {
|
|
951
|
-
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
952
|
-
if (loadMode.opsBeforeReturn === "sequenceNumber") {
|
|
953
|
-
(0, internal_2.assert)(loadToSequenceNumber !== undefined, 0x727 /* sequenceNumber should be defined */);
|
|
954
|
-
// Note: It is possible that we think the latest snapshot is newer than the specified sequence number
|
|
955
|
-
// due to saved ops that may be replayed after the snapshot.
|
|
956
|
-
// https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
|
|
957
|
-
if (lastProcessedSequenceNumber > loadToSequenceNumber) {
|
|
958
|
-
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.");
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
// Force readonly mode - this will ensure we don't receive an error for the lack of join op
|
|
962
|
-
this.forceReadonly(true);
|
|
963
|
-
// We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
|
|
964
|
-
const opHandler = () => {
|
|
965
|
-
if (loadToSequenceNumber === undefined) {
|
|
966
|
-
// If there is no specified sequence number, pause after the inbound queue is empty.
|
|
967
|
-
if (this.deltaManager.inbound.length !== 0) {
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
else {
|
|
972
|
-
// If there is a specified sequence number, keep processing until we reach it.
|
|
973
|
-
if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
// Pause op processing once we have processed the desired number of ops.
|
|
978
|
-
void this.deltaManager.inbound.pause();
|
|
979
|
-
void this.deltaManager.outbound.pause();
|
|
980
|
-
this.off("op", opHandler);
|
|
981
|
-
};
|
|
982
|
-
if ((loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
|
|
983
|
-
this.deltaManager.lastSequenceNumber === loadToSequenceNumber) {
|
|
984
|
-
// If we have already reached the desired sequence number, call opHandler() to pause immediately.
|
|
985
|
-
opHandler();
|
|
986
|
-
}
|
|
987
|
-
else {
|
|
988
|
-
// If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
|
|
989
|
-
this.on("op", opHandler);
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
996
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
993
997
|
// Kick off any ops fetching if required.
|
|
994
998
|
switch (loadMode.opsBeforeReturn) {
|
|
@@ -997,7 +1001,6 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
997
1001
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
998
1002
|
this.attachDeltaManagerOpHandler(attributes, loadMode.deltaConnection !== "none" ? "all" : "none", lastProcessedSequenceNumber);
|
|
999
1003
|
break;
|
|
1000
|
-
case "sequenceNumber":
|
|
1001
1004
|
case "cached":
|
|
1002
1005
|
case "all":
|
|
1003
1006
|
opsBeforeReturnP = this.attachDeltaManagerOpHandler(attributes, loadMode.opsBeforeReturn, lastProcessedSequenceNumber);
|
|
@@ -1007,10 +1010,16 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1007
1010
|
}
|
|
1008
1011
|
// ...load in the existing quorum
|
|
1009
1012
|
// Initialize the protocol handler
|
|
1010
|
-
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter,
|
|
1013
|
+
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, baseSnapshotTree);
|
|
1014
|
+
// If we are loading from pending state, we start with old clientId.
|
|
1015
|
+
// We switch to latest connection clientId only after setLoaded().
|
|
1016
|
+
(0, internal_2.assert)(this.clientId === undefined, 0x96d /* there should be no clientId yet */);
|
|
1017
|
+
if (pendingLocalState?.clientId !== undefined) {
|
|
1018
|
+
this.protocolHandler.audience.setCurrentClientId(pendingLocalState?.clientId);
|
|
1019
|
+
}
|
|
1011
1020
|
timings.phase3 = client_utils_1.performance.now();
|
|
1012
1021
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1013
|
-
await this.instantiateRuntime(codeDetails,
|
|
1022
|
+
await this.instantiateRuntime(codeDetails, baseSnapshotTree,
|
|
1014
1023
|
// give runtime a dummy value so it knows we're loading from a stash blob
|
|
1015
1024
|
pendingLocalState ? pendingLocalState?.pendingRuntimeState ?? {} : undefined, (0, internal_3.isInstanceOfISnapshot)(baseSnapshot) ? baseSnapshot : undefined);
|
|
1016
1025
|
// replay saved ops
|
|
@@ -1024,6 +1033,7 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1024
1033
|
await this.runtime.notifyOpReplay?.(message);
|
|
1025
1034
|
}
|
|
1026
1035
|
pendingLocalState.savedOps = [];
|
|
1036
|
+
this.storageAdapter.clearPendingState();
|
|
1027
1037
|
}
|
|
1028
1038
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
1029
1039
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
@@ -1035,20 +1045,11 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1035
1045
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1036
1046
|
this._deltaManager.inbound.pause();
|
|
1037
1047
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
this.
|
|
1043
|
-
await new Promise((resolve, reject) => {
|
|
1044
|
-
const opHandler = (message) => {
|
|
1045
|
-
if (message.sequenceNumber >= loadToSequenceNumber) {
|
|
1046
|
-
resolve();
|
|
1047
|
-
this.off("op", opHandler);
|
|
1048
|
-
}
|
|
1049
|
-
};
|
|
1050
|
-
this.on("op", opHandler);
|
|
1051
|
-
});
|
|
1048
|
+
// Internal context is fully loaded at this point
|
|
1049
|
+
// Move to loaded before calling this.handleDeltaConnectionArg() - latter allows ops & signals in, which
|
|
1050
|
+
// may result in container moving to "connected" state. Such transitions are allowed only in loaded state.
|
|
1051
|
+
this.setLoaded();
|
|
1052
|
+
this.handleDeltaConnectionArg(loadMode.deltaConnection);
|
|
1052
1053
|
}
|
|
1053
1054
|
// Safety net: static version of Container.load() should have learned about it through "closed" handler.
|
|
1054
1055
|
// But if that did not happen for some reason, fail load for sure.
|
|
@@ -1058,8 +1059,6 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1058
1059
|
if (this.closed) {
|
|
1059
1060
|
throw new Error("Container was closed while load()");
|
|
1060
1061
|
}
|
|
1061
|
-
// Internal context is fully loaded at this point
|
|
1062
|
-
this.setLoaded();
|
|
1063
1062
|
timings.end = client_utils_1.performance.now();
|
|
1064
1063
|
this.subLogger.sendTelemetryEvent({
|
|
1065
1064
|
eventName: "LoadStagesTimings",
|
|
@@ -1239,7 +1238,21 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1239
1238
|
deltaManager.inboundSignal.pause();
|
|
1240
1239
|
deltaManager.on("connect", (details, _opsBehind) => {
|
|
1241
1240
|
(0, internal_2.assert)(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
|
|
1242
|
-
|
|
1241
|
+
// Delay raising events until setLoaded()
|
|
1242
|
+
// Here are some of the reasons why this design is chosen:
|
|
1243
|
+
// 1. Various processes track speed of connection. But we are not processing ops or signal while container is loading,
|
|
1244
|
+
// and thus we can't move forward across connection modes. This results in telemetry errors (like NoJoinOp) that
|
|
1245
|
+
// have nothing to do with connection flow itself
|
|
1246
|
+
// 2. This also makes it hard to reason about recovery (like reconnection) in case we might have lost JoinSignal. Reconnecting
|
|
1247
|
+
// in loading phase is useless (get back to same state), but at the same time not doing it may result in broken connection
|
|
1248
|
+
// without recovery (after we loaded).
|
|
1249
|
+
// 3. We expose non-consistent view. ContainerRuntime may start loading in non-connected state, but end in connected, with
|
|
1250
|
+
// no events telling about it (until we loaded). Most of the code relies on a fact that state changes when events fire.
|
|
1251
|
+
// This will not delay any processes (as observed by the user). I.e. once container moves to loaded phase,
|
|
1252
|
+
// we immediately would transition across all phases, if we have proper signals / ops ready.
|
|
1253
|
+
if (this.loaded) {
|
|
1254
|
+
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1255
|
+
}
|
|
1243
1256
|
});
|
|
1244
1257
|
deltaManager.on("establishingConnection", (reason) => {
|
|
1245
1258
|
this.connectionStateHandler.establishingConnection(reason);
|
|
@@ -1249,8 +1262,14 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1249
1262
|
});
|
|
1250
1263
|
deltaManager.on("disconnect", (text, error) => {
|
|
1251
1264
|
this.noopHeuristic?.notifyDisconnect();
|
|
1252
|
-
|
|
1253
|
-
|
|
1265
|
+
const reason = { text, error };
|
|
1266
|
+
// Symmetry with "connect" events
|
|
1267
|
+
if (this.loaded) {
|
|
1268
|
+
this.connectionStateHandler.receivedDisconnectEvent(reason);
|
|
1269
|
+
}
|
|
1270
|
+
else if (!this.closed) {
|
|
1271
|
+
// Raise cancellation to get state machine back to initial state
|
|
1272
|
+
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1254
1273
|
}
|
|
1255
1274
|
});
|
|
1256
1275
|
deltaManager.on("throttled", (warning) => {
|
|
@@ -1263,7 +1282,9 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1263
1282
|
this.emit("warning", warn);
|
|
1264
1283
|
});
|
|
1265
1284
|
deltaManager.on("readonly", (readonly) => {
|
|
1266
|
-
|
|
1285
|
+
if (this.loaded) {
|
|
1286
|
+
this.setContextConnectedState(this.connectionState === connectionState_js_1.ConnectionState.Connected, readonly);
|
|
1287
|
+
}
|
|
1267
1288
|
this.emit("readonly", readonly);
|
|
1268
1289
|
});
|
|
1269
1290
|
deltaManager.on("closed", (error) => {
|
|
@@ -1305,8 +1326,7 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1305
1326
|
// This info is of most interesting while Catching Up.
|
|
1306
1327
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
1307
1328
|
// Need to check that we have already loaded and fetched the snapshot.
|
|
1308
|
-
if (this.deltaManager.hasCheckpointSequenceNumber &&
|
|
1309
|
-
this._lifecycleState === "loaded") {
|
|
1329
|
+
if (this.deltaManager.hasCheckpointSequenceNumber && this.loaded) {
|
|
1310
1330
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
1311
1331
|
}
|
|
1312
1332
|
}
|
|
@@ -1320,7 +1340,7 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1320
1340
|
reason: reason?.text,
|
|
1321
1341
|
connectionInitiationReason,
|
|
1322
1342
|
pendingClientId: this.connectionStateHandler.pendingClientId,
|
|
1323
|
-
clientId: this.clientId,
|
|
1343
|
+
clientId: this.connectionStateHandler.clientId,
|
|
1324
1344
|
autoReconnect,
|
|
1325
1345
|
opsBehind,
|
|
1326
1346
|
online: internal_3.OnlineStatus[(0, internal_3.isOnline)()],
|
|
@@ -1336,20 +1356,23 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1336
1356
|
this.firstConnection = false;
|
|
1337
1357
|
}
|
|
1338
1358
|
}
|
|
1339
|
-
propagateConnectionState(
|
|
1340
|
-
|
|
1341
|
-
|
|
1359
|
+
propagateConnectionState(disconnectedReason) {
|
|
1360
|
+
const connected = this.connectionState === connectionState_js_1.ConnectionState.Connected;
|
|
1361
|
+
if (connected) {
|
|
1362
|
+
const clientId = this.connectionStateHandler.clientId;
|
|
1363
|
+
(0, internal_2.assert)(clientId !== undefined, 0x96e /* there has to be clientId */);
|
|
1364
|
+
this.protocolHandler.audience.setCurrentClientId(clientId);
|
|
1365
|
+
}
|
|
1366
|
+
// We communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
1342
1367
|
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
1343
|
-
if (
|
|
1344
|
-
this.connectionState !== connectionState_js_1.ConnectionState.Connected &&
|
|
1368
|
+
if (this.connectionState !== connectionState_js_1.ConnectionState.Connected &&
|
|
1345
1369
|
this.connectionState !== connectionState_js_1.ConnectionState.Disconnected) {
|
|
1346
1370
|
return;
|
|
1347
1371
|
}
|
|
1348
|
-
const state = this.connectionState === connectionState_js_1.ConnectionState.Connected;
|
|
1349
1372
|
// Both protocol and context should not be undefined if we got so far.
|
|
1350
|
-
this.setContextConnectedState(
|
|
1351
|
-
this.protocolHandler.setConnectionState(
|
|
1352
|
-
(0, internal_4.raiseConnectedEvent)(this.mc.logger, this,
|
|
1373
|
+
this.setContextConnectedState(connected, this.readOnlyInfo.readonly ?? false);
|
|
1374
|
+
this.protocolHandler.setConnectionState(connected, this.clientId);
|
|
1375
|
+
(0, internal_4.raiseConnectedEvent)(this.mc.logger, this, connected, this.clientId, disconnectedReason?.text);
|
|
1353
1376
|
}
|
|
1354
1377
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1355
1378
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
@@ -1470,24 +1493,29 @@ class Container extends internal_4.EventEmitterWithErrorHandling {
|
|
|
1470
1493
|
/**
|
|
1471
1494
|
* Set the connected state of the ContainerContext
|
|
1472
1495
|
* This controls the "connected" state of the ContainerRuntime as well
|
|
1473
|
-
* @param
|
|
1496
|
+
* @param connected - Is the container currently connected?
|
|
1474
1497
|
* @param readonly - Is the container in readonly mode?
|
|
1475
1498
|
*/
|
|
1476
|
-
setContextConnectedState(
|
|
1477
|
-
if (this._runtime?.disposed === false) {
|
|
1499
|
+
setContextConnectedState(connected, readonly) {
|
|
1500
|
+
if (this._runtime?.disposed === false && this.loaded) {
|
|
1478
1501
|
/**
|
|
1479
1502
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1480
1503
|
* ops getting through to the DeltaManager.
|
|
1481
1504
|
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
1482
1505
|
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
1483
1506
|
*/
|
|
1484
|
-
this.runtime.setConnectionState(
|
|
1507
|
+
this.runtime.setConnectionState(connected && !readonly, this.clientId);
|
|
1485
1508
|
}
|
|
1486
1509
|
}
|
|
1487
|
-
handleDeltaConnectionArg(
|
|
1510
|
+
handleDeltaConnectionArg(deltaConnectionArg, connectionArgs) {
|
|
1511
|
+
// This ensures that we allow transitions to "connected" state only after container has been fully loaded
|
|
1512
|
+
// and we propagate such events to container runtime. All events prior to being loaded are ignored.
|
|
1513
|
+
// This means if we get here in non-loaded state, we might not deliver proper events to container runtime,
|
|
1514
|
+
// and runtime implementation may miss such events.
|
|
1515
|
+
(0, internal_2.assert)(this.loaded, 0x96f /* has to be called after container transitions to loaded state */);
|
|
1488
1516
|
switch (deltaConnectionArg) {
|
|
1489
1517
|
case undefined:
|
|
1490
|
-
if (
|
|
1518
|
+
if (connectionArgs) {
|
|
1491
1519
|
// connect to delta stream now since we did not before
|
|
1492
1520
|
this.connectToDeltaStream(connectionArgs);
|
|
1493
1521
|
}
|