@fluidframework/container-loader 2.0.0-internal.5.3.2 → 2.0.0-internal.6.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 +85 -0
- package/README.md +6 -3
- package/dist/audience.d.ts +1 -0
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +3 -1
- package/dist/audience.js.map +1 -1
- package/dist/connectionManager.d.ts +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +30 -36
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +2 -1
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +9 -16
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +12 -8
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +199 -156
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -12
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +1 -20
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.js +3 -5
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +11 -2
- 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 +16 -4
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +79 -33
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +1 -2
- package/dist/deltaQueue.js.map +1 -1
- package/dist/loader.d.ts +12 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +73 -47
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +2 -3
- 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/utils.d.ts +8 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -6
- package/dist/utils.js.map +1 -1
- package/lib/audience.d.ts +1 -0
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +3 -1
- package/lib/audience.js.map +1 -1
- package/lib/connectionManager.d.ts +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +32 -35
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +2 -1
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +9 -16
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +12 -8
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +200 -157
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -12
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +1 -20
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.js +3 -5
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +11 -2
- 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 +16 -4
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +77 -28
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +1 -2
- package/lib/deltaQueue.js.map +1 -1
- package/lib/loader.d.ts +12 -0
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +73 -47
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +2 -3
- 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/utils.d.ts +8 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +22 -5
- package/lib/utils.js.map +1 -1
- package/package.json +14 -14
- package/src/audience.ts +6 -0
- package/src/connectionManager.ts +13 -14
- package/src/connectionStateHandler.ts +3 -2
- package/src/container.ts +178 -120
- package/src/containerContext.ts +0 -24
- package/src/contracts.ts +16 -5
- package/src/debugLogger.ts +113 -0
- package/src/deltaManager.ts +50 -9
- package/src/loader.ts +53 -30
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +0 -1
- package/src/quorum.ts +0 -10
- package/src/utils.ts +29 -0
package/dist/container.js
CHANGED
|
@@ -111,7 +111,7 @@ async function waitContainerToCatchUp(container) {
|
|
|
111
111
|
exports.waitContainerToCatchUp = waitContainerToCatchUp;
|
|
112
112
|
const getCodeProposal =
|
|
113
113
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
114
|
-
(quorum) =>
|
|
114
|
+
(quorum) => quorum.get("code") ?? quorum.get("code2");
|
|
115
115
|
/**
|
|
116
116
|
* Helper function to report to telemetry cases where operation takes longer than expected (200ms)
|
|
117
117
|
* @param logger - logger to use
|
|
@@ -132,7 +132,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
132
132
|
* @internal
|
|
133
133
|
*/
|
|
134
134
|
constructor(createProps, loadProps) {
|
|
135
|
-
var _a;
|
|
136
135
|
super((name, error) => {
|
|
137
136
|
this.mc.logger.sendErrorEvent({
|
|
138
137
|
eventName: "ContainerEventHandlerException",
|
|
@@ -160,7 +159,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
160
159
|
this.inboundQueuePausedFromInit = true;
|
|
161
160
|
this.firstConnection = true;
|
|
162
161
|
this.connectionTransitionTimes = [];
|
|
163
|
-
this.messageCountAfterDisconnection = 0;
|
|
164
162
|
this.attachStarted = false;
|
|
165
163
|
this._dirtyContainer = false;
|
|
166
164
|
this.savedOps = [];
|
|
@@ -183,8 +181,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
183
181
|
};
|
|
184
182
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
185
183
|
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
|
|
186
|
-
const pendingLocalState = loadProps
|
|
187
|
-
this._canReconnect = canReconnect
|
|
184
|
+
const pendingLocalState = loadProps?.pendingLocalState;
|
|
185
|
+
this._canReconnect = canReconnect ?? true;
|
|
188
186
|
this.clientDetailsOverride = clientDetailsOverride;
|
|
189
187
|
this.urlResolver = urlResolver;
|
|
190
188
|
this.serviceFactory = documentServiceFactory;
|
|
@@ -192,14 +190,17 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
192
190
|
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
193
191
|
// all clients that were loaded from the same loader (including summarizer clients).
|
|
194
192
|
// Tracking alternative ways to handle this in AB#4129.
|
|
195
|
-
this.options =
|
|
193
|
+
this.options = { ...options };
|
|
196
194
|
this.scope = scope;
|
|
197
195
|
this.detachedBlobStorage = detachedBlobStorage;
|
|
198
196
|
this.protocolHandlerBuilder =
|
|
199
|
-
protocolHandlerBuilder
|
|
197
|
+
protocolHandlerBuilder ?? ((...args) => new protocol_1.ProtocolHandler(...args, new audience_1.Audience()));
|
|
200
198
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
201
199
|
this.clone = async (_loadProps, createParamOverrides) => {
|
|
202
|
-
return Container.load(_loadProps,
|
|
200
|
+
return Container.load(_loadProps, {
|
|
201
|
+
...createProps,
|
|
202
|
+
...createParamOverrides,
|
|
203
|
+
});
|
|
203
204
|
};
|
|
204
205
|
// Create logger for data stores to use
|
|
205
206
|
const type = this.client.details.type;
|
|
@@ -207,42 +208,42 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
207
208
|
const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
|
|
208
209
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
209
210
|
// 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
|
-
|
|
211
|
+
this.subLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
212
|
+
logger: subLogger,
|
|
213
|
+
properties: {
|
|
214
|
+
all: {
|
|
215
|
+
clientType,
|
|
216
|
+
containerId: (0, uuid_1.v4)(),
|
|
217
|
+
docId: () => this.resolvedUrl?.id,
|
|
218
|
+
containerAttachState: () => this._attachState,
|
|
219
|
+
containerLifecycleState: () => this._lifecycleState,
|
|
220
|
+
containerConnectionState: () => connectionState_1.ConnectionState[this.connectionState],
|
|
221
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
222
|
+
},
|
|
223
|
+
// we need to be judicious with our logging here to avoid generating too much data
|
|
224
|
+
// all data logged here should be broadly applicable, and not specific to a
|
|
225
|
+
// specific error or class of errors
|
|
226
|
+
error: {
|
|
227
|
+
// load information to associate errors with the specific load point
|
|
228
|
+
dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
|
|
229
|
+
dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
|
|
230
|
+
dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
|
|
231
|
+
containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
|
|
232
|
+
containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
|
|
233
|
+
// message information to associate errors with the specific execution state
|
|
234
|
+
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
235
|
+
dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
|
|
236
|
+
dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
|
|
237
|
+
dmLastMsqSeqClientId: () => this.deltaManager?.lastMessage?.clientId === null
|
|
237
238
|
? "null"
|
|
238
|
-
:
|
|
239
|
+
: this.deltaManager?.lastMessage?.clientId,
|
|
240
|
+
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
241
|
+
connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
239
242
|
},
|
|
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
243
|
},
|
|
243
244
|
});
|
|
244
245
|
// Prefix all events in this file with container-loader
|
|
245
|
-
this.mc = (0, telemetry_utils_1.
|
|
246
|
+
this.mc = (0, telemetry_utils_1.createChildMonitoringContext)({ logger: this.subLogger, namespace: "Container" });
|
|
246
247
|
this._deltaManager = this.createDeltaManager();
|
|
247
248
|
this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
|
|
248
249
|
logger: this.mc.logger,
|
|
@@ -266,9 +267,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
266
267
|
// Report issues only if we already loaded container - op processing is paused while container is loading,
|
|
267
268
|
// so we always time-out processing of join op in cases where fetching snapshot takes a minute.
|
|
268
269
|
// 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
|
-
|
|
270
|
+
this._deltaManager.logConnectionIssue({
|
|
271
|
+
eventName,
|
|
272
|
+
mode,
|
|
273
|
+
category: this._lifecycleState === "loading" ? "generic" : category,
|
|
274
|
+
duration: common_utils_1.performance.now() -
|
|
275
|
+
this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp],
|
|
276
|
+
...(details === undefined ? {} : { details: JSON.stringify(details) }),
|
|
277
|
+
});
|
|
272
278
|
// If this is "write" connection, it took too long to receive join op. But in most cases that's due
|
|
273
279
|
// to very slow op fetches and we will eventually get there.
|
|
274
280
|
// For "read" connections, we get here due to self join signal not arriving on time. We will need to
|
|
@@ -285,7 +291,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
285
291
|
clientShouldHaveLeft: (clientId) => {
|
|
286
292
|
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
287
293
|
},
|
|
288
|
-
}, this.deltaManager, pendingLocalState
|
|
294
|
+
}, this.deltaManager, pendingLocalState?.clientId);
|
|
289
295
|
this.on(savedContainerEvent, () => {
|
|
290
296
|
this.connectionStateHandler.containerSaved();
|
|
291
297
|
});
|
|
@@ -294,17 +300,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
294
300
|
// using this callback and fix them up.
|
|
295
301
|
const addProtocolSummaryIfMissing = (summaryTree) => (0, driver_utils_1.isCombinedAppAndProtocolSummary)(summaryTree) === true
|
|
296
302
|
? summaryTree
|
|
297
|
-
: (0,
|
|
303
|
+
: (0, utils_1.combineAppAndProtocolSummary)(summaryTree, this.captureProtocolSummary());
|
|
298
304
|
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
299
305
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
300
|
-
const forceEnableSummarizeProtocolTree =
|
|
301
|
-
|
|
306
|
+
const forceEnableSummarizeProtocolTree = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
307
|
+
options.summarizeProtocolTree;
|
|
308
|
+
this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
302
309
|
const isDomAvailable = typeof document === "object" &&
|
|
303
310
|
document !== null &&
|
|
304
311
|
typeof document.addEventListener === "function" &&
|
|
305
312
|
document.addEventListener !== null;
|
|
306
|
-
// keep track of last time page was visible for telemetry
|
|
307
|
-
if (isDomAvailable) {
|
|
313
|
+
// keep track of last time page was visible for telemetry (on interactive clients only)
|
|
314
|
+
if (isDomAvailable && interactive) {
|
|
308
315
|
this.lastVisible = document.hidden ? common_utils_1.performance.now() : undefined;
|
|
309
316
|
this.visibilityEventHandler = () => {
|
|
310
317
|
if (document.hidden) {
|
|
@@ -325,7 +332,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
325
332
|
* @internal
|
|
326
333
|
*/
|
|
327
334
|
static async load(loadProps, createProps) {
|
|
328
|
-
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
335
|
+
const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } = loadProps;
|
|
329
336
|
const container = new Container(createProps, loadProps);
|
|
330
337
|
const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
|
|
331
338
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
@@ -333,19 +340,20 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
333
340
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
334
341
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
335
342
|
const mode = pendingLocalState
|
|
336
|
-
?
|
|
343
|
+
? { ...(loadMode ?? defaultMode), opsBeforeReturn: undefined }
|
|
344
|
+
: loadMode ?? defaultMode;
|
|
337
345
|
const onClosed = (err) => {
|
|
338
346
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
339
|
-
reject(err
|
|
347
|
+
reject(err ?? new container_utils_1.GenericError("Container closed without error during load"));
|
|
340
348
|
};
|
|
341
349
|
container.on("closed", onClosed);
|
|
342
350
|
container
|
|
343
|
-
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
351
|
+
.load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
|
|
344
352
|
.finally(() => {
|
|
345
353
|
container.removeListener("closed", onClosed);
|
|
346
354
|
})
|
|
347
355
|
.then((props) => {
|
|
348
|
-
event.end(
|
|
356
|
+
event.end({ ...props, ...loadMode });
|
|
349
357
|
resolve(container);
|
|
350
358
|
}, (error) => {
|
|
351
359
|
const err = (0, telemetry_utils_1.normalizeError)(error);
|
|
@@ -389,10 +397,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
389
397
|
}
|
|
390
398
|
}
|
|
391
399
|
get closed() {
|
|
392
|
-
return (this._lifecycleState === "closing" ||
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
400
|
+
return (this._lifecycleState === "closing" || this._lifecycleState === "closed" || this.disposed);
|
|
401
|
+
}
|
|
402
|
+
get disposed() {
|
|
403
|
+
return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
|
|
396
404
|
}
|
|
397
405
|
get runtime() {
|
|
398
406
|
if (this._runtime === undefined) {
|
|
@@ -413,7 +421,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
413
421
|
return this;
|
|
414
422
|
}
|
|
415
423
|
get resolvedUrl() {
|
|
416
|
-
var _a;
|
|
417
424
|
/**
|
|
418
425
|
* All attached containers will have a document service,
|
|
419
426
|
* this is required, as attached containers are attached to
|
|
@@ -425,7 +432,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
425
432
|
* is always the same as the containers, as we had to
|
|
426
433
|
* obtain the resolved url, and then create the service from it.
|
|
427
434
|
*/
|
|
428
|
-
return
|
|
435
|
+
return this.service?.resolvedUrl;
|
|
429
436
|
}
|
|
430
437
|
get readOnlyInfo() {
|
|
431
438
|
return this._deltaManager.readOnlyInfo;
|
|
@@ -453,8 +460,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
453
460
|
return this._clientId;
|
|
454
461
|
}
|
|
455
462
|
get offlineLoadEnabled() {
|
|
456
|
-
|
|
457
|
-
|
|
463
|
+
const enabled = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ??
|
|
464
|
+
this.options?.enableOfflineLoad === true;
|
|
458
465
|
// summarizer will not have any pending state we want to save
|
|
459
466
|
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
460
467
|
}
|
|
@@ -491,18 +498,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
491
498
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
492
499
|
*/
|
|
493
500
|
async getEntryPoint() {
|
|
494
|
-
var _a, _b;
|
|
495
501
|
if (this._disposed) {
|
|
496
502
|
throw new container_utils_1.UsageError("The context is already disposed");
|
|
497
503
|
}
|
|
498
504
|
if (this._runtime !== undefined) {
|
|
499
|
-
return
|
|
505
|
+
return this._runtime.getEntryPoint?.();
|
|
500
506
|
}
|
|
501
507
|
return new Promise((resolve, reject) => {
|
|
502
508
|
const runtimeInstantiatedHandler = () => {
|
|
503
|
-
var _a, _b;
|
|
504
509
|
(0, common_utils_1.assert)(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
|
|
505
|
-
resolve(
|
|
510
|
+
resolve(this._runtime.getEntryPoint?.());
|
|
506
511
|
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
507
512
|
};
|
|
508
513
|
const disposedHandler = () => {
|
|
@@ -536,7 +541,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
536
541
|
(0, common_utils_1.assert)(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
|
|
537
542
|
}
|
|
538
543
|
closeCore(error) {
|
|
539
|
-
var _a;
|
|
540
544
|
(0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
541
545
|
try {
|
|
542
546
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -552,7 +556,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
552
556
|
: "generic",
|
|
553
557
|
}, error);
|
|
554
558
|
this._lifecycleState = "closing";
|
|
555
|
-
|
|
559
|
+
this._protocolHandler?.close();
|
|
556
560
|
this.connectionStateHandler.dispose();
|
|
557
561
|
}
|
|
558
562
|
catch (exception) {
|
|
@@ -572,7 +576,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
572
576
|
}
|
|
573
577
|
}
|
|
574
578
|
disposeCore(error) {
|
|
575
|
-
var _a, _b, _c;
|
|
576
579
|
(0, common_utils_1.assert)(!this._disposed, 0x54c /* Container already disposed */);
|
|
577
580
|
this._disposed = true;
|
|
578
581
|
try {
|
|
@@ -589,15 +592,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
589
592
|
if (this._lifecycleState !== "closed") {
|
|
590
593
|
this._lifecycleState = "disposing";
|
|
591
594
|
}
|
|
592
|
-
|
|
595
|
+
this._protocolHandler?.close();
|
|
593
596
|
this.connectionStateHandler.dispose();
|
|
594
597
|
const maybeError = error !== undefined ? new Error(error.message) : undefined;
|
|
595
|
-
|
|
598
|
+
this._runtime?.dispose(maybeError);
|
|
596
599
|
this.storageAdapter.dispose();
|
|
597
600
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
598
601
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
599
602
|
// Driver need to ensure all caches are cleared on critical errors
|
|
600
|
-
|
|
603
|
+
this.service?.dispose(error);
|
|
601
604
|
}
|
|
602
605
|
catch (exception) {
|
|
603
606
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerDisposeException" }, exception);
|
|
@@ -613,15 +616,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
613
616
|
this._lifecycleEvents.emit("disposed");
|
|
614
617
|
}
|
|
615
618
|
}
|
|
616
|
-
closeAndGetPendingLocalState() {
|
|
619
|
+
async closeAndGetPendingLocalState() {
|
|
617
620
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
618
621
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
619
622
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
620
|
-
|
|
623
|
+
this.disconnect(); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
624
|
+
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
621
625
|
this.close();
|
|
622
626
|
return pendingState;
|
|
623
627
|
}
|
|
624
|
-
getPendingLocalState() {
|
|
628
|
+
async getPendingLocalState() {
|
|
629
|
+
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
630
|
+
}
|
|
631
|
+
async getPendingLocalStateCore(props) {
|
|
625
632
|
if (!this.offlineLoadEnabled) {
|
|
626
633
|
throw new container_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
|
|
627
634
|
}
|
|
@@ -632,8 +639,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
632
639
|
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
633
640
|
(0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
634
641
|
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
642
|
+
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
635
643
|
const pendingState = {
|
|
636
|
-
pendingRuntimeState
|
|
644
|
+
pendingRuntimeState,
|
|
637
645
|
baseSnapshot: this.baseSnapshot,
|
|
638
646
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
639
647
|
savedOps: this.savedOps,
|
|
@@ -651,7 +659,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
651
659
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
|
|
652
660
|
const appSummary = this.runtime.createSummary();
|
|
653
661
|
const protocolSummary = this.captureProtocolSummary();
|
|
654
|
-
const combinedSummary = (0,
|
|
662
|
+
const combinedSummary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
655
663
|
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
656
664
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
657
665
|
type: protocol_definitions_1.SummaryType.Blob,
|
|
@@ -662,7 +670,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
662
670
|
}
|
|
663
671
|
async attach(request) {
|
|
664
672
|
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
665
|
-
var _a;
|
|
666
673
|
if (this._lifecycleState !== "loaded") {
|
|
667
674
|
// pre-0.58 error message: containerNotValidForAttach
|
|
668
675
|
throw new container_utils_1.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
|
|
@@ -680,7 +687,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
680
687
|
// semantics around what the attach means as far as async code goes.
|
|
681
688
|
const appSummary = this.runtime.createSummary();
|
|
682
689
|
const protocolSummary = this.captureProtocolSummary();
|
|
683
|
-
summary = (0,
|
|
690
|
+
summary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
684
691
|
// Set the state as attaching as we are starting the process of attaching container.
|
|
685
692
|
// This should be fired after taking the summary because it is the place where we are
|
|
686
693
|
// starting to attach the container to storage.
|
|
@@ -725,7 +732,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
725
732
|
// take summary and upload
|
|
726
733
|
const appSummary = this.runtime.createSummary(redirectTable);
|
|
727
734
|
const protocolSummary = this.captureProtocolSummary();
|
|
728
|
-
summary = (0,
|
|
735
|
+
summary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
729
736
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
730
737
|
this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
|
|
731
738
|
this.emit("attaching");
|
|
@@ -754,7 +761,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
754
761
|
catch (error) {
|
|
755
762
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
756
763
|
const newError = (0, telemetry_utils_1.normalizeError)(error);
|
|
757
|
-
newError.addTelemetryProperties({ resolvedUrl:
|
|
764
|
+
newError.addTelemetryProperties({ resolvedUrl: this.resolvedUrl?.url });
|
|
758
765
|
this.close(newError);
|
|
759
766
|
throw newError;
|
|
760
767
|
}
|
|
@@ -861,7 +868,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
861
868
|
* Determines if the currently loaded module satisfies the incoming constraint code details
|
|
862
869
|
*/
|
|
863
870
|
async satisfies(constraintCodeDetails) {
|
|
864
|
-
var _a, _b;
|
|
865
871
|
// If we have no module, it can't satisfy anything.
|
|
866
872
|
if (this._loadedModule === undefined) {
|
|
867
873
|
return false;
|
|
@@ -871,8 +877,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
871
877
|
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
872
878
|
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
873
879
|
}
|
|
874
|
-
const maybeCompareExport =
|
|
875
|
-
if (
|
|
880
|
+
const maybeCompareExport = this._loadedModule?.module.fluidExport;
|
|
881
|
+
if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
|
|
876
882
|
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
877
883
|
}
|
|
878
884
|
// If there are no comparers, then it's impossible to know if the currently loaded package satisfies
|
|
@@ -882,7 +888,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
882
888
|
return false;
|
|
883
889
|
}
|
|
884
890
|
for (const comparer of comparers) {
|
|
885
|
-
const satisfies = await comparer.satisfies(
|
|
891
|
+
const satisfies = await comparer.satisfies(this._loadedModule?.details, constraintCodeDetails);
|
|
886
892
|
if (satisfies === false) {
|
|
887
893
|
return false;
|
|
888
894
|
}
|
|
@@ -905,8 +911,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
905
911
|
*
|
|
906
912
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
907
913
|
*/
|
|
908
|
-
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
909
|
-
var _a, _b, _c;
|
|
914
|
+
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState, loadToSequenceNumber) {
|
|
910
915
|
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
911
916
|
// Ideally we always connect as "read" by default.
|
|
912
917
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
@@ -955,9 +960,51 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
955
960
|
}
|
|
956
961
|
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
957
962
|
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
958
|
-
const sequenceNumber =
|
|
959
|
-
const dmAttributes = sequenceNumber !== undefined ?
|
|
963
|
+
const sequenceNumber = pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber;
|
|
964
|
+
const dmAttributes = sequenceNumber !== undefined ? { ...attributes, sequenceNumber } : attributes;
|
|
960
965
|
let opsBeforeReturnP;
|
|
966
|
+
if (loadMode.pauseAfterLoad === true) {
|
|
967
|
+
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
968
|
+
if (loadMode.opsBeforeReturn === "sequenceNumber") {
|
|
969
|
+
(0, common_utils_1.assert)(loadToSequenceNumber !== undefined, 0x727 /* sequenceNumber should be defined */);
|
|
970
|
+
// Note: It is possible that we think the latest snapshot is newer than the specified sequence number
|
|
971
|
+
// due to saved ops that may be replayed after the snapshot.
|
|
972
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
|
|
973
|
+
if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
|
|
974
|
+
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.");
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
// Force readonly mode - this will ensure we don't receive an error for the lack of join op
|
|
978
|
+
this.forceReadonly(true);
|
|
979
|
+
// We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
|
|
980
|
+
const opHandler = () => {
|
|
981
|
+
if (loadToSequenceNumber === undefined) {
|
|
982
|
+
// If there is no specified sequence number, pause after the inbound queue is empty.
|
|
983
|
+
if (this.deltaManager.inbound.length !== 0) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
// If there is a specified sequence number, keep processing until we reach it.
|
|
989
|
+
if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
// Pause op processing once we have processed the desired number of ops.
|
|
994
|
+
void this.deltaManager.inbound.pause();
|
|
995
|
+
void this.deltaManager.outbound.pause();
|
|
996
|
+
this.off("op", opHandler);
|
|
997
|
+
};
|
|
998
|
+
if ((loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
|
|
999
|
+
this.deltaManager.lastSequenceNumber === loadToSequenceNumber) {
|
|
1000
|
+
// If we have already reached the desired sequence number, call opHandler() to pause immediately.
|
|
1001
|
+
opHandler();
|
|
1002
|
+
}
|
|
1003
|
+
else {
|
|
1004
|
+
// If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
|
|
1005
|
+
this.on("op", opHandler);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
961
1008
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
962
1009
|
// Kick off any ops fetching if required.
|
|
963
1010
|
switch (loadMode.opsBeforeReturn) {
|
|
@@ -966,6 +1013,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
966
1013
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
967
1014
|
this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
|
|
968
1015
|
break;
|
|
1016
|
+
case "sequenceNumber":
|
|
1017
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
|
|
1018
|
+
break;
|
|
969
1019
|
case "cached":
|
|
970
1020
|
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
971
1021
|
break;
|
|
@@ -979,19 +1029,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
979
1029
|
// Initialize the protocol handler
|
|
980
1030
|
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
981
1031
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
982
|
-
await this.
|
|
983
|
-
codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
|
|
1032
|
+
await this.instantiateRuntime(codeDetails, snapshot, pendingLocalState?.pendingRuntimeState);
|
|
984
1033
|
// replay saved ops
|
|
985
1034
|
if (pendingLocalState) {
|
|
986
1035
|
for (const message of pendingLocalState.savedOps) {
|
|
987
1036
|
this.processRemoteMessage(message);
|
|
988
1037
|
// allow runtime to apply stashed ops at this op's sequence number
|
|
989
|
-
await
|
|
1038
|
+
await this.runtime.notifyOpReplay?.(message);
|
|
990
1039
|
}
|
|
991
1040
|
pendingLocalState.savedOps = [];
|
|
992
1041
|
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
993
1042
|
(0, common_utils_1.assert)(this.clientId === undefined, 0x5d6 /* Unexpected clientId when setting stashed clientId */);
|
|
994
|
-
this._clientId = pendingLocalState
|
|
1043
|
+
this._clientId = pendingLocalState?.clientId;
|
|
995
1044
|
}
|
|
996
1045
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
997
1046
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
@@ -1022,6 +1071,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1022
1071
|
(0, common_utils_1.unreachableCase)(loadMode.deltaConnection);
|
|
1023
1072
|
}
|
|
1024
1073
|
}
|
|
1074
|
+
// If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
|
|
1075
|
+
if (loadToSequenceNumber !== undefined &&
|
|
1076
|
+
this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
1077
|
+
await new Promise((resolve, reject) => {
|
|
1078
|
+
const opHandler = (message) => {
|
|
1079
|
+
if (message.sequenceNumber >= loadToSequenceNumber) {
|
|
1080
|
+
resolve();
|
|
1081
|
+
this.off("op", opHandler);
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
this.on("op", opHandler);
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1025
1087
|
// Safety net: static version of Container.load() should have learned about it through "closed" handler.
|
|
1026
1088
|
// But if that did not happen for some reason, fail load for sure.
|
|
1027
1089
|
// Otherwise we can get into situations where container is closed and does not try to connect to ordering
|
|
@@ -1039,7 +1101,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1039
1101
|
dmLastKnownSeqNumber: this._deltaManager.lastKnownSeqNumber,
|
|
1040
1102
|
};
|
|
1041
1103
|
}
|
|
1042
|
-
async createDetached(
|
|
1104
|
+
async createDetached(codeDetails) {
|
|
1043
1105
|
const attributes = {
|
|
1044
1106
|
sequenceNumber: detachedContainerRefSeqNumber,
|
|
1045
1107
|
term: protocol_1.OnlyValidTermValue,
|
|
@@ -1047,14 +1109,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1047
1109
|
};
|
|
1048
1110
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1049
1111
|
// Need to just seed the source data in the code quorum. Quorum itself is empty
|
|
1050
|
-
const qValues = (0, quorum_1.initQuorumValuesFromCodeDetails)(
|
|
1112
|
+
const qValues = (0, quorum_1.initQuorumValuesFromCodeDetails)(codeDetails);
|
|
1051
1113
|
this.initializeProtocolState(attributes, {
|
|
1052
1114
|
members: [],
|
|
1053
1115
|
proposals: [],
|
|
1054
1116
|
values: qValues,
|
|
1055
1117
|
});
|
|
1056
|
-
|
|
1057
|
-
await this.instantiateContextDetached(false);
|
|
1118
|
+
await this.instantiateRuntime(codeDetails, undefined);
|
|
1058
1119
|
this.setLoaded();
|
|
1059
1120
|
}
|
|
1060
1121
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
@@ -1069,14 +1130,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1069
1130
|
// Initialize the protocol handler
|
|
1070
1131
|
const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshotTree);
|
|
1071
1132
|
const qValues = await (0, driver_utils_1.readAndParse)(this.storageAdapter, baseTree.blobs.quorumValues);
|
|
1072
|
-
const codeDetails = (0, quorum_1.getCodeDetailsFromQuorumValues)(qValues);
|
|
1073
1133
|
this.initializeProtocolState(attributes, {
|
|
1074
1134
|
members: [],
|
|
1075
1135
|
proposals: [],
|
|
1076
|
-
values:
|
|
1136
|
+
values: qValues,
|
|
1077
1137
|
});
|
|
1078
|
-
|
|
1079
|
-
snapshotTree);
|
|
1138
|
+
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1139
|
+
await this.instantiateRuntime(codeDetails, snapshotTree);
|
|
1080
1140
|
this.setLoaded();
|
|
1081
1141
|
}
|
|
1082
1142
|
async getDocumentAttributes(storage, tree) {
|
|
@@ -1113,7 +1173,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1113
1173
|
}
|
|
1114
1174
|
initializeProtocolState(attributes, quorumSnapshot) {
|
|
1115
1175
|
const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })));
|
|
1116
|
-
const protocolLogger =
|
|
1176
|
+
const protocolLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
1177
|
+
logger: this.subLogger,
|
|
1178
|
+
namespace: "ProtocolHandler",
|
|
1179
|
+
});
|
|
1117
1180
|
protocol.quorum.on("error", (error) => {
|
|
1118
1181
|
protocolLogger.sendErrorEvent(error);
|
|
1119
1182
|
});
|
|
@@ -1175,8 +1238,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1175
1238
|
return pkg;
|
|
1176
1239
|
}
|
|
1177
1240
|
get client() {
|
|
1178
|
-
|
|
1179
|
-
const client = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.client) !== undefined
|
|
1241
|
+
const client = this.options?.client !== undefined
|
|
1180
1242
|
? this.options.client
|
|
1181
1243
|
: {
|
|
1182
1244
|
details: {
|
|
@@ -1207,7 +1269,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1207
1269
|
}
|
|
1208
1270
|
createDeltaManager() {
|
|
1209
1271
|
const serviceProvider = () => this.service;
|
|
1210
|
-
const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, telemetry_utils_1.
|
|
1272
|
+
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
1273
|
// Disable inbound queues as Container is not ready to accept any ops until we are fully loaded!
|
|
1212
1274
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1213
1275
|
deltaManager.inbound.pause();
|
|
@@ -1224,8 +1286,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1224
1286
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1225
1287
|
});
|
|
1226
1288
|
deltaManager.on("disconnect", (reason, error) => {
|
|
1227
|
-
|
|
1228
|
-
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
|
|
1289
|
+
this.noopHeuristic?.notifyDisconnect();
|
|
1229
1290
|
if (!this.closed) {
|
|
1230
1291
|
this.connectionStateHandler.receivedDisconnectEvent(reason, error);
|
|
1231
1292
|
}
|
|
@@ -1260,7 +1321,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1260
1321
|
}, prefetchType);
|
|
1261
1322
|
}
|
|
1262
1323
|
logConnectionStateChangeTelemetry(value, oldState, reason, error) {
|
|
1263
|
-
var _a;
|
|
1264
1324
|
// Log actual event
|
|
1265
1325
|
const time = common_utils_1.performance.now();
|
|
1266
1326
|
this.connectionTransitionTimes[value] = time;
|
|
@@ -1277,7 +1337,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1277
1337
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
1278
1338
|
durationFromDisconnected =
|
|
1279
1339
|
time - this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected];
|
|
1280
|
-
durationFromDisconnected = telemetry_utils_1.
|
|
1340
|
+
durationFromDisconnected = (0, telemetry_utils_1.formatTick)(durationFromDisconnected);
|
|
1281
1341
|
}
|
|
1282
1342
|
else if (value === connectionState_1.ConnectionState.CatchingUp) {
|
|
1283
1343
|
// This info is of most interesting while Catching Up.
|
|
@@ -1290,19 +1350,31 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1290
1350
|
}
|
|
1291
1351
|
connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
|
|
1292
1352
|
}
|
|
1293
|
-
this.mc.logger.sendPerformanceEvent(
|
|
1353
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1354
|
+
eventName: `ConnectionStateChange_${connectionState_1.ConnectionState[value]}`,
|
|
1355
|
+
from: connectionState_1.ConnectionState[oldState],
|
|
1356
|
+
duration,
|
|
1294
1357
|
durationFromDisconnected,
|
|
1295
1358
|
reason,
|
|
1296
|
-
connectionInitiationReason,
|
|
1297
|
-
|
|
1359
|
+
connectionInitiationReason,
|
|
1360
|
+
pendingClientId: this.connectionStateHandler.pendingClientId,
|
|
1361
|
+
clientId: this.clientId,
|
|
1362
|
+
autoReconnect,
|
|
1363
|
+
opsBehind,
|
|
1364
|
+
online: driver_utils_1.OnlineStatus[(0, driver_utils_1.isOnline)()],
|
|
1365
|
+
lastVisible: this.lastVisible !== undefined
|
|
1298
1366
|
? common_utils_1.performance.now() - this.lastVisible
|
|
1299
|
-
: undefined,
|
|
1367
|
+
: undefined,
|
|
1368
|
+
checkpointSequenceNumber,
|
|
1369
|
+
quorumSize: this._protocolHandler?.quorum.getMembers().size,
|
|
1370
|
+
isDirty: this.isDirty,
|
|
1371
|
+
...this._deltaManager.connectionProps,
|
|
1372
|
+
}, error);
|
|
1300
1373
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
1301
1374
|
this.firstConnection = false;
|
|
1302
1375
|
}
|
|
1303
1376
|
}
|
|
1304
1377
|
propagateConnectionState(initialTransition, disconnectedReason) {
|
|
1305
|
-
var _a;
|
|
1306
1378
|
// When container loaded, we want to propagate initial connection state.
|
|
1307
1379
|
// After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
1308
1380
|
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
@@ -1312,22 +1384,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1312
1384
|
return;
|
|
1313
1385
|
}
|
|
1314
1386
|
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
1387
|
// Both protocol and context should not be undefined if we got so far.
|
|
1322
|
-
this.setContextConnectedState(state,
|
|
1388
|
+
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
1323
1389
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1324
1390
|
(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
|
-
}
|
|
1331
1391
|
}
|
|
1332
1392
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1333
1393
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
@@ -1365,13 +1425,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1365
1425
|
return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch */, undefined /* metadata */, undefined /* compression */, referenceSequenceNumber);
|
|
1366
1426
|
}
|
|
1367
1427
|
submitMessage(type, contents, batch, metadata, compression, referenceSequenceNumber) {
|
|
1368
|
-
var _a;
|
|
1369
1428
|
if (this.connectionState !== connectionState_1.ConnectionState.Connected) {
|
|
1370
1429
|
this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
|
|
1371
1430
|
return -1;
|
|
1372
1431
|
}
|
|
1373
|
-
this.
|
|
1374
|
-
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
|
|
1432
|
+
this.noopHeuristic?.notifyMessageSent();
|
|
1375
1433
|
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1376
1434
|
}
|
|
1377
1435
|
processRemoteMessage(message) {
|
|
@@ -1446,8 +1504,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1446
1504
|
* @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
|
|
1447
1505
|
*/
|
|
1448
1506
|
async fetchSnapshotTree(specifiedVersion) {
|
|
1449
|
-
|
|
1450
|
-
const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : null);
|
|
1507
|
+
const version = await this.getVersion(specifiedVersion ?? null);
|
|
1451
1508
|
if (version === undefined && specifiedVersion !== undefined) {
|
|
1452
1509
|
// We should have a defined version to load from if specified version requested
|
|
1453
1510
|
this.mc.logger.sendErrorEvent({
|
|
@@ -1456,22 +1513,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1456
1513
|
});
|
|
1457
1514
|
}
|
|
1458
1515
|
this._loadedFromVersion = version;
|
|
1459
|
-
const snapshot = (
|
|
1516
|
+
const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
|
|
1460
1517
|
if (snapshot === undefined && version !== undefined) {
|
|
1461
1518
|
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
1462
1519
|
}
|
|
1463
|
-
return { snapshot, versionId: version
|
|
1464
|
-
}
|
|
1465
|
-
async instantiateContextDetached(existing, snapshot) {
|
|
1466
|
-
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1467
|
-
if (codeDetails === undefined) {
|
|
1468
|
-
throw new Error("pkg should be provided in create flow!!");
|
|
1469
|
-
}
|
|
1470
|
-
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
1520
|
+
return { snapshot, versionId: version?.id };
|
|
1471
1521
|
}
|
|
1472
|
-
async
|
|
1473
|
-
|
|
1474
|
-
(0, common_utils_1.assert)(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1522
|
+
async instantiateRuntime(codeDetails, snapshot, pendingLocalState) {
|
|
1523
|
+
(0, common_utils_1.assert)(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1475
1524
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1476
1525
|
// are set. Global requests will still go directly to the loader
|
|
1477
1526
|
const maybeLoader = this.scope;
|
|
@@ -1482,25 +1531,20 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1482
1531
|
// An older interface ICodeLoader could return an IFluidModule which didn't have details.
|
|
1483
1532
|
// If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
|
|
1484
1533
|
// TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
|
|
1485
|
-
details:
|
|
1534
|
+
details: loadCodeResult.details ?? codeDetails,
|
|
1486
1535
|
};
|
|
1487
1536
|
const fluidExport = this._loadedModule.module.fluidExport;
|
|
1488
|
-
const runtimeFactory = fluidExport
|
|
1537
|
+
const runtimeFactory = fluidExport?.IRuntimeFactory;
|
|
1489
1538
|
if (runtimeFactory === undefined) {
|
|
1490
1539
|
throw new Error(packageNotFactoryError);
|
|
1491
1540
|
}
|
|
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
|
-
});
|
|
1541
|
+
const getSpecifiedCodeDetails = () => (this.protocolHandler.quorum.get("code") ??
|
|
1542
|
+
this.protocolHandler.quorum.get("code2"));
|
|
1543
|
+
const existing = snapshot !== undefined;
|
|
1544
|
+
const context = new containerContext_1.ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1500
1545
|
this._runtime = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1501
1546
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1502
1547
|
this._loadedCodeDetails = codeDetails;
|
|
1503
|
-
this.emit("contextChanged", codeDetails);
|
|
1504
1548
|
}
|
|
1505
1549
|
/**
|
|
1506
1550
|
* Set the connected state of the ContainerContext
|
|
@@ -1509,8 +1553,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1509
1553
|
* @param readonly - Is the container in readonly mode?
|
|
1510
1554
|
*/
|
|
1511
1555
|
setContextConnectedState(state, readonly) {
|
|
1512
|
-
|
|
1513
|
-
if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1556
|
+
if (this._runtime?.disposed === false) {
|
|
1514
1557
|
/**
|
|
1515
1558
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1516
1559
|
* ops getting through to the DeltaManager.
|