@fluidframework/container-loader 2.0.0-internal.6.0.0 → 2.0.0-internal.6.1.1
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 +4 -0
- package/dist/connectionManager.d.ts +3 -3
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +33 -24
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +14 -14
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +17 -12
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +27 -39
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +6 -1
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +8 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/contracts.d.ts +11 -7
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +4 -4
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +5 -4
- package/dist/deltaManager.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 +23 -1
- package/dist/protocol.js.map +1 -1
- package/lib/connectionManager.d.ts +3 -3
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +33 -24
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +14 -14
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +17 -12
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +28 -40
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +6 -1
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +8 -1
- package/lib/containerContext.js.map +1 -1
- package/lib/contracts.d.ts +11 -7
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +4 -4
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +5 -4
- package/lib/deltaManager.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 +23 -1
- package/lib/protocol.js.map +1 -1
- package/package.json +14 -14
- package/src/connectionManager.ts +46 -31
- package/src/connectionStateHandler.ts +28 -36
- package/src/container.ts +47 -51
- package/src/containerContext.ts +8 -0
- package/src/contracts.ts +12 -6
- package/src/deltaManager.ts +19 -13
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +33 -1
package/src/connectionManager.ts
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
IConnectionManager,
|
|
49
49
|
IConnectionManagerFactoryArgs,
|
|
50
50
|
IConnectionDetailsInternal,
|
|
51
|
+
IConnectionStateChangeReason,
|
|
51
52
|
} from "./contracts";
|
|
52
53
|
import { DeltaQueue } from "./deltaQueue";
|
|
53
54
|
import { SignalType } from "./protocol";
|
|
@@ -336,7 +337,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
336
337
|
|
|
337
338
|
private static detailsFromConnection(
|
|
338
339
|
connection: IDocumentDeltaConnection,
|
|
339
|
-
reason:
|
|
340
|
+
reason: IConnectionStateChangeReason,
|
|
340
341
|
): IConnectionDetailsInternal {
|
|
341
342
|
return {
|
|
342
343
|
claims: connection.claims,
|
|
@@ -389,7 +390,10 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
389
390
|
|
|
390
391
|
this._outbound.clear();
|
|
391
392
|
|
|
392
|
-
const disconnectReason =
|
|
393
|
+
const disconnectReason: IConnectionStateChangeReason = {
|
|
394
|
+
text: "Closing DeltaManager",
|
|
395
|
+
error,
|
|
396
|
+
};
|
|
393
397
|
|
|
394
398
|
// This raises "disconnect" event if we have active connection.
|
|
395
399
|
this.disconnectFromDeltaStream(disconnectReason);
|
|
@@ -406,7 +410,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
406
410
|
* Enables or disables automatic reconnecting.
|
|
407
411
|
* Will throw an error if reconnectMode set to Never.
|
|
408
412
|
*/
|
|
409
|
-
public setAutoReconnect(mode: ReconnectMode): void {
|
|
413
|
+
public setAutoReconnect(mode: ReconnectMode, reason: IConnectionStateChangeReason): void {
|
|
410
414
|
assert(
|
|
411
415
|
mode !== ReconnectMode.Never && this._reconnectMode !== ReconnectMode.Never,
|
|
412
416
|
0x278 /* "API is not supported for non-connecting or closed container" */,
|
|
@@ -416,7 +420,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
416
420
|
|
|
417
421
|
if (mode !== ReconnectMode.Enabled) {
|
|
418
422
|
// immediately disconnect - do not rely on service eventually dropping connection.
|
|
419
|
-
this.disconnectFromDeltaStream(
|
|
423
|
+
this.disconnectFromDeltaStream(reason);
|
|
420
424
|
}
|
|
421
425
|
}
|
|
422
426
|
|
|
@@ -463,12 +467,12 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
463
467
|
this.logger.sendErrorEvent({ eventName: "ForceReadonlyPendingChanged" });
|
|
464
468
|
}
|
|
465
469
|
|
|
466
|
-
reconnect = this.disconnectFromDeltaStream("Force readonly");
|
|
470
|
+
reconnect = this.disconnectFromDeltaStream({ text: "Force readonly" });
|
|
467
471
|
}
|
|
468
472
|
this.props.readonlyChangeHandler(this.readonly);
|
|
469
473
|
if (reconnect) {
|
|
470
474
|
// reconnect if we disconnected from before.
|
|
471
|
-
this.triggerConnect("Force Readonly", "read");
|
|
475
|
+
this.triggerConnect({ text: "Force Readonly" }, "read");
|
|
472
476
|
}
|
|
473
477
|
}
|
|
474
478
|
}
|
|
@@ -481,14 +485,17 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
481
485
|
}
|
|
482
486
|
}
|
|
483
487
|
|
|
484
|
-
public connect(reason:
|
|
485
|
-
this.connectCore(reason, connectionMode).catch((
|
|
486
|
-
const normalizedError = normalizeError(
|
|
488
|
+
public connect(reason: IConnectionStateChangeReason, connectionMode?: ConnectionMode) {
|
|
489
|
+
this.connectCore(reason, connectionMode).catch((e) => {
|
|
490
|
+
const normalizedError = normalizeError(e, { props: fatalConnectErrorProp });
|
|
487
491
|
this.props.closeHandler(normalizedError);
|
|
488
492
|
});
|
|
489
493
|
}
|
|
490
494
|
|
|
491
|
-
private async connectCore(
|
|
495
|
+
private async connectCore(
|
|
496
|
+
reason: IConnectionStateChangeReason,
|
|
497
|
+
connectionMode?: ConnectionMode,
|
|
498
|
+
): Promise<void> {
|
|
492
499
|
assert(!this._disposed, 0x26a /* "not closed" */);
|
|
493
500
|
|
|
494
501
|
if (this.connection !== undefined) {
|
|
@@ -664,7 +671,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
664
671
|
* And report the error if it escapes for any reason.
|
|
665
672
|
* @param args - The connection arguments
|
|
666
673
|
*/
|
|
667
|
-
private triggerConnect(reason:
|
|
674
|
+
private triggerConnect(reason: IConnectionStateChangeReason, connectionMode: ConnectionMode) {
|
|
668
675
|
// reconnect() includes async awaits, and that causes potential race conditions
|
|
669
676
|
// where we might already have a connection. If it were to happen, it's possible that we will connect
|
|
670
677
|
// with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
|
|
@@ -684,7 +691,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
684
691
|
* @param error - Error causing the disconnect if any.
|
|
685
692
|
* @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect
|
|
686
693
|
*/
|
|
687
|
-
private disconnectFromDeltaStream(reason:
|
|
694
|
+
private disconnectFromDeltaStream(reason: IConnectionStateChangeReason): boolean {
|
|
688
695
|
this.pendingReconnect = false;
|
|
689
696
|
|
|
690
697
|
if (this.connection === undefined) {
|
|
@@ -717,7 +724,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
717
724
|
this._outbound.clear();
|
|
718
725
|
connection.dispose();
|
|
719
726
|
|
|
720
|
-
this.props.disconnectHandler(reason
|
|
727
|
+
this.props.disconnectHandler(reason);
|
|
721
728
|
|
|
722
729
|
this._connectionVerboseProps = {};
|
|
723
730
|
|
|
@@ -727,7 +734,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
727
734
|
/**
|
|
728
735
|
* Cancel in-progress connection attempt.
|
|
729
736
|
*/
|
|
730
|
-
private cancelConnection(reason:
|
|
737
|
+
private cancelConnection(reason: IConnectionStateChangeReason) {
|
|
731
738
|
assert(
|
|
732
739
|
this.pendingConnection !== undefined,
|
|
733
740
|
0x345 /* this.pendingConnection is undefined when trying to cancel */,
|
|
@@ -735,7 +742,10 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
735
742
|
this.pendingConnection.abort();
|
|
736
743
|
this.pendingConnection = undefined;
|
|
737
744
|
this.logger.sendTelemetryEvent({ eventName: "ConnectionCancelReceived" });
|
|
738
|
-
this.props.cancelConnectionHandler(
|
|
745
|
+
this.props.cancelConnectionHandler({
|
|
746
|
+
text: `Cancel Pending Connection due to ${reason.text}`,
|
|
747
|
+
error: reason.error,
|
|
748
|
+
});
|
|
739
749
|
}
|
|
740
750
|
|
|
741
751
|
/**
|
|
@@ -746,7 +756,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
746
756
|
private setupNewSuccessfulConnection(
|
|
747
757
|
connection: IDocumentDeltaConnection,
|
|
748
758
|
requestedMode: ConnectionMode,
|
|
749
|
-
reason:
|
|
759
|
+
reason: IConnectionStateChangeReason,
|
|
750
760
|
) {
|
|
751
761
|
// Old connection should have been cleaned up before establishing a new one
|
|
752
762
|
assert(
|
|
@@ -789,7 +799,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
789
799
|
|
|
790
800
|
if (this._disposed) {
|
|
791
801
|
// Raise proper events, Log telemetry event and close connection.
|
|
792
|
-
this.disconnectFromDeltaStream("ConnectionManager already closed");
|
|
802
|
+
this.disconnectFromDeltaStream({ text: "ConnectionManager already closed" });
|
|
793
803
|
return;
|
|
794
804
|
}
|
|
795
805
|
|
|
@@ -894,7 +904,9 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
894
904
|
* @returns A promise that resolves when the connection is reestablished or we stop trying
|
|
895
905
|
*/
|
|
896
906
|
private reconnectOnError(requestedMode: ConnectionMode, error: IAnyDriverError) {
|
|
897
|
-
this.reconnect(requestedMode, error.message, error).catch(
|
|
907
|
+
this.reconnect(requestedMode, { text: error.message, error }).catch(
|
|
908
|
+
this.props.closeHandler,
|
|
909
|
+
);
|
|
898
910
|
}
|
|
899
911
|
|
|
900
912
|
/**
|
|
@@ -906,26 +918,25 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
906
918
|
*/
|
|
907
919
|
private async reconnect(
|
|
908
920
|
requestedMode: ConnectionMode,
|
|
909
|
-
|
|
910
|
-
error?: IAnyDriverError,
|
|
921
|
+
reason: IConnectionStateChangeReason<IAnyDriverError>,
|
|
911
922
|
) {
|
|
912
923
|
// We quite often get protocol errors before / after observing nack/disconnect
|
|
913
924
|
// we do not want to run through same sequence twice.
|
|
914
925
|
// If we're already disconnected/disconnecting it's not appropriate to call this again.
|
|
915
926
|
assert(this.connection !== undefined, 0x0eb /* "Missing connection for reconnect" */);
|
|
916
927
|
|
|
917
|
-
this.disconnectFromDeltaStream(
|
|
928
|
+
this.disconnectFromDeltaStream(reason);
|
|
918
929
|
|
|
919
930
|
// We will always trigger reconnect, even if canRetry is false.
|
|
920
931
|
// Any truly fatal error state will result in container close upon attempted reconnect,
|
|
921
932
|
// which is a preferable to closing abruptly when a live connection fails.
|
|
922
|
-
if (error
|
|
933
|
+
if (reason.error?.canRetry === false) {
|
|
923
934
|
this.logger.sendTelemetryEvent(
|
|
924
935
|
{
|
|
925
936
|
eventName: "reconnectingDespiteFatalError",
|
|
926
937
|
reconnectMode: this.reconnectMode,
|
|
927
938
|
},
|
|
928
|
-
error,
|
|
939
|
+
reason.error,
|
|
929
940
|
);
|
|
930
941
|
}
|
|
931
942
|
|
|
@@ -942,9 +953,9 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
942
953
|
}
|
|
943
954
|
|
|
944
955
|
// If the error tells us to wait before retrying, then do so.
|
|
945
|
-
const delayMs = getRetryDelayFromError(error);
|
|
946
|
-
if (error !== undefined && delayMs !== undefined) {
|
|
947
|
-
this.props.reconnectionDelayHandler(delayMs, error);
|
|
956
|
+
const delayMs = getRetryDelayFromError(reason.error);
|
|
957
|
+
if (reason.error !== undefined && delayMs !== undefined) {
|
|
958
|
+
this.props.reconnectionDelayHandler(delayMs, reason.error);
|
|
948
959
|
await new Promise<void>((resolve) => {
|
|
949
960
|
setTimeout(resolve, delayMs);
|
|
950
961
|
});
|
|
@@ -956,9 +967,13 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
956
967
|
await waitForOnline();
|
|
957
968
|
|
|
958
969
|
this.triggerConnect(
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
970
|
+
{
|
|
971
|
+
text:
|
|
972
|
+
reason.error !== undefined
|
|
973
|
+
? "Reconnecting due to Error"
|
|
974
|
+
: `Reconnecting due to: ${reason.text}`,
|
|
975
|
+
error: reason.error,
|
|
976
|
+
},
|
|
962
977
|
requestedMode,
|
|
963
978
|
);
|
|
964
979
|
}
|
|
@@ -1029,7 +1044,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
1029
1044
|
// still valid?
|
|
1030
1045
|
await this.reconnect(
|
|
1031
1046
|
"write", // connectionMode
|
|
1032
|
-
"Switch to write", // message
|
|
1047
|
+
{ text: "Switch to write" }, // message
|
|
1033
1048
|
);
|
|
1034
1049
|
}
|
|
1035
1050
|
})
|
|
@@ -1083,7 +1098,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
1083
1098
|
// Note - this may close container!
|
|
1084
1099
|
this.reconnect(
|
|
1085
1100
|
"read", // connectionMode
|
|
1086
|
-
"Switch to read", // message
|
|
1101
|
+
{ text: "Switch to read" }, // message
|
|
1087
1102
|
).catch((error) => {
|
|
1088
1103
|
this.logger.sendErrorEvent({ eventName: "SwitchToReadConnection" }, error);
|
|
1089
1104
|
});
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { assert, Timer } from "@fluidframework/common-utils";
|
|
8
8
|
import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
9
|
-
import { IAnyDriverError } from "@fluidframework/driver-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";
|
|
15
|
+
import { IAnyDriverError } from "@fluidframework/driver-definitions";
|
|
16
16
|
import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor";
|
|
17
17
|
import { ConnectionState } from "./connectionState";
|
|
18
|
-
import { IConnectionDetailsInternal } from "./contracts";
|
|
18
|
+
import { IConnectionDetailsInternal, IConnectionStateChangeReason } from "./contracts";
|
|
19
19
|
import { IProtocolHandler } from "./protocol";
|
|
20
20
|
|
|
21
21
|
// Based on recent data, it looks like majority of cases where we get stuck are due to really slow or
|
|
@@ -33,8 +33,7 @@ export interface IConnectionStateHandlerInputs {
|
|
|
33
33
|
connectionStateChanged: (
|
|
34
34
|
value: ConnectionState,
|
|
35
35
|
oldState: ConnectionState,
|
|
36
|
-
reason?:
|
|
37
|
-
error?: IAnyDriverError,
|
|
36
|
+
reason?: IConnectionStateChangeReason,
|
|
38
37
|
) => void;
|
|
39
38
|
/** Whether to expect the client to join in write mode on next connection */
|
|
40
39
|
shouldClientJoinWrite: () => boolean;
|
|
@@ -61,14 +60,14 @@ export interface IConnectionStateHandler {
|
|
|
61
60
|
dispose(): void;
|
|
62
61
|
initProtocol(protocol: IProtocolHandler): void;
|
|
63
62
|
receivedConnectEvent(details: IConnectionDetailsInternal): void;
|
|
64
|
-
receivedDisconnectEvent(reason:
|
|
65
|
-
establishingConnection(reason:
|
|
63
|
+
receivedDisconnectEvent(reason: IConnectionStateChangeReason): void;
|
|
64
|
+
establishingConnection(reason: IConnectionStateChangeReason): void;
|
|
66
65
|
/**
|
|
67
66
|
* Switches state to disconnected when we are still establishing connection during container.load(),
|
|
68
67
|
* container connect() or reconnect and the container gets closed or disposed or disconnect happens.
|
|
69
68
|
* @param reason - reason for cancelling the connection.
|
|
70
69
|
*/
|
|
71
|
-
cancelEstablishingConnection(reason:
|
|
70
|
+
cancelEstablishingConnection(reason: IConnectionStateChangeReason): void;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
export function createConnectionStateHandler(
|
|
@@ -150,15 +149,15 @@ class ConnectionStateHandlerPassThrough
|
|
|
150
149
|
public initProtocol(protocol: IProtocolHandler) {
|
|
151
150
|
return this.pimpl.initProtocol(protocol);
|
|
152
151
|
}
|
|
153
|
-
public receivedDisconnectEvent(reason:
|
|
154
|
-
return this.pimpl.receivedDisconnectEvent(reason
|
|
152
|
+
public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
|
|
153
|
+
return this.pimpl.receivedDisconnectEvent(reason);
|
|
155
154
|
}
|
|
156
155
|
|
|
157
|
-
public establishingConnection(reason:
|
|
156
|
+
public establishingConnection(reason: IConnectionStateChangeReason) {
|
|
158
157
|
return this.pimpl.establishingConnection(reason);
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
public cancelEstablishingConnection(reason:
|
|
160
|
+
public cancelEstablishingConnection(reason: IConnectionStateChangeReason) {
|
|
162
161
|
return this.pimpl.cancelEstablishingConnection(reason);
|
|
163
162
|
}
|
|
164
163
|
|
|
@@ -176,10 +175,9 @@ class ConnectionStateHandlerPassThrough
|
|
|
176
175
|
public connectionStateChanged(
|
|
177
176
|
value: ConnectionState,
|
|
178
177
|
oldState: ConnectionState,
|
|
179
|
-
reason?:
|
|
180
|
-
error?: IAnyDriverError,
|
|
178
|
+
reason?: IConnectionStateChangeReason,
|
|
181
179
|
) {
|
|
182
|
-
return this.inputs.connectionStateChanged(value, oldState, reason
|
|
180
|
+
return this.inputs.connectionStateChanged(value, oldState, reason);
|
|
183
181
|
}
|
|
184
182
|
public shouldClientJoinWrite() {
|
|
185
183
|
return this.inputs.shouldClientJoinWrite();
|
|
@@ -223,8 +221,7 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
223
221
|
public connectionStateChanged(
|
|
224
222
|
value: ConnectionState,
|
|
225
223
|
oldState: ConnectionState,
|
|
226
|
-
reason?:
|
|
227
|
-
error?: IAnyDriverError,
|
|
224
|
+
reason?: IConnectionStateChangeReason<IAnyDriverError>,
|
|
228
225
|
) {
|
|
229
226
|
switch (value) {
|
|
230
227
|
case ConnectionState.Connected:
|
|
@@ -268,7 +265,7 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
268
265
|
default:
|
|
269
266
|
}
|
|
270
267
|
this._connectionState = value;
|
|
271
|
-
this.inputs.connectionStateChanged(value, oldState, reason
|
|
268
|
+
this.inputs.connectionStateChanged(value, oldState, reason);
|
|
272
269
|
}
|
|
273
270
|
|
|
274
271
|
private readonly transitionToConnectedState = () => {
|
|
@@ -277,11 +274,9 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
277
274
|
assert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);
|
|
278
275
|
assert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);
|
|
279
276
|
this._connectionState = ConnectionState.Connected;
|
|
280
|
-
this.inputs.connectionStateChanged(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
"caught up",
|
|
284
|
-
);
|
|
277
|
+
this.inputs.connectionStateChanged(ConnectionState.Connected, ConnectionState.CatchingUp, {
|
|
278
|
+
text: "caught up",
|
|
279
|
+
});
|
|
285
280
|
};
|
|
286
281
|
}
|
|
287
282
|
|
|
@@ -495,12 +490,12 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
495
490
|
}
|
|
496
491
|
}
|
|
497
492
|
|
|
498
|
-
public receivedDisconnectEvent(reason:
|
|
493
|
+
public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
|
|
499
494
|
this.connection = undefined;
|
|
500
|
-
this.setConnectionState(ConnectionState.Disconnected, reason
|
|
495
|
+
this.setConnectionState(ConnectionState.Disconnected, reason);
|
|
501
496
|
}
|
|
502
497
|
|
|
503
|
-
public cancelEstablishingConnection(reason:
|
|
498
|
+
public cancelEstablishingConnection(reason: IConnectionStateChangeReason) {
|
|
504
499
|
assert(
|
|
505
500
|
this._connectionState === ConnectionState.EstablishingConnection,
|
|
506
501
|
0x6d3 /* Connection state should be EstablishingConnection */,
|
|
@@ -511,14 +506,13 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
511
506
|
this.handler.connectionStateChanged(ConnectionState.Disconnected, oldState, reason);
|
|
512
507
|
}
|
|
513
508
|
|
|
514
|
-
public establishingConnection(reason:
|
|
509
|
+
public establishingConnection(reason: IConnectionStateChangeReason) {
|
|
515
510
|
const oldState = this._connectionState;
|
|
516
511
|
this._connectionState = ConnectionState.EstablishingConnection;
|
|
517
|
-
this.handler.connectionStateChanged(
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
);
|
|
512
|
+
this.handler.connectionStateChanged(ConnectionState.EstablishingConnection, oldState, {
|
|
513
|
+
text: `Establishing Connection due to ${reason.text}`,
|
|
514
|
+
error: reason.error,
|
|
515
|
+
});
|
|
522
516
|
}
|
|
523
517
|
|
|
524
518
|
private shouldWaitForJoinSignal() {
|
|
@@ -583,14 +577,12 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
583
577
|
|
|
584
578
|
private setConnectionState(
|
|
585
579
|
value: ConnectionState.Disconnected,
|
|
586
|
-
reason:
|
|
587
|
-
error?: IAnyDriverError,
|
|
580
|
+
reason: IConnectionStateChangeReason,
|
|
588
581
|
): void;
|
|
589
582
|
private setConnectionState(value: ConnectionState.Connected): void;
|
|
590
583
|
private setConnectionState(
|
|
591
584
|
value: ConnectionState.Disconnected | ConnectionState.Connected,
|
|
592
|
-
reason?:
|
|
593
|
-
error?: IAnyDriverError,
|
|
585
|
+
reason?: IConnectionStateChangeReason,
|
|
594
586
|
): void {
|
|
595
587
|
if (this.connectionState === value) {
|
|
596
588
|
// Already in the desired state - exit early
|
|
@@ -651,7 +643,7 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
651
643
|
}
|
|
652
644
|
|
|
653
645
|
// Report transition before we propagate event across layers
|
|
654
|
-
this.handler.connectionStateChanged(this._connectionState, oldState, reason
|
|
646
|
+
this.handler.connectionStateChanged(this._connectionState, oldState, reason);
|
|
655
647
|
}
|
|
656
648
|
|
|
657
649
|
// Helper method to switch between quorum and audience.
|
package/src/container.ts
CHANGED
|
@@ -40,17 +40,16 @@ import {
|
|
|
40
40
|
IProvideFluidCodeDetailsComparer,
|
|
41
41
|
IFluidCodeDetailsComparer,
|
|
42
42
|
IRuntime,
|
|
43
|
-
isFluidCodeDetails,
|
|
44
|
-
IThrottlingWarning,
|
|
45
43
|
ReadOnlyInfo,
|
|
44
|
+
isFluidCodeDetails,
|
|
46
45
|
} from "@fluidframework/container-definitions";
|
|
47
46
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
48
47
|
import {
|
|
49
|
-
IAnyDriverError,
|
|
50
48
|
IDocumentService,
|
|
51
49
|
IDocumentServiceFactory,
|
|
52
50
|
IDocumentStorageService,
|
|
53
51
|
IResolvedUrl,
|
|
52
|
+
IThrottlingWarning,
|
|
54
53
|
IUrlResolver,
|
|
55
54
|
} from "@fluidframework/driver-definitions";
|
|
56
55
|
import {
|
|
@@ -60,7 +59,6 @@ import {
|
|
|
60
59
|
runWithRetry,
|
|
61
60
|
isCombinedAppAndProtocolSummary,
|
|
62
61
|
MessageType2,
|
|
63
|
-
canBeCoalescedByService,
|
|
64
62
|
} from "@fluidframework/driver-utils";
|
|
65
63
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
66
64
|
import {
|
|
@@ -102,6 +100,7 @@ import {
|
|
|
102
100
|
IConnectionManagerFactoryArgs,
|
|
103
101
|
getPackageName,
|
|
104
102
|
IConnectionDetailsInternal,
|
|
103
|
+
IConnectionStateChangeReason,
|
|
105
104
|
} from "./contracts";
|
|
106
105
|
import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
107
106
|
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
|
|
@@ -761,7 +760,19 @@ export class Container
|
|
|
761
760
|
this.scope = scope;
|
|
762
761
|
this.detachedBlobStorage = detachedBlobStorage;
|
|
763
762
|
this.protocolHandlerBuilder =
|
|
764
|
-
protocolHandlerBuilder ??
|
|
763
|
+
protocolHandlerBuilder ??
|
|
764
|
+
((
|
|
765
|
+
attributes: IDocumentAttributes,
|
|
766
|
+
quorumSnapshot: IQuorumSnapshot,
|
|
767
|
+
sendProposal: (key: string, value: any) => number,
|
|
768
|
+
) =>
|
|
769
|
+
new ProtocolHandler(
|
|
770
|
+
attributes,
|
|
771
|
+
quorumSnapshot,
|
|
772
|
+
sendProposal,
|
|
773
|
+
new Audience(),
|
|
774
|
+
(clientId: string) => this.clientsWhoShouldHaveLeft.has(clientId),
|
|
775
|
+
));
|
|
765
776
|
|
|
766
777
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
767
778
|
this.clone = async (
|
|
@@ -827,11 +838,11 @@ export class Container
|
|
|
827
838
|
this.connectionStateHandler = createConnectionStateHandler(
|
|
828
839
|
{
|
|
829
840
|
logger: this.mc.logger,
|
|
830
|
-
connectionStateChanged: (value, oldState, reason
|
|
841
|
+
connectionStateChanged: (value, oldState, reason) => {
|
|
831
842
|
if (value === ConnectionState.Connected) {
|
|
832
843
|
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
833
844
|
}
|
|
834
|
-
this.logConnectionStateChangeTelemetry(value, oldState, reason
|
|
845
|
+
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
835
846
|
if (this._lifecycleState === "loaded") {
|
|
836
847
|
this.propagateConnectionState(
|
|
837
848
|
false /* initial transition */,
|
|
@@ -873,8 +884,9 @@ export class Container
|
|
|
873
884
|
// Other possible recovery path - move to connected state (i.e. ConnectionStateHandler.joinOpTimer
|
|
874
885
|
// to call this.applyForConnectedState("addMemberEvent") for "read" connections)
|
|
875
886
|
if (mode === "read") {
|
|
876
|
-
|
|
877
|
-
this.
|
|
887
|
+
const reason = { text: "NoJoinSignal" };
|
|
888
|
+
this.disconnectInternal(reason);
|
|
889
|
+
this.connectInternal({ reason, fetchOpsFromStorage: false });
|
|
878
890
|
}
|
|
879
891
|
},
|
|
880
892
|
clientShouldHaveLeft: (clientId: string) => {
|
|
@@ -1071,7 +1083,7 @@ export class Container
|
|
|
1071
1083
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
1072
1084
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
1073
1085
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
1074
|
-
this.
|
|
1086
|
+
this.disconnectInternal({ text: "closeAndGetPendingLocalState" }); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
1075
1087
|
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
1076
1088
|
this.close();
|
|
1077
1089
|
return pendingState;
|
|
@@ -1267,7 +1279,7 @@ export class Container
|
|
|
1267
1279
|
if (!this.closed) {
|
|
1268
1280
|
this.resumeInternal({
|
|
1269
1281
|
fetchOpsFromStorage: false,
|
|
1270
|
-
reason: "createDetached",
|
|
1282
|
+
reason: { text: "createDetached" },
|
|
1271
1283
|
});
|
|
1272
1284
|
}
|
|
1273
1285
|
} catch (error) {
|
|
@@ -1291,7 +1303,7 @@ export class Container
|
|
|
1291
1303
|
);
|
|
1292
1304
|
}
|
|
1293
1305
|
|
|
1294
|
-
private setAutoReconnectInternal(mode: ReconnectMode) {
|
|
1306
|
+
private setAutoReconnectInternal(mode: ReconnectMode, reason: IConnectionStateChangeReason) {
|
|
1295
1307
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
1296
1308
|
|
|
1297
1309
|
if (currentMode === mode) {
|
|
@@ -1310,7 +1322,7 @@ export class Container
|
|
|
1310
1322
|
duration,
|
|
1311
1323
|
});
|
|
1312
1324
|
|
|
1313
|
-
this._deltaManager.connectionManager.setAutoReconnect(mode);
|
|
1325
|
+
this._deltaManager.connectionManager.setAutoReconnect(mode, reason);
|
|
1314
1326
|
}
|
|
1315
1327
|
|
|
1316
1328
|
public connect() {
|
|
@@ -1322,7 +1334,10 @@ export class Container
|
|
|
1322
1334
|
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
1323
1335
|
// If there is gap, we will learn about it once connected, but the gap should be small (if any),
|
|
1324
1336
|
// assuming that connect() is called quickly after initial container boot.
|
|
1325
|
-
this.connectInternal({
|
|
1337
|
+
this.connectInternal({
|
|
1338
|
+
reason: { text: "DocumentConnect" },
|
|
1339
|
+
fetchOpsFromStorage: false,
|
|
1340
|
+
});
|
|
1326
1341
|
}
|
|
1327
1342
|
}
|
|
1328
1343
|
|
|
@@ -1338,23 +1353,23 @@ export class Container
|
|
|
1338
1353
|
|
|
1339
1354
|
// Set Auto Reconnect Mode
|
|
1340
1355
|
const mode = ReconnectMode.Enabled;
|
|
1341
|
-
this.setAutoReconnectInternal(mode);
|
|
1356
|
+
this.setAutoReconnectInternal(mode, args.reason);
|
|
1342
1357
|
}
|
|
1343
1358
|
|
|
1344
1359
|
public disconnect() {
|
|
1345
1360
|
if (this.closed) {
|
|
1346
1361
|
throw new UsageError(`The Container is closed and cannot be disconnected`);
|
|
1347
1362
|
} else {
|
|
1348
|
-
this.disconnectInternal();
|
|
1363
|
+
this.disconnectInternal({ text: "DocumentDisconnect" });
|
|
1349
1364
|
}
|
|
1350
1365
|
}
|
|
1351
1366
|
|
|
1352
|
-
private disconnectInternal() {
|
|
1367
|
+
private disconnectInternal(reason: IConnectionStateChangeReason) {
|
|
1353
1368
|
assert(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
|
|
1354
1369
|
|
|
1355
1370
|
// Set Auto Reconnect Mode
|
|
1356
1371
|
const mode = ReconnectMode.Disabled;
|
|
1357
|
-
this.setAutoReconnectInternal(mode);
|
|
1372
|
+
this.setAutoReconnectInternal(mode, reason);
|
|
1358
1373
|
}
|
|
1359
1374
|
|
|
1360
1375
|
private resumeInternal(args: IConnectionArgs) {
|
|
@@ -1506,7 +1521,7 @@ export class Container
|
|
|
1506
1521
|
// A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
|
|
1507
1522
|
// B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
|
|
1508
1523
|
const connectionArgs: IConnectionArgs = {
|
|
1509
|
-
reason: "DocumentOpen",
|
|
1524
|
+
reason: { text: "DocumentOpen" },
|
|
1510
1525
|
mode: "write",
|
|
1511
1526
|
fetchOpsFromStorage: false,
|
|
1512
1527
|
};
|
|
@@ -2009,18 +2024,18 @@ export class Container
|
|
|
2009
2024
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
2010
2025
|
});
|
|
2011
2026
|
|
|
2012
|
-
deltaManager.on("establishingConnection", (reason:
|
|
2027
|
+
deltaManager.on("establishingConnection", (reason: IConnectionStateChangeReason) => {
|
|
2013
2028
|
this.connectionStateHandler.establishingConnection(reason);
|
|
2014
2029
|
});
|
|
2015
2030
|
|
|
2016
|
-
deltaManager.on("cancelEstablishingConnection", (reason:
|
|
2031
|
+
deltaManager.on("cancelEstablishingConnection", (reason: IConnectionStateChangeReason) => {
|
|
2017
2032
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
2018
2033
|
});
|
|
2019
2034
|
|
|
2020
|
-
deltaManager.on("disconnect", (reason:
|
|
2035
|
+
deltaManager.on("disconnect", (reason: IConnectionStateChangeReason) => {
|
|
2021
2036
|
this.noopHeuristic?.notifyDisconnect();
|
|
2022
2037
|
if (!this.closed) {
|
|
2023
|
-
this.connectionStateHandler.receivedDisconnectEvent(reason
|
|
2038
|
+
this.connectionStateHandler.receivedDisconnectEvent(reason);
|
|
2024
2039
|
}
|
|
2025
2040
|
});
|
|
2026
2041
|
|
|
@@ -2073,8 +2088,7 @@ export class Container
|
|
|
2073
2088
|
private logConnectionStateChangeTelemetry(
|
|
2074
2089
|
value: ConnectionState,
|
|
2075
2090
|
oldState: ConnectionState,
|
|
2076
|
-
reason?:
|
|
2077
|
-
error?: IAnyDriverError,
|
|
2091
|
+
reason?: IConnectionStateChangeReason,
|
|
2078
2092
|
) {
|
|
2079
2093
|
// Log actual event
|
|
2080
2094
|
const time = performance.now();
|
|
@@ -2113,7 +2127,7 @@ export class Container
|
|
|
2113
2127
|
from: ConnectionState[oldState],
|
|
2114
2128
|
duration,
|
|
2115
2129
|
durationFromDisconnected,
|
|
2116
|
-
reason,
|
|
2130
|
+
reason: reason?.text,
|
|
2117
2131
|
connectionInitiationReason,
|
|
2118
2132
|
pendingClientId: this.connectionStateHandler.pendingClientId,
|
|
2119
2133
|
clientId: this.clientId,
|
|
@@ -2129,7 +2143,7 @@ export class Container
|
|
|
2129
2143
|
isDirty: this.isDirty,
|
|
2130
2144
|
...this._deltaManager.connectionProps,
|
|
2131
2145
|
},
|
|
2132
|
-
error,
|
|
2146
|
+
reason?.error,
|
|
2133
2147
|
);
|
|
2134
2148
|
|
|
2135
2149
|
if (value === ConnectionState.Connected) {
|
|
@@ -2137,7 +2151,10 @@ export class Container
|
|
|
2137
2151
|
}
|
|
2138
2152
|
}
|
|
2139
2153
|
|
|
2140
|
-
private propagateConnectionState(
|
|
2154
|
+
private propagateConnectionState(
|
|
2155
|
+
initialTransition: boolean,
|
|
2156
|
+
disconnectedReason?: IConnectionStateChangeReason,
|
|
2157
|
+
) {
|
|
2141
2158
|
// When container loaded, we want to propagate initial connection state.
|
|
2142
2159
|
// After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
2143
2160
|
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
@@ -2154,7 +2171,7 @@ export class Container
|
|
|
2154
2171
|
|
|
2155
2172
|
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
2156
2173
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
2157
|
-
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
2174
|
+
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason?.text);
|
|
2158
2175
|
}
|
|
2159
2176
|
|
|
2160
2177
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
@@ -2247,28 +2264,6 @@ export class Container
|
|
|
2247
2264
|
}
|
|
2248
2265
|
const local = this.clientId === message.clientId;
|
|
2249
2266
|
|
|
2250
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
2251
|
-
// flagged should have left, or from a client that's not in the quorum but should be
|
|
2252
|
-
if (message.clientId != null) {
|
|
2253
|
-
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
2254
|
-
|
|
2255
|
-
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
2256
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
2257
|
-
throw new Error("Remote message's clientId is missing from the quorum");
|
|
2258
|
-
}
|
|
2259
|
-
|
|
2260
|
-
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
2261
|
-
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
2262
|
-
// document we don't need to blow up aggressively.
|
|
2263
|
-
if (
|
|
2264
|
-
this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
2265
|
-
!canBeCoalescedByService(message)
|
|
2266
|
-
) {
|
|
2267
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
2268
|
-
throw new Error("Remote message's clientId already should have left");
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
2267
|
// Allow the protocol handler to process the message
|
|
2273
2268
|
const result = this.protocolHandler.processMessage(message, local);
|
|
2274
2269
|
|
|
@@ -2413,6 +2408,7 @@ export class Container
|
|
|
2413
2408
|
(error?: ICriticalContainerError) => this.close(error),
|
|
2414
2409
|
this.updateDirtyContainerState,
|
|
2415
2410
|
this.getAbsoluteUrl,
|
|
2411
|
+
() => this.resolvedUrl?.id,
|
|
2416
2412
|
() => this.clientId,
|
|
2417
2413
|
() => this.attachState,
|
|
2418
2414
|
() => this.connected,
|