@fluidframework/container-loader 2.0.0-dev.5.3.2.178189 → 2.0.0-dev.6.4.0.191258
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 +131 -0
- package/README.md +10 -6
- package/dist/audience.d.ts +1 -0
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +5 -3
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.js +2 -2
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +5 -5
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +97 -93
- 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 +50 -52
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +20 -9
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +327 -277
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -7
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +2 -14
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +12 -13
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +21 -8
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +3 -3
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.d.ts +30 -0
- package/dist/debugLogger.d.ts.map +1 -0
- package/dist/debugLogger.js +95 -0
- package/dist/debugLogger.js.map +1 -0
- package/dist/deltaManager.d.ts +21 -10
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +114 -66
- 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 +10 -10
- package/dist/deltaQueue.js.map +1 -1
- package/dist/disposal.d.ts +2 -2
- package/dist/disposal.d.ts.map +1 -1
- package/dist/disposal.js +1 -1
- package/dist/disposal.js.map +1 -1
- package/dist/error.d.ts +23 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +32 -0
- package/dist/error.js.map +1 -0
- package/dist/loader.d.ts +22 -3
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +82 -51
- package/dist/loader.js.map +1 -1
- package/dist/noopHeuristic.d.ts +2 -2
- package/dist/noopHeuristic.d.ts.map +1 -1
- package/dist/noopHeuristic.js +6 -5
- package/dist/noopHeuristic.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/quorum.d.ts +4 -1
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js +1 -13
- package/dist/quorum.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -4
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts +8 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +30 -11
- package/dist/utils.js.map +1 -1
- package/lib/audience.d.ts +1 -0
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +4 -2
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.js +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +5 -5
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +74 -67
- 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 +27 -29
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +20 -9
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +288 -238
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -7
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +2 -14
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +5 -6
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +21 -8
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +3 -3
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.d.ts +30 -0
- package/lib/debugLogger.d.ts.map +1 -0
- package/lib/debugLogger.js +91 -0
- package/lib/debugLogger.js.map +1 -0
- package/lib/deltaManager.d.ts +21 -10
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +88 -37
- 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 +3 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/disposal.d.ts +2 -2
- package/lib/disposal.d.ts.map +1 -1
- package/lib/disposal.js +1 -1
- package/lib/disposal.js.map +1 -1
- package/lib/error.d.ts +23 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +28 -0
- package/lib/error.js.map +1 -0
- package/lib/loader.d.ts +22 -3
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +82 -51
- package/lib/loader.js.map +1 -1
- package/lib/noopHeuristic.d.ts +2 -2
- package/lib/noopHeuristic.d.ts.map +1 -1
- package/lib/noopHeuristic.js +2 -1
- package/lib/noopHeuristic.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/quorum.d.ts +4 -1
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js +0 -11
- package/lib/quorum.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +2 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts +8 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +25 -7
- package/lib/utils.js.map +1 -1
- package/package.json +26 -32
- package/src/audience.ts +7 -1
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +75 -51
- package/src/connectionStateHandler.ts +31 -38
- package/src/container.ts +335 -240
- package/src/containerContext.ts +0 -16
- package/src/containerStorageAdapter.ts +2 -1
- package/src/contracts.ts +27 -11
- package/src/debugLogger.ts +113 -0
- package/src/deltaManager.ts +84 -34
- package/src/deltaQueue.ts +2 -1
- package/src/disposal.ts +2 -2
- package/src/error.ts +44 -0
- package/src/loader.ts +83 -35
- package/src/noopHeuristic.ts +3 -2
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +33 -2
- package/src/quorum.ts +0 -10
- package/src/retriableDocumentStorageService.ts +2 -4
- package/src/utils.ts +33 -8
package/dist/container.js
CHANGED
|
@@ -11,9 +11,10 @@ exports.Container = exports.ReportIfTooLong = exports.waitContainerToCatchUp = v
|
|
|
11
11
|
// eslint-disable-next-line import/no-internal-modules
|
|
12
12
|
const merge_1 = __importDefault(require("lodash/merge"));
|
|
13
13
|
const uuid_1 = require("uuid");
|
|
14
|
-
const
|
|
14
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
15
|
+
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
16
|
+
const core_interfaces_1 = require("@fluidframework/core-interfaces");
|
|
15
17
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
16
|
-
const container_utils_1 = require("@fluidframework/container-utils");
|
|
17
18
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
18
19
|
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
19
20
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
@@ -54,7 +55,7 @@ const packageNotFactoryError = "Code package does not implement IRuntimeFactory"
|
|
|
54
55
|
async function waitContainerToCatchUp(container) {
|
|
55
56
|
// Make sure we stop waiting if container is closed.
|
|
56
57
|
if (container.closed) {
|
|
57
|
-
throw new
|
|
58
|
+
throw new telemetry_utils_1.UsageError("waitContainerToCatchUp: Container closed");
|
|
58
59
|
}
|
|
59
60
|
return new Promise((resolve, reject) => {
|
|
60
61
|
const deltaManager = container.deltaManager;
|
|
@@ -62,8 +63,8 @@ async function waitContainerToCatchUp(container) {
|
|
|
62
63
|
container.off("closed", closedCallback);
|
|
63
64
|
const baseMessage = "Container closed while waiting to catch up";
|
|
64
65
|
reject(err !== undefined
|
|
65
|
-
? (0, telemetry_utils_1.wrapError)(err, (innerMessage) => new
|
|
66
|
-
: new
|
|
66
|
+
? (0, telemetry_utils_1.wrapError)(err, (innerMessage) => new telemetry_utils_1.GenericError(`${baseMessage}: ${innerMessage}`))
|
|
67
|
+
: new telemetry_utils_1.GenericError(baseMessage));
|
|
67
68
|
};
|
|
68
69
|
container.on("closed", closedCallback);
|
|
69
70
|
// Depending on config, transition to "connected" state may include the guarantee
|
|
@@ -71,11 +72,11 @@ async function waitContainerToCatchUp(container) {
|
|
|
71
72
|
// Waiting for "connected" state in either case gets us at least to our own Join op
|
|
72
73
|
// which is a reasonable approximation of "caught up"
|
|
73
74
|
const waitForOps = () => {
|
|
74
|
-
(0,
|
|
75
|
+
(0, core_utils_1.assert)(container.connectionState === connectionState_1.ConnectionState.CatchingUp ||
|
|
75
76
|
container.connectionState === connectionState_1.ConnectionState.Connected, 0x0cd /* "Container disconnected while waiting for ops!" */);
|
|
76
77
|
const hasCheckpointSequenceNumber = deltaManager.hasCheckpointSequenceNumber;
|
|
77
78
|
const connectionOpSeqNumber = deltaManager.lastKnownSeqNumber;
|
|
78
|
-
(0,
|
|
79
|
+
(0, core_utils_1.assert)(deltaManager.lastSequenceNumber <= connectionOpSeqNumber, 0x266 /* "lastKnownSeqNumber should never be below last processed sequence number" */);
|
|
79
80
|
if (deltaManager.lastSequenceNumber === connectionOpSeqNumber) {
|
|
80
81
|
container.off("closed", closedCallback);
|
|
81
82
|
resolve(hasCheckpointSequenceNumber);
|
|
@@ -111,7 +112,7 @@ async function waitContainerToCatchUp(container) {
|
|
|
111
112
|
exports.waitContainerToCatchUp = waitContainerToCatchUp;
|
|
112
113
|
const getCodeProposal =
|
|
113
114
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
114
|
-
(quorum) =>
|
|
115
|
+
(quorum) => quorum.get("code") ?? quorum.get("code2");
|
|
115
116
|
/**
|
|
116
117
|
* Helper function to report to telemetry cases where operation takes longer than expected (200ms)
|
|
117
118
|
* @param logger - logger to use
|
|
@@ -132,7 +133,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
132
133
|
* @internal
|
|
133
134
|
*/
|
|
134
135
|
constructor(createProps, loadProps) {
|
|
135
|
-
var _a;
|
|
136
136
|
super((name, error) => {
|
|
137
137
|
this.mc.logger.sendErrorEvent({
|
|
138
138
|
eventName: "ContainerEventHandlerException",
|
|
@@ -160,13 +160,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
160
160
|
this.inboundQueuePausedFromInit = true;
|
|
161
161
|
this.firstConnection = true;
|
|
162
162
|
this.connectionTransitionTimes = [];
|
|
163
|
-
this.messageCountAfterDisconnection = 0;
|
|
164
163
|
this.attachStarted = false;
|
|
165
164
|
this._dirtyContainer = false;
|
|
166
165
|
this.savedOps = [];
|
|
167
166
|
this.clientsWhoShouldHaveLeft = new Set();
|
|
168
|
-
this.setAutoReconnectTime =
|
|
169
|
-
this._lifecycleEvents = new
|
|
167
|
+
this.setAutoReconnectTime = client_utils_1.performance.now();
|
|
168
|
+
this._lifecycleEvents = new client_utils_1.TypedEventEmitter();
|
|
170
169
|
this._disposed = false;
|
|
171
170
|
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
172
171
|
if (this.resolvedUrl === undefined) {
|
|
@@ -182,9 +181,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
182
181
|
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
183
182
|
};
|
|
184
183
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
185
|
-
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] =
|
|
186
|
-
const pendingLocalState = loadProps
|
|
187
|
-
this.
|
|
184
|
+
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = client_utils_1.performance.now();
|
|
185
|
+
const pendingLocalState = loadProps?.pendingLocalState;
|
|
186
|
+
this._clientId = pendingLocalState?.clientId;
|
|
187
|
+
this._canReconnect = canReconnect ?? true;
|
|
188
188
|
this.clientDetailsOverride = clientDetailsOverride;
|
|
189
189
|
this.urlResolver = urlResolver;
|
|
190
190
|
this.serviceFactory = documentServiceFactory;
|
|
@@ -192,14 +192,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
192
192
|
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
193
193
|
// all clients that were loaded from the same loader (including summarizer clients).
|
|
194
194
|
// Tracking alternative ways to handle this in AB#4129.
|
|
195
|
-
this.options =
|
|
195
|
+
this.options = { ...options };
|
|
196
196
|
this.scope = scope;
|
|
197
197
|
this.detachedBlobStorage = detachedBlobStorage;
|
|
198
198
|
this.protocolHandlerBuilder =
|
|
199
|
-
protocolHandlerBuilder
|
|
199
|
+
protocolHandlerBuilder ??
|
|
200
|
+
((attributes, quorumSnapshot, sendProposal) => new protocol_1.ProtocolHandler(attributes, quorumSnapshot, sendProposal, new audience_1.Audience(), (clientId) => this.clientsWhoShouldHaveLeft.has(clientId)));
|
|
200
201
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
201
202
|
this.clone = async (_loadProps, createParamOverrides) => {
|
|
202
|
-
return Container.load(_loadProps,
|
|
203
|
+
return Container.load(_loadProps, {
|
|
204
|
+
...createProps,
|
|
205
|
+
...createParamOverrides,
|
|
206
|
+
});
|
|
203
207
|
};
|
|
204
208
|
// Create logger for data stores to use
|
|
205
209
|
const type = this.client.details.type;
|
|
@@ -207,50 +211,50 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
207
211
|
const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
|
|
208
212
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
209
213
|
// We assign the id later so property getter is used.
|
|
210
|
-
this.subLogger = telemetry_utils_1.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
214
|
+
this.subLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
215
|
+
logger: subLogger,
|
|
216
|
+
properties: {
|
|
217
|
+
all: {
|
|
218
|
+
clientType,
|
|
219
|
+
containerId: (0, uuid_1.v4)(),
|
|
220
|
+
docId: () => this.resolvedUrl?.id,
|
|
221
|
+
containerAttachState: () => this._attachState,
|
|
222
|
+
containerLifecycleState: () => this._lifecycleState,
|
|
223
|
+
containerConnectionState: () => connectionState_1.ConnectionState[this.connectionState],
|
|
224
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
225
|
+
},
|
|
226
|
+
// we need to be judicious with our logging here to avoid generating too much data
|
|
227
|
+
// all data logged here should be broadly applicable, and not specific to a
|
|
228
|
+
// specific error or class of errors
|
|
229
|
+
error: {
|
|
230
|
+
// load information to associate errors with the specific load point
|
|
231
|
+
dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
|
|
232
|
+
dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
|
|
233
|
+
dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
|
|
234
|
+
containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
|
|
235
|
+
containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
|
|
236
|
+
// message information to associate errors with the specific execution state
|
|
237
|
+
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
238
|
+
dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
|
|
239
|
+
dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
|
|
240
|
+
dmLastMsqSeqClientId: () => this.deltaManager?.lastMessage?.clientId === null
|
|
237
241
|
? "null"
|
|
238
|
-
:
|
|
242
|
+
: this.deltaManager?.lastMessage?.clientId,
|
|
243
|
+
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
244
|
+
connectionStateDuration: () => client_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
239
245
|
},
|
|
240
|
-
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; },
|
|
241
|
-
connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
242
246
|
},
|
|
243
247
|
});
|
|
244
248
|
// Prefix all events in this file with container-loader
|
|
245
|
-
this.mc = (0, telemetry_utils_1.
|
|
249
|
+
this.mc = (0, telemetry_utils_1.createChildMonitoringContext)({ logger: this.subLogger, namespace: "Container" });
|
|
246
250
|
this._deltaManager = this.createDeltaManager();
|
|
247
251
|
this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
|
|
248
252
|
logger: this.mc.logger,
|
|
249
|
-
connectionStateChanged: (value, oldState, reason
|
|
253
|
+
connectionStateChanged: (value, oldState, reason) => {
|
|
250
254
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
251
255
|
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
252
256
|
}
|
|
253
|
-
this.logConnectionStateChangeTelemetry(value, oldState, reason
|
|
257
|
+
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
254
258
|
if (this._lifecycleState === "loaded") {
|
|
255
259
|
this.propagateConnectionState(false /* initial transition */, value === connectionState_1.ConnectionState.Disconnected
|
|
256
260
|
? reason
|
|
@@ -266,9 +270,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
266
270
|
// Report issues only if we already loaded container - op processing is paused while container is loading,
|
|
267
271
|
// so we always time-out processing of join op in cases where fetching snapshot takes a minute.
|
|
268
272
|
// It's not a problem with op processing itself - such issues should be tracked as part of boot perf monitoring instead.
|
|
269
|
-
this._deltaManager.logConnectionIssue(
|
|
270
|
-
|
|
271
|
-
|
|
273
|
+
this._deltaManager.logConnectionIssue({
|
|
274
|
+
eventName,
|
|
275
|
+
mode,
|
|
276
|
+
category: this._lifecycleState === "loading" ? "generic" : category,
|
|
277
|
+
duration: client_utils_1.performance.now() -
|
|
278
|
+
this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp],
|
|
279
|
+
...(details === undefined ? {} : { details: JSON.stringify(details) }),
|
|
280
|
+
});
|
|
272
281
|
// If this is "write" connection, it took too long to receive join op. But in most cases that's due
|
|
273
282
|
// to very slow op fetches and we will eventually get there.
|
|
274
283
|
// For "read" connections, we get here due to self join signal not arriving on time. We will need to
|
|
@@ -278,14 +287,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
278
287
|
// Other possible recovery path - move to connected state (i.e. ConnectionStateHandler.joinOpTimer
|
|
279
288
|
// to call this.applyForConnectedState("addMemberEvent") for "read" connections)
|
|
280
289
|
if (mode === "read") {
|
|
281
|
-
|
|
282
|
-
this.
|
|
290
|
+
const reason = { text: "NoJoinSignal" };
|
|
291
|
+
this.disconnectInternal(reason);
|
|
292
|
+
this.connectInternal({ reason, fetchOpsFromStorage: false });
|
|
283
293
|
}
|
|
284
294
|
},
|
|
285
295
|
clientShouldHaveLeft: (clientId) => {
|
|
286
296
|
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
287
297
|
},
|
|
288
|
-
}, this.deltaManager, pendingLocalState
|
|
298
|
+
}, this.deltaManager, pendingLocalState?.clientId);
|
|
289
299
|
this.on(savedContainerEvent, () => {
|
|
290
300
|
this.connectionStateHandler.containerSaved();
|
|
291
301
|
});
|
|
@@ -294,21 +304,22 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
294
304
|
// using this callback and fix them up.
|
|
295
305
|
const addProtocolSummaryIfMissing = (summaryTree) => (0, driver_utils_1.isCombinedAppAndProtocolSummary)(summaryTree) === true
|
|
296
306
|
? summaryTree
|
|
297
|
-
: (0,
|
|
307
|
+
: (0, utils_1.combineAppAndProtocolSummary)(summaryTree, this.captureProtocolSummary());
|
|
298
308
|
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
299
309
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
300
|
-
const forceEnableSummarizeProtocolTree =
|
|
301
|
-
|
|
310
|
+
const forceEnableSummarizeProtocolTree = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
311
|
+
options.summarizeProtocolTree;
|
|
312
|
+
this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
302
313
|
const isDomAvailable = typeof document === "object" &&
|
|
303
314
|
document !== null &&
|
|
304
315
|
typeof document.addEventListener === "function" &&
|
|
305
316
|
document.addEventListener !== null;
|
|
306
|
-
// keep track of last time page was visible for telemetry
|
|
307
|
-
if (isDomAvailable) {
|
|
308
|
-
this.lastVisible = document.hidden ?
|
|
317
|
+
// keep track of last time page was visible for telemetry (on interactive clients only)
|
|
318
|
+
if (isDomAvailable && interactive) {
|
|
319
|
+
this.lastVisible = document.hidden ? client_utils_1.performance.now() : undefined;
|
|
309
320
|
this.visibilityEventHandler = () => {
|
|
310
321
|
if (document.hidden) {
|
|
311
|
-
this.lastVisible =
|
|
322
|
+
this.lastVisible = client_utils_1.performance.now();
|
|
312
323
|
}
|
|
313
324
|
else {
|
|
314
325
|
// settimeout so this will hopefully fire after disconnect event if being hidden caused it
|
|
@@ -325,7 +336,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
325
336
|
* @internal
|
|
326
337
|
*/
|
|
327
338
|
static async load(loadProps, createProps) {
|
|
328
|
-
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
339
|
+
const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } = loadProps;
|
|
329
340
|
const container = new Container(createProps, loadProps);
|
|
330
341
|
const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
|
|
331
342
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
@@ -333,26 +344,31 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
333
344
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
334
345
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
335
346
|
const mode = pendingLocalState
|
|
336
|
-
?
|
|
347
|
+
? { ...(loadMode ?? defaultMode), opsBeforeReturn: undefined }
|
|
348
|
+
: loadMode ?? defaultMode;
|
|
337
349
|
const onClosed = (err) => {
|
|
338
350
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
339
|
-
reject(err
|
|
351
|
+
reject(err ?? new telemetry_utils_1.GenericError("Container closed without error during load"));
|
|
340
352
|
};
|
|
341
353
|
container.on("closed", onClosed);
|
|
342
354
|
container
|
|
343
|
-
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
355
|
+
.load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
|
|
344
356
|
.finally(() => {
|
|
345
357
|
container.removeListener("closed", onClosed);
|
|
346
358
|
})
|
|
347
359
|
.then((props) => {
|
|
348
|
-
event.end(
|
|
360
|
+
event.end({ ...props, ...loadMode });
|
|
349
361
|
resolve(container);
|
|
350
362
|
}, (error) => {
|
|
351
363
|
const err = (0, telemetry_utils_1.normalizeError)(error);
|
|
352
364
|
// Depending where error happens, we can be attempting to connect to web socket
|
|
353
365
|
// and continuously retrying (consider offline mode)
|
|
354
366
|
// Host has no container to close, so it's prudent to do it here
|
|
367
|
+
// Note: We could only dispose the container instead of just close but that would
|
|
368
|
+
// the telemetry where users sometimes search for ContainerClose event to look
|
|
369
|
+
// for load failures. So not removing this at this time.
|
|
355
370
|
container.close(err);
|
|
371
|
+
container.dispose(err);
|
|
356
372
|
onClosed(err);
|
|
357
373
|
});
|
|
358
374
|
}), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
|
|
@@ -389,10 +405,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
389
405
|
}
|
|
390
406
|
}
|
|
391
407
|
get closed() {
|
|
392
|
-
return (this._lifecycleState === "closing" ||
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
408
|
+
return (this._lifecycleState === "closing" || this._lifecycleState === "closed" || this.disposed);
|
|
409
|
+
}
|
|
410
|
+
get disposed() {
|
|
411
|
+
return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
|
|
396
412
|
}
|
|
397
413
|
get runtime() {
|
|
398
414
|
if (this._runtime === undefined) {
|
|
@@ -413,7 +429,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
413
429
|
return this;
|
|
414
430
|
}
|
|
415
431
|
get resolvedUrl() {
|
|
416
|
-
var _a;
|
|
417
432
|
/**
|
|
418
433
|
* All attached containers will have a document service,
|
|
419
434
|
* this is required, as attached containers are attached to
|
|
@@ -425,7 +440,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
425
440
|
* is always the same as the containers, as we had to
|
|
426
441
|
* obtain the resolved url, and then create the service from it.
|
|
427
442
|
*/
|
|
428
|
-
return
|
|
443
|
+
return this.service?.resolvedUrl;
|
|
429
444
|
}
|
|
430
445
|
get readOnlyInfo() {
|
|
431
446
|
return this._deltaManager.readOnlyInfo;
|
|
@@ -453,8 +468,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
453
468
|
return this._clientId;
|
|
454
469
|
}
|
|
455
470
|
get offlineLoadEnabled() {
|
|
456
|
-
|
|
457
|
-
|
|
471
|
+
const enabled = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ??
|
|
472
|
+
this.options?.enableOfflineLoad === true;
|
|
458
473
|
// summarizer will not have any pending state we want to save
|
|
459
474
|
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
460
475
|
}
|
|
@@ -491,18 +506,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
491
506
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
492
507
|
*/
|
|
493
508
|
async getEntryPoint() {
|
|
494
|
-
var _a, _b;
|
|
495
509
|
if (this._disposed) {
|
|
496
|
-
throw new
|
|
510
|
+
throw new telemetry_utils_1.UsageError("The context is already disposed");
|
|
497
511
|
}
|
|
498
512
|
if (this._runtime !== undefined) {
|
|
499
|
-
return
|
|
513
|
+
return this._runtime.getEntryPoint?.();
|
|
500
514
|
}
|
|
501
515
|
return new Promise((resolve, reject) => {
|
|
502
516
|
const runtimeInstantiatedHandler = () => {
|
|
503
|
-
|
|
504
|
-
(
|
|
505
|
-
resolve((_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
517
|
+
(0, core_utils_1.assert)(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
|
|
518
|
+
resolve(this._runtime.getEntryPoint?.());
|
|
506
519
|
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
507
520
|
};
|
|
508
521
|
const disposedHandler = () => {
|
|
@@ -532,12 +545,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
532
545
|
this.verifyClosed();
|
|
533
546
|
}
|
|
534
547
|
verifyClosed() {
|
|
535
|
-
(0,
|
|
536
|
-
(0,
|
|
548
|
+
(0, core_utils_1.assert)(this.connectionState === connectionState_1.ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
549
|
+
(0, core_utils_1.assert)(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
|
|
537
550
|
}
|
|
538
551
|
closeCore(error) {
|
|
539
|
-
|
|
540
|
-
(0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
552
|
+
(0, core_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
541
553
|
try {
|
|
542
554
|
// Ensure that we raise all key events even if one of these throws
|
|
543
555
|
try {
|
|
@@ -552,7 +564,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
552
564
|
: "generic",
|
|
553
565
|
}, error);
|
|
554
566
|
this._lifecycleState = "closing";
|
|
555
|
-
|
|
567
|
+
this._protocolHandler?.close();
|
|
556
568
|
this.connectionStateHandler.dispose();
|
|
557
569
|
}
|
|
558
570
|
catch (exception) {
|
|
@@ -572,8 +584,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
572
584
|
}
|
|
573
585
|
}
|
|
574
586
|
disposeCore(error) {
|
|
575
|
-
|
|
576
|
-
(0, common_utils_1.assert)(!this._disposed, 0x54c /* Container already disposed */);
|
|
587
|
+
(0, core_utils_1.assert)(!this._disposed, 0x54c /* Container already disposed */);
|
|
577
588
|
this._disposed = true;
|
|
578
589
|
try {
|
|
579
590
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -589,15 +600,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
589
600
|
if (this._lifecycleState !== "closed") {
|
|
590
601
|
this._lifecycleState = "disposing";
|
|
591
602
|
}
|
|
592
|
-
|
|
603
|
+
this._protocolHandler?.close();
|
|
593
604
|
this.connectionStateHandler.dispose();
|
|
594
605
|
const maybeError = error !== undefined ? new Error(error.message) : undefined;
|
|
595
|
-
|
|
606
|
+
this._runtime?.dispose(maybeError);
|
|
596
607
|
this.storageAdapter.dispose();
|
|
597
608
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
598
609
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
599
610
|
// Driver need to ensure all caches are cleared on critical errors
|
|
600
|
-
|
|
611
|
+
this.service?.dispose(error);
|
|
601
612
|
}
|
|
602
613
|
catch (exception) {
|
|
603
614
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerDisposeException" }, exception);
|
|
@@ -613,45 +624,57 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
613
624
|
this._lifecycleEvents.emit("disposed");
|
|
614
625
|
}
|
|
615
626
|
}
|
|
616
|
-
closeAndGetPendingLocalState() {
|
|
627
|
+
async closeAndGetPendingLocalState() {
|
|
617
628
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
618
629
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
619
630
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
620
|
-
|
|
631
|
+
this.disconnectInternal({ text: "closeAndGetPendingLocalState" }); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
632
|
+
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
621
633
|
this.close();
|
|
622
634
|
return pendingState;
|
|
623
635
|
}
|
|
624
|
-
getPendingLocalState() {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
633
|
-
(0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
634
|
-
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
635
|
-
const pendingState = {
|
|
636
|
-
pendingRuntimeState: this.runtime.getPendingLocalState(),
|
|
637
|
-
baseSnapshot: this.baseSnapshot,
|
|
638
|
-
snapshotBlobs: this.baseSnapshotBlobs,
|
|
639
|
-
savedOps: this.savedOps,
|
|
640
|
-
url: this.resolvedUrl.url,
|
|
641
|
-
term: protocol_1.OnlyValidTermValue,
|
|
636
|
+
async getPendingLocalState() {
|
|
637
|
+
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
638
|
+
}
|
|
639
|
+
async getPendingLocalStateCore(props) {
|
|
640
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, {
|
|
641
|
+
eventName: "getPendingLocalState",
|
|
642
|
+
notifyImminentClosure: props.notifyImminentClosure,
|
|
643
|
+
savedOpsSize: this.savedOps.length,
|
|
642
644
|
clientId: this.clientId,
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
645
|
+
}, async () => {
|
|
646
|
+
if (!this.offlineLoadEnabled) {
|
|
647
|
+
throw new telemetry_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
|
|
648
|
+
}
|
|
649
|
+
if (this.closed || this._disposed) {
|
|
650
|
+
throw new telemetry_utils_1.UsageError("Pending state cannot be retried if the container is closed or disposed");
|
|
651
|
+
}
|
|
652
|
+
(0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
653
|
+
(0, core_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
654
|
+
(0, core_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
655
|
+
(0, core_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
656
|
+
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
657
|
+
const pendingState = {
|
|
658
|
+
pendingRuntimeState,
|
|
659
|
+
baseSnapshot: this.baseSnapshot,
|
|
660
|
+
snapshotBlobs: this.baseSnapshotBlobs,
|
|
661
|
+
savedOps: this.savedOps,
|
|
662
|
+
url: this.resolvedUrl.url,
|
|
663
|
+
term: protocol_1.OnlyValidTermValue,
|
|
664
|
+
// no need to save this if there is no pending runtime state
|
|
665
|
+
clientId: pendingRuntimeState !== undefined ? this.clientId : undefined,
|
|
666
|
+
};
|
|
667
|
+
return JSON.stringify(pendingState);
|
|
668
|
+
});
|
|
646
669
|
}
|
|
647
670
|
get attachState() {
|
|
648
671
|
return this._attachState;
|
|
649
672
|
}
|
|
650
673
|
serialize() {
|
|
651
|
-
(0,
|
|
674
|
+
(0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
|
|
652
675
|
const appSummary = this.runtime.createSummary();
|
|
653
676
|
const protocolSummary = this.captureProtocolSummary();
|
|
654
|
-
const combinedSummary = (0,
|
|
677
|
+
const combinedSummary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
655
678
|
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
656
679
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
657
680
|
type: protocol_definitions_1.SummaryType.Blob,
|
|
@@ -660,27 +683,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
660
683
|
}
|
|
661
684
|
return JSON.stringify(combinedSummary);
|
|
662
685
|
}
|
|
663
|
-
async attach(request) {
|
|
686
|
+
async attach(request, attachProps) {
|
|
664
687
|
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
665
|
-
var _a;
|
|
666
688
|
if (this._lifecycleState !== "loaded") {
|
|
667
689
|
// pre-0.58 error message: containerNotValidForAttach
|
|
668
|
-
throw new
|
|
690
|
+
throw new telemetry_utils_1.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
|
|
669
691
|
}
|
|
670
692
|
// If container is already attached or attach is in progress, throw an error.
|
|
671
|
-
(0,
|
|
693
|
+
(0, core_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
672
694
|
this.attachStarted = true;
|
|
673
695
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
674
696
|
const hasAttachmentBlobs = this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
675
697
|
try {
|
|
676
|
-
(0,
|
|
698
|
+
(0, core_utils_1.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
677
699
|
let summary;
|
|
678
700
|
if (!hasAttachmentBlobs) {
|
|
679
701
|
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
680
702
|
// semantics around what the attach means as far as async code goes.
|
|
681
703
|
const appSummary = this.runtime.createSummary();
|
|
682
704
|
const protocolSummary = this.captureProtocolSummary();
|
|
683
|
-
summary = (0,
|
|
705
|
+
summary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
684
706
|
// Set the state as attaching as we are starting the process of attaching container.
|
|
685
707
|
// This should be fired after taking the summary because it is the place where we are
|
|
686
708
|
// starting to attach the container to storage.
|
|
@@ -698,7 +720,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
698
720
|
// Actually go and create the resolved document
|
|
699
721
|
if (this.service === undefined) {
|
|
700
722
|
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
701
|
-
(0,
|
|
723
|
+
(0, core_utils_1.assert)(this.client.details.type !== summarizerClientType &&
|
|
702
724
|
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
703
725
|
this.service = await (0, driver_utils_1.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
704
726
|
cancel: this._deltaManager.closeAbortController.signal,
|
|
@@ -707,7 +729,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
707
729
|
await this.storageAdapter.connectToService(this.service);
|
|
708
730
|
if (hasAttachmentBlobs) {
|
|
709
731
|
// upload blobs to storage
|
|
710
|
-
(0,
|
|
732
|
+
(0, core_utils_1.assert)(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
711
733
|
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
712
734
|
// support blob handles that only know about the local IDs
|
|
713
735
|
const redirectTable = new Map();
|
|
@@ -725,7 +747,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
725
747
|
// take summary and upload
|
|
726
748
|
const appSummary = this.runtime.createSummary(redirectTable);
|
|
727
749
|
const protocolSummary = this.captureProtocolSummary();
|
|
728
|
-
summary = (0,
|
|
750
|
+
summary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
729
751
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
730
752
|
this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
|
|
731
753
|
this.emit("attaching");
|
|
@@ -745,16 +767,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
745
767
|
this.runtime.setAttachState(container_definitions_1.AttachState.Attached);
|
|
746
768
|
this.emit("attached");
|
|
747
769
|
if (!this.closed) {
|
|
748
|
-
this.
|
|
770
|
+
this.handleDeltaConnectionArg({
|
|
749
771
|
fetchOpsFromStorage: false,
|
|
750
|
-
reason: "createDetached",
|
|
751
|
-
});
|
|
772
|
+
reason: { text: "createDetached" },
|
|
773
|
+
}, attachProps?.deltaConnection);
|
|
752
774
|
}
|
|
753
775
|
}
|
|
754
776
|
catch (error) {
|
|
755
777
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
756
778
|
const newError = (0, telemetry_utils_1.normalizeError)(error);
|
|
757
|
-
newError.addTelemetryProperties({ resolvedUrl:
|
|
779
|
+
newError.addTelemetryProperties({ resolvedUrl: this.resolvedUrl?.url });
|
|
758
780
|
this.close(newError);
|
|
759
781
|
throw newError;
|
|
760
782
|
}
|
|
@@ -763,12 +785,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
763
785
|
async request(path) {
|
|
764
786
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
|
|
765
787
|
}
|
|
766
|
-
setAutoReconnectInternal(mode) {
|
|
788
|
+
setAutoReconnectInternal(mode, reason) {
|
|
767
789
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
768
790
|
if (currentMode === mode) {
|
|
769
791
|
return;
|
|
770
792
|
}
|
|
771
|
-
const now =
|
|
793
|
+
const now = client_utils_1.performance.now();
|
|
772
794
|
const duration = now - this.setAutoReconnectTime;
|
|
773
795
|
this.setAutoReconnectTime = now;
|
|
774
796
|
this.mc.logger.sendTelemetryEvent({
|
|
@@ -777,47 +799,50 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
777
799
|
connectionState: connectionState_1.ConnectionState[this.connectionState],
|
|
778
800
|
duration,
|
|
779
801
|
});
|
|
780
|
-
this._deltaManager.connectionManager.setAutoReconnect(mode);
|
|
802
|
+
this._deltaManager.connectionManager.setAutoReconnect(mode, reason);
|
|
781
803
|
}
|
|
782
804
|
connect() {
|
|
783
805
|
if (this.closed) {
|
|
784
|
-
throw new
|
|
806
|
+
throw new telemetry_utils_1.UsageError(`The Container is closed and cannot be connected`);
|
|
785
807
|
}
|
|
786
808
|
else if (this._attachState !== container_definitions_1.AttachState.Attached) {
|
|
787
|
-
throw new
|
|
809
|
+
throw new telemetry_utils_1.UsageError(`The Container is not attached and cannot be connected`);
|
|
788
810
|
}
|
|
789
811
|
else if (!this.connected) {
|
|
790
812
|
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
791
813
|
// If there is gap, we will learn about it once connected, but the gap should be small (if any),
|
|
792
814
|
// assuming that connect() is called quickly after initial container boot.
|
|
793
|
-
this.connectInternal({
|
|
815
|
+
this.connectInternal({
|
|
816
|
+
reason: { text: "DocumentConnect" },
|
|
817
|
+
fetchOpsFromStorage: false,
|
|
818
|
+
});
|
|
794
819
|
}
|
|
795
820
|
}
|
|
796
821
|
connectInternal(args) {
|
|
797
|
-
(0,
|
|
798
|
-
(0,
|
|
822
|
+
(0, core_utils_1.assert)(!this.closed, 0x2c5 /* "Attempting to connect() a closed Container" */);
|
|
823
|
+
(0, core_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Attached, 0x2c6 /* "Attempting to connect() a container that is not attached" */);
|
|
799
824
|
// Resume processing ops and connect to delta stream
|
|
800
825
|
this.resumeInternal(args);
|
|
801
826
|
// Set Auto Reconnect Mode
|
|
802
827
|
const mode = contracts_1.ReconnectMode.Enabled;
|
|
803
|
-
this.setAutoReconnectInternal(mode);
|
|
828
|
+
this.setAutoReconnectInternal(mode, args.reason);
|
|
804
829
|
}
|
|
805
830
|
disconnect() {
|
|
806
831
|
if (this.closed) {
|
|
807
|
-
throw new
|
|
832
|
+
throw new telemetry_utils_1.UsageError(`The Container is closed and cannot be disconnected`);
|
|
808
833
|
}
|
|
809
834
|
else {
|
|
810
|
-
this.disconnectInternal();
|
|
835
|
+
this.disconnectInternal({ text: "DocumentDisconnect" });
|
|
811
836
|
}
|
|
812
837
|
}
|
|
813
|
-
disconnectInternal() {
|
|
814
|
-
(0,
|
|
838
|
+
disconnectInternal(reason) {
|
|
839
|
+
(0, core_utils_1.assert)(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
|
|
815
840
|
// Set Auto Reconnect Mode
|
|
816
841
|
const mode = contracts_1.ReconnectMode.Disabled;
|
|
817
|
-
this.setAutoReconnectInternal(mode);
|
|
842
|
+
this.setAutoReconnectInternal(mode, reason);
|
|
818
843
|
}
|
|
819
844
|
resumeInternal(args) {
|
|
820
|
-
(0,
|
|
845
|
+
(0, core_utils_1.assert)(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
|
|
821
846
|
// Resume processing ops
|
|
822
847
|
if (this.inboundQueuePausedFromInit) {
|
|
823
848
|
this.inboundQueuePausedFromInit = false;
|
|
@@ -854,14 +879,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
854
879
|
return;
|
|
855
880
|
}
|
|
856
881
|
// pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
|
|
857
|
-
const error = new
|
|
882
|
+
const error = new telemetry_utils_1.GenericError("Existing context does not satisfy incoming proposal");
|
|
858
883
|
this.close(error);
|
|
859
884
|
}
|
|
860
885
|
/**
|
|
861
886
|
* Determines if the currently loaded module satisfies the incoming constraint code details
|
|
862
887
|
*/
|
|
863
888
|
async satisfies(constraintCodeDetails) {
|
|
864
|
-
var _a, _b;
|
|
865
889
|
// If we have no module, it can't satisfy anything.
|
|
866
890
|
if (this._loadedModule === undefined) {
|
|
867
891
|
return false;
|
|
@@ -871,8 +895,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
871
895
|
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
872
896
|
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
873
897
|
}
|
|
874
|
-
const maybeCompareExport =
|
|
875
|
-
if (
|
|
898
|
+
const maybeCompareExport = this._loadedModule?.module.fluidExport;
|
|
899
|
+
if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
|
|
876
900
|
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
877
901
|
}
|
|
878
902
|
// If there are no comparers, then it's impossible to know if the currently loaded package satisfies
|
|
@@ -882,7 +906,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
882
906
|
return false;
|
|
883
907
|
}
|
|
884
908
|
for (const comparer of comparers) {
|
|
885
|
-
const satisfies = await comparer.satisfies(
|
|
909
|
+
const satisfies = await comparer.satisfies(this._loadedModule?.details, constraintCodeDetails);
|
|
886
910
|
if (satisfies === false) {
|
|
887
911
|
return false;
|
|
888
912
|
}
|
|
@@ -905,8 +929,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
905
929
|
*
|
|
906
930
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
907
931
|
*/
|
|
908
|
-
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
909
|
-
|
|
932
|
+
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState, loadToSequenceNumber) {
|
|
933
|
+
const timings = { phase1: client_utils_1.performance.now() };
|
|
910
934
|
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
911
935
|
// Ideally we always connect as "read" by default.
|
|
912
936
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
@@ -918,7 +942,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
918
942
|
// A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
|
|
919
943
|
// B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
|
|
920
944
|
const connectionArgs = {
|
|
921
|
-
reason: "DocumentOpen",
|
|
945
|
+
reason: { text: "DocumentOpen" },
|
|
922
946
|
mode: "write",
|
|
923
947
|
fetchOpsFromStorage: false,
|
|
924
948
|
};
|
|
@@ -937,6 +961,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
937
961
|
});
|
|
938
962
|
}
|
|
939
963
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
964
|
+
timings.phase2 = client_utils_1.performance.now();
|
|
940
965
|
// Fetch specified snapshot.
|
|
941
966
|
const { snapshot, versionId } = pendingLocalState === undefined
|
|
942
967
|
? await this.fetchSnapshotTree(specifiedVersion)
|
|
@@ -946,7 +971,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
946
971
|
this.baseSnapshotBlobs = pendingLocalState.snapshotBlobs;
|
|
947
972
|
}
|
|
948
973
|
else {
|
|
949
|
-
(0,
|
|
974
|
+
(0, core_utils_1.assert)(snapshot !== undefined, 0x237 /* "Snapshot should exist" */);
|
|
950
975
|
if (this.offlineLoadEnabled) {
|
|
951
976
|
this.baseSnapshot = snapshot;
|
|
952
977
|
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
@@ -955,9 +980,51 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
955
980
|
}
|
|
956
981
|
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
957
982
|
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
958
|
-
const sequenceNumber =
|
|
959
|
-
const dmAttributes = sequenceNumber !== undefined ?
|
|
983
|
+
const sequenceNumber = pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber;
|
|
984
|
+
const dmAttributes = sequenceNumber !== undefined ? { ...attributes, sequenceNumber } : attributes;
|
|
960
985
|
let opsBeforeReturnP;
|
|
986
|
+
if (loadMode.pauseAfterLoad === true) {
|
|
987
|
+
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
988
|
+
if (loadMode.opsBeforeReturn === "sequenceNumber") {
|
|
989
|
+
(0, core_utils_1.assert)(loadToSequenceNumber !== undefined, 0x727 /* sequenceNumber should be defined */);
|
|
990
|
+
// Note: It is possible that we think the latest snapshot is newer than the specified sequence number
|
|
991
|
+
// due to saved ops that may be replayed after the snapshot.
|
|
992
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
|
|
993
|
+
if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
|
|
994
|
+
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.");
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
// Force readonly mode - this will ensure we don't receive an error for the lack of join op
|
|
998
|
+
this.forceReadonly(true);
|
|
999
|
+
// We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
|
|
1000
|
+
const opHandler = () => {
|
|
1001
|
+
if (loadToSequenceNumber === undefined) {
|
|
1002
|
+
// If there is no specified sequence number, pause after the inbound queue is empty.
|
|
1003
|
+
if (this.deltaManager.inbound.length !== 0) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
// If there is a specified sequence number, keep processing until we reach it.
|
|
1009
|
+
if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
// Pause op processing once we have processed the desired number of ops.
|
|
1014
|
+
void this.deltaManager.inbound.pause();
|
|
1015
|
+
void this.deltaManager.outbound.pause();
|
|
1016
|
+
this.off("op", opHandler);
|
|
1017
|
+
};
|
|
1018
|
+
if ((loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
|
|
1019
|
+
this.deltaManager.lastSequenceNumber === loadToSequenceNumber) {
|
|
1020
|
+
// If we have already reached the desired sequence number, call opHandler() to pause immediately.
|
|
1021
|
+
opHandler();
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
// If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
|
|
1025
|
+
this.on("op", opHandler);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
961
1028
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
962
1029
|
// Kick off any ops fetching if required.
|
|
963
1030
|
switch (loadMode.opsBeforeReturn) {
|
|
@@ -966,32 +1033,30 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
966
1033
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
967
1034
|
this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
|
|
968
1035
|
break;
|
|
1036
|
+
case "sequenceNumber":
|
|
969
1037
|
case "cached":
|
|
970
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
971
|
-
break;
|
|
972
1038
|
case "all":
|
|
973
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes,
|
|
1039
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, loadMode.opsBeforeReturn);
|
|
974
1040
|
break;
|
|
975
1041
|
default:
|
|
976
|
-
(0,
|
|
1042
|
+
(0, core_utils_1.unreachableCase)(loadMode.opsBeforeReturn);
|
|
977
1043
|
}
|
|
978
1044
|
// ...load in the existing quorum
|
|
979
1045
|
// Initialize the protocol handler
|
|
980
1046
|
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
1047
|
+
timings.phase3 = client_utils_1.performance.now();
|
|
981
1048
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
982
|
-
await this.
|
|
983
|
-
|
|
1049
|
+
await this.instantiateRuntime(codeDetails, snapshot,
|
|
1050
|
+
// give runtime a dummy value so it knows we're loading from a stash blob
|
|
1051
|
+
pendingLocalState ? pendingLocalState?.pendingRuntimeState ?? {} : undefined);
|
|
984
1052
|
// replay saved ops
|
|
985
1053
|
if (pendingLocalState) {
|
|
986
1054
|
for (const message of pendingLocalState.savedOps) {
|
|
987
1055
|
this.processRemoteMessage(message);
|
|
988
1056
|
// allow runtime to apply stashed ops at this op's sequence number
|
|
989
|
-
await
|
|
1057
|
+
await this.runtime.notifyOpReplay?.(message);
|
|
990
1058
|
}
|
|
991
1059
|
pendingLocalState.savedOps = [];
|
|
992
|
-
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
993
|
-
(0, common_utils_1.assert)(this.clientId === undefined, 0x5d6 /* Unexpected clientId when setting stashed clientId */);
|
|
994
|
-
this._clientId = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId;
|
|
995
1060
|
}
|
|
996
1061
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
997
1062
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
@@ -1003,24 +1068,20 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1003
1068
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1004
1069
|
this._deltaManager.inbound.pause();
|
|
1005
1070
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1071
|
+
this.handleDeltaConnectionArg(connectionArgs, loadMode.deltaConnection, pendingLocalState !== undefined);
|
|
1072
|
+
}
|
|
1073
|
+
// If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
|
|
1074
|
+
if (loadToSequenceNumber !== undefined &&
|
|
1075
|
+
this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
1076
|
+
await new Promise((resolve, reject) => {
|
|
1077
|
+
const opHandler = (message) => {
|
|
1078
|
+
if (message.sequenceNumber >= loadToSequenceNumber) {
|
|
1079
|
+
resolve();
|
|
1080
|
+
this.off("op", opHandler);
|
|
1011
1081
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
this.inboundQueuePausedFromInit = false;
|
|
1016
|
-
this._deltaManager.inbound.resume();
|
|
1017
|
-
this._deltaManager.inboundSignal.resume();
|
|
1018
|
-
break;
|
|
1019
|
-
case "none":
|
|
1020
|
-
break;
|
|
1021
|
-
default:
|
|
1022
|
-
(0, common_utils_1.unreachableCase)(loadMode.deltaConnection);
|
|
1023
|
-
}
|
|
1082
|
+
};
|
|
1083
|
+
this.on("op", opHandler);
|
|
1084
|
+
});
|
|
1024
1085
|
}
|
|
1025
1086
|
// Safety net: static version of Container.load() should have learned about it through "closed" handler.
|
|
1026
1087
|
// But if that did not happen for some reason, fail load for sure.
|
|
@@ -1032,6 +1093,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1032
1093
|
}
|
|
1033
1094
|
// Internal context is fully loaded at this point
|
|
1034
1095
|
this.setLoaded();
|
|
1096
|
+
timings.end = client_utils_1.performance.now();
|
|
1097
|
+
this.subLogger.sendTelemetryEvent({
|
|
1098
|
+
eventName: "LoadStagesTimings",
|
|
1099
|
+
details: JSON.stringify(timings),
|
|
1100
|
+
}, undefined, core_interfaces_1.LogLevel.verbose);
|
|
1035
1101
|
return {
|
|
1036
1102
|
sequenceNumber: attributes.sequenceNumber,
|
|
1037
1103
|
version: versionId,
|
|
@@ -1039,7 +1105,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1039
1105
|
dmLastKnownSeqNumber: this._deltaManager.lastKnownSeqNumber,
|
|
1040
1106
|
};
|
|
1041
1107
|
}
|
|
1042
|
-
async createDetached(
|
|
1108
|
+
async createDetached(codeDetails) {
|
|
1043
1109
|
const attributes = {
|
|
1044
1110
|
sequenceNumber: detachedContainerRefSeqNumber,
|
|
1045
1111
|
term: protocol_1.OnlyValidTermValue,
|
|
@@ -1047,19 +1113,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1047
1113
|
};
|
|
1048
1114
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1049
1115
|
// Need to just seed the source data in the code quorum. Quorum itself is empty
|
|
1050
|
-
const qValues = (0, quorum_1.initQuorumValuesFromCodeDetails)(
|
|
1116
|
+
const qValues = (0, quorum_1.initQuorumValuesFromCodeDetails)(codeDetails);
|
|
1051
1117
|
this.initializeProtocolState(attributes, {
|
|
1052
1118
|
members: [],
|
|
1053
1119
|
proposals: [],
|
|
1054
1120
|
values: qValues,
|
|
1055
1121
|
});
|
|
1056
|
-
|
|
1057
|
-
await this.instantiateContextDetached(false);
|
|
1122
|
+
await this.instantiateRuntime(codeDetails, undefined);
|
|
1058
1123
|
this.setLoaded();
|
|
1059
1124
|
}
|
|
1060
1125
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
1061
1126
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
1062
|
-
(0,
|
|
1127
|
+
(0, core_utils_1.assert)(!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1063
1128
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
1064
1129
|
}
|
|
1065
1130
|
const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
|
|
@@ -1069,14 +1134,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1069
1134
|
// Initialize the protocol handler
|
|
1070
1135
|
const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshotTree);
|
|
1071
1136
|
const qValues = await (0, driver_utils_1.readAndParse)(this.storageAdapter, baseTree.blobs.quorumValues);
|
|
1072
|
-
const codeDetails = (0, quorum_1.getCodeDetailsFromQuorumValues)(qValues);
|
|
1073
1137
|
this.initializeProtocolState(attributes, {
|
|
1074
1138
|
members: [],
|
|
1075
1139
|
proposals: [],
|
|
1076
|
-
values:
|
|
1140
|
+
values: qValues,
|
|
1077
1141
|
});
|
|
1078
|
-
|
|
1079
|
-
snapshotTree);
|
|
1142
|
+
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1143
|
+
await this.instantiateRuntime(codeDetails, snapshotTree);
|
|
1080
1144
|
this.setLoaded();
|
|
1081
1145
|
}
|
|
1082
1146
|
async getDocumentAttributes(storage, tree) {
|
|
@@ -1113,7 +1177,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1113
1177
|
}
|
|
1114
1178
|
initializeProtocolState(attributes, quorumSnapshot) {
|
|
1115
1179
|
const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })));
|
|
1116
|
-
const protocolLogger =
|
|
1180
|
+
const protocolLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
1181
|
+
logger: this.subLogger,
|
|
1182
|
+
namespace: "ProtocolHandler",
|
|
1183
|
+
});
|
|
1117
1184
|
protocol.quorum.on("error", (error) => {
|
|
1118
1185
|
protocolLogger.sendErrorEvent(error);
|
|
1119
1186
|
});
|
|
@@ -1175,8 +1242,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1175
1242
|
return pkg;
|
|
1176
1243
|
}
|
|
1177
1244
|
get client() {
|
|
1178
|
-
|
|
1179
|
-
const client = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.client) !== undefined
|
|
1245
|
+
const client = this.options?.client !== undefined
|
|
1180
1246
|
? this.options.client
|
|
1181
1247
|
: {
|
|
1182
1248
|
details: {
|
|
@@ -1207,14 +1273,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1207
1273
|
}
|
|
1208
1274
|
createDeltaManager() {
|
|
1209
1275
|
const serviceProvider = () => this.service;
|
|
1210
|
-
const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, telemetry_utils_1.
|
|
1276
|
+
const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, (0, telemetry_utils_1.createChildLogger)({ logger: this.subLogger, namespace: "DeltaManager" }), () => this.activeConnection(), (props) => new connectionManager_1.ConnectionManager(serviceProvider, () => this.isDirty, this.client, this._canReconnect, (0, telemetry_utils_1.createChildLogger)({ logger: this.subLogger, namespace: "ConnectionManager" }), props));
|
|
1211
1277
|
// Disable inbound queues as Container is not ready to accept any ops until we are fully loaded!
|
|
1212
1278
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1213
1279
|
deltaManager.inbound.pause();
|
|
1214
1280
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1215
1281
|
deltaManager.inboundSignal.pause();
|
|
1216
1282
|
deltaManager.on("connect", (details, _opsBehind) => {
|
|
1217
|
-
(0,
|
|
1283
|
+
(0, core_utils_1.assert)(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
|
|
1218
1284
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1219
1285
|
});
|
|
1220
1286
|
deltaManager.on("establishingConnection", (reason) => {
|
|
@@ -1223,11 +1289,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1223
1289
|
deltaManager.on("cancelEstablishingConnection", (reason) => {
|
|
1224
1290
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1225
1291
|
});
|
|
1226
|
-
deltaManager.on("disconnect", (reason
|
|
1227
|
-
|
|
1228
|
-
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
|
|
1292
|
+
deltaManager.on("disconnect", (reason) => {
|
|
1293
|
+
this.noopHeuristic?.notifyDisconnect();
|
|
1229
1294
|
if (!this.closed) {
|
|
1230
|
-
this.connectionStateHandler.receivedDisconnectEvent(reason
|
|
1295
|
+
this.connectionStateHandler.receivedDisconnectEvent(reason);
|
|
1231
1296
|
}
|
|
1232
1297
|
});
|
|
1233
1298
|
deltaManager.on("throttled", (warning) => {
|
|
@@ -1259,10 +1324,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1259
1324
|
},
|
|
1260
1325
|
}, prefetchType);
|
|
1261
1326
|
}
|
|
1262
|
-
logConnectionStateChangeTelemetry(value, oldState, reason
|
|
1263
|
-
var _a;
|
|
1327
|
+
logConnectionStateChangeTelemetry(value, oldState, reason) {
|
|
1264
1328
|
// Log actual event
|
|
1265
|
-
const time =
|
|
1329
|
+
const time = client_utils_1.performance.now();
|
|
1266
1330
|
this.connectionTransitionTimes[value] = time;
|
|
1267
1331
|
const duration = time - this.connectionTransitionTimes[oldState];
|
|
1268
1332
|
let durationFromDisconnected;
|
|
@@ -1277,7 +1341,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1277
1341
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
1278
1342
|
durationFromDisconnected =
|
|
1279
1343
|
time - this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected];
|
|
1280
|
-
durationFromDisconnected = telemetry_utils_1.
|
|
1344
|
+
durationFromDisconnected = (0, telemetry_utils_1.formatTick)(durationFromDisconnected);
|
|
1281
1345
|
}
|
|
1282
1346
|
else if (value === connectionState_1.ConnectionState.CatchingUp) {
|
|
1283
1347
|
// This info is of most interesting while Catching Up.
|
|
@@ -1290,19 +1354,31 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1290
1354
|
}
|
|
1291
1355
|
connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
|
|
1292
1356
|
}
|
|
1293
|
-
this.mc.logger.sendPerformanceEvent(
|
|
1357
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1358
|
+
eventName: `ConnectionStateChange_${connectionState_1.ConnectionState[value]}`,
|
|
1359
|
+
from: connectionState_1.ConnectionState[oldState],
|
|
1360
|
+
duration,
|
|
1294
1361
|
durationFromDisconnected,
|
|
1295
|
-
reason,
|
|
1296
|
-
connectionInitiationReason,
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1362
|
+
reason: reason?.text,
|
|
1363
|
+
connectionInitiationReason,
|
|
1364
|
+
pendingClientId: this.connectionStateHandler.pendingClientId,
|
|
1365
|
+
clientId: this.clientId,
|
|
1366
|
+
autoReconnect,
|
|
1367
|
+
opsBehind,
|
|
1368
|
+
online: driver_utils_1.OnlineStatus[(0, driver_utils_1.isOnline)()],
|
|
1369
|
+
lastVisible: this.lastVisible !== undefined
|
|
1370
|
+
? client_utils_1.performance.now() - this.lastVisible
|
|
1371
|
+
: undefined,
|
|
1372
|
+
checkpointSequenceNumber,
|
|
1373
|
+
quorumSize: this._protocolHandler?.quorum.getMembers().size,
|
|
1374
|
+
isDirty: this.isDirty,
|
|
1375
|
+
...this._deltaManager.connectionProps,
|
|
1376
|
+
}, reason?.error);
|
|
1300
1377
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
1301
1378
|
this.firstConnection = false;
|
|
1302
1379
|
}
|
|
1303
1380
|
}
|
|
1304
1381
|
propagateConnectionState(initialTransition, disconnectedReason) {
|
|
1305
|
-
var _a;
|
|
1306
1382
|
// When container loaded, we want to propagate initial connection state.
|
|
1307
1383
|
// After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
1308
1384
|
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
@@ -1312,22 +1388,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1312
1388
|
return;
|
|
1313
1389
|
}
|
|
1314
1390
|
const state = this.connectionState === connectionState_1.ConnectionState.Connected;
|
|
1315
|
-
const logOpsOnReconnect = this.connectionState === connectionState_1.ConnectionState.Connected &&
|
|
1316
|
-
!this.firstConnection &&
|
|
1317
|
-
this.connectionMode === "write";
|
|
1318
|
-
if (logOpsOnReconnect) {
|
|
1319
|
-
this.messageCountAfterDisconnection = 0;
|
|
1320
|
-
}
|
|
1321
1391
|
// Both protocol and context should not be undefined if we got so far.
|
|
1322
|
-
this.setContextConnectedState(state,
|
|
1392
|
+
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
1323
1393
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1324
|
-
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
1325
|
-
if (logOpsOnReconnect) {
|
|
1326
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1327
|
-
eventName: "OpsSentOnReconnect",
|
|
1328
|
-
count: this.messageCountAfterDisconnection,
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1394
|
+
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId, disconnectedReason?.text);
|
|
1331
1395
|
}
|
|
1332
1396
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1333
1397
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
@@ -1337,7 +1401,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1337
1401
|
case protocol_definitions_1.MessageType.Summarize:
|
|
1338
1402
|
return this.submitSummaryMessage(contents);
|
|
1339
1403
|
default: {
|
|
1340
|
-
const newError = new
|
|
1404
|
+
const newError = new telemetry_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
|
|
1341
1405
|
this.close(newError);
|
|
1342
1406
|
return -1;
|
|
1343
1407
|
}
|
|
@@ -1365,13 +1429,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1365
1429
|
return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch */, undefined /* metadata */, undefined /* compression */, referenceSequenceNumber);
|
|
1366
1430
|
}
|
|
1367
1431
|
submitMessage(type, contents, batch, metadata, compression, referenceSequenceNumber) {
|
|
1368
|
-
var _a;
|
|
1369
1432
|
if (this.connectionState !== connectionState_1.ConnectionState.Connected) {
|
|
1370
1433
|
this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
|
|
1371
1434
|
return -1;
|
|
1372
1435
|
}
|
|
1373
|
-
this.
|
|
1374
|
-
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
|
|
1436
|
+
this.noopHeuristic?.notifyMessageSent();
|
|
1375
1437
|
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1376
1438
|
}
|
|
1377
1439
|
processRemoteMessage(message) {
|
|
@@ -1379,23 +1441,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1379
1441
|
this.savedOps.push(message);
|
|
1380
1442
|
}
|
|
1381
1443
|
const local = this.clientId === message.clientId;
|
|
1382
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
1383
|
-
// flagged should have left, or from a client that's not in the quorum but should be
|
|
1384
|
-
if (message.clientId != null) {
|
|
1385
|
-
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
1386
|
-
if (client === undefined && message.type !== protocol_definitions_1.MessageType.ClientJoin) {
|
|
1387
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1388
|
-
throw new Error("Remote message's clientId is missing from the quorum");
|
|
1389
|
-
}
|
|
1390
|
-
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
1391
|
-
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
1392
|
-
// document we don't need to blow up aggressively.
|
|
1393
|
-
if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
1394
|
-
!(0, driver_utils_1.canBeCoalescedByService)(message)) {
|
|
1395
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1396
|
-
throw new Error("Remote message's clientId already should have left");
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
1444
|
// Allow the protocol handler to process the message
|
|
1400
1445
|
const result = this.protocolHandler.processMessage(message, local);
|
|
1401
1446
|
// Forward messages to the loaded runtime for processing
|
|
@@ -1408,13 +1453,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1408
1453
|
// That means that if relay service changes settings, such changes will impact only newly booted
|
|
1409
1454
|
// clients.
|
|
1410
1455
|
// All existing will continue to use settings they got earlier.
|
|
1411
|
-
(0,
|
|
1456
|
+
(0, core_utils_1.assert)(serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1412
1457
|
this.noopHeuristic = new noopHeuristic_1.NoopHeuristic(serviceConfiguration.noopTimeFrequency, serviceConfiguration.noopCountFrequency);
|
|
1413
1458
|
this.noopHeuristic.on("wantsNoop", () => {
|
|
1414
1459
|
// On disconnect we notify the heuristic which should prevent it from wanting a noop.
|
|
1415
1460
|
// Hitting this assert would imply we lost activeConnection between notifying the heuristic of a processed message and
|
|
1416
1461
|
// running the microtask that the heuristic queued in response.
|
|
1417
|
-
(0,
|
|
1462
|
+
(0, core_utils_1.assert)(this.activeConnection(), 0x241 /* "Trying to send noop without active connection" */);
|
|
1418
1463
|
this.submitMessage(protocol_definitions_1.MessageType.NoOp);
|
|
1419
1464
|
});
|
|
1420
1465
|
}
|
|
@@ -1446,8 +1491,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1446
1491
|
* @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
|
|
1447
1492
|
*/
|
|
1448
1493
|
async fetchSnapshotTree(specifiedVersion) {
|
|
1449
|
-
|
|
1450
|
-
const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : null);
|
|
1494
|
+
const version = await this.getVersion(specifiedVersion ?? null);
|
|
1451
1495
|
if (version === undefined && specifiedVersion !== undefined) {
|
|
1452
1496
|
// We should have a defined version to load from if specified version requested
|
|
1453
1497
|
this.mc.logger.sendErrorEvent({
|
|
@@ -1456,22 +1500,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1456
1500
|
});
|
|
1457
1501
|
}
|
|
1458
1502
|
this._loadedFromVersion = version;
|
|
1459
|
-
const snapshot = (
|
|
1503
|
+
const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
|
|
1460
1504
|
if (snapshot === undefined && version !== undefined) {
|
|
1461
1505
|
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
1462
1506
|
}
|
|
1463
|
-
return { snapshot, versionId: version
|
|
1507
|
+
return { snapshot, versionId: version?.id };
|
|
1464
1508
|
}
|
|
1465
|
-
async
|
|
1466
|
-
|
|
1467
|
-
if (codeDetails === undefined) {
|
|
1468
|
-
throw new Error("pkg should be provided in create flow!!");
|
|
1469
|
-
}
|
|
1470
|
-
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
1471
|
-
}
|
|
1472
|
-
async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
|
|
1473
|
-
var _a, _b;
|
|
1474
|
-
(0, common_utils_1.assert)(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1509
|
+
async instantiateRuntime(codeDetails, snapshot, pendingLocalState) {
|
|
1510
|
+
(0, core_utils_1.assert)(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1475
1511
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1476
1512
|
// are set. Global requests will still go directly to the loader
|
|
1477
1513
|
const maybeLoader = this.scope;
|
|
@@ -1482,25 +1518,20 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1482
1518
|
// An older interface ICodeLoader could return an IFluidModule which didn't have details.
|
|
1483
1519
|
// If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
|
|
1484
1520
|
// TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
|
|
1485
|
-
details:
|
|
1521
|
+
details: loadCodeResult.details ?? codeDetails,
|
|
1486
1522
|
};
|
|
1487
1523
|
const fluidExport = this._loadedModule.module.fluidExport;
|
|
1488
|
-
const runtimeFactory = fluidExport
|
|
1524
|
+
const runtimeFactory = fluidExport?.IRuntimeFactory;
|
|
1489
1525
|
if (runtimeFactory === undefined) {
|
|
1490
1526
|
throw new Error(packageNotFactoryError);
|
|
1491
1527
|
}
|
|
1492
|
-
const getSpecifiedCodeDetails = () =>
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
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, () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; }, () => this.clientId, () => this._deltaManager.serviceConfiguration, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1497
|
-
this._lifecycleEvents.once("disposed", () => {
|
|
1498
|
-
context.dispose();
|
|
1499
|
-
});
|
|
1528
|
+
const getSpecifiedCodeDetails = () => (this.protocolHandler.quorum.get("code") ??
|
|
1529
|
+
this.protocolHandler.quorum.get("code2"));
|
|
1530
|
+
const existing = snapshot !== undefined;
|
|
1531
|
+
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.resolvedUrl?.id, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1500
1532
|
this._runtime = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1501
1533
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1502
1534
|
this._loadedCodeDetails = codeDetails;
|
|
1503
|
-
this.emit("contextChanged", codeDetails);
|
|
1504
1535
|
}
|
|
1505
1536
|
/**
|
|
1506
1537
|
* Set the connected state of the ContainerContext
|
|
@@ -1509,8 +1540,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1509
1540
|
* @param readonly - Is the container in readonly mode?
|
|
1510
1541
|
*/
|
|
1511
1542
|
setContextConnectedState(state, readonly) {
|
|
1512
|
-
|
|
1513
|
-
if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1543
|
+
if (this._runtime?.disposed === false) {
|
|
1514
1544
|
/**
|
|
1515
1545
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1516
1546
|
* ops getting through to the DeltaManager.
|
|
@@ -1520,6 +1550,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1520
1550
|
this.runtime.setConnectionState(state && !readonly, this.clientId);
|
|
1521
1551
|
}
|
|
1522
1552
|
}
|
|
1553
|
+
handleDeltaConnectionArg(connectionArgs, deltaConnectionArg, canConnect = true) {
|
|
1554
|
+
switch (deltaConnectionArg) {
|
|
1555
|
+
case undefined:
|
|
1556
|
+
if (canConnect) {
|
|
1557
|
+
// connect to delta stream now since we did not before
|
|
1558
|
+
this.connectToDeltaStream(connectionArgs);
|
|
1559
|
+
}
|
|
1560
|
+
// intentional fallthrough
|
|
1561
|
+
case "delayed":
|
|
1562
|
+
(0, core_utils_1.assert)(this.inboundQueuePausedFromInit, 0x346 /* inboundQueuePausedFromInit should be true */);
|
|
1563
|
+
this.inboundQueuePausedFromInit = false;
|
|
1564
|
+
this._deltaManager.inbound.resume();
|
|
1565
|
+
this._deltaManager.inboundSignal.resume();
|
|
1566
|
+
break;
|
|
1567
|
+
case "none":
|
|
1568
|
+
break;
|
|
1569
|
+
default:
|
|
1570
|
+
(0, core_utils_1.unreachableCase)(deltaConnectionArg);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1523
1573
|
}
|
|
1524
1574
|
exports.Container = Container;
|
|
1525
1575
|
//# sourceMappingURL=container.js.map
|