@fluidframework/container-loader 2.0.0-dev.5.3.2.178189 → 2.0.0-dev.6.4.0.191457
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +131 -0
- package/README.md +10 -6
- package/dist/audience.d.ts +1 -0
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +5 -3
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.js +2 -2
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +5 -5
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +97 -93
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +15 -14
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +50 -52
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +20 -9
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +327 -277
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -7
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +2 -14
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +12 -13
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +21 -8
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +3 -3
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.d.ts +30 -0
- package/dist/debugLogger.d.ts.map +1 -0
- package/dist/debugLogger.js +95 -0
- package/dist/debugLogger.js.map +1 -0
- package/dist/deltaManager.d.ts +21 -10
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +114 -66
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +10 -10
- package/dist/deltaQueue.js.map +1 -1
- package/dist/disposal.d.ts +2 -2
- package/dist/disposal.d.ts.map +1 -1
- package/dist/disposal.js +1 -1
- package/dist/disposal.js.map +1 -1
- package/dist/error.d.ts +23 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +32 -0
- package/dist/error.js.map +1 -0
- package/dist/loader.d.ts +22 -3
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +82 -51
- package/dist/loader.js.map +1 -1
- package/dist/noopHeuristic.d.ts +2 -2
- package/dist/noopHeuristic.d.ts.map +1 -1
- package/dist/noopHeuristic.js +6 -5
- package/dist/noopHeuristic.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +4 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +25 -4
- package/dist/protocol.js.map +1 -1
- package/dist/quorum.d.ts +4 -1
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js +1 -13
- package/dist/quorum.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -4
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts +8 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +30 -11
- package/dist/utils.js.map +1 -1
- package/lib/audience.d.ts +1 -0
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +4 -2
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.js +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +5 -5
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +74 -67
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +15 -14
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +27 -29
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +20 -9
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +288 -238
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -7
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +2 -14
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +5 -6
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +21 -8
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +3 -3
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.d.ts +30 -0
- package/lib/debugLogger.d.ts.map +1 -0
- package/lib/debugLogger.js +91 -0
- package/lib/debugLogger.js.map +1 -0
- package/lib/deltaManager.d.ts +21 -10
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +88 -37
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +3 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/disposal.d.ts +2 -2
- package/lib/disposal.d.ts.map +1 -1
- package/lib/disposal.js +1 -1
- package/lib/disposal.js.map +1 -1
- package/lib/error.d.ts +23 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +28 -0
- package/lib/error.js.map +1 -0
- package/lib/loader.d.ts +22 -3
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +82 -51
- package/lib/loader.js.map +1 -1
- package/lib/noopHeuristic.d.ts +2 -2
- package/lib/noopHeuristic.d.ts.map +1 -1
- package/lib/noopHeuristic.js +2 -1
- package/lib/noopHeuristic.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +4 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +25 -4
- package/lib/protocol.js.map +1 -1
- package/lib/quorum.d.ts +4 -1
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js +0 -11
- package/lib/quorum.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +2 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts +8 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +25 -7
- package/lib/utils.js.map +1 -1
- package/package.json +26 -32
- package/src/audience.ts +7 -1
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +75 -51
- package/src/connectionStateHandler.ts +31 -38
- package/src/container.ts +335 -240
- package/src/containerContext.ts +0 -16
- package/src/containerStorageAdapter.ts +2 -1
- package/src/contracts.ts +27 -11
- package/src/debugLogger.ts +113 -0
- package/src/deltaManager.ts +84 -34
- package/src/deltaQueue.ts +2 -1
- package/src/disposal.ts +2 -2
- package/src/error.ts +44 -0
- package/src/loader.ts +83 -35
- package/src/noopHeuristic.ts +3 -2
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +33 -2
- package/src/quorum.ts +0 -10
- package/src/retriableDocumentStorageService.ts +2 -4
- package/src/utils.ts +33 -8
package/src/connectionManager.ts
CHANGED
|
@@ -3,16 +3,14 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { default as AbortController } from "abort-controller";
|
|
7
6
|
import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
|
|
8
|
-
import { assert
|
|
7
|
+
import { assert } from "@fluidframework/core-utils";
|
|
8
|
+
import { performance, TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
9
9
|
import {
|
|
10
|
+
ICriticalContainerError,
|
|
10
11
|
IDeltaQueue,
|
|
11
12
|
ReadOnlyInfo,
|
|
12
|
-
IConnectionDetailsInternal,
|
|
13
|
-
ICriticalContainerError,
|
|
14
13
|
} from "@fluidframework/container-definitions";
|
|
15
|
-
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
16
14
|
import {
|
|
17
15
|
IAnyDriverError,
|
|
18
16
|
IDocumentService,
|
|
@@ -26,6 +24,7 @@ import {
|
|
|
26
24
|
getRetryDelayFromError,
|
|
27
25
|
logNetworkFailure,
|
|
28
26
|
isRuntimeMessage,
|
|
27
|
+
calculateMaxWaitTime,
|
|
29
28
|
} from "@fluidframework/driver-utils";
|
|
30
29
|
import {
|
|
31
30
|
ConnectionMode,
|
|
@@ -44,16 +43,23 @@ import {
|
|
|
44
43
|
ISequencedDocumentSystemMessage,
|
|
45
44
|
} from "@fluidframework/protocol-definitions";
|
|
46
45
|
import {
|
|
46
|
+
formatTick,
|
|
47
|
+
GenericError,
|
|
47
48
|
ITelemetryLoggerExt,
|
|
48
|
-
TelemetryLogger,
|
|
49
49
|
normalizeError,
|
|
50
|
+
UsageError,
|
|
50
51
|
} from "@fluidframework/telemetry-utils";
|
|
51
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
ReconnectMode,
|
|
54
|
+
IConnectionManager,
|
|
55
|
+
IConnectionManagerFactoryArgs,
|
|
56
|
+
IConnectionDetailsInternal,
|
|
57
|
+
IConnectionStateChangeReason,
|
|
58
|
+
} from "./contracts";
|
|
52
59
|
import { DeltaQueue } from "./deltaQueue";
|
|
53
60
|
import { SignalType } from "./protocol";
|
|
54
61
|
import { isDeltaStreamConnectionForbiddenError } from "./utils";
|
|
55
62
|
|
|
56
|
-
const MaxReconnectDelayInMs = 8000;
|
|
57
63
|
const InitialReconnectDelayInMs = 1000;
|
|
58
64
|
const DefaultChunkSize = 16 * 1024;
|
|
59
65
|
|
|
@@ -337,7 +343,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
337
343
|
|
|
338
344
|
private static detailsFromConnection(
|
|
339
345
|
connection: IDocumentDeltaConnection,
|
|
340
|
-
reason:
|
|
346
|
+
reason: IConnectionStateChangeReason,
|
|
341
347
|
): IConnectionDetailsInternal {
|
|
342
348
|
return {
|
|
343
349
|
claims: connection.claims,
|
|
@@ -356,7 +362,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
356
362
|
constructor(
|
|
357
363
|
private readonly serviceProvider: () => IDocumentService | undefined,
|
|
358
364
|
public readonly containerDirty: () => boolean,
|
|
359
|
-
private client: IClient,
|
|
365
|
+
private readonly client: IClient,
|
|
360
366
|
reconnectAllowed: boolean,
|
|
361
367
|
private readonly logger: ITelemetryLoggerExt,
|
|
362
368
|
private readonly props: IConnectionManagerFactoryArgs,
|
|
@@ -390,8 +396,12 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
390
396
|
|
|
391
397
|
this._outbound.clear();
|
|
392
398
|
|
|
393
|
-
const disconnectReason =
|
|
399
|
+
const disconnectReason: IConnectionStateChangeReason = {
|
|
400
|
+
text: "Closing DeltaManager",
|
|
401
|
+
error,
|
|
402
|
+
};
|
|
394
403
|
|
|
404
|
+
const oldReadonlyValue = this.readonly;
|
|
395
405
|
// This raises "disconnect" event if we have active connection.
|
|
396
406
|
this.disconnectFromDeltaStream(disconnectReason);
|
|
397
407
|
|
|
@@ -399,7 +409,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
399
409
|
// Notify everyone we are in read-only state.
|
|
400
410
|
// Useful for data stores in case we hit some critical error,
|
|
401
411
|
// to switch to a mode where user edits are not accepted
|
|
402
|
-
this.set_readonlyPermissions(true);
|
|
412
|
+
this.set_readonlyPermissions(true, oldReadonlyValue);
|
|
403
413
|
}
|
|
404
414
|
}
|
|
405
415
|
|
|
@@ -407,7 +417,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
407
417
|
* Enables or disables automatic reconnecting.
|
|
408
418
|
* Will throw an error if reconnectMode set to Never.
|
|
409
419
|
*/
|
|
410
|
-
public setAutoReconnect(mode: ReconnectMode): void {
|
|
420
|
+
public setAutoReconnect(mode: ReconnectMode, reason: IConnectionStateChangeReason): void {
|
|
411
421
|
assert(
|
|
412
422
|
mode !== ReconnectMode.Never && this._reconnectMode !== ReconnectMode.Never,
|
|
413
423
|
0x278 /* "API is not supported for non-connecting or closed container" */,
|
|
@@ -417,7 +427,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
417
427
|
|
|
418
428
|
if (mode !== ReconnectMode.Enabled) {
|
|
419
429
|
// immediately disconnect - do not rely on service eventually dropping connection.
|
|
420
|
-
this.disconnectFromDeltaStream(
|
|
430
|
+
this.disconnectFromDeltaStream(reason);
|
|
421
431
|
}
|
|
422
432
|
}
|
|
423
433
|
|
|
@@ -464,32 +474,37 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
464
474
|
this.logger.sendErrorEvent({ eventName: "ForceReadonlyPendingChanged" });
|
|
465
475
|
}
|
|
466
476
|
|
|
467
|
-
reconnect = this.disconnectFromDeltaStream("Force readonly");
|
|
477
|
+
reconnect = this.disconnectFromDeltaStream({ text: "Force readonly" });
|
|
468
478
|
}
|
|
469
479
|
this.props.readonlyChangeHandler(this.readonly);
|
|
470
480
|
if (reconnect) {
|
|
471
481
|
// reconnect if we disconnected from before.
|
|
472
|
-
this.triggerConnect("Force Readonly", "read");
|
|
482
|
+
this.triggerConnect({ text: "Force Readonly" }, "read");
|
|
473
483
|
}
|
|
474
484
|
}
|
|
475
485
|
}
|
|
476
486
|
|
|
477
|
-
private set_readonlyPermissions(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
487
|
+
private set_readonlyPermissions(
|
|
488
|
+
newReadonlyValue: boolean,
|
|
489
|
+
oldReadonlyValue: boolean | undefined,
|
|
490
|
+
) {
|
|
491
|
+
this._readonlyPermissions = newReadonlyValue;
|
|
492
|
+
if (oldReadonlyValue !== this.readonly) {
|
|
481
493
|
this.props.readonlyChangeHandler(this.readonly);
|
|
482
494
|
}
|
|
483
495
|
}
|
|
484
496
|
|
|
485
|
-
public connect(reason:
|
|
486
|
-
this.connectCore(reason, connectionMode).catch((
|
|
487
|
-
const normalizedError = normalizeError(
|
|
497
|
+
public connect(reason: IConnectionStateChangeReason, connectionMode?: ConnectionMode) {
|
|
498
|
+
this.connectCore(reason, connectionMode).catch((e) => {
|
|
499
|
+
const normalizedError = normalizeError(e, { props: fatalConnectErrorProp });
|
|
488
500
|
this.props.closeHandler(normalizedError);
|
|
489
501
|
});
|
|
490
502
|
}
|
|
491
503
|
|
|
492
|
-
private async connectCore(
|
|
504
|
+
private async connectCore(
|
|
505
|
+
reason: IConnectionStateChangeReason,
|
|
506
|
+
connectionMode?: ConnectionMode,
|
|
507
|
+
): Promise<void> {
|
|
493
508
|
assert(!this._disposed, 0x26a /* "not closed" */);
|
|
494
509
|
|
|
495
510
|
if (this.connection !== undefined) {
|
|
@@ -553,7 +568,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
553
568
|
this.logger.sendTelemetryEvent({
|
|
554
569
|
eventName: "ConnectionAttemptCancelled",
|
|
555
570
|
attempts: connectRepeatCount,
|
|
556
|
-
duration:
|
|
571
|
+
duration: formatTick(performance.now() - connectStartTime),
|
|
557
572
|
connectionEstablished: false,
|
|
558
573
|
});
|
|
559
574
|
return;
|
|
@@ -593,7 +608,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
593
608
|
attempts: connectRepeatCount,
|
|
594
609
|
delay: delayMs, // milliseconds
|
|
595
610
|
eventName: "DeltaConnectionFailureToConnect",
|
|
596
|
-
duration:
|
|
611
|
+
duration: formatTick(performance.now() - connectStartTime),
|
|
597
612
|
},
|
|
598
613
|
origError,
|
|
599
614
|
);
|
|
@@ -613,7 +628,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
613
628
|
// We skip this delay if we're confident we're offline, because we probably just need to wait to come back online.
|
|
614
629
|
await new Promise<void>((resolve) => {
|
|
615
630
|
setTimeout(resolve, delayMs);
|
|
616
|
-
delayMs = Math.min(delayMs * 2,
|
|
631
|
+
delayMs = Math.min(delayMs * 2, calculateMaxWaitTime(origError));
|
|
617
632
|
});
|
|
618
633
|
}
|
|
619
634
|
|
|
@@ -639,7 +654,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
639
654
|
{
|
|
640
655
|
eventName: "MultipleDeltaConnectionFailures",
|
|
641
656
|
attempts: connectRepeatCount,
|
|
642
|
-
duration:
|
|
657
|
+
duration: formatTick(performance.now() - connectStartTime),
|
|
643
658
|
},
|
|
644
659
|
lastError,
|
|
645
660
|
);
|
|
@@ -651,7 +666,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
651
666
|
this.logger.sendTelemetryEvent({
|
|
652
667
|
eventName: "ConnectionAttemptCancelled",
|
|
653
668
|
attempts: connectRepeatCount,
|
|
654
|
-
duration:
|
|
669
|
+
duration: formatTick(performance.now() - connectStartTime),
|
|
655
670
|
connectionEstablished: true,
|
|
656
671
|
});
|
|
657
672
|
return;
|
|
@@ -665,7 +680,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
665
680
|
* And report the error if it escapes for any reason.
|
|
666
681
|
* @param args - The connection arguments
|
|
667
682
|
*/
|
|
668
|
-
private triggerConnect(reason:
|
|
683
|
+
private triggerConnect(reason: IConnectionStateChangeReason, connectionMode: ConnectionMode) {
|
|
669
684
|
// reconnect() includes async awaits, and that causes potential race conditions
|
|
670
685
|
// where we might already have a connection. If it were to happen, it's possible that we will connect
|
|
671
686
|
// with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
|
|
@@ -685,7 +700,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
685
700
|
* @param error - Error causing the disconnect if any.
|
|
686
701
|
* @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect
|
|
687
702
|
*/
|
|
688
|
-
private disconnectFromDeltaStream(reason:
|
|
703
|
+
private disconnectFromDeltaStream(reason: IConnectionStateChangeReason): boolean {
|
|
689
704
|
this.pendingReconnect = false;
|
|
690
705
|
|
|
691
706
|
if (this.connection === undefined) {
|
|
@@ -718,7 +733,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
718
733
|
this._outbound.clear();
|
|
719
734
|
connection.dispose();
|
|
720
735
|
|
|
721
|
-
this.props.disconnectHandler(reason
|
|
736
|
+
this.props.disconnectHandler(reason);
|
|
722
737
|
|
|
723
738
|
this._connectionVerboseProps = {};
|
|
724
739
|
|
|
@@ -728,7 +743,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
728
743
|
/**
|
|
729
744
|
* Cancel in-progress connection attempt.
|
|
730
745
|
*/
|
|
731
|
-
private cancelConnection(reason:
|
|
746
|
+
private cancelConnection(reason: IConnectionStateChangeReason) {
|
|
732
747
|
assert(
|
|
733
748
|
this.pendingConnection !== undefined,
|
|
734
749
|
0x345 /* this.pendingConnection is undefined when trying to cancel */,
|
|
@@ -736,7 +751,10 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
736
751
|
this.pendingConnection.abort();
|
|
737
752
|
this.pendingConnection = undefined;
|
|
738
753
|
this.logger.sendTelemetryEvent({ eventName: "ConnectionCancelReceived" });
|
|
739
|
-
this.props.cancelConnectionHandler(
|
|
754
|
+
this.props.cancelConnectionHandler({
|
|
755
|
+
text: `Cancel Pending Connection due to ${reason.text}`,
|
|
756
|
+
error: reason.error,
|
|
757
|
+
});
|
|
740
758
|
}
|
|
741
759
|
|
|
742
760
|
/**
|
|
@@ -747,7 +765,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
747
765
|
private setupNewSuccessfulConnection(
|
|
748
766
|
connection: IDocumentDeltaConnection,
|
|
749
767
|
requestedMode: ConnectionMode,
|
|
750
|
-
reason:
|
|
768
|
+
reason: IConnectionStateChangeReason,
|
|
751
769
|
) {
|
|
752
770
|
// Old connection should have been cleaned up before establishing a new one
|
|
753
771
|
assert(
|
|
@@ -761,6 +779,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
761
779
|
|
|
762
780
|
this.pendingConnection = undefined;
|
|
763
781
|
|
|
782
|
+
const oldReadonlyValue = this.readonly;
|
|
764
783
|
this.connection = connection;
|
|
765
784
|
|
|
766
785
|
// Does information in scopes & mode matches?
|
|
@@ -786,11 +805,11 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
786
805
|
0x0e8 /* "readonly perf with write connection" */,
|
|
787
806
|
);
|
|
788
807
|
|
|
789
|
-
this.set_readonlyPermissions(readonly);
|
|
808
|
+
this.set_readonlyPermissions(readonly, oldReadonlyValue);
|
|
790
809
|
|
|
791
810
|
if (this._disposed) {
|
|
792
811
|
// Raise proper events, Log telemetry event and close connection.
|
|
793
|
-
this.disconnectFromDeltaStream("ConnectionManager already closed");
|
|
812
|
+
this.disconnectFromDeltaStream({ text: "ConnectionManager already closed" });
|
|
794
813
|
return;
|
|
795
814
|
}
|
|
796
815
|
|
|
@@ -895,7 +914,9 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
895
914
|
* @returns A promise that resolves when the connection is reestablished or we stop trying
|
|
896
915
|
*/
|
|
897
916
|
private reconnectOnError(requestedMode: ConnectionMode, error: IAnyDriverError) {
|
|
898
|
-
this.reconnect(requestedMode, error.message, error).catch(
|
|
917
|
+
this.reconnect(requestedMode, { text: error.message, error }).catch(
|
|
918
|
+
this.props.closeHandler,
|
|
919
|
+
);
|
|
899
920
|
}
|
|
900
921
|
|
|
901
922
|
/**
|
|
@@ -907,26 +928,25 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
907
928
|
*/
|
|
908
929
|
private async reconnect(
|
|
909
930
|
requestedMode: ConnectionMode,
|
|
910
|
-
|
|
911
|
-
error?: IAnyDriverError,
|
|
931
|
+
reason: IConnectionStateChangeReason<IAnyDriverError>,
|
|
912
932
|
) {
|
|
913
933
|
// We quite often get protocol errors before / after observing nack/disconnect
|
|
914
934
|
// we do not want to run through same sequence twice.
|
|
915
935
|
// If we're already disconnected/disconnecting it's not appropriate to call this again.
|
|
916
936
|
assert(this.connection !== undefined, 0x0eb /* "Missing connection for reconnect" */);
|
|
917
937
|
|
|
918
|
-
this.disconnectFromDeltaStream(
|
|
938
|
+
this.disconnectFromDeltaStream(reason);
|
|
919
939
|
|
|
920
940
|
// We will always trigger reconnect, even if canRetry is false.
|
|
921
941
|
// Any truly fatal error state will result in container close upon attempted reconnect,
|
|
922
942
|
// which is a preferable to closing abruptly when a live connection fails.
|
|
923
|
-
if (error
|
|
943
|
+
if (reason.error?.canRetry === false) {
|
|
924
944
|
this.logger.sendTelemetryEvent(
|
|
925
945
|
{
|
|
926
946
|
eventName: "reconnectingDespiteFatalError",
|
|
927
947
|
reconnectMode: this.reconnectMode,
|
|
928
948
|
},
|
|
929
|
-
error,
|
|
949
|
+
reason.error,
|
|
930
950
|
);
|
|
931
951
|
}
|
|
932
952
|
|
|
@@ -943,9 +963,9 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
943
963
|
}
|
|
944
964
|
|
|
945
965
|
// If the error tells us to wait before retrying, then do so.
|
|
946
|
-
const delayMs = getRetryDelayFromError(error);
|
|
947
|
-
if (error !== undefined && delayMs !== undefined) {
|
|
948
|
-
this.props.reconnectionDelayHandler(delayMs, error);
|
|
966
|
+
const delayMs = getRetryDelayFromError(reason.error);
|
|
967
|
+
if (reason.error !== undefined && delayMs !== undefined) {
|
|
968
|
+
this.props.reconnectionDelayHandler(delayMs, reason.error);
|
|
949
969
|
await new Promise<void>((resolve) => {
|
|
950
970
|
setTimeout(resolve, delayMs);
|
|
951
971
|
});
|
|
@@ -957,9 +977,13 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
957
977
|
await waitForOnline();
|
|
958
978
|
|
|
959
979
|
this.triggerConnect(
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
980
|
+
{
|
|
981
|
+
text:
|
|
982
|
+
reason.error !== undefined
|
|
983
|
+
? "Reconnecting due to Error"
|
|
984
|
+
: `Reconnecting due to: ${reason.text}`,
|
|
985
|
+
error: reason.error,
|
|
986
|
+
},
|
|
963
987
|
requestedMode,
|
|
964
988
|
);
|
|
965
989
|
}
|
|
@@ -1030,7 +1054,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
1030
1054
|
// still valid?
|
|
1031
1055
|
await this.reconnect(
|
|
1032
1056
|
"write", // connectionMode
|
|
1033
|
-
"Switch to write", // message
|
|
1057
|
+
{ text: "Switch to write" }, // message
|
|
1034
1058
|
);
|
|
1035
1059
|
}
|
|
1036
1060
|
})
|
|
@@ -1084,7 +1108,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
1084
1108
|
// Note - this may close container!
|
|
1085
1109
|
this.reconnect(
|
|
1086
1110
|
"read", // connectionMode
|
|
1087
|
-
"Switch to read", // message
|
|
1111
|
+
{ text: "Switch to read" }, // message
|
|
1088
1112
|
).catch((error) => {
|
|
1089
1113
|
this.logger.sendErrorEvent({ eventName: "SwitchToReadConnection" }, error);
|
|
1090
1114
|
});
|
|
@@ -4,17 +4,18 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
|
|
7
|
-
import { assert, Timer } from "@fluidframework/
|
|
8
|
-
import {
|
|
9
|
-
import { IAnyDriverError } from "@fluidframework/driver-definitions";
|
|
7
|
+
import { assert, Timer } from "@fluidframework/core-utils";
|
|
8
|
+
import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
10
9
|
import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
|
|
11
10
|
import {
|
|
12
11
|
ITelemetryLoggerExt,
|
|
13
12
|
PerformanceEvent,
|
|
14
13
|
loggerToMonitoringContext,
|
|
15
14
|
} from "@fluidframework/telemetry-utils";
|
|
16
|
-
import {
|
|
15
|
+
import { IAnyDriverError } from "@fluidframework/driver-definitions";
|
|
17
16
|
import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor";
|
|
17
|
+
import { ConnectionState } from "./connectionState";
|
|
18
|
+
import { IConnectionDetailsInternal, IConnectionStateChangeReason } from "./contracts";
|
|
18
19
|
import { IProtocolHandler } from "./protocol";
|
|
19
20
|
|
|
20
21
|
// Based on recent data, it looks like majority of cases where we get stuck are due to really slow or
|
|
@@ -32,8 +33,7 @@ export interface IConnectionStateHandlerInputs {
|
|
|
32
33
|
connectionStateChanged: (
|
|
33
34
|
value: ConnectionState,
|
|
34
35
|
oldState: ConnectionState,
|
|
35
|
-
reason?:
|
|
36
|
-
error?: IAnyDriverError,
|
|
36
|
+
reason?: IConnectionStateChangeReason,
|
|
37
37
|
) => void;
|
|
38
38
|
/** Whether to expect the client to join in write mode on next connection */
|
|
39
39
|
shouldClientJoinWrite: () => boolean;
|
|
@@ -60,14 +60,14 @@ export interface IConnectionStateHandler {
|
|
|
60
60
|
dispose(): void;
|
|
61
61
|
initProtocol(protocol: IProtocolHandler): void;
|
|
62
62
|
receivedConnectEvent(details: IConnectionDetailsInternal): void;
|
|
63
|
-
receivedDisconnectEvent(reason:
|
|
64
|
-
establishingConnection(reason:
|
|
63
|
+
receivedDisconnectEvent(reason: IConnectionStateChangeReason): void;
|
|
64
|
+
establishingConnection(reason: IConnectionStateChangeReason): void;
|
|
65
65
|
/**
|
|
66
66
|
* Switches state to disconnected when we are still establishing connection during container.load(),
|
|
67
67
|
* container connect() or reconnect and the container gets closed or disposed or disconnect happens.
|
|
68
68
|
* @param reason - reason for cancelling the connection.
|
|
69
69
|
*/
|
|
70
|
-
cancelEstablishingConnection(reason:
|
|
70
|
+
cancelEstablishingConnection(reason: IConnectionStateChangeReason): void;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export function createConnectionStateHandler(
|
|
@@ -149,15 +149,15 @@ class ConnectionStateHandlerPassThrough
|
|
|
149
149
|
public initProtocol(protocol: IProtocolHandler) {
|
|
150
150
|
return this.pimpl.initProtocol(protocol);
|
|
151
151
|
}
|
|
152
|
-
public receivedDisconnectEvent(reason:
|
|
153
|
-
return this.pimpl.receivedDisconnectEvent(reason
|
|
152
|
+
public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
|
|
153
|
+
return this.pimpl.receivedDisconnectEvent(reason);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
public establishingConnection(reason:
|
|
156
|
+
public establishingConnection(reason: IConnectionStateChangeReason) {
|
|
157
157
|
return this.pimpl.establishingConnection(reason);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
public cancelEstablishingConnection(reason:
|
|
160
|
+
public cancelEstablishingConnection(reason: IConnectionStateChangeReason) {
|
|
161
161
|
return this.pimpl.cancelEstablishingConnection(reason);
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -175,10 +175,9 @@ class ConnectionStateHandlerPassThrough
|
|
|
175
175
|
public connectionStateChanged(
|
|
176
176
|
value: ConnectionState,
|
|
177
177
|
oldState: ConnectionState,
|
|
178
|
-
reason?:
|
|
179
|
-
error?: IAnyDriverError,
|
|
178
|
+
reason?: IConnectionStateChangeReason,
|
|
180
179
|
) {
|
|
181
|
-
return this.inputs.connectionStateChanged(value, oldState, reason
|
|
180
|
+
return this.inputs.connectionStateChanged(value, oldState, reason);
|
|
182
181
|
}
|
|
183
182
|
public shouldClientJoinWrite() {
|
|
184
183
|
return this.inputs.shouldClientJoinWrite();
|
|
@@ -222,8 +221,7 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
222
221
|
public connectionStateChanged(
|
|
223
222
|
value: ConnectionState,
|
|
224
223
|
oldState: ConnectionState,
|
|
225
|
-
reason?:
|
|
226
|
-
error?: IAnyDriverError,
|
|
224
|
+
reason?: IConnectionStateChangeReason<IAnyDriverError>,
|
|
227
225
|
) {
|
|
228
226
|
switch (value) {
|
|
229
227
|
case ConnectionState.Connected:
|
|
@@ -267,7 +265,7 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
267
265
|
default:
|
|
268
266
|
}
|
|
269
267
|
this._connectionState = value;
|
|
270
|
-
this.inputs.connectionStateChanged(value, oldState, reason
|
|
268
|
+
this.inputs.connectionStateChanged(value, oldState, reason);
|
|
271
269
|
}
|
|
272
270
|
|
|
273
271
|
private readonly transitionToConnectedState = () => {
|
|
@@ -276,11 +274,9 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
276
274
|
assert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);
|
|
277
275
|
assert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);
|
|
278
276
|
this._connectionState = ConnectionState.Connected;
|
|
279
|
-
this.inputs.connectionStateChanged(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
"caught up",
|
|
283
|
-
);
|
|
277
|
+
this.inputs.connectionStateChanged(ConnectionState.Connected, ConnectionState.CatchingUp, {
|
|
278
|
+
text: "caught up",
|
|
279
|
+
});
|
|
284
280
|
};
|
|
285
281
|
}
|
|
286
282
|
|
|
@@ -494,12 +490,12 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
494
490
|
}
|
|
495
491
|
}
|
|
496
492
|
|
|
497
|
-
public receivedDisconnectEvent(reason:
|
|
493
|
+
public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
|
|
498
494
|
this.connection = undefined;
|
|
499
|
-
this.setConnectionState(ConnectionState.Disconnected, reason
|
|
495
|
+
this.setConnectionState(ConnectionState.Disconnected, reason);
|
|
500
496
|
}
|
|
501
497
|
|
|
502
|
-
public cancelEstablishingConnection(reason:
|
|
498
|
+
public cancelEstablishingConnection(reason: IConnectionStateChangeReason) {
|
|
503
499
|
assert(
|
|
504
500
|
this._connectionState === ConnectionState.EstablishingConnection,
|
|
505
501
|
0x6d3 /* Connection state should be EstablishingConnection */,
|
|
@@ -510,14 +506,13 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
510
506
|
this.handler.connectionStateChanged(ConnectionState.Disconnected, oldState, reason);
|
|
511
507
|
}
|
|
512
508
|
|
|
513
|
-
public establishingConnection(reason:
|
|
509
|
+
public establishingConnection(reason: IConnectionStateChangeReason) {
|
|
514
510
|
const oldState = this._connectionState;
|
|
515
511
|
this._connectionState = ConnectionState.EstablishingConnection;
|
|
516
|
-
this.handler.connectionStateChanged(
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
);
|
|
512
|
+
this.handler.connectionStateChanged(ConnectionState.EstablishingConnection, oldState, {
|
|
513
|
+
text: `Establishing Connection due to ${reason.text}`,
|
|
514
|
+
error: reason.error,
|
|
515
|
+
});
|
|
521
516
|
}
|
|
522
517
|
|
|
523
518
|
private shouldWaitForJoinSignal() {
|
|
@@ -582,14 +577,12 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
582
577
|
|
|
583
578
|
private setConnectionState(
|
|
584
579
|
value: ConnectionState.Disconnected,
|
|
585
|
-
reason:
|
|
586
|
-
error?: IAnyDriverError,
|
|
580
|
+
reason: IConnectionStateChangeReason,
|
|
587
581
|
): void;
|
|
588
582
|
private setConnectionState(value: ConnectionState.Connected): void;
|
|
589
583
|
private setConnectionState(
|
|
590
584
|
value: ConnectionState.Disconnected | ConnectionState.Connected,
|
|
591
|
-
reason?:
|
|
592
|
-
error?: IAnyDriverError,
|
|
585
|
+
reason?: IConnectionStateChangeReason,
|
|
593
586
|
): void {
|
|
594
587
|
if (this.connectionState === value) {
|
|
595
588
|
// Already in the desired state - exit early
|
|
@@ -650,7 +643,7 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
650
643
|
}
|
|
651
644
|
|
|
652
645
|
// Report transition before we propagate event across layers
|
|
653
|
-
this.handler.connectionStateChanged(this._connectionState, oldState, reason
|
|
646
|
+
this.handler.connectionStateChanged(this._connectionState, oldState, reason);
|
|
654
647
|
}
|
|
655
648
|
|
|
656
649
|
// Helper method to switch between quorum and audience.
|