@fluidframework/container-loader 2.0.0-internal.6.1.1 → 2.0.0-internal.6.3.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 +42 -0
- package/README.md +4 -3
- package/dist/audience.js +2 -2
- 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 +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +38 -37
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.js +24 -24
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +8 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +117 -98
- package/dist/container.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +10 -9
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +2 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.js +2 -2
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +3 -4
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +39 -38
- 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 +9 -8
- 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 +9 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +12 -7
- 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/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -4
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +9 -8
- package/dist/utils.js.map +1 -1
- package/lib/audience.js +1 -1
- 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 +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +10 -9
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.js +1 -1
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +8 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +73 -54
- package/lib/container.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +2 -1
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +2 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.js +1 -1
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +3 -4
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +6 -5
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +2 -1
- 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 +9 -2
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +11 -6
- 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/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +2 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +4 -3
- package/lib/utils.js.map +1 -1
- package/package.json +21 -27
- package/src/audience.ts +1 -1
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +20 -10
- package/src/connectionStateHandler.ts +1 -1
- package/src/container.ts +120 -79
- package/src/containerStorageAdapter.ts +2 -1
- package/src/contracts.ts +1 -2
- package/src/debugLogger.ts +1 -1
- package/src/deltaManager.ts +16 -13
- package/src/deltaQueue.ts +2 -1
- package/src/disposal.ts +2 -2
- package/src/error.ts +44 -0
- package/src/loader.ts +30 -5
- package/src/noopHeuristic.ts +3 -2
- package/src/packageVersion.ts +1 -1
- package/src/retriableDocumentStorageService.ts +2 -4
- package/src/utils.ts +4 -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);
|
|
@@ -163,8 +164,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
163
164
|
this._dirtyContainer = false;
|
|
164
165
|
this.savedOps = [];
|
|
165
166
|
this.clientsWhoShouldHaveLeft = new Set();
|
|
166
|
-
this.setAutoReconnectTime =
|
|
167
|
-
this._lifecycleEvents = new
|
|
167
|
+
this.setAutoReconnectTime = client_utils_1.performance.now();
|
|
168
|
+
this._lifecycleEvents = new client_utils_1.TypedEventEmitter();
|
|
168
169
|
this._disposed = false;
|
|
169
170
|
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
170
171
|
if (this.resolvedUrl === undefined) {
|
|
@@ -180,8 +181,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
180
181
|
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
181
182
|
};
|
|
182
183
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
183
|
-
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] =
|
|
184
|
+
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = client_utils_1.performance.now();
|
|
184
185
|
const pendingLocalState = loadProps?.pendingLocalState;
|
|
186
|
+
this._clientId = pendingLocalState?.clientId;
|
|
185
187
|
this._canReconnect = canReconnect ?? true;
|
|
186
188
|
this.clientDetailsOverride = clientDetailsOverride;
|
|
187
189
|
this.urlResolver = urlResolver;
|
|
@@ -239,7 +241,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
239
241
|
? "null"
|
|
240
242
|
: this.deltaManager?.lastMessage?.clientId,
|
|
241
243
|
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
242
|
-
connectionStateDuration: () =>
|
|
244
|
+
connectionStateDuration: () => client_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
243
245
|
},
|
|
244
246
|
},
|
|
245
247
|
});
|
|
@@ -272,7 +274,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
272
274
|
eventName,
|
|
273
275
|
mode,
|
|
274
276
|
category: this._lifecycleState === "loading" ? "generic" : category,
|
|
275
|
-
duration:
|
|
277
|
+
duration: client_utils_1.performance.now() -
|
|
276
278
|
this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp],
|
|
277
279
|
...(details === undefined ? {} : { details: JSON.stringify(details) }),
|
|
278
280
|
});
|
|
@@ -314,10 +316,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
314
316
|
document.addEventListener !== null;
|
|
315
317
|
// keep track of last time page was visible for telemetry (on interactive clients only)
|
|
316
318
|
if (isDomAvailable && interactive) {
|
|
317
|
-
this.lastVisible = document.hidden ?
|
|
319
|
+
this.lastVisible = document.hidden ? client_utils_1.performance.now() : undefined;
|
|
318
320
|
this.visibilityEventHandler = () => {
|
|
319
321
|
if (document.hidden) {
|
|
320
|
-
this.lastVisible =
|
|
322
|
+
this.lastVisible = client_utils_1.performance.now();
|
|
321
323
|
}
|
|
322
324
|
else {
|
|
323
325
|
// settimeout so this will hopefully fire after disconnect event if being hidden caused it
|
|
@@ -346,7 +348,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
346
348
|
: loadMode ?? defaultMode;
|
|
347
349
|
const onClosed = (err) => {
|
|
348
350
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
349
|
-
reject(err ?? new
|
|
351
|
+
reject(err ?? new telemetry_utils_1.GenericError("Container closed without error during load"));
|
|
350
352
|
};
|
|
351
353
|
container.on("closed", onClosed);
|
|
352
354
|
container
|
|
@@ -362,7 +364,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
362
364
|
// Depending where error happens, we can be attempting to connect to web socket
|
|
363
365
|
// and continuously retrying (consider offline mode)
|
|
364
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.
|
|
365
370
|
container.close(err);
|
|
371
|
+
container.dispose(err);
|
|
366
372
|
onClosed(err);
|
|
367
373
|
});
|
|
368
374
|
}), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
|
|
@@ -501,14 +507,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
501
507
|
*/
|
|
502
508
|
async getEntryPoint() {
|
|
503
509
|
if (this._disposed) {
|
|
504
|
-
throw new
|
|
510
|
+
throw new telemetry_utils_1.UsageError("The context is already disposed");
|
|
505
511
|
}
|
|
506
512
|
if (this._runtime !== undefined) {
|
|
507
513
|
return this._runtime.getEntryPoint?.();
|
|
508
514
|
}
|
|
509
515
|
return new Promise((resolve, reject) => {
|
|
510
516
|
const runtimeInstantiatedHandler = () => {
|
|
511
|
-
(0,
|
|
517
|
+
(0, core_utils_1.assert)(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
|
|
512
518
|
resolve(this._runtime.getEntryPoint?.());
|
|
513
519
|
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
514
520
|
};
|
|
@@ -539,11 +545,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
539
545
|
this.verifyClosed();
|
|
540
546
|
}
|
|
541
547
|
verifyClosed() {
|
|
542
|
-
(0,
|
|
543
|
-
(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 */);
|
|
544
550
|
}
|
|
545
551
|
closeCore(error) {
|
|
546
|
-
(0,
|
|
552
|
+
(0, core_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
547
553
|
try {
|
|
548
554
|
// Ensure that we raise all key events even if one of these throws
|
|
549
555
|
try {
|
|
@@ -578,7 +584,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
578
584
|
}
|
|
579
585
|
}
|
|
580
586
|
disposeCore(error) {
|
|
581
|
-
(0,
|
|
587
|
+
(0, core_utils_1.assert)(!this._disposed, 0x54c /* Container already disposed */);
|
|
582
588
|
this._disposed = true;
|
|
583
589
|
try {
|
|
584
590
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -631,34 +637,41 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
631
637
|
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
632
638
|
}
|
|
633
639
|
async getPendingLocalStateCore(props) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
throw new container_utils_1.UsageError("Pending state cannot be retried if the container is closed or disposed");
|
|
639
|
-
}
|
|
640
|
-
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
641
|
-
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
642
|
-
(0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
643
|
-
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
644
|
-
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
645
|
-
const pendingState = {
|
|
646
|
-
pendingRuntimeState,
|
|
647
|
-
baseSnapshot: this.baseSnapshot,
|
|
648
|
-
snapshotBlobs: this.baseSnapshotBlobs,
|
|
649
|
-
savedOps: this.savedOps,
|
|
650
|
-
url: this.resolvedUrl.url,
|
|
651
|
-
term: protocol_1.OnlyValidTermValue,
|
|
640
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, {
|
|
641
|
+
eventName: "getPendingLocalState",
|
|
642
|
+
notifyImminentClosure: props.notifyImminentClosure,
|
|
643
|
+
savedOpsSize: this.savedOps.length,
|
|
652
644
|
clientId: this.clientId,
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
|
|
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
|
+
});
|
|
656
669
|
}
|
|
657
670
|
get attachState() {
|
|
658
671
|
return this._attachState;
|
|
659
672
|
}
|
|
660
673
|
serialize() {
|
|
661
|
-
(0,
|
|
674
|
+
(0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
|
|
662
675
|
const appSummary = this.runtime.createSummary();
|
|
663
676
|
const protocolSummary = this.captureProtocolSummary();
|
|
664
677
|
const combinedSummary = (0, utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
@@ -670,19 +683,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
670
683
|
}
|
|
671
684
|
return JSON.stringify(combinedSummary);
|
|
672
685
|
}
|
|
673
|
-
async attach(request) {
|
|
686
|
+
async attach(request, attachProps) {
|
|
674
687
|
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
675
688
|
if (this._lifecycleState !== "loaded") {
|
|
676
689
|
// pre-0.58 error message: containerNotValidForAttach
|
|
677
|
-
throw new
|
|
690
|
+
throw new telemetry_utils_1.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
|
|
678
691
|
}
|
|
679
692
|
// If container is already attached or attach is in progress, throw an error.
|
|
680
|
-
(0,
|
|
693
|
+
(0, core_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
681
694
|
this.attachStarted = true;
|
|
682
695
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
683
696
|
const hasAttachmentBlobs = this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
684
697
|
try {
|
|
685
|
-
(0,
|
|
698
|
+
(0, core_utils_1.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
686
699
|
let summary;
|
|
687
700
|
if (!hasAttachmentBlobs) {
|
|
688
701
|
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
@@ -707,7 +720,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
707
720
|
// Actually go and create the resolved document
|
|
708
721
|
if (this.service === undefined) {
|
|
709
722
|
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
710
|
-
(0,
|
|
723
|
+
(0, core_utils_1.assert)(this.client.details.type !== summarizerClientType &&
|
|
711
724
|
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
712
725
|
this.service = await (0, driver_utils_1.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
713
726
|
cancel: this._deltaManager.closeAbortController.signal,
|
|
@@ -716,7 +729,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
716
729
|
await this.storageAdapter.connectToService(this.service);
|
|
717
730
|
if (hasAttachmentBlobs) {
|
|
718
731
|
// upload blobs to storage
|
|
719
|
-
(0,
|
|
732
|
+
(0, core_utils_1.assert)(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
720
733
|
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
721
734
|
// support blob handles that only know about the local IDs
|
|
722
735
|
const redirectTable = new Map();
|
|
@@ -754,10 +767,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
754
767
|
this.runtime.setAttachState(container_definitions_1.AttachState.Attached);
|
|
755
768
|
this.emit("attached");
|
|
756
769
|
if (!this.closed) {
|
|
757
|
-
this.
|
|
770
|
+
this.handleDeltaConnectionArg({
|
|
758
771
|
fetchOpsFromStorage: false,
|
|
759
772
|
reason: { text: "createDetached" },
|
|
760
|
-
});
|
|
773
|
+
}, attachProps?.deltaConnection);
|
|
761
774
|
}
|
|
762
775
|
}
|
|
763
776
|
catch (error) {
|
|
@@ -777,7 +790,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
777
790
|
if (currentMode === mode) {
|
|
778
791
|
return;
|
|
779
792
|
}
|
|
780
|
-
const now =
|
|
793
|
+
const now = client_utils_1.performance.now();
|
|
781
794
|
const duration = now - this.setAutoReconnectTime;
|
|
782
795
|
this.setAutoReconnectTime = now;
|
|
783
796
|
this.mc.logger.sendTelemetryEvent({
|
|
@@ -790,10 +803,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
790
803
|
}
|
|
791
804
|
connect() {
|
|
792
805
|
if (this.closed) {
|
|
793
|
-
throw new
|
|
806
|
+
throw new telemetry_utils_1.UsageError(`The Container is closed and cannot be connected`);
|
|
794
807
|
}
|
|
795
808
|
else if (this._attachState !== container_definitions_1.AttachState.Attached) {
|
|
796
|
-
throw new
|
|
809
|
+
throw new telemetry_utils_1.UsageError(`The Container is not attached and cannot be connected`);
|
|
797
810
|
}
|
|
798
811
|
else if (!this.connected) {
|
|
799
812
|
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
@@ -806,8 +819,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
806
819
|
}
|
|
807
820
|
}
|
|
808
821
|
connectInternal(args) {
|
|
809
|
-
(0,
|
|
810
|
-
(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" */);
|
|
811
824
|
// Resume processing ops and connect to delta stream
|
|
812
825
|
this.resumeInternal(args);
|
|
813
826
|
// Set Auto Reconnect Mode
|
|
@@ -816,20 +829,20 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
816
829
|
}
|
|
817
830
|
disconnect() {
|
|
818
831
|
if (this.closed) {
|
|
819
|
-
throw new
|
|
832
|
+
throw new telemetry_utils_1.UsageError(`The Container is closed and cannot be disconnected`);
|
|
820
833
|
}
|
|
821
834
|
else {
|
|
822
835
|
this.disconnectInternal({ text: "DocumentDisconnect" });
|
|
823
836
|
}
|
|
824
837
|
}
|
|
825
838
|
disconnectInternal(reason) {
|
|
826
|
-
(0,
|
|
839
|
+
(0, core_utils_1.assert)(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
|
|
827
840
|
// Set Auto Reconnect Mode
|
|
828
841
|
const mode = contracts_1.ReconnectMode.Disabled;
|
|
829
842
|
this.setAutoReconnectInternal(mode, reason);
|
|
830
843
|
}
|
|
831
844
|
resumeInternal(args) {
|
|
832
|
-
(0,
|
|
845
|
+
(0, core_utils_1.assert)(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
|
|
833
846
|
// Resume processing ops
|
|
834
847
|
if (this.inboundQueuePausedFromInit) {
|
|
835
848
|
this.inboundQueuePausedFromInit = false;
|
|
@@ -866,7 +879,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
866
879
|
return;
|
|
867
880
|
}
|
|
868
881
|
// pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
|
|
869
|
-
const error = new
|
|
882
|
+
const error = new telemetry_utils_1.GenericError("Existing context does not satisfy incoming proposal");
|
|
870
883
|
this.close(error);
|
|
871
884
|
}
|
|
872
885
|
/**
|
|
@@ -917,6 +930,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
917
930
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
918
931
|
*/
|
|
919
932
|
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState, loadToSequenceNumber) {
|
|
933
|
+
const timings = { phase1: client_utils_1.performance.now() };
|
|
920
934
|
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
921
935
|
// Ideally we always connect as "read" by default.
|
|
922
936
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
@@ -947,6 +961,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
947
961
|
});
|
|
948
962
|
}
|
|
949
963
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
964
|
+
timings.phase2 = client_utils_1.performance.now();
|
|
950
965
|
// Fetch specified snapshot.
|
|
951
966
|
const { snapshot, versionId } = pendingLocalState === undefined
|
|
952
967
|
? await this.fetchSnapshotTree(specifiedVersion)
|
|
@@ -956,7 +971,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
956
971
|
this.baseSnapshotBlobs = pendingLocalState.snapshotBlobs;
|
|
957
972
|
}
|
|
958
973
|
else {
|
|
959
|
-
(0,
|
|
974
|
+
(0, core_utils_1.assert)(snapshot !== undefined, 0x237 /* "Snapshot should exist" */);
|
|
960
975
|
if (this.offlineLoadEnabled) {
|
|
961
976
|
this.baseSnapshot = snapshot;
|
|
962
977
|
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
@@ -971,7 +986,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
971
986
|
if (loadMode.pauseAfterLoad === true) {
|
|
972
987
|
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
973
988
|
if (loadMode.opsBeforeReturn === "sequenceNumber") {
|
|
974
|
-
(0,
|
|
989
|
+
(0, core_utils_1.assert)(loadToSequenceNumber !== undefined, 0x727 /* sequenceNumber should be defined */);
|
|
975
990
|
// Note: It is possible that we think the latest snapshot is newer than the specified sequence number
|
|
976
991
|
// due to saved ops that may be replayed after the snapshot.
|
|
977
992
|
// https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
|
|
@@ -1019,22 +1034,21 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1019
1034
|
this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
|
|
1020
1035
|
break;
|
|
1021
1036
|
case "sequenceNumber":
|
|
1022
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
|
|
1023
|
-
break;
|
|
1024
1037
|
case "cached":
|
|
1025
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
1026
|
-
break;
|
|
1027
1038
|
case "all":
|
|
1028
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes,
|
|
1039
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, loadMode.opsBeforeReturn);
|
|
1029
1040
|
break;
|
|
1030
1041
|
default:
|
|
1031
|
-
(0,
|
|
1042
|
+
(0, core_utils_1.unreachableCase)(loadMode.opsBeforeReturn);
|
|
1032
1043
|
}
|
|
1033
1044
|
// ...load in the existing quorum
|
|
1034
1045
|
// Initialize the protocol handler
|
|
1035
1046
|
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
1047
|
+
timings.phase3 = client_utils_1.performance.now();
|
|
1036
1048
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1037
|
-
await this.instantiateRuntime(codeDetails, snapshot,
|
|
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);
|
|
1038
1052
|
// replay saved ops
|
|
1039
1053
|
if (pendingLocalState) {
|
|
1040
1054
|
for (const message of pendingLocalState.savedOps) {
|
|
@@ -1043,9 +1057,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1043
1057
|
await this.runtime.notifyOpReplay?.(message);
|
|
1044
1058
|
}
|
|
1045
1059
|
pendingLocalState.savedOps = [];
|
|
1046
|
-
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
1047
|
-
(0, common_utils_1.assert)(this.clientId === undefined, 0x5d6 /* Unexpected clientId when setting stashed clientId */);
|
|
1048
|
-
this._clientId = pendingLocalState?.clientId;
|
|
1049
1060
|
}
|
|
1050
1061
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
1051
1062
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
@@ -1057,24 +1068,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1057
1068
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1058
1069
|
this._deltaManager.inbound.pause();
|
|
1059
1070
|
}
|
|
1060
|
-
|
|
1061
|
-
case undefined:
|
|
1062
|
-
if (pendingLocalState) {
|
|
1063
|
-
// connect to delta stream now since we did not before
|
|
1064
|
-
this.connectToDeltaStream(connectionArgs);
|
|
1065
|
-
}
|
|
1066
|
-
// intentional fallthrough
|
|
1067
|
-
case "delayed":
|
|
1068
|
-
(0, common_utils_1.assert)(this.inboundQueuePausedFromInit, 0x346 /* inboundQueuePausedFromInit should be true */);
|
|
1069
|
-
this.inboundQueuePausedFromInit = false;
|
|
1070
|
-
this._deltaManager.inbound.resume();
|
|
1071
|
-
this._deltaManager.inboundSignal.resume();
|
|
1072
|
-
break;
|
|
1073
|
-
case "none":
|
|
1074
|
-
break;
|
|
1075
|
-
default:
|
|
1076
|
-
(0, common_utils_1.unreachableCase)(loadMode.deltaConnection);
|
|
1077
|
-
}
|
|
1071
|
+
this.handleDeltaConnectionArg(connectionArgs, loadMode.deltaConnection, pendingLocalState !== undefined);
|
|
1078
1072
|
}
|
|
1079
1073
|
// If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
|
|
1080
1074
|
if (loadToSequenceNumber !== undefined &&
|
|
@@ -1099,6 +1093,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1099
1093
|
}
|
|
1100
1094
|
// Internal context is fully loaded at this point
|
|
1101
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);
|
|
1102
1101
|
return {
|
|
1103
1102
|
sequenceNumber: attributes.sequenceNumber,
|
|
1104
1103
|
version: versionId,
|
|
@@ -1125,7 +1124,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1125
1124
|
}
|
|
1126
1125
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
1127
1126
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
1128
|
-
(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" */);
|
|
1129
1128
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
1130
1129
|
}
|
|
1131
1130
|
const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
|
|
@@ -1281,7 +1280,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1281
1280
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1282
1281
|
deltaManager.inboundSignal.pause();
|
|
1283
1282
|
deltaManager.on("connect", (details, _opsBehind) => {
|
|
1284
|
-
(0,
|
|
1283
|
+
(0, core_utils_1.assert)(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
|
|
1285
1284
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1286
1285
|
});
|
|
1287
1286
|
deltaManager.on("establishingConnection", (reason) => {
|
|
@@ -1327,7 +1326,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1327
1326
|
}
|
|
1328
1327
|
logConnectionStateChangeTelemetry(value, oldState, reason) {
|
|
1329
1328
|
// Log actual event
|
|
1330
|
-
const time =
|
|
1329
|
+
const time = client_utils_1.performance.now();
|
|
1331
1330
|
this.connectionTransitionTimes[value] = time;
|
|
1332
1331
|
const duration = time - this.connectionTransitionTimes[oldState];
|
|
1333
1332
|
let durationFromDisconnected;
|
|
@@ -1368,7 +1367,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1368
1367
|
opsBehind,
|
|
1369
1368
|
online: driver_utils_1.OnlineStatus[(0, driver_utils_1.isOnline)()],
|
|
1370
1369
|
lastVisible: this.lastVisible !== undefined
|
|
1371
|
-
?
|
|
1370
|
+
? client_utils_1.performance.now() - this.lastVisible
|
|
1372
1371
|
: undefined,
|
|
1373
1372
|
checkpointSequenceNumber,
|
|
1374
1373
|
quorumSize: this._protocolHandler?.quorum.getMembers().size,
|
|
@@ -1402,7 +1401,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1402
1401
|
case protocol_definitions_1.MessageType.Summarize:
|
|
1403
1402
|
return this.submitSummaryMessage(contents);
|
|
1404
1403
|
default: {
|
|
1405
|
-
const newError = new
|
|
1404
|
+
const newError = new telemetry_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
|
|
1406
1405
|
this.close(newError);
|
|
1407
1406
|
return -1;
|
|
1408
1407
|
}
|
|
@@ -1454,13 +1453,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1454
1453
|
// That means that if relay service changes settings, such changes will impact only newly booted
|
|
1455
1454
|
// clients.
|
|
1456
1455
|
// All existing will continue to use settings they got earlier.
|
|
1457
|
-
(0,
|
|
1456
|
+
(0, core_utils_1.assert)(serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1458
1457
|
this.noopHeuristic = new noopHeuristic_1.NoopHeuristic(serviceConfiguration.noopTimeFrequency, serviceConfiguration.noopCountFrequency);
|
|
1459
1458
|
this.noopHeuristic.on("wantsNoop", () => {
|
|
1460
1459
|
// On disconnect we notify the heuristic which should prevent it from wanting a noop.
|
|
1461
1460
|
// Hitting this assert would imply we lost activeConnection between notifying the heuristic of a processed message and
|
|
1462
1461
|
// running the microtask that the heuristic queued in response.
|
|
1463
|
-
(0,
|
|
1462
|
+
(0, core_utils_1.assert)(this.activeConnection(), 0x241 /* "Trying to send noop without active connection" */);
|
|
1464
1463
|
this.submitMessage(protocol_definitions_1.MessageType.NoOp);
|
|
1465
1464
|
});
|
|
1466
1465
|
}
|
|
@@ -1508,7 +1507,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1508
1507
|
return { snapshot, versionId: version?.id };
|
|
1509
1508
|
}
|
|
1510
1509
|
async instantiateRuntime(codeDetails, snapshot, pendingLocalState) {
|
|
1511
|
-
(0,
|
|
1510
|
+
(0, core_utils_1.assert)(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1512
1511
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1513
1512
|
// are set. Global requests will still go directly to the loader
|
|
1514
1513
|
const maybeLoader = this.scope;
|
|
@@ -1551,6 +1550,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1551
1550
|
this.runtime.setConnectionState(state && !readonly, this.clientId);
|
|
1552
1551
|
}
|
|
1553
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
|
+
}
|
|
1554
1573
|
}
|
|
1555
1574
|
exports.Container = Container;
|
|
1556
1575
|
//# sourceMappingURL=container.js.map
|