@fluidframework/container-loader 0.59.4001 → 1.1.0-75972
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/.eslintrc.js +1 -1
- package/README.md +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +3 -1
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.d.ts +15 -3
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionState.js +15 -3
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +47 -11
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +108 -38
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +20 -28
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +97 -153
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +6 -4
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +8 -7
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +4 -5
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +4 -7
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +9 -1
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts +0 -1
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaManagerProxy.js +0 -3
- package/dist/deltaManagerProxy.js.map +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +8 -3
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +1 -13
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +2 -3
- 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 -3
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +0 -1
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +3 -4
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -7
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -2
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +4 -2
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionState.d.ts +15 -3
- package/lib/connectionState.d.ts.map +1 -1
- package/lib/connectionState.js +15 -3
- package/lib/connectionState.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +47 -11
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +108 -38
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +20 -28
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +98 -154
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +6 -4
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +8 -7
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +4 -5
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +4 -7
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +1 -1
- package/lib/contracts.js +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +9 -1
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts +0 -1
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaManagerProxy.js +0 -3
- package/lib/deltaManagerProxy.js.map +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +8 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +1 -13
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +2 -3
- 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 -3
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js +0 -1
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +3 -4
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +4 -7
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +3 -2
- package/lib/utils.js.map +1 -1
- package/package.json +14 -27
- package/src/connectionManager.ts +4 -6
- package/src/connectionState.ts +20 -6
- package/src/connectionStateHandler.ts +136 -56
- package/src/container.ts +139 -185
- package/src/containerContext.ts +10 -10
- package/src/containerStorageAdapter.ts +5 -10
- package/src/contracts.ts +1 -1
- package/src/deltaManager.ts +8 -2
- package/src/deltaManagerProxy.ts +0 -4
- package/src/deltaQueue.ts +7 -3
- package/src/index.ts +1 -0
- package/src/loader.ts +4 -21
- package/src/packageVersion.ts +1 -1
- package/src/protocolTreeDocumentStorageService.ts +0 -1
- package/src/retriableDocumentStorageService.ts +4 -12
- package/src/utils.ts +3 -2
|
@@ -3,36 +3,58 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
|
+
import { ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
|
|
7
|
+
import { assert, Timer } from "@fluidframework/common-utils";
|
|
7
8
|
import { IConnectionDetails } from "@fluidframework/container-definitions";
|
|
8
|
-
import {
|
|
9
|
+
import { ILocalSequencedClient, IProtocolHandler } from "@fluidframework/protocol-base";
|
|
10
|
+
import { ConnectionMode, IQuorumClients } from "@fluidframework/protocol-definitions";
|
|
9
11
|
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
10
|
-
import { assert, Timer } from "@fluidframework/common-utils";
|
|
11
12
|
import { ConnectionState } from "./connectionState";
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
/** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */
|
|
15
|
+
export interface IConnectionStateHandlerInputs {
|
|
16
|
+
/** Provides access to the clients currently in the quorum */
|
|
14
17
|
quorumClients: () => IQuorumClients | undefined;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
oldState: ConnectionState,
|
|
18
|
-
|
|
19
|
-
) => void;
|
|
18
|
+
/** Log to telemetry any change in state, included to Connecting */
|
|
19
|
+
logConnectionStateChangeTelemetry:
|
|
20
|
+
(value: ConnectionState, oldState: ConnectionState, reason?: string | undefined) => void;
|
|
21
|
+
/** Whether to expect the client to join in write mode on next connection */
|
|
20
22
|
shouldClientJoinWrite: () => boolean;
|
|
23
|
+
/** (Optional) How long should we wait on our previous client's Leave op before transitioning to Connected again */
|
|
21
24
|
maxClientLeaveWaitTime: number | undefined;
|
|
22
|
-
|
|
25
|
+
/** Log an issue encountered while in the Connecting state. details will be logged as a JSON string */
|
|
26
|
+
logConnectionIssue: (eventName: string, details?: ITelemetryProperties) => void;
|
|
27
|
+
/** Callback whenever the ConnectionState changes between Disconnected and Connected */
|
|
23
28
|
connectionStateChanged: () => void;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
shouldHaveLeft?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const JoinOpTimer = 45000;
|
|
31
|
+
const JoinOpTimeoutMs = 45000;
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* In the lifetime of a container, the connection will likely disconnect and reconnect periodically.
|
|
35
|
+
* This class ensures that any ops sent by this container instance on previous connection are either
|
|
36
|
+
* sequenced or blocked by the server before emitting the new "connected" event and allowing runtime to resubmit ops.
|
|
37
|
+
*
|
|
38
|
+
* Each connection is assigned a clientId by the service, and the connection is book-ended by a Join and a Leave op
|
|
39
|
+
* generated by the service. Due to the distributed nature of the ordering service, in the case of reconnect we cannot
|
|
40
|
+
* make any assumptions about ordering of operations between the old and new connections - i.e. new Join op could
|
|
41
|
+
* be sequenced before old Leave op (and some acks from pending ops that were in flight when we disconnected).
|
|
42
|
+
*
|
|
43
|
+
* The job of this class is to encapsulate the transition period during reconnect, which is identified by
|
|
44
|
+
* ConnectionState.CatchingUp. Specifically, before moving to Connected state with the new clientId, it ensures that:
|
|
45
|
+
* (A) We process the Leave op for the previous clientId. This allows us to properly handle any acks from in-flight ops
|
|
46
|
+
* that got sequenced with the old clientId (we'll recognize them as local ops). After the Leave op, any other
|
|
47
|
+
* pending ops can safely be submitted with the new clientId without fear of duplication in the sequenced op stream.
|
|
48
|
+
* (B) We process the Join op for the new clientId (identified when the underlying connection was first established),
|
|
49
|
+
* indicating the service is ready to sequence ops sent with the new clientId.
|
|
50
|
+
*
|
|
51
|
+
* For (A) we give up waiting after some time (same timeout as server uses), and go ahead and transition to Connected.
|
|
52
|
+
* For (B) we log telemetry if it takes too long, but still only transition to Connected when the Join op is processed
|
|
53
|
+
* and we are added to the Quorum.
|
|
54
|
+
*/
|
|
32
55
|
export class ConnectionStateHandler {
|
|
33
56
|
private _connectionState = ConnectionState.Disconnected;
|
|
34
57
|
private _pendingClientId: string | undefined;
|
|
35
|
-
private _clientId: string | undefined;
|
|
36
58
|
private readonly prevClientLeftTimer: Timer;
|
|
37
59
|
private readonly joinOpTimer: Timer;
|
|
38
60
|
|
|
@@ -55,8 +77,9 @@ export class ConnectionStateHandler {
|
|
|
55
77
|
}
|
|
56
78
|
|
|
57
79
|
constructor(
|
|
58
|
-
private readonly handler:
|
|
80
|
+
private readonly handler: IConnectionStateHandlerInputs,
|
|
59
81
|
private readonly logger: ITelemetryLogger,
|
|
82
|
+
private _clientId?: string,
|
|
60
83
|
) {
|
|
61
84
|
this.prevClientLeftTimer = new Timer(
|
|
62
85
|
// Default is 5 min for which we are going to wait for its own "leave" message. This is same as
|
|
@@ -73,13 +96,21 @@ export class ConnectionStateHandler {
|
|
|
73
96
|
// timing out ops fetches. So attempt recovery infrequently. Also fetch uses 30 second timeout, so
|
|
74
97
|
// if retrying fixes the problem, we should not see these events.
|
|
75
98
|
this.joinOpTimer = new Timer(
|
|
76
|
-
|
|
99
|
+
JoinOpTimeoutMs,
|
|
77
100
|
() => {
|
|
78
101
|
// I've observed timer firing within couple ms from disconnect event, looks like
|
|
79
102
|
// queued timer callback is not cancelled if timer is cancelled while callback sits in the queue.
|
|
80
|
-
if (this.connectionState
|
|
81
|
-
|
|
103
|
+
if (this.connectionState !== ConnectionState.CatchingUp) {
|
|
104
|
+
return;
|
|
82
105
|
}
|
|
106
|
+
const quorumClients = this.handler.quorumClients();
|
|
107
|
+
const details = {
|
|
108
|
+
quorumInitialized: quorumClients !== undefined,
|
|
109
|
+
hasPendingClientId: this.pendingClientId !== undefined,
|
|
110
|
+
inQuorum: quorumClients?.getMember(this.pendingClientId ?? "") !== undefined,
|
|
111
|
+
waitingForLeaveOp: this.waitingForLeaveOp,
|
|
112
|
+
};
|
|
113
|
+
this.handler.logConnectionIssue("NoJoinOp", details);
|
|
83
114
|
},
|
|
84
115
|
);
|
|
85
116
|
}
|
|
@@ -94,6 +125,10 @@ export class ConnectionStateHandler {
|
|
|
94
125
|
this.joinOpTimer.clear();
|
|
95
126
|
}
|
|
96
127
|
|
|
128
|
+
private get waitingForLeaveOp() {
|
|
129
|
+
return this.prevClientLeftTimer.hasTimer;
|
|
130
|
+
}
|
|
131
|
+
|
|
97
132
|
public dispose() {
|
|
98
133
|
assert(!this.joinOpTimer.hasTimer, 0x2a5 /* "join timer" */);
|
|
99
134
|
this.prevClientLeftTimer.clear();
|
|
@@ -102,13 +137,13 @@ export class ConnectionStateHandler {
|
|
|
102
137
|
public containerSaved() {
|
|
103
138
|
// If we were waiting for moving to Connected state, then only apply for state change. Since the container
|
|
104
139
|
// is now saved and we don't have any ops to roundtrip, we can clear the timer and apply for connected state.
|
|
105
|
-
if (this.
|
|
140
|
+
if (this.waitingForLeaveOp) {
|
|
106
141
|
this.prevClientLeftTimer.clear();
|
|
107
142
|
this.applyForConnectedState("containerSaved");
|
|
108
143
|
}
|
|
109
144
|
}
|
|
110
145
|
|
|
111
|
-
|
|
146
|
+
private receivedAddMemberEvent(clientId: string) {
|
|
112
147
|
// This is the only one that requires the pending client ID
|
|
113
148
|
if (clientId === this.pendingClientId) {
|
|
114
149
|
if (this.joinOpTimer.hasTimer) {
|
|
@@ -119,11 +154,13 @@ export class ConnectionStateHandler {
|
|
|
119
154
|
this.handler.logConnectionIssue("ReceivedJoinOp");
|
|
120
155
|
}
|
|
121
156
|
// Start the event in case we are waiting for leave or timeout.
|
|
122
|
-
if (this.
|
|
157
|
+
if (this.waitingForLeaveOp) {
|
|
123
158
|
this.waitEvent = PerformanceEvent.start(this.logger, {
|
|
124
159
|
eventName: "WaitBeforeClientLeave",
|
|
125
|
-
|
|
126
|
-
|
|
160
|
+
details: JSON.stringify({
|
|
161
|
+
waitOnClientId: this._clientId,
|
|
162
|
+
hadOutstandingOps: this.handler.shouldClientJoinWrite(),
|
|
163
|
+
}),
|
|
127
164
|
});
|
|
128
165
|
}
|
|
129
166
|
this.applyForConnectedState("addMemberEvent");
|
|
@@ -133,13 +170,18 @@ export class ConnectionStateHandler {
|
|
|
133
170
|
private applyForConnectedState(source: "removeMemberEvent" | "addMemberEvent" | "timeout" | "containerSaved") {
|
|
134
171
|
const quorumClients = this.handler.quorumClients();
|
|
135
172
|
assert(quorumClients !== undefined, 0x236 /* "In all cases it should be already installed" */);
|
|
173
|
+
|
|
174
|
+
assert(this.waitingForLeaveOp === false ||
|
|
175
|
+
(this.clientId !== undefined && quorumClients.getMember(this.clientId) !== undefined),
|
|
176
|
+
0x2e2 /* "Must only wait for leave message when clientId in quorum" */);
|
|
177
|
+
|
|
136
178
|
// Move to connected state only if we are in Connecting state, we have seen our join op
|
|
137
179
|
// and there is no timer running which means we are not waiting for previous client to leave
|
|
138
|
-
// or timeout has
|
|
180
|
+
// or timeout has occurred while doing so.
|
|
139
181
|
if (this.pendingClientId !== this.clientId
|
|
140
182
|
&& this.pendingClientId !== undefined
|
|
141
183
|
&& quorumClients.getMember(this.pendingClientId) !== undefined
|
|
142
|
-
&& !this.
|
|
184
|
+
&& !this.waitingForLeaveOp
|
|
143
185
|
) {
|
|
144
186
|
this.waitEvent?.end({ source });
|
|
145
187
|
this.setConnectionState(ConnectionState.Connected);
|
|
@@ -148,17 +190,18 @@ export class ConnectionStateHandler {
|
|
|
148
190
|
this.logger.sendTelemetryEvent({
|
|
149
191
|
eventName: "connectedStateRejected",
|
|
150
192
|
category: source === "timeout" ? "error" : "generic",
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
193
|
+
details: JSON.stringify({
|
|
194
|
+
source,
|
|
195
|
+
pendingClientId: this.pendingClientId,
|
|
196
|
+
clientId: this.clientId,
|
|
197
|
+
waitingForLeaveOp: this.waitingForLeaveOp,
|
|
198
|
+
inQuorum: quorumClients?.getMember(this.pendingClientId ?? "") !== undefined,
|
|
199
|
+
}),
|
|
157
200
|
});
|
|
158
201
|
}
|
|
159
202
|
}
|
|
160
203
|
|
|
161
|
-
|
|
204
|
+
private receivedRemoveMemberEvent(clientId: string) {
|
|
162
205
|
// If the client which has left was us, then finish the timer.
|
|
163
206
|
if (this.clientId === clientId) {
|
|
164
207
|
this.prevClientLeftTimer.clear();
|
|
@@ -173,43 +216,63 @@ export class ConnectionStateHandler {
|
|
|
173
216
|
this.setConnectionState(ConnectionState.Disconnected, reason);
|
|
174
217
|
}
|
|
175
218
|
|
|
219
|
+
/**
|
|
220
|
+
* The "connect" event indicates the connection to the Relay Service is live.
|
|
221
|
+
* However, some additional conditions must be met before we can fully transition to
|
|
222
|
+
* "Connected" state. This function handles that interim period, known as "Connecting" state.
|
|
223
|
+
* @param connectionMode - Read or Write connection
|
|
224
|
+
* @param details - Connection details returned from the ordering service
|
|
225
|
+
*/
|
|
176
226
|
public receivedConnectEvent(
|
|
177
227
|
connectionMode: ConnectionMode,
|
|
178
228
|
details: IConnectionDetails,
|
|
179
229
|
) {
|
|
180
230
|
const oldState = this._connectionState;
|
|
181
|
-
this._connectionState = ConnectionState.
|
|
231
|
+
this._connectionState = ConnectionState.CatchingUp;
|
|
232
|
+
|
|
233
|
+
const writeConnection = connectionMode === "write";
|
|
234
|
+
assert(writeConnection || !this.handler.shouldClientJoinWrite(),
|
|
235
|
+
0x30a /* shouldClientJoinWrite should imply this is a writeConnection */);
|
|
236
|
+
assert(writeConnection || !this.waitingForLeaveOp,
|
|
237
|
+
0x2a6 /* "waitingForLeaveOp should imply writeConnection (we need to be ready to flush pending ops)" */);
|
|
238
|
+
|
|
239
|
+
// Note that this may be undefined since the connection is established proactively on load
|
|
240
|
+
// and the quorum may still be under initialization.
|
|
241
|
+
const quorumClients: IQuorumClients | undefined = this.handler.quorumClients();
|
|
182
242
|
|
|
183
243
|
// Stash the clientID to detect when transitioning from connecting (socket.io channel open) to connected
|
|
184
244
|
// (have received the join message for the client ID)
|
|
185
245
|
// This is especially important in the reconnect case. It's possible there could be outstanding
|
|
186
246
|
// ops sent by this client, so we should keep the old client id until we see our own client's
|
|
187
|
-
// join message. after we see the join message for
|
|
247
|
+
// join message. after we see the join message for our new connection with our new client id,
|
|
188
248
|
// we know there can no longer be outstanding ops that we sent with the previous client id.
|
|
189
249
|
this._pendingClientId = details.clientId;
|
|
190
250
|
|
|
191
|
-
// Report telemetry after we set
|
|
192
|
-
this.handler.logConnectionStateChangeTelemetry(ConnectionState.
|
|
251
|
+
// IMPORTANT: Report telemetry after we set _pendingClientId, but before transitioning to Connected state
|
|
252
|
+
this.handler.logConnectionStateChangeTelemetry(ConnectionState.CatchingUp, oldState);
|
|
193
253
|
|
|
194
|
-
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
// Given async processes, it's possible that we have already processed our own join message before
|
|
254
|
+
// For write connections, this pending clientId could be in the quorum already (i.e. join op already processed).
|
|
255
|
+
// We are fetching ops from storage in parallel to connecting to Relay Service,
|
|
256
|
+
// and given async processes, it's possible that we have already processed our own join message before
|
|
198
257
|
// connection was fully established.
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
} else if (connectionMode === "write") {
|
|
258
|
+
// If quorumClients itself is undefined, we expect it will process the join op after it's initialized.
|
|
259
|
+
const waitingForJoinOp = writeConnection && quorumClients?.getMember(this._pendingClientId) === undefined;
|
|
260
|
+
|
|
261
|
+
if (waitingForJoinOp) {
|
|
262
|
+
// Previous client left, and we are waiting for our own join op. When it is processed we'll join the quorum
|
|
263
|
+
// and attempt to transition to Connected state via receivedAddMemberEvent.
|
|
206
264
|
this.startJoinOpTimer();
|
|
265
|
+
} else if (!this.waitingForLeaveOp) {
|
|
266
|
+
// We're not waiting for Join or Leave op (if read-only connection those don't even apply),
|
|
267
|
+
// go ahead and declare the state to be Connected!
|
|
268
|
+
// If we are waiting for Leave op still, do nothing for now, we will transition to Connected later.
|
|
269
|
+
this.setConnectionState(ConnectionState.Connected);
|
|
207
270
|
}
|
|
208
271
|
}
|
|
209
272
|
|
|
210
|
-
private setConnectionState(value: ConnectionState.Disconnected, reason: string);
|
|
211
|
-
private setConnectionState(value: ConnectionState.Connected);
|
|
212
|
-
private setConnectionState(value: ConnectionState, reason?: string) {
|
|
273
|
+
private setConnectionState(value: ConnectionState.Disconnected, reason: string): void;
|
|
274
|
+
private setConnectionState(value: ConnectionState.Connected): void;
|
|
275
|
+
private setConnectionState(value: ConnectionState, reason?: string): void {
|
|
213
276
|
if (this.connectionState === value) {
|
|
214
277
|
// Already in the desired state - exit early
|
|
215
278
|
this.logger.sendErrorEvent({ eventName: "setConnectionStateSame", value });
|
|
@@ -224,7 +287,7 @@ export class ConnectionStateHandler {
|
|
|
224
287
|
client = quorumClients?.getMember(this._clientId);
|
|
225
288
|
}
|
|
226
289
|
if (value === ConnectionState.Connected) {
|
|
227
|
-
assert(oldState === ConnectionState.
|
|
290
|
+
assert(oldState === ConnectionState.CatchingUp,
|
|
228
291
|
0x1d8 /* "Should only transition from Connecting state" */);
|
|
229
292
|
// Mark our old client should have left in the quorum if it's still there
|
|
230
293
|
if (client !== undefined) {
|
|
@@ -249,9 +312,11 @@ export class ConnectionStateHandler {
|
|
|
249
312
|
// Adding this event temporarily so that we can get help debugging if something goes wrong.
|
|
250
313
|
this.logger.sendTelemetryEvent({
|
|
251
314
|
eventName: "noWaitOnDisconnected",
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
315
|
+
details: JSON.stringify({
|
|
316
|
+
inQuorum: client !== undefined,
|
|
317
|
+
waitingForLeaveOp: this.waitingForLeaveOp,
|
|
318
|
+
hadOutstandingOps: this.handler.shouldClientJoinWrite(),
|
|
319
|
+
}),
|
|
255
320
|
});
|
|
256
321
|
}
|
|
257
322
|
}
|
|
@@ -262,4 +327,19 @@ export class ConnectionStateHandler {
|
|
|
262
327
|
// Propagate event across layers
|
|
263
328
|
this.handler.connectionStateChanged();
|
|
264
329
|
}
|
|
330
|
+
|
|
331
|
+
public initProtocol(protocol: IProtocolHandler) {
|
|
332
|
+
protocol.quorum.on("addMember", (clientId, _details) => {
|
|
333
|
+
this.receivedAddMemberEvent(clientId);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
protocol.quorum.on("removeMember", (clientId) => {
|
|
337
|
+
this.receivedRemoveMemberEvent(clientId);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// if we have a clientId from a previous container we need to wait for its leave message
|
|
341
|
+
if (this.clientId !== undefined && protocol.quorum.getMember(this.clientId) !== undefined) {
|
|
342
|
+
this.prevClientLeftTimer.restart();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
265
345
|
}
|