@fluidframework/container-loader 0.52.0 → 0.54.0-47413
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/dist/connectionManager.d.ts +153 -0
- package/dist/connectionManager.d.ts.map +1 -0
- package/dist/connectionManager.js +664 -0
- package/dist/connectionManager.js.map +1 -0
- package/dist/connectionStateHandler.d.ts +1 -0
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +6 -0
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +2 -22
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +121 -151
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +1 -0
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +4 -0
- package/dist/containerContext.js.map +1 -1
- package/dist/contracts.d.ts +112 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +14 -0
- package/dist/contracts.js.map +1 -0
- package/dist/deltaManager.d.ts +26 -142
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +143 -770
- package/dist/deltaManager.js.map +1 -1
- package/dist/loader.d.ts +14 -4
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +10 -4
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +2 -2
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/connectionManager.d.ts +153 -0
- package/lib/connectionManager.d.ts.map +1 -0
- package/lib/connectionManager.js +660 -0
- package/lib/connectionManager.js.map +1 -0
- package/lib/connectionStateHandler.d.ts +1 -0
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +6 -0
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +2 -22
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +122 -152
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +1 -0
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +4 -0
- package/lib/containerContext.js.map +1 -1
- package/lib/contracts.d.ts +112 -0
- package/lib/contracts.d.ts.map +1 -0
- package/lib/contracts.js +11 -0
- package/lib/contracts.js.map +1 -0
- package/lib/deltaManager.d.ts +26 -142
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +147 -774
- package/lib/deltaManager.js.map +1 -1
- package/lib/loader.d.ts +14 -4
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +11 -5
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +2 -2
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/connectionManager.ts +892 -0
- package/src/connectionStateHandler.ts +8 -0
- package/src/container.ts +165 -187
- package/src/containerContext.ts +4 -0
- package/src/contracts.ts +156 -0
- package/src/deltaManager.ts +181 -978
- package/src/loader.ts +59 -27
- package/src/packageVersion.ts +1 -1
package/dist/container.js
CHANGED
|
@@ -21,6 +21,7 @@ const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
|
21
21
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
22
22
|
const audience_1 = require("./audience");
|
|
23
23
|
const containerContext_1 = require("./containerContext");
|
|
24
|
+
const contracts_1 = require("./contracts");
|
|
24
25
|
const deltaManager_1 = require("./deltaManager");
|
|
25
26
|
const deltaManagerProxy_1 = require("./deltaManagerProxy");
|
|
26
27
|
const loader_1 = require("./loader");
|
|
@@ -32,6 +33,7 @@ const containerStorageAdapter_1 = require("./containerStorageAdapter");
|
|
|
32
33
|
const utils_1 = require("./utils");
|
|
33
34
|
const quorum_1 = require("./quorum");
|
|
34
35
|
const collabWindowTracker_1 = require("./collabWindowTracker");
|
|
36
|
+
const connectionManager_1 = require("./connectionManager");
|
|
35
37
|
const detachedContainerRefSeqNumber = 0;
|
|
36
38
|
const dirtyContainerEvent = "dirty";
|
|
37
39
|
const savedContainerEvent = "saved";
|
|
@@ -98,6 +100,8 @@ async function waitContainerToCatchUp(container) {
|
|
|
98
100
|
waitForOps();
|
|
99
101
|
};
|
|
100
102
|
container.on(telemetry_utils_1.connectedEventName, callback);
|
|
103
|
+
// TODO: Remove null check after next release #8523
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
101
105
|
container.resume();
|
|
102
106
|
});
|
|
103
107
|
}
|
|
@@ -144,7 +148,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
144
148
|
this.subLogger = telemetry_utils_1.ChildLogger.create(loader.services.subLogger, undefined, {
|
|
145
149
|
all: {
|
|
146
150
|
clientType,
|
|
147
|
-
loaderVersion: packageVersion_1.pkgVersion,
|
|
148
151
|
containerId: uuid_1.v4(),
|
|
149
152
|
docId: () => this.id,
|
|
150
153
|
containerAttachState: () => this._attachState,
|
|
@@ -173,7 +176,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
173
176
|
this.connectionStateHandler = new connectionStateHandler_1.ConnectionStateHandler({
|
|
174
177
|
protocolHandler: () => this._protocolHandler,
|
|
175
178
|
logConnectionStateChangeTelemetry: (value, oldState, reason) => this.logConnectionStateChangeTelemetry(value, oldState, reason),
|
|
176
|
-
shouldClientJoinWrite: () => this._deltaManager.shouldJoinWrite(),
|
|
179
|
+
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
177
180
|
maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
|
|
178
181
|
logConnectionIssue: (eventName) => {
|
|
179
182
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
@@ -229,22 +232,22 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
229
232
|
switch (event) {
|
|
230
233
|
case dirtyContainerEvent:
|
|
231
234
|
if (this._dirtyContainer) {
|
|
232
|
-
listener(
|
|
235
|
+
listener();
|
|
233
236
|
}
|
|
234
237
|
break;
|
|
235
238
|
case savedContainerEvent:
|
|
236
239
|
if (!this._dirtyContainer) {
|
|
237
|
-
listener(
|
|
240
|
+
listener();
|
|
238
241
|
}
|
|
239
242
|
break;
|
|
240
243
|
case telemetry_utils_1.connectedEventName:
|
|
241
244
|
if (this.connected) {
|
|
242
|
-
listener(
|
|
245
|
+
listener(this.clientId);
|
|
243
246
|
}
|
|
244
247
|
break;
|
|
245
248
|
case telemetry_utils_1.disconnectedEventName:
|
|
246
249
|
if (!this.connected) {
|
|
247
|
-
listener(
|
|
250
|
+
listener();
|
|
248
251
|
}
|
|
249
252
|
break;
|
|
250
253
|
default:
|
|
@@ -291,16 +294,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
291
294
|
container.close(err);
|
|
292
295
|
onClosed(err);
|
|
293
296
|
});
|
|
294
|
-
}), { start: true, end: true, cancel: "
|
|
297
|
+
}), { start: true, end: true, cancel: "generic" });
|
|
295
298
|
}
|
|
296
299
|
/**
|
|
297
300
|
* Create a new container in a detached state.
|
|
298
301
|
*/
|
|
299
302
|
static async createDetached(loader, codeDetails) {
|
|
300
303
|
const container = new Container(loader, {});
|
|
301
|
-
container.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
305
|
+
container._lifecycleState = "loading";
|
|
306
|
+
await container.createDetached(codeDetails);
|
|
307
|
+
return container;
|
|
308
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
304
309
|
}
|
|
305
310
|
/**
|
|
306
311
|
* Create a new container in a detached state that is initialized with a
|
|
@@ -308,10 +313,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
308
313
|
*/
|
|
309
314
|
static async rehydrateDetachedFromSnapshot(loader, snapshot) {
|
|
310
315
|
const container = new Container(loader, {});
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
316
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
317
|
+
const deserializedSummary = JSON.parse(snapshot);
|
|
318
|
+
container._lifecycleState = "loading";
|
|
319
|
+
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
320
|
+
return container;
|
|
321
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
315
322
|
}
|
|
316
323
|
get loaded() {
|
|
317
324
|
return (this._lifecycleState !== "created" && this._lifecycleState !== "loading");
|
|
@@ -349,6 +356,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
349
356
|
}
|
|
350
357
|
return this._protocolHandler;
|
|
351
358
|
}
|
|
359
|
+
get connectionMode() { return this._deltaManager.connectionManager.connectionMode; }
|
|
352
360
|
get IFluidRouter() { return this; }
|
|
353
361
|
get resolvedUrl() {
|
|
354
362
|
return this._resolvedUrl;
|
|
@@ -356,31 +364,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
356
364
|
get loadedFromVersion() {
|
|
357
365
|
return this._loadedFromVersion;
|
|
358
366
|
}
|
|
359
|
-
/**
|
|
360
|
-
* Tells if container is in read-only mode.
|
|
361
|
-
* Data stores should listen for "readonly" notifications and disallow user making changes to data stores.
|
|
362
|
-
* Readonly state can be because of no storage write permission,
|
|
363
|
-
* or due to host forcing readonly mode for container.
|
|
364
|
-
*
|
|
365
|
-
* We do not differentiate here between no write access to storage vs. host disallowing changes to container -
|
|
366
|
-
* in all cases container runtime and data stores should respect readonly state and not allow local changes.
|
|
367
|
-
*
|
|
368
|
-
* It is undefined if we have not yet established websocket connection
|
|
369
|
-
* and do not know if user has write access to a file.
|
|
370
|
-
* @deprecated - use readOnlyInfo
|
|
371
|
-
*/
|
|
372
|
-
get readonly() {
|
|
373
|
-
return this._deltaManager.readonly;
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Tells if user has no write permissions for file in storage
|
|
377
|
-
* It is undefined if we have not yet established websocket connection
|
|
378
|
-
* and do not know if user has write access to a file.
|
|
379
|
-
* @deprecated - use readOnlyInfo
|
|
380
|
-
*/
|
|
381
|
-
get readonlyPermissions() {
|
|
382
|
-
return this._deltaManager.readonlyPermissions;
|
|
383
|
-
}
|
|
384
367
|
get readOnlyInfo() {
|
|
385
368
|
return this._deltaManager.readOnlyInfo;
|
|
386
369
|
}
|
|
@@ -388,7 +371,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
388
371
|
* Tracks host requiring read-only mode.
|
|
389
372
|
*/
|
|
390
373
|
forceReadonly(readonly) {
|
|
391
|
-
this._deltaManager.forceReadonly(readonly);
|
|
374
|
+
this._deltaManager.connectionManager.forceReadonly(readonly);
|
|
392
375
|
}
|
|
393
376
|
get id() {
|
|
394
377
|
var _a, _b;
|
|
@@ -422,7 +405,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
422
405
|
* Set once this.connected is true, otherwise undefined
|
|
423
406
|
*/
|
|
424
407
|
get scopes() {
|
|
425
|
-
return this._deltaManager.scopes;
|
|
408
|
+
return this._deltaManager.connectionManager.scopes;
|
|
426
409
|
}
|
|
427
410
|
get clientDetails() {
|
|
428
411
|
return this._deltaManager.clientDetails;
|
|
@@ -489,6 +472,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
489
472
|
try {
|
|
490
473
|
this._deltaManager.close(error);
|
|
491
474
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
475
|
+
this.connectionStateHandler.dispose();
|
|
492
476
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
493
477
|
common_utils_1.assert(this.connectionState === ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
494
478
|
(_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
|
|
@@ -538,89 +522,88 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
538
522
|
return JSON.stringify(combinedSummary);
|
|
539
523
|
}
|
|
540
524
|
async attach(request) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
// If container is already attached or attach is in progress, throw an error.
|
|
545
|
-
common_utils_1.assert(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
546
|
-
this.attachStarted = true;
|
|
547
|
-
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
548
|
-
const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
|
|
549
|
-
&& this.loader.services.detachedBlobStorage.size > 0;
|
|
550
|
-
try {
|
|
551
|
-
common_utils_1.assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
552
|
-
let summary;
|
|
553
|
-
if (!hasAttachmentBlobs) {
|
|
554
|
-
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
555
|
-
// semantics around what the attach means as far as async code goes.
|
|
556
|
-
const appSummary = this.context.createSummary();
|
|
557
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
558
|
-
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
559
|
-
// Set the state as attaching as we are starting the process of attaching container.
|
|
560
|
-
// This should be fired after taking the summary because it is the place where we are
|
|
561
|
-
// starting to attach the container to storage.
|
|
562
|
-
// Also, this should only be fired in detached container.
|
|
563
|
-
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
564
|
-
this.context.notifyAttaching();
|
|
565
|
-
}
|
|
566
|
-
// Actually go and create the resolved document
|
|
567
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
568
|
-
driver_utils_1.ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
569
|
-
if (this.service === undefined) {
|
|
570
|
-
this.service = await driver_utils_1.runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
|
|
525
|
+
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.logger, { eventName: "Attach" }, async () => {
|
|
526
|
+
if (this._lifecycleState !== "loaded") {
|
|
527
|
+
throw new container_utils_1.UsageError(`containerNotValidForAttach [${this._lifecycleState}]`);
|
|
571
528
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
this.
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
529
|
+
// If container is already attached or attach is in progress, throw an error.
|
|
530
|
+
common_utils_1.assert(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
531
|
+
this.attachStarted = true;
|
|
532
|
+
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
533
|
+
const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
|
|
534
|
+
&& this.loader.services.detachedBlobStorage.size > 0;
|
|
535
|
+
try {
|
|
536
|
+
common_utils_1.assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
537
|
+
let summary;
|
|
538
|
+
if (!hasAttachmentBlobs) {
|
|
539
|
+
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
540
|
+
// semantics around what the attach means as far as async code goes.
|
|
541
|
+
const appSummary = this.context.createSummary();
|
|
542
|
+
const protocolSummary = this.captureProtocolSummary();
|
|
543
|
+
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
544
|
+
// Set the state as attaching as we are starting the process of attaching container.
|
|
545
|
+
// This should be fired after taking the summary because it is the place where we are
|
|
546
|
+
// starting to attach the container to storage.
|
|
547
|
+
// Also, this should only be fired in detached container.
|
|
548
|
+
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
549
|
+
this.context.notifyAttaching();
|
|
550
|
+
}
|
|
551
|
+
// Actually go and create the resolved document
|
|
552
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
553
|
+
driver_utils_1.ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
554
|
+
if (this.service === undefined) {
|
|
555
|
+
this.service = await driver_utils_1.runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
|
|
556
|
+
}
|
|
557
|
+
const resolvedUrl = this.service.resolvedUrl;
|
|
558
|
+
driver_utils_1.ensureFluidResolvedUrl(resolvedUrl);
|
|
559
|
+
this._resolvedUrl = resolvedUrl;
|
|
560
|
+
await this.connectStorageService();
|
|
561
|
+
if (hasAttachmentBlobs) {
|
|
562
|
+
// upload blobs to storage
|
|
563
|
+
common_utils_1.assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
564
|
+
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
565
|
+
// support blob handles that only know about the local IDs
|
|
566
|
+
const redirectTable = new Map();
|
|
567
|
+
// if new blobs are added while uploading, upload them too
|
|
568
|
+
while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
|
|
569
|
+
const newIds = this.loader.services.detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
|
|
570
|
+
for (const id of newIds) {
|
|
571
|
+
const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
|
|
572
|
+
const response = await this.storageService.createBlob(blob);
|
|
573
|
+
redirectTable.set(id, response.id);
|
|
574
|
+
}
|
|
589
575
|
}
|
|
576
|
+
// take summary and upload
|
|
577
|
+
const appSummary = this.context.createSummary(redirectTable);
|
|
578
|
+
const protocolSummary = this.captureProtocolSummary();
|
|
579
|
+
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
580
|
+
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
581
|
+
this.context.notifyAttaching();
|
|
582
|
+
await this.storageService.uploadSummaryWithContext(summary, {
|
|
583
|
+
referenceSequenceNumber: 0,
|
|
584
|
+
ackHandle: undefined,
|
|
585
|
+
proposalHandle: undefined,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
this._attachState = container_definitions_1.AttachState.Attached;
|
|
589
|
+
this.emit("attached");
|
|
590
|
+
// Propagate current connection state through the system.
|
|
591
|
+
this.propagateConnectionState();
|
|
592
|
+
if (!this.closed) {
|
|
593
|
+
this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
|
|
590
594
|
}
|
|
591
|
-
// take summary and upload
|
|
592
|
-
const appSummary = this.context.createSummary(redirectTable);
|
|
593
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
594
|
-
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
595
|
-
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
596
|
-
this.context.notifyAttaching();
|
|
597
|
-
await this.storageService.uploadSummaryWithContext(summary, {
|
|
598
|
-
referenceSequenceNumber: 0,
|
|
599
|
-
ackHandle: undefined,
|
|
600
|
-
proposalHandle: undefined,
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
this._attachState = container_definitions_1.AttachState.Attached;
|
|
604
|
-
this.emit("attached");
|
|
605
|
-
// Propagate current connection state through the system.
|
|
606
|
-
this.propagateConnectionState();
|
|
607
|
-
if (!this.closed) {
|
|
608
|
-
this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
|
|
609
595
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
596
|
+
catch (error) {
|
|
597
|
+
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
598
|
+
const newError = telemetry_utils_1.normalizeError(error);
|
|
599
|
+
const resolvedUrl = this.resolvedUrl;
|
|
600
|
+
if (driver_utils_1.isFluidResolvedUrl(resolvedUrl)) {
|
|
601
|
+
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
602
|
+
}
|
|
603
|
+
this.close(newError);
|
|
604
|
+
throw newError;
|
|
620
605
|
}
|
|
621
|
-
|
|
622
|
-
throw newError;
|
|
623
|
-
}
|
|
606
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
624
607
|
}
|
|
625
608
|
async request(path) {
|
|
626
609
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
|
|
@@ -648,8 +631,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
648
631
|
if (this.closed) {
|
|
649
632
|
throw new Error("Attempting to setAutoReconnect() a closed Container");
|
|
650
633
|
}
|
|
651
|
-
const mode = reconnect ?
|
|
652
|
-
const currentMode = this._deltaManager.reconnectMode;
|
|
634
|
+
const mode = reconnect ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Disabled;
|
|
635
|
+
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
653
636
|
if (currentMode === mode) {
|
|
654
637
|
return;
|
|
655
638
|
}
|
|
@@ -658,11 +641,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
658
641
|
this.setAutoReconnectTime = now;
|
|
659
642
|
this.logger.sendTelemetryEvent({
|
|
660
643
|
eventName: reconnect ? "AutoReconnectEnabled" : "AutoReconnectDisabled",
|
|
661
|
-
connectionMode: this.
|
|
644
|
+
connectionMode: this.connectionMode,
|
|
662
645
|
connectionState: ConnectionState[this.connectionState],
|
|
663
646
|
duration,
|
|
664
647
|
});
|
|
665
|
-
this._deltaManager.setAutoReconnect(mode);
|
|
648
|
+
this._deltaManager.connectionManager.setAutoReconnect(mode);
|
|
666
649
|
// If container state is not attached and resumed, then don't connect to delta stream. Also don't set the
|
|
667
650
|
// manual reconnection flag to true as we haven't made the initial connection yet.
|
|
668
651
|
if (reconnect && this._attachState === container_definitions_1.AttachState.Attached && this.resumedOpProcessingAfterLoad) {
|
|
@@ -671,13 +654,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
671
654
|
this.manualReconnectInProgress = true;
|
|
672
655
|
}
|
|
673
656
|
// Ensure connection to web socket
|
|
674
|
-
this.connectToDeltaStream({ reason: "autoReconnect" })
|
|
675
|
-
// All errors are reported through events ("error" / "disconnected") and telemetry in DeltaManager
|
|
676
|
-
// So there shouldn't be a need to record error here.
|
|
677
|
-
// But we have number of cases where reconnects do not happen, and no errors are recorded, so
|
|
678
|
-
// adding this log point for easier diagnostics
|
|
679
|
-
this.logger.sendTelemetryEvent({ eventName: "setAutoReconnectError" }, error);
|
|
680
|
-
});
|
|
657
|
+
this.connectToDeltaStream({ reason: "autoReconnect" });
|
|
681
658
|
}
|
|
682
659
|
}
|
|
683
660
|
resume() {
|
|
@@ -697,8 +674,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
697
674
|
this._deltaManager.inboundSignal.resume();
|
|
698
675
|
}
|
|
699
676
|
// Ensure connection to web socket
|
|
700
|
-
|
|
701
|
-
this.connectToDeltaStream(args).catch(() => { });
|
|
677
|
+
this.connectToDeltaStream(args);
|
|
702
678
|
}
|
|
703
679
|
/**
|
|
704
680
|
* Raise non-critical error to host. Calling this API will not close container.
|
|
@@ -825,13 +801,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
825
801
|
this.connectionTransitionTimes[ConnectionState.Disconnected] = common_utils_1.performance.now();
|
|
826
802
|
}
|
|
827
803
|
}
|
|
828
|
-
|
|
804
|
+
connectToDeltaStream(args) {
|
|
829
805
|
this.recordConnectStartTime();
|
|
830
806
|
// All agents need "write" access, including summarizer.
|
|
831
807
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
832
808
|
args.mode = "write";
|
|
833
809
|
}
|
|
834
|
-
|
|
810
|
+
this._deltaManager.connect(args);
|
|
835
811
|
}
|
|
836
812
|
/**
|
|
837
813
|
* Load container.
|
|
@@ -845,7 +821,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
845
821
|
throw new Error("Attempting to load without a resolved url");
|
|
846
822
|
}
|
|
847
823
|
this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger);
|
|
848
|
-
let startConnectionP;
|
|
849
824
|
// Ideally we always connect as "read" by default.
|
|
850
825
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
851
826
|
// We should not rely on it by (one of them will address the issue, but we need to address both)
|
|
@@ -859,8 +834,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
859
834
|
// Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
|
|
860
835
|
// DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
|
|
861
836
|
if (loadMode.deltaConnection === undefined) {
|
|
862
|
-
|
|
863
|
-
startConnectionP.catch((error) => { });
|
|
837
|
+
this.connectToDeltaStream(connectionArgs);
|
|
864
838
|
}
|
|
865
839
|
await this.connectStorageService();
|
|
866
840
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
@@ -1114,6 +1088,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1114
1088
|
if (this.clientDetailsOverride !== undefined) {
|
|
1115
1089
|
merge_1.default(client.details, this.clientDetailsOverride);
|
|
1116
1090
|
}
|
|
1091
|
+
client.details.environment = [client.details.environment, ` loaderVersion:${packageVersion_1.pkgVersion}`].join(";");
|
|
1117
1092
|
return client;
|
|
1118
1093
|
}
|
|
1119
1094
|
/**
|
|
@@ -1123,17 +1098,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1123
1098
|
* If it's not true, runtime is not in position to send ops.
|
|
1124
1099
|
*/
|
|
1125
1100
|
activeConnection() {
|
|
1126
|
-
|
|
1127
|
-
this.
|
|
1128
|
-
// Check for presence of current client in quorum for "write" connections - inactive clients
|
|
1129
|
-
// would get leave op after some long timeout (5 min) and that should automatically transition
|
|
1130
|
-
// state to "read" mode.
|
|
1131
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1132
|
-
common_utils_1.assert(!active || this.getQuorum().getMember(this.clientId) !== undefined, 0x276 /* "active connection not present in quorum" */);
|
|
1133
|
-
return active;
|
|
1101
|
+
return this.connectionState === ConnectionState.Connected &&
|
|
1102
|
+
this.connectionMode === "write";
|
|
1134
1103
|
}
|
|
1135
1104
|
createDeltaManager() {
|
|
1136
|
-
const
|
|
1105
|
+
const serviceProvider = () => this.service;
|
|
1106
|
+
const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, telemetry_utils_1.ChildLogger.create(this.subLogger, "DeltaManager"), () => this.activeConnection(), (props) => new connectionManager_1.ConnectionManager(serviceProvider, this.client, this._canReconnect, telemetry_utils_1.ChildLogger.create(this.subLogger, "ConnectionManager"), props));
|
|
1137
1107
|
// Disable inbound queues as Container is not ready to accept any ops until we are fully loaded!
|
|
1138
1108
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1139
1109
|
deltaManager.inbound.pause();
|
|
@@ -1141,12 +1111,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1141
1111
|
deltaManager.inboundSignal.pause();
|
|
1142
1112
|
deltaManager.on("connect", (details, opsBehind) => {
|
|
1143
1113
|
var _a;
|
|
1144
|
-
this.connectionStateHandler.receivedConnectEvent(this._deltaManager.connectionMode, details);
|
|
1145
1114
|
// Back-compat for new client and old server.
|
|
1146
1115
|
this._audience.clear();
|
|
1147
1116
|
for (const priorClient of (_a = details.initialClients) !== null && _a !== void 0 ? _a : []) {
|
|
1148
1117
|
this._audience.addMember(priorClient.clientId, priorClient.client);
|
|
1149
1118
|
}
|
|
1119
|
+
this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details);
|
|
1150
1120
|
});
|
|
1151
1121
|
deltaManager.on("disconnect", (reason) => {
|
|
1152
1122
|
this.manualReconnectInProgress = false;
|
|
@@ -1184,7 +1154,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1184
1154
|
let checkpointSequenceNumber;
|
|
1185
1155
|
let opsBehind;
|
|
1186
1156
|
if (value === ConnectionState.Disconnected) {
|
|
1187
|
-
autoReconnect = this._deltaManager.reconnectMode;
|
|
1157
|
+
autoReconnect = this._deltaManager.connectionManager.reconnectMode;
|
|
1188
1158
|
}
|
|
1189
1159
|
else {
|
|
1190
1160
|
if (value === ConnectionState.Connected) {
|
|
@@ -1211,8 +1181,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1211
1181
|
this.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${ConnectionState[value]}`, from: ConnectionState[oldState], duration,
|
|
1212
1182
|
durationFromDisconnected,
|
|
1213
1183
|
reason,
|
|
1214
|
-
connectionInitiationReason,
|
|
1215
|
-
opsBehind, online: driver_utils_1.OnlineStatus[driver_utils_1.isOnline()], lastVisible: this.lastVisible !== undefined ? common_utils_1.performance.now() - this.lastVisible : undefined, checkpointSequenceNumber }, this._deltaManager.connectionProps
|
|
1184
|
+
connectionInitiationReason, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
|
|
1185
|
+
opsBehind, online: driver_utils_1.OnlineStatus[driver_utils_1.isOnline()], lastVisible: this.lastVisible !== undefined ? common_utils_1.performance.now() - this.lastVisible : undefined, checkpointSequenceNumber }, this._deltaManager.connectionProps));
|
|
1216
1186
|
if (value === ConnectionState.Connected) {
|
|
1217
1187
|
this.firstConnection = false;
|
|
1218
1188
|
this.manualReconnectInProgress = false;
|
|
@@ -1221,7 +1191,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1221
1191
|
propagateConnectionState() {
|
|
1222
1192
|
const logOpsOnReconnect = this.connectionState === ConnectionState.Connected &&
|
|
1223
1193
|
!this.firstConnection &&
|
|
1224
|
-
this.
|
|
1194
|
+
this.connectionMode === "write";
|
|
1225
1195
|
if (logOpsOnReconnect) {
|
|
1226
1196
|
this.messageCountAfterDisconnection = 0;
|
|
1227
1197
|
}
|