@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
|
@@ -51,8 +51,8 @@ class ConnectionStateHandlerPassThrough {
|
|
|
51
51
|
initProtocol(protocol) {
|
|
52
52
|
return this.pimpl.initProtocol(protocol);
|
|
53
53
|
}
|
|
54
|
-
receivedDisconnectEvent(reason
|
|
55
|
-
return this.pimpl.receivedDisconnectEvent(reason
|
|
54
|
+
receivedDisconnectEvent(reason) {
|
|
55
|
+
return this.pimpl.receivedDisconnectEvent(reason);
|
|
56
56
|
}
|
|
57
57
|
establishingConnection(reason) {
|
|
58
58
|
return this.pimpl.establishingConnection(reason);
|
|
@@ -69,8 +69,8 @@ class ConnectionStateHandlerPassThrough {
|
|
|
69
69
|
get logger() {
|
|
70
70
|
return this.inputs.logger;
|
|
71
71
|
}
|
|
72
|
-
connectionStateChanged(value, oldState, reason
|
|
73
|
-
return this.inputs.connectionStateChanged(value, oldState, reason
|
|
72
|
+
connectionStateChanged(value, oldState, reason) {
|
|
73
|
+
return this.inputs.connectionStateChanged(value, oldState, reason);
|
|
74
74
|
}
|
|
75
75
|
shouldClientJoinWrite() {
|
|
76
76
|
return this.inputs.shouldClientJoinWrite();
|
|
@@ -99,14 +99,16 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
99
99
|
assert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);
|
|
100
100
|
assert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);
|
|
101
101
|
this._connectionState = ConnectionState.Connected;
|
|
102
|
-
this.inputs.connectionStateChanged(ConnectionState.Connected, ConnectionState.CatchingUp,
|
|
102
|
+
this.inputs.connectionStateChanged(ConnectionState.Connected, ConnectionState.CatchingUp, {
|
|
103
|
+
text: "caught up",
|
|
104
|
+
});
|
|
103
105
|
};
|
|
104
106
|
this._connectionState = this.pimpl.connectionState;
|
|
105
107
|
}
|
|
106
108
|
get connectionState() {
|
|
107
109
|
return this._connectionState;
|
|
108
110
|
}
|
|
109
|
-
connectionStateChanged(value, oldState, reason
|
|
111
|
+
connectionStateChanged(value, oldState, reason) {
|
|
110
112
|
switch (value) {
|
|
111
113
|
case ConnectionState.Connected:
|
|
112
114
|
assert(this._connectionState === ConnectionState.CatchingUp, 0x3e1 /* connectivity transitions */);
|
|
@@ -134,7 +136,7 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
|
|
|
134
136
|
default:
|
|
135
137
|
}
|
|
136
138
|
this._connectionState = value;
|
|
137
|
-
this.inputs.connectionStateChanged(value, oldState, reason
|
|
139
|
+
this.inputs.connectionStateChanged(value, oldState, reason);
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
/**
|
|
@@ -298,9 +300,9 @@ class ConnectionStateHandler {
|
|
|
298
300
|
this.applyForConnectedState("removeMemberEvent");
|
|
299
301
|
}
|
|
300
302
|
}
|
|
301
|
-
receivedDisconnectEvent(reason
|
|
303
|
+
receivedDisconnectEvent(reason) {
|
|
302
304
|
this.connection = undefined;
|
|
303
|
-
this.setConnectionState(ConnectionState.Disconnected, reason
|
|
305
|
+
this.setConnectionState(ConnectionState.Disconnected, reason);
|
|
304
306
|
}
|
|
305
307
|
cancelEstablishingConnection(reason) {
|
|
306
308
|
assert(this._connectionState === ConnectionState.EstablishingConnection, 0x6d3 /* Connection state should be EstablishingConnection */);
|
|
@@ -312,7 +314,10 @@ class ConnectionStateHandler {
|
|
|
312
314
|
establishingConnection(reason) {
|
|
313
315
|
const oldState = this._connectionState;
|
|
314
316
|
this._connectionState = ConnectionState.EstablishingConnection;
|
|
315
|
-
this.handler.connectionStateChanged(ConnectionState.EstablishingConnection, oldState,
|
|
317
|
+
this.handler.connectionStateChanged(ConnectionState.EstablishingConnection, oldState, {
|
|
318
|
+
text: `Establishing Connection due to ${reason.text}`,
|
|
319
|
+
error: reason.error,
|
|
320
|
+
});
|
|
316
321
|
}
|
|
317
322
|
shouldWaitForJoinSignal() {
|
|
318
323
|
assert(this.connection !== undefined, 0x4b4 /* all callers call here with active connection */);
|
|
@@ -365,7 +370,7 @@ class ConnectionStateHandler {
|
|
|
365
370
|
}
|
|
366
371
|
// else - We are waiting for Leave op still, do nothing for now, we will transition to Connected later
|
|
367
372
|
}
|
|
368
|
-
setConnectionState(value, reason
|
|
373
|
+
setConnectionState(value, reason) {
|
|
369
374
|
if (this.connectionState === value) {
|
|
370
375
|
// Already in the desired state - exit early
|
|
371
376
|
this.handler.logger.sendErrorEvent({ eventName: "setConnectionStateSame", value });
|
|
@@ -417,7 +422,7 @@ class ConnectionStateHandler {
|
|
|
417
422
|
}
|
|
418
423
|
}
|
|
419
424
|
// Report transition before we propagate event across layers
|
|
420
|
-
this.handler.connectionStateChanged(this._connectionState, oldState, reason
|
|
425
|
+
this.handler.connectionStateChanged(this._connectionState, oldState, reason);
|
|
421
426
|
}
|
|
422
427
|
// Helper method to switch between quorum and audience.
|
|
423
428
|
// Old design was checking only quorum for "write" clients.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionStateHandler.js","sourceRoot":"","sources":["../src/connectionStateHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryProperties, TelemetryEventCategory } from \"@fluidframework/core-interfaces\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { IAnyDriverError } from \"@fluidframework/driver-definitions\";\nimport { ISequencedClient, IClient } from \"@fluidframework/protocol-definitions\";\nimport {\n\tITelemetryLoggerExt,\n\tPerformanceEvent,\n\tloggerToMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { CatchUpMonitor, ICatchUpMonitor } from \"./catchUpMonitor\";\nimport { ConnectionState } from \"./connectionState\";\nimport { IConnectionDetailsInternal } from \"./contracts\";\nimport { IProtocolHandler } from \"./protocol\";\n\n// Based on recent data, it looks like majority of cases where we get stuck are due to really slow or\n// timing out ops fetches. So attempt recovery infrequently. Also fetch uses 30 second timeout, so\n// if retrying fixes the problem, we should not see these events.\nconst JoinOpTimeoutMs = 45000;\n\n// Timeout waiting for \"self\" join signal, before giving up\nconst JoinSignalTimeoutMs = 5000;\n\n/** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */\nexport interface IConnectionStateHandlerInputs {\n\tlogger: ITelemetryLoggerExt;\n\t/** Log to telemetry any change in state, included to Connecting */\n\tconnectionStateChanged: (\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t\terror?: IAnyDriverError,\n\t) => void;\n\t/** Whether to expect the client to join in write mode on next connection */\n\tshouldClientJoinWrite: () => boolean;\n\t/** (Optional) How long should we wait on our previous client's Leave op before transitioning to Connected again */\n\tmaxClientLeaveWaitTime: number | undefined;\n\t/** Log an issue encountered while in the Connecting state. details will be logged as a JSON string */\n\tlogConnectionIssue: (\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) => void;\n\t/** Callback to note that an old local client ID is still present in the Quorum that should have left and should now be considered invalid */\n\tclientShouldHaveLeft: (clientId: string) => void;\n}\n\n/**\n * interface that connection state handler implements\n */\nexport interface IConnectionStateHandler {\n\treadonly connectionState: ConnectionState;\n\treadonly pendingClientId: string | undefined;\n\n\tcontainerSaved(): void;\n\tdispose(): void;\n\tinitProtocol(protocol: IProtocolHandler): void;\n\treceivedConnectEvent(details: IConnectionDetailsInternal): void;\n\treceivedDisconnectEvent(reason: string, error?: IAnyDriverError): void;\n\testablishingConnection(reason: string): void;\n\t/**\n\t * Switches state to disconnected when we are still establishing connection during container.load(),\n\t * container connect() or reconnect and the container gets closed or disposed or disconnect happens.\n\t * @param reason - reason for cancelling the connection.\n\t */\n\tcancelEstablishingConnection(reason: string): void;\n}\n\nexport function createConnectionStateHandler(\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tconst mc = loggerToMonitoringContext(inputs.logger);\n\treturn createConnectionStateHandlerCore(\n\t\tmc.config.getBoolean(\"Fluid.Container.CatchUpBeforeDeclaringConnected\") === true, // connectedRaisedWhenCaughtUp\n\t\tmc.config.getBoolean(\"Fluid.Container.EnableJoinSignalWait\") === true, // readClientsWaitForJoinSignal\n\t\tinputs,\n\t\tdeltaManager,\n\t\tclientId,\n\t);\n}\n\nexport function createConnectionStateHandlerCore(\n\tconnectedRaisedWhenCaughtUp: boolean,\n\treadClientsWaitForJoinSignal: boolean,\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tif (!connectedRaisedWhenCaughtUp) {\n\t\treturn new ConnectionStateHandler(inputs, readClientsWaitForJoinSignal, clientId);\n\t}\n\treturn new ConnectionStateCatchup(\n\t\tinputs,\n\t\t(handler: IConnectionStateHandlerInputs) =>\n\t\t\tnew ConnectionStateHandler(handler, readClientsWaitForJoinSignal, clientId),\n\t\tdeltaManager,\n\t);\n}\n\n/**\n * Helper internal interface to abstract away Audience & Quorum\n */\ninterface IMembership {\n\ton(\n\t\teventName: \"addMember\" | \"removeMember\",\n\t\tlistener: (clientId: string, details: IClient | ISequencedClient) => void,\n\t);\n\tgetMember(clientId: string): undefined | unknown;\n}\n\n/**\n * Class that can be used as a base class for building IConnectionStateHandler adapters / pipeline.\n * It implements both ends of communication interfaces and passes data back and forward\n */\nclass ConnectionStateHandlerPassThrough\n\timplements IConnectionStateHandler, IConnectionStateHandlerInputs\n{\n\tprotected readonly pimpl: IConnectionStateHandler;\n\n\tconstructor(\n\t\tprotected readonly inputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t) {\n\t\tthis.pimpl = pimplFactory(this);\n\t}\n\n\t/**\n\t * IConnectionStateHandler\n\t */\n\tpublic get connectionState() {\n\t\treturn this.pimpl.connectionState;\n\t}\n\tpublic get pendingClientId() {\n\t\treturn this.pimpl.pendingClientId;\n\t}\n\n\tpublic containerSaved() {\n\t\treturn this.pimpl.containerSaved();\n\t}\n\tpublic dispose() {\n\t\treturn this.pimpl.dispose();\n\t}\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\treturn this.pimpl.initProtocol(protocol);\n\t}\n\tpublic receivedDisconnectEvent(reason: string, error?: IAnyDriverError) {\n\t\treturn this.pimpl.receivedDisconnectEvent(reason, error);\n\t}\n\n\tpublic establishingConnection(reason: string) {\n\t\treturn this.pimpl.establishingConnection(reason);\n\t}\n\n\tpublic cancelEstablishingConnection(reason: string) {\n\t\treturn this.pimpl.cancelEstablishingConnection(reason);\n\t}\n\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\treturn this.pimpl.receivedConnectEvent(details);\n\t}\n\n\t/**\n\t * IConnectionStateHandlerInputs\n\t */\n\n\tpublic get logger() {\n\t\treturn this.inputs.logger;\n\t}\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t\terror?: IAnyDriverError,\n\t) {\n\t\treturn this.inputs.connectionStateChanged(value, oldState, reason, error);\n\t}\n\tpublic shouldClientJoinWrite() {\n\t\treturn this.inputs.shouldClientJoinWrite();\n\t}\n\tpublic get maxClientLeaveWaitTime() {\n\t\treturn this.inputs.maxClientLeaveWaitTime;\n\t}\n\tpublic logConnectionIssue(\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) {\n\t\treturn this.inputs.logConnectionIssue(eventName, category, details);\n\t}\n\tpublic clientShouldHaveLeft(clientId: string) {\n\t\treturn this.inputs.clientShouldHaveLeft(clientId);\n\t}\n}\n\n/**\n * Implementation of IConnectionStateHandler pass-through adapter that waits for specific sequence number\n * before raising connected event\n */\nclass ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {\n\tprivate catchUpMonitor: ICatchUpMonitor | undefined;\n\n\tconstructor(\n\t\tinputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t\tprivate readonly deltaManager: IDeltaManager<any, any>,\n\t) {\n\t\tsuper(inputs, pimplFactory);\n\t\tthis._connectionState = this.pimpl.connectionState;\n\t}\n\n\tprivate _connectionState: ConnectionState;\n\tpublic get connectionState() {\n\t\treturn this._connectionState;\n\t}\n\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t\terror?: IAnyDriverError,\n\t) {\n\t\tswitch (value) {\n\t\t\tcase ConnectionState.Connected:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.CatchingUp,\n\t\t\t\t\t0x3e1 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\t// Create catch-up monitor here (not earlier), as we might get more exact info by now about how far\n\t\t\t\t// client is behind through join signal. This is only true if base layer uses signals (i.e. audience,\n\t\t\t\t// not quorum, including for \"rea\" connections) to make decisions about moving to \"connected\" state.\n\t\t\t\t// In addition to that, in its current form, doing this in ConnectionState.CatchingUp is dangerous as\n\t\t\t\t// we might get callback right away, and it will screw up state transition (as code outside of switch\n\t\t\t\t// statement will overwrite current state).\n\t\t\t\tassert(\n\t\t\t\t\tthis.catchUpMonitor === undefined,\n\t\t\t\t\t0x3eb /* catchUpMonitor should be gone */,\n\t\t\t\t);\n\t\t\t\tthis.catchUpMonitor = new CatchUpMonitor(\n\t\t\t\t\tthis.deltaManager,\n\t\t\t\t\tthis.transitionToConnectedState,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase ConnectionState.Disconnected:\n\t\t\t\tthis.catchUpMonitor?.dispose();\n\t\t\t\tthis.catchUpMonitor = undefined;\n\t\t\t\tbreak;\n\t\t\t// ConnectionState.EstablishingConnection state would be set when we start establishing connection\n\t\t\t// during container.connect() or reconnect because of an error.\n\t\t\tcase ConnectionState.EstablishingConnection:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.Disconnected,\n\t\t\t\t\t0x6d2 /* connectivity transition to establishing connection */,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ConnectionState.CatchingUp:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.EstablishingConnection,\n\t\t\t\t\t0x3e3 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t}\n\t\tthis._connectionState = value;\n\t\tthis.inputs.connectionStateChanged(value, oldState, reason, error);\n\t}\n\n\tprivate readonly transitionToConnectedState = () => {\n\t\t// Defensive measure, we should always be in Connecting state when this is called.\n\t\tconst state = this.pimpl.connectionState;\n\t\tassert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);\n\t\tassert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);\n\t\tthis._connectionState = ConnectionState.Connected;\n\t\tthis.inputs.connectionStateChanged(\n\t\t\tConnectionState.Connected,\n\t\t\tConnectionState.CatchingUp,\n\t\t\t\"caught up\",\n\t\t);\n\t};\n}\n\n/**\n * In the lifetime of a container, the connection will likely disconnect and reconnect periodically.\n * This class ensures that any ops sent by this container instance on previous connection are either\n * sequenced or blocked by the server before emitting the new \"connected\" event and allowing runtime to resubmit ops.\n *\n * Each connection is assigned a clientId by the service, and the connection is book-ended by a Join and a Leave op\n * generated by the service. Due to the distributed nature of the Relay Service, in the case of reconnect we cannot\n * make any assumptions about ordering of operations between the old and new connections - i.e. new Join op could\n * be sequenced before old Leave op (and some acks from pending ops that were in flight when we disconnected).\n *\n * The job of this class is to encapsulate the transition period during reconnect, which is identified by\n * ConnectionState.CatchingUp. Specifically, before moving to Connected state with the new clientId, it ensures that:\n *\n * a. We process the Leave op for the previous clientId. This allows us to properly handle any acks from in-flight ops\n * that got sequenced with the old clientId (we'll recognize them as local ops). After the Leave op, any other\n * pending ops can safely be submitted with the new clientId without fear of duplication in the sequenced op stream.\n *\n * b. We process the Join op for the new clientId (identified when the underlying connection was first established),\n * indicating the service is ready to sequence ops sent with the new clientId.\n *\n * c. We process all ops known at the time the underlying connection was established (so we are \"caught up\")\n *\n * For (a) we give up waiting after some time (same timeout as server uses), and go ahead and transition to Connected.\n *\n * For (b) we log telemetry if it takes too long, but still only transition to Connected when the Join op/signal is\n * processed.\n *\n * For (c) this is optional behavior, controlled by the parameters of receivedConnectEvent\n */\nclass ConnectionStateHandler implements IConnectionStateHandler {\n\tprivate _connectionState = ConnectionState.Disconnected;\n\tprivate _pendingClientId: string | undefined;\n\tprivate readonly prevClientLeftTimer: Timer;\n\tprivate readonly joinOpTimer: Timer;\n\tprivate protocol?: IProtocolHandler;\n\tprivate connection?: IConnectionDetailsInternal;\n\tprivate _clientId?: string;\n\n\tprivate waitEvent: PerformanceEvent | undefined;\n\n\tpublic get connectionState(): ConnectionState {\n\t\treturn this._connectionState;\n\t}\n\n\tprivate get clientId(): string | undefined {\n\t\treturn this._clientId;\n\t}\n\n\tpublic get pendingClientId(): string | undefined {\n\t\treturn this._pendingClientId;\n\t}\n\n\tconstructor(\n\t\tprivate readonly handler: IConnectionStateHandlerInputs,\n\t\tprivate readonly readClientsWaitForJoinSignal: boolean,\n\t\tclientIdFromPausedSession?: string,\n\t) {\n\t\tthis._clientId = clientIdFromPausedSession;\n\t\tthis.prevClientLeftTimer = new Timer(\n\t\t\t// Default is 5 min for which we are going to wait for its own \"leave\" message. This is same as\n\t\t\t// the max time on server after which leave op is sent.\n\t\t\tthis.handler.maxClientLeaveWaitTime ?? 300000,\n\t\t\t() => {\n\t\t\t\tassert(\n\t\t\t\t\tthis.connectionState !== ConnectionState.Connected,\n\t\t\t\t\t0x2ac /* \"Connected when timeout waiting for leave from previous session fired!\" */,\n\t\t\t\t);\n\t\t\t\tthis.applyForConnectedState(\"timeout\");\n\t\t\t},\n\t\t);\n\n\t\tthis.joinOpTimer = new Timer(\n\t\t\t0, // default value is not used - startJoinOpTimer() explicitly provides timeout\n\t\t\t() => {\n\t\t\t\t// I've observed timer firing within couple ms from disconnect event, looks like\n\t\t\t\t// queued timer callback is not cancelled if timer is cancelled while callback sits in the queue.\n\t\t\t\tif (this.connectionState !== ConnectionState.CatchingUp) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst details = {\n\t\t\t\t\tprotocolInitialized: this.protocol !== undefined,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t};\n\t\t\t\tthis.handler.logConnectionIssue(\"NoJoinOp\", \"error\", details);\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate startJoinOpTimer() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x234 /* \"has joinOpTimer\" */);\n\t\tassert(this.connection !== undefined, 0x4b3 /* have connection */);\n\t\tthis.joinOpTimer.start(\n\t\t\tthis.connection.mode === \"write\" ? JoinOpTimeoutMs : JoinSignalTimeoutMs,\n\t\t);\n\t}\n\n\tprivate stopJoinOpTimer() {\n\t\tassert(this.joinOpTimer.hasTimer, 0x235 /* \"no joinOpTimer\" */);\n\t\tthis.joinOpTimer.clear();\n\t}\n\n\tprivate get waitingForLeaveOp() {\n\t\treturn this.prevClientLeftTimer.hasTimer;\n\t}\n\n\tpublic dispose() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x2a5 /* \"join timer\" */);\n\t\tthis.prevClientLeftTimer.clear();\n\t}\n\n\tpublic containerSaved() {\n\t\t// If we were waiting for moving to Connected state, then only apply for state change. Since the container\n\t\t// is now saved and we don't have any ops to roundtrip, we can clear the timer and apply for connected state.\n\t\tif (this.waitingForLeaveOp) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"containerSaved\");\n\t\t}\n\t}\n\n\tprivate receivedAddMemberEvent(clientId: string) {\n\t\t// This is the only one that requires the pending client ID\n\t\tif (clientId === this.pendingClientId) {\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t} else if (this.shouldWaitForJoinSignal()) {\n\t\t\t\t// timer has already fired, meaning it took too long to get join op/signal.\n\t\t\t\t// Record how long it actually took to recover.\n\t\t\t\t// This is generic event, as it by itself is not an error.\n\t\t\t\t// We also have a case where NoJoinOp happens during container boot (we do not report it as error in such case),\n\t\t\t\t// if this log statement happens after boot - we do not want to consider it error case.\n\t\t\t\tthis.handler.logConnectionIssue(\"ReceivedJoinOp\", \"generic\");\n\t\t\t}\n\t\t\t// Start the event in case we are waiting for leave or timeout.\n\t\t\tif (this.waitingForLeaveOp) {\n\t\t\t\tthis.waitEvent = PerformanceEvent.start(this.handler.logger, {\n\t\t\t\t\teventName: \"WaitBeforeClientLeave\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\twaitOnClientId: this._clientId,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.applyForConnectedState(\"addMemberEvent\");\n\t\t} else if (clientId === this.clientId) {\n\t\t\t// If we see our clientId and it's not also our pending ID, it's our own join op\n\t\t\t// being replayed, so start the timer in case our previous client is still in quorum\n\t\t\tassert(\n\t\t\t\t!this.waitingForLeaveOp,\n\t\t\t\t0x5d2 /* Unexpected join op with current clientId while waiting */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\tthis.connectionState !== ConnectionState.Connected,\n\t\t\t\t0x5d3 /* Unexpected join op with current clientId while connected */,\n\t\t\t);\n\t\t\tthis.prevClientLeftTimer.restart();\n\t\t}\n\t}\n\n\tprivate applyForConnectedState(\n\t\tsource: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\" | \"containerSaved\",\n\t) {\n\t\tassert(\n\t\t\tthis.protocol !== undefined,\n\t\t\t0x236 /* \"In all cases it should be already installed\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t!this.waitingForLeaveOp || this.hasMember(this.clientId),\n\t\t\t0x2e2 /* \"Must only wait for leave message when clientId in quorum\" */,\n\t\t);\n\n\t\t// Move to connected state only if we are in Connecting state, we have seen our join op\n\t\t// and there is no timer running which means we are not waiting for previous client to leave\n\t\t// or timeout has occurred while doing so.\n\t\tif (\n\t\t\tthis.pendingClientId !== this.clientId &&\n\t\t\tthis.hasMember(this.pendingClientId) &&\n\t\t\t!this.waitingForLeaveOp\n\t\t) {\n\t\t\tthis.waitEvent?.end({ source });\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t} else {\n\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t// We may not see any ops due to being disconnected all that time - that's not an error!\n\t\t\tconst error =\n\t\t\t\tsource === \"timeout\" && this.connectionState !== ConnectionState.Disconnected;\n\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"connectedStateRejected\",\n\t\t\t\tcategory: error ? \"error\" : \"generic\",\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\tsource,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientId: this.clientId,\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate receivedRemoveMemberEvent(clientId: string) {\n\t\t// If the client which has left was us, then finish the timer.\n\t\tif (this.clientId === clientId) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"removeMemberEvent\");\n\t\t}\n\t}\n\n\tpublic receivedDisconnectEvent(reason: string, error?: IAnyDriverError) {\n\t\tthis.connection = undefined;\n\t\tthis.setConnectionState(ConnectionState.Disconnected, reason, error);\n\t}\n\n\tpublic cancelEstablishingConnection(reason: string) {\n\t\tassert(\n\t\t\tthis._connectionState === ConnectionState.EstablishingConnection,\n\t\t\t0x6d3 /* Connection state should be EstablishingConnection */,\n\t\t);\n\t\tassert(this.connection === undefined, 0x6d4 /* No connetion should be present */);\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.Disconnected;\n\t\tthis.handler.connectionStateChanged(ConnectionState.Disconnected, oldState, reason);\n\t}\n\n\tpublic establishingConnection(reason: string) {\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.EstablishingConnection;\n\t\tthis.handler.connectionStateChanged(\n\t\t\tConnectionState.EstablishingConnection,\n\t\t\toldState,\n\t\t\t`Establishing Connection due to ${reason}`,\n\t\t);\n\t}\n\n\tprivate shouldWaitForJoinSignal() {\n\t\tassert(\n\t\t\tthis.connection !== undefined,\n\t\t\t0x4b4 /* all callers call here with active connection */,\n\t\t);\n\t\treturn this.connection.mode === \"write\" || this.readClientsWaitForJoinSignal;\n\t}\n\n\t/**\n\t * The \"connect\" event indicates the connection to the Relay Service is live.\n\t * However, some additional conditions must be met before we can fully transition to\n\t * \"Connected\" state. This function handles that interim period, known as \"Connecting\" state.\n\t * @param details - Connection details returned from the Relay Service\n\t * @param deltaManager - DeltaManager to be used for delaying Connected transition until caught up.\n\t * If it's undefined, then don't delay and transition to Connected as soon as Leave/Join op are accounted for\n\t */\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\tthis.connection = details;\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.CatchingUp;\n\n\t\t// The following checks are wrong. They are only valid if user has write access to a file.\n\t\t// If user lost such access mid-session, user will not be able to get \"write\" connection.\n\t\t//\n\t\t// const writeConnection = details.mode === \"write\";\n\t\t// assert(!this.handler.shouldClientJoinWrite() || writeConnection,\n\t\t// 0x30a /* shouldClientJoinWrite should imply this is a writeConnection */);\n\t\t// assert(!this.waitingForLeaveOp || writeConnection,\n\t\t// 0x2a6 /* \"waitingForLeaveOp should imply writeConnection (we need to be ready to flush pending ops)\" */);\n\n\t\t// Stash the clientID to detect when transitioning from connecting (socket.io channel open) to connected\n\t\t// (have received the join message for the client ID)\n\t\t// This is especially important in the reconnect case. It's possible there could be outstanding\n\t\t// ops sent by this client, so we should keep the old client id until we see our own client's\n\t\t// join message. after we see the join message for our new connection with our new client id,\n\t\t// we know there can no longer be outstanding ops that we sent with the previous client id.\n\t\tthis._pendingClientId = details.clientId;\n\n\t\t// IMPORTANT: Report telemetry after we set _pendingClientId, but before transitioning to Connected state\n\t\tthis.handler.connectionStateChanged(ConnectionState.CatchingUp, oldState, details.reason);\n\n\t\t// Check if we need to wait for join op/signal, and if we need to wait for leave op from previous connection.\n\t\t// Pending clientId could have joined already (i.e. join op/signal already processed):\n\t\t// We are fetching ops from storage in parallel to connecting to Relay Service,\n\t\t// and given async processes, it's possible that we have already processed our own join message before\n\t\t// connection was fully established.\n\t\tif (!this.hasMember(this._pendingClientId) && this.shouldWaitForJoinSignal()) {\n\t\t\t// We are waiting for our own join op / signal. When it is processed\n\t\t\t// we'll attempt to transition to Connected state via receivedAddMemberEvent() flow.\n\t\t\tthis.startJoinOpTimer();\n\t\t} else if (!this.waitingForLeaveOp) {\n\t\t\t// We're not waiting for Join or Leave op (if read-only connection those don't even apply),\n\t\t\t// go ahead and declare the state to be Connected!\n\t\t\t// If we are waiting for Leave op still, do nothing for now, we will transition to Connected later.\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t}\n\t\t// else - We are waiting for Leave op still, do nothing for now, we will transition to Connected later\n\t}\n\n\tprivate setConnectionState(\n\t\tvalue: ConnectionState.Disconnected,\n\t\treason: string,\n\t\terror?: IAnyDriverError,\n\t): void;\n\tprivate setConnectionState(value: ConnectionState.Connected): void;\n\tprivate setConnectionState(\n\t\tvalue: ConnectionState.Disconnected | ConnectionState.Connected,\n\t\treason?: string,\n\t\terror?: IAnyDriverError,\n\t): void {\n\t\tif (this.connectionState === value) {\n\t\t\t// Already in the desired state - exit early\n\t\t\tthis.handler.logger.sendErrorEvent({ eventName: \"setConnectionStateSame\", value });\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = value;\n\n\t\t// This is the only place in code that deals with quorum. The rest works with audience\n\t\t// The code below ensures that we do not send ops until we know that old \"write\" client's disconnect\n\t\t// produced (and sequenced) leave op\n\t\tconst currentClientInQuorum =\n\t\t\tthis._clientId !== undefined &&\n\t\t\tthis.protocol?.quorum?.getMember(this._clientId) !== undefined;\n\t\tif (value === ConnectionState.Connected) {\n\t\t\tassert(\n\t\t\t\toldState === ConnectionState.CatchingUp,\n\t\t\t\t0x1d8 /* \"Should only transition from Connecting state\" */,\n\t\t\t);\n\t\t\t// Mark our old client should have left in the quorum if it's still there\n\t\t\tif (currentClientInQuorum) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tthis.handler.clientShouldHaveLeft(this._clientId!);\n\t\t\t}\n\t\t\tthis._clientId = this.pendingClientId;\n\t\t} else if (value === ConnectionState.Disconnected) {\n\t\t\t// Clear pending state immediately to prepare for reconnect\n\t\t\tthis._pendingClientId = undefined;\n\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t}\n\n\t\t\t// Only wait for \"leave\" message if the connected client exists in the quorum and had some non-acked ops\n\t\t\t// Also check if the timer is not already running as\n\t\t\t// we could receive \"Disconnected\" event multiple times without getting connected and in that case we\n\t\t\t// don't want to reset the timer as we still want to wait on original client which started this timer.\n\t\t\tif (\n\t\t\t\tcurrentClientInQuorum &&\n\t\t\t\tthis.handler.shouldClientJoinWrite() &&\n\t\t\t\t!this.waitingForLeaveOp // same as !this.prevClientLeftTimer.hasTimer\n\t\t\t) {\n\t\t\t\tthis.prevClientLeftTimer.restart();\n\t\t\t} else {\n\t\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"noWaitOnDisconnected\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\tclientId: this._clientId,\n\t\t\t\t\t\tinQuorum: currentClientInQuorum,\n\t\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Report transition before we propagate event across layers\n\t\tthis.handler.connectionStateChanged(this._connectionState, oldState, reason, error);\n\t}\n\n\t// Helper method to switch between quorum and audience.\n\t// Old design was checking only quorum for \"write\" clients.\n\t// Latest change checks audience for all types of connections.\n\tprotected get membership(): IMembership | undefined {\n\t\t// We could always use audience here, and in practice it will probably be correct.\n\t\t// (including case when this.readClientsWaitForJoinSignal === false).\n\t\t// But only if it's superset of quorum, i.e. when filtered to \"write\" clients, they are always identical!\n\t\t// It's safer to assume that we have bugs and engaging kill-bit switch should bring us back to well-known\n\t\t// and tested state!\n\t\treturn this.readClientsWaitForJoinSignal ? this.protocol?.audience : this.protocol?.quorum;\n\t}\n\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\tthis.protocol = protocol;\n\n\t\tthis.membership?.on(\"addMember\", (clientId, details) => {\n\t\t\tassert(\n\t\t\t\t(details as IClient).mode === \"read\" ||\n\t\t\t\t\tprotocol.quorum.getMember(clientId) !== undefined,\n\t\t\t\t0x4b5 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedAddMemberEvent(clientId);\n\t\t});\n\n\t\tthis.membership?.on(\"removeMember\", (clientId) => {\n\t\t\tassert(\n\t\t\t\tprotocol.quorum.getMember(clientId) === undefined,\n\t\t\t\t0x4b6 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedRemoveMemberEvent(clientId);\n\t\t});\n\n\t\t/* There is a tiny tiny race possible, where these events happen in this order:\n 1. A connection is established (no \"cached\" mode is used, so it happens in parallel / faster than other steps)\n 2. Some other client produces a summary\n 3. We get \"lucky\" and load from that summary as our initial snapshot\n 4. ConnectionStateHandler.initProtocol is called, \"self\" is already in the quorum.\n We could avoid this sequence (and delete test case for it) if we move connection lower in Container.load()\n */\n\t\tif (this.hasMember(this.pendingClientId)) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tthis.receivedAddMemberEvent(this.pendingClientId!);\n\t\t}\n\n\t\t// if we have a clientId from a previous container we need to wait for its leave message\n\t\tif (this.clientId !== undefined && this.hasMember(this.clientId)) {\n\t\t\tthis.prevClientLeftTimer.restart();\n\t\t}\n\t}\n\n\tprotected hasMember(clientId?: string) {\n\t\treturn this.membership?.getMember(clientId ?? \"\") !== undefined;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connectionStateHandler.js","sourceRoot":"","sources":["../src/connectionStateHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryProperties, TelemetryEventCategory } from \"@fluidframework/core-interfaces\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ISequencedClient, IClient } from \"@fluidframework/protocol-definitions\";\nimport {\n\tITelemetryLoggerExt,\n\tPerformanceEvent,\n\tloggerToMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { IAnyDriverError } from \"@fluidframework/driver-definitions\";\nimport { CatchUpMonitor, ICatchUpMonitor } from \"./catchUpMonitor\";\nimport { ConnectionState } from \"./connectionState\";\nimport { IConnectionDetailsInternal, IConnectionStateChangeReason } from \"./contracts\";\nimport { IProtocolHandler } from \"./protocol\";\n\n// Based on recent data, it looks like majority of cases where we get stuck are due to really slow or\n// timing out ops fetches. So attempt recovery infrequently. Also fetch uses 30 second timeout, so\n// if retrying fixes the problem, we should not see these events.\nconst JoinOpTimeoutMs = 45000;\n\n// Timeout waiting for \"self\" join signal, before giving up\nconst JoinSignalTimeoutMs = 5000;\n\n/** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */\nexport interface IConnectionStateHandlerInputs {\n\tlogger: ITelemetryLoggerExt;\n\t/** Log to telemetry any change in state, included to Connecting */\n\tconnectionStateChanged: (\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: IConnectionStateChangeReason,\n\t) => void;\n\t/** Whether to expect the client to join in write mode on next connection */\n\tshouldClientJoinWrite: () => boolean;\n\t/** (Optional) How long should we wait on our previous client's Leave op before transitioning to Connected again */\n\tmaxClientLeaveWaitTime: number | undefined;\n\t/** Log an issue encountered while in the Connecting state. details will be logged as a JSON string */\n\tlogConnectionIssue: (\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) => void;\n\t/** Callback to note that an old local client ID is still present in the Quorum that should have left and should now be considered invalid */\n\tclientShouldHaveLeft: (clientId: string) => void;\n}\n\n/**\n * interface that connection state handler implements\n */\nexport interface IConnectionStateHandler {\n\treadonly connectionState: ConnectionState;\n\treadonly pendingClientId: string | undefined;\n\n\tcontainerSaved(): void;\n\tdispose(): void;\n\tinitProtocol(protocol: IProtocolHandler): void;\n\treceivedConnectEvent(details: IConnectionDetailsInternal): void;\n\treceivedDisconnectEvent(reason: IConnectionStateChangeReason): void;\n\testablishingConnection(reason: IConnectionStateChangeReason): void;\n\t/**\n\t * Switches state to disconnected when we are still establishing connection during container.load(),\n\t * container connect() or reconnect and the container gets closed or disposed or disconnect happens.\n\t * @param reason - reason for cancelling the connection.\n\t */\n\tcancelEstablishingConnection(reason: IConnectionStateChangeReason): void;\n}\n\nexport function createConnectionStateHandler(\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tconst mc = loggerToMonitoringContext(inputs.logger);\n\treturn createConnectionStateHandlerCore(\n\t\tmc.config.getBoolean(\"Fluid.Container.CatchUpBeforeDeclaringConnected\") === true, // connectedRaisedWhenCaughtUp\n\t\tmc.config.getBoolean(\"Fluid.Container.EnableJoinSignalWait\") === true, // readClientsWaitForJoinSignal\n\t\tinputs,\n\t\tdeltaManager,\n\t\tclientId,\n\t);\n}\n\nexport function createConnectionStateHandlerCore(\n\tconnectedRaisedWhenCaughtUp: boolean,\n\treadClientsWaitForJoinSignal: boolean,\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tif (!connectedRaisedWhenCaughtUp) {\n\t\treturn new ConnectionStateHandler(inputs, readClientsWaitForJoinSignal, clientId);\n\t}\n\treturn new ConnectionStateCatchup(\n\t\tinputs,\n\t\t(handler: IConnectionStateHandlerInputs) =>\n\t\t\tnew ConnectionStateHandler(handler, readClientsWaitForJoinSignal, clientId),\n\t\tdeltaManager,\n\t);\n}\n\n/**\n * Helper internal interface to abstract away Audience & Quorum\n */\ninterface IMembership {\n\ton(\n\t\teventName: \"addMember\" | \"removeMember\",\n\t\tlistener: (clientId: string, details: IClient | ISequencedClient) => void,\n\t);\n\tgetMember(clientId: string): undefined | unknown;\n}\n\n/**\n * Class that can be used as a base class for building IConnectionStateHandler adapters / pipeline.\n * It implements both ends of communication interfaces and passes data back and forward\n */\nclass ConnectionStateHandlerPassThrough\n\timplements IConnectionStateHandler, IConnectionStateHandlerInputs\n{\n\tprotected readonly pimpl: IConnectionStateHandler;\n\n\tconstructor(\n\t\tprotected readonly inputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t) {\n\t\tthis.pimpl = pimplFactory(this);\n\t}\n\n\t/**\n\t * IConnectionStateHandler\n\t */\n\tpublic get connectionState() {\n\t\treturn this.pimpl.connectionState;\n\t}\n\tpublic get pendingClientId() {\n\t\treturn this.pimpl.pendingClientId;\n\t}\n\n\tpublic containerSaved() {\n\t\treturn this.pimpl.containerSaved();\n\t}\n\tpublic dispose() {\n\t\treturn this.pimpl.dispose();\n\t}\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\treturn this.pimpl.initProtocol(protocol);\n\t}\n\tpublic receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {\n\t\treturn this.pimpl.receivedDisconnectEvent(reason);\n\t}\n\n\tpublic establishingConnection(reason: IConnectionStateChangeReason) {\n\t\treturn this.pimpl.establishingConnection(reason);\n\t}\n\n\tpublic cancelEstablishingConnection(reason: IConnectionStateChangeReason) {\n\t\treturn this.pimpl.cancelEstablishingConnection(reason);\n\t}\n\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\treturn this.pimpl.receivedConnectEvent(details);\n\t}\n\n\t/**\n\t * IConnectionStateHandlerInputs\n\t */\n\n\tpublic get logger() {\n\t\treturn this.inputs.logger;\n\t}\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: IConnectionStateChangeReason,\n\t) {\n\t\treturn this.inputs.connectionStateChanged(value, oldState, reason);\n\t}\n\tpublic shouldClientJoinWrite() {\n\t\treturn this.inputs.shouldClientJoinWrite();\n\t}\n\tpublic get maxClientLeaveWaitTime() {\n\t\treturn this.inputs.maxClientLeaveWaitTime;\n\t}\n\tpublic logConnectionIssue(\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) {\n\t\treturn this.inputs.logConnectionIssue(eventName, category, details);\n\t}\n\tpublic clientShouldHaveLeft(clientId: string) {\n\t\treturn this.inputs.clientShouldHaveLeft(clientId);\n\t}\n}\n\n/**\n * Implementation of IConnectionStateHandler pass-through adapter that waits for specific sequence number\n * before raising connected event\n */\nclass ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {\n\tprivate catchUpMonitor: ICatchUpMonitor | undefined;\n\n\tconstructor(\n\t\tinputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t\tprivate readonly deltaManager: IDeltaManager<any, any>,\n\t) {\n\t\tsuper(inputs, pimplFactory);\n\t\tthis._connectionState = this.pimpl.connectionState;\n\t}\n\n\tprivate _connectionState: ConnectionState;\n\tpublic get connectionState() {\n\t\treturn this._connectionState;\n\t}\n\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: IConnectionStateChangeReason<IAnyDriverError>,\n\t) {\n\t\tswitch (value) {\n\t\t\tcase ConnectionState.Connected:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.CatchingUp,\n\t\t\t\t\t0x3e1 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\t// Create catch-up monitor here (not earlier), as we might get more exact info by now about how far\n\t\t\t\t// client is behind through join signal. This is only true if base layer uses signals (i.e. audience,\n\t\t\t\t// not quorum, including for \"rea\" connections) to make decisions about moving to \"connected\" state.\n\t\t\t\t// In addition to that, in its current form, doing this in ConnectionState.CatchingUp is dangerous as\n\t\t\t\t// we might get callback right away, and it will screw up state transition (as code outside of switch\n\t\t\t\t// statement will overwrite current state).\n\t\t\t\tassert(\n\t\t\t\t\tthis.catchUpMonitor === undefined,\n\t\t\t\t\t0x3eb /* catchUpMonitor should be gone */,\n\t\t\t\t);\n\t\t\t\tthis.catchUpMonitor = new CatchUpMonitor(\n\t\t\t\t\tthis.deltaManager,\n\t\t\t\t\tthis.transitionToConnectedState,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase ConnectionState.Disconnected:\n\t\t\t\tthis.catchUpMonitor?.dispose();\n\t\t\t\tthis.catchUpMonitor = undefined;\n\t\t\t\tbreak;\n\t\t\t// ConnectionState.EstablishingConnection state would be set when we start establishing connection\n\t\t\t// during container.connect() or reconnect because of an error.\n\t\t\tcase ConnectionState.EstablishingConnection:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.Disconnected,\n\t\t\t\t\t0x6d2 /* connectivity transition to establishing connection */,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ConnectionState.CatchingUp:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.EstablishingConnection,\n\t\t\t\t\t0x3e3 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t}\n\t\tthis._connectionState = value;\n\t\tthis.inputs.connectionStateChanged(value, oldState, reason);\n\t}\n\n\tprivate readonly transitionToConnectedState = () => {\n\t\t// Defensive measure, we should always be in Connecting state when this is called.\n\t\tconst state = this.pimpl.connectionState;\n\t\tassert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);\n\t\tassert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);\n\t\tthis._connectionState = ConnectionState.Connected;\n\t\tthis.inputs.connectionStateChanged(ConnectionState.Connected, ConnectionState.CatchingUp, {\n\t\t\ttext: \"caught up\",\n\t\t});\n\t};\n}\n\n/**\n * In the lifetime of a container, the connection will likely disconnect and reconnect periodically.\n * This class ensures that any ops sent by this container instance on previous connection are either\n * sequenced or blocked by the server before emitting the new \"connected\" event and allowing runtime to resubmit ops.\n *\n * Each connection is assigned a clientId by the service, and the connection is book-ended by a Join and a Leave op\n * generated by the service. Due to the distributed nature of the Relay Service, in the case of reconnect we cannot\n * make any assumptions about ordering of operations between the old and new connections - i.e. new Join op could\n * be sequenced before old Leave op (and some acks from pending ops that were in flight when we disconnected).\n *\n * The job of this class is to encapsulate the transition period during reconnect, which is identified by\n * ConnectionState.CatchingUp. Specifically, before moving to Connected state with the new clientId, it ensures that:\n *\n * a. We process the Leave op for the previous clientId. This allows us to properly handle any acks from in-flight ops\n * that got sequenced with the old clientId (we'll recognize them as local ops). After the Leave op, any other\n * pending ops can safely be submitted with the new clientId without fear of duplication in the sequenced op stream.\n *\n * b. We process the Join op for the new clientId (identified when the underlying connection was first established),\n * indicating the service is ready to sequence ops sent with the new clientId.\n *\n * c. We process all ops known at the time the underlying connection was established (so we are \"caught up\")\n *\n * For (a) we give up waiting after some time (same timeout as server uses), and go ahead and transition to Connected.\n *\n * For (b) we log telemetry if it takes too long, but still only transition to Connected when the Join op/signal is\n * processed.\n *\n * For (c) this is optional behavior, controlled by the parameters of receivedConnectEvent\n */\nclass ConnectionStateHandler implements IConnectionStateHandler {\n\tprivate _connectionState = ConnectionState.Disconnected;\n\tprivate _pendingClientId: string | undefined;\n\tprivate readonly prevClientLeftTimer: Timer;\n\tprivate readonly joinOpTimer: Timer;\n\tprivate protocol?: IProtocolHandler;\n\tprivate connection?: IConnectionDetailsInternal;\n\tprivate _clientId?: string;\n\n\tprivate waitEvent: PerformanceEvent | undefined;\n\n\tpublic get connectionState(): ConnectionState {\n\t\treturn this._connectionState;\n\t}\n\n\tprivate get clientId(): string | undefined {\n\t\treturn this._clientId;\n\t}\n\n\tpublic get pendingClientId(): string | undefined {\n\t\treturn this._pendingClientId;\n\t}\n\n\tconstructor(\n\t\tprivate readonly handler: IConnectionStateHandlerInputs,\n\t\tprivate readonly readClientsWaitForJoinSignal: boolean,\n\t\tclientIdFromPausedSession?: string,\n\t) {\n\t\tthis._clientId = clientIdFromPausedSession;\n\t\tthis.prevClientLeftTimer = new Timer(\n\t\t\t// Default is 5 min for which we are going to wait for its own \"leave\" message. This is same as\n\t\t\t// the max time on server after which leave op is sent.\n\t\t\tthis.handler.maxClientLeaveWaitTime ?? 300000,\n\t\t\t() => {\n\t\t\t\tassert(\n\t\t\t\t\tthis.connectionState !== ConnectionState.Connected,\n\t\t\t\t\t0x2ac /* \"Connected when timeout waiting for leave from previous session fired!\" */,\n\t\t\t\t);\n\t\t\t\tthis.applyForConnectedState(\"timeout\");\n\t\t\t},\n\t\t);\n\n\t\tthis.joinOpTimer = new Timer(\n\t\t\t0, // default value is not used - startJoinOpTimer() explicitly provides timeout\n\t\t\t() => {\n\t\t\t\t// I've observed timer firing within couple ms from disconnect event, looks like\n\t\t\t\t// queued timer callback is not cancelled if timer is cancelled while callback sits in the queue.\n\t\t\t\tif (this.connectionState !== ConnectionState.CatchingUp) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst details = {\n\t\t\t\t\tprotocolInitialized: this.protocol !== undefined,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t};\n\t\t\t\tthis.handler.logConnectionIssue(\"NoJoinOp\", \"error\", details);\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate startJoinOpTimer() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x234 /* \"has joinOpTimer\" */);\n\t\tassert(this.connection !== undefined, 0x4b3 /* have connection */);\n\t\tthis.joinOpTimer.start(\n\t\t\tthis.connection.mode === \"write\" ? JoinOpTimeoutMs : JoinSignalTimeoutMs,\n\t\t);\n\t}\n\n\tprivate stopJoinOpTimer() {\n\t\tassert(this.joinOpTimer.hasTimer, 0x235 /* \"no joinOpTimer\" */);\n\t\tthis.joinOpTimer.clear();\n\t}\n\n\tprivate get waitingForLeaveOp() {\n\t\treturn this.prevClientLeftTimer.hasTimer;\n\t}\n\n\tpublic dispose() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x2a5 /* \"join timer\" */);\n\t\tthis.prevClientLeftTimer.clear();\n\t}\n\n\tpublic containerSaved() {\n\t\t// If we were waiting for moving to Connected state, then only apply for state change. Since the container\n\t\t// is now saved and we don't have any ops to roundtrip, we can clear the timer and apply for connected state.\n\t\tif (this.waitingForLeaveOp) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"containerSaved\");\n\t\t}\n\t}\n\n\tprivate receivedAddMemberEvent(clientId: string) {\n\t\t// This is the only one that requires the pending client ID\n\t\tif (clientId === this.pendingClientId) {\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t} else if (this.shouldWaitForJoinSignal()) {\n\t\t\t\t// timer has already fired, meaning it took too long to get join op/signal.\n\t\t\t\t// Record how long it actually took to recover.\n\t\t\t\t// This is generic event, as it by itself is not an error.\n\t\t\t\t// We also have a case where NoJoinOp happens during container boot (we do not report it as error in such case),\n\t\t\t\t// if this log statement happens after boot - we do not want to consider it error case.\n\t\t\t\tthis.handler.logConnectionIssue(\"ReceivedJoinOp\", \"generic\");\n\t\t\t}\n\t\t\t// Start the event in case we are waiting for leave or timeout.\n\t\t\tif (this.waitingForLeaveOp) {\n\t\t\t\tthis.waitEvent = PerformanceEvent.start(this.handler.logger, {\n\t\t\t\t\teventName: \"WaitBeforeClientLeave\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\twaitOnClientId: this._clientId,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.applyForConnectedState(\"addMemberEvent\");\n\t\t} else if (clientId === this.clientId) {\n\t\t\t// If we see our clientId and it's not also our pending ID, it's our own join op\n\t\t\t// being replayed, so start the timer in case our previous client is still in quorum\n\t\t\tassert(\n\t\t\t\t!this.waitingForLeaveOp,\n\t\t\t\t0x5d2 /* Unexpected join op with current clientId while waiting */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\tthis.connectionState !== ConnectionState.Connected,\n\t\t\t\t0x5d3 /* Unexpected join op with current clientId while connected */,\n\t\t\t);\n\t\t\tthis.prevClientLeftTimer.restart();\n\t\t}\n\t}\n\n\tprivate applyForConnectedState(\n\t\tsource: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\" | \"containerSaved\",\n\t) {\n\t\tassert(\n\t\t\tthis.protocol !== undefined,\n\t\t\t0x236 /* \"In all cases it should be already installed\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t!this.waitingForLeaveOp || this.hasMember(this.clientId),\n\t\t\t0x2e2 /* \"Must only wait for leave message when clientId in quorum\" */,\n\t\t);\n\n\t\t// Move to connected state only if we are in Connecting state, we have seen our join op\n\t\t// and there is no timer running which means we are not waiting for previous client to leave\n\t\t// or timeout has occurred while doing so.\n\t\tif (\n\t\t\tthis.pendingClientId !== this.clientId &&\n\t\t\tthis.hasMember(this.pendingClientId) &&\n\t\t\t!this.waitingForLeaveOp\n\t\t) {\n\t\t\tthis.waitEvent?.end({ source });\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t} else {\n\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t// We may not see any ops due to being disconnected all that time - that's not an error!\n\t\t\tconst error =\n\t\t\t\tsource === \"timeout\" && this.connectionState !== ConnectionState.Disconnected;\n\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"connectedStateRejected\",\n\t\t\t\tcategory: error ? \"error\" : \"generic\",\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\tsource,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientId: this.clientId,\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate receivedRemoveMemberEvent(clientId: string) {\n\t\t// If the client which has left was us, then finish the timer.\n\t\tif (this.clientId === clientId) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"removeMemberEvent\");\n\t\t}\n\t}\n\n\tpublic receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {\n\t\tthis.connection = undefined;\n\t\tthis.setConnectionState(ConnectionState.Disconnected, reason);\n\t}\n\n\tpublic cancelEstablishingConnection(reason: IConnectionStateChangeReason) {\n\t\tassert(\n\t\t\tthis._connectionState === ConnectionState.EstablishingConnection,\n\t\t\t0x6d3 /* Connection state should be EstablishingConnection */,\n\t\t);\n\t\tassert(this.connection === undefined, 0x6d4 /* No connetion should be present */);\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.Disconnected;\n\t\tthis.handler.connectionStateChanged(ConnectionState.Disconnected, oldState, reason);\n\t}\n\n\tpublic establishingConnection(reason: IConnectionStateChangeReason) {\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.EstablishingConnection;\n\t\tthis.handler.connectionStateChanged(ConnectionState.EstablishingConnection, oldState, {\n\t\t\ttext: `Establishing Connection due to ${reason.text}`,\n\t\t\terror: reason.error,\n\t\t});\n\t}\n\n\tprivate shouldWaitForJoinSignal() {\n\t\tassert(\n\t\t\tthis.connection !== undefined,\n\t\t\t0x4b4 /* all callers call here with active connection */,\n\t\t);\n\t\treturn this.connection.mode === \"write\" || this.readClientsWaitForJoinSignal;\n\t}\n\n\t/**\n\t * The \"connect\" event indicates the connection to the Relay Service is live.\n\t * However, some additional conditions must be met before we can fully transition to\n\t * \"Connected\" state. This function handles that interim period, known as \"Connecting\" state.\n\t * @param details - Connection details returned from the Relay Service\n\t * @param deltaManager - DeltaManager to be used for delaying Connected transition until caught up.\n\t * If it's undefined, then don't delay and transition to Connected as soon as Leave/Join op are accounted for\n\t */\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\tthis.connection = details;\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.CatchingUp;\n\n\t\t// The following checks are wrong. They are only valid if user has write access to a file.\n\t\t// If user lost such access mid-session, user will not be able to get \"write\" connection.\n\t\t//\n\t\t// const writeConnection = details.mode === \"write\";\n\t\t// assert(!this.handler.shouldClientJoinWrite() || writeConnection,\n\t\t// 0x30a /* shouldClientJoinWrite should imply this is a writeConnection */);\n\t\t// assert(!this.waitingForLeaveOp || writeConnection,\n\t\t// 0x2a6 /* \"waitingForLeaveOp should imply writeConnection (we need to be ready to flush pending ops)\" */);\n\n\t\t// Stash the clientID to detect when transitioning from connecting (socket.io channel open) to connected\n\t\t// (have received the join message for the client ID)\n\t\t// This is especially important in the reconnect case. It's possible there could be outstanding\n\t\t// ops sent by this client, so we should keep the old client id until we see our own client's\n\t\t// join message. after we see the join message for our new connection with our new client id,\n\t\t// we know there can no longer be outstanding ops that we sent with the previous client id.\n\t\tthis._pendingClientId = details.clientId;\n\n\t\t// IMPORTANT: Report telemetry after we set _pendingClientId, but before transitioning to Connected state\n\t\tthis.handler.connectionStateChanged(ConnectionState.CatchingUp, oldState, details.reason);\n\n\t\t// Check if we need to wait for join op/signal, and if we need to wait for leave op from previous connection.\n\t\t// Pending clientId could have joined already (i.e. join op/signal already processed):\n\t\t// We are fetching ops from storage in parallel to connecting to Relay Service,\n\t\t// and given async processes, it's possible that we have already processed our own join message before\n\t\t// connection was fully established.\n\t\tif (!this.hasMember(this._pendingClientId) && this.shouldWaitForJoinSignal()) {\n\t\t\t// We are waiting for our own join op / signal. When it is processed\n\t\t\t// we'll attempt to transition to Connected state via receivedAddMemberEvent() flow.\n\t\t\tthis.startJoinOpTimer();\n\t\t} else if (!this.waitingForLeaveOp) {\n\t\t\t// We're not waiting for Join or Leave op (if read-only connection those don't even apply),\n\t\t\t// go ahead and declare the state to be Connected!\n\t\t\t// If we are waiting for Leave op still, do nothing for now, we will transition to Connected later.\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t}\n\t\t// else - We are waiting for Leave op still, do nothing for now, we will transition to Connected later\n\t}\n\n\tprivate setConnectionState(\n\t\tvalue: ConnectionState.Disconnected,\n\t\treason: IConnectionStateChangeReason,\n\t): void;\n\tprivate setConnectionState(value: ConnectionState.Connected): void;\n\tprivate setConnectionState(\n\t\tvalue: ConnectionState.Disconnected | ConnectionState.Connected,\n\t\treason?: IConnectionStateChangeReason,\n\t): void {\n\t\tif (this.connectionState === value) {\n\t\t\t// Already in the desired state - exit early\n\t\t\tthis.handler.logger.sendErrorEvent({ eventName: \"setConnectionStateSame\", value });\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = value;\n\n\t\t// This is the only place in code that deals with quorum. The rest works with audience\n\t\t// The code below ensures that we do not send ops until we know that old \"write\" client's disconnect\n\t\t// produced (and sequenced) leave op\n\t\tconst currentClientInQuorum =\n\t\t\tthis._clientId !== undefined &&\n\t\t\tthis.protocol?.quorum?.getMember(this._clientId) !== undefined;\n\t\tif (value === ConnectionState.Connected) {\n\t\t\tassert(\n\t\t\t\toldState === ConnectionState.CatchingUp,\n\t\t\t\t0x1d8 /* \"Should only transition from Connecting state\" */,\n\t\t\t);\n\t\t\t// Mark our old client should have left in the quorum if it's still there\n\t\t\tif (currentClientInQuorum) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tthis.handler.clientShouldHaveLeft(this._clientId!);\n\t\t\t}\n\t\t\tthis._clientId = this.pendingClientId;\n\t\t} else if (value === ConnectionState.Disconnected) {\n\t\t\t// Clear pending state immediately to prepare for reconnect\n\t\t\tthis._pendingClientId = undefined;\n\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t}\n\n\t\t\t// Only wait for \"leave\" message if the connected client exists in the quorum and had some non-acked ops\n\t\t\t// Also check if the timer is not already running as\n\t\t\t// we could receive \"Disconnected\" event multiple times without getting connected and in that case we\n\t\t\t// don't want to reset the timer as we still want to wait on original client which started this timer.\n\t\t\tif (\n\t\t\t\tcurrentClientInQuorum &&\n\t\t\t\tthis.handler.shouldClientJoinWrite() &&\n\t\t\t\t!this.waitingForLeaveOp // same as !this.prevClientLeftTimer.hasTimer\n\t\t\t) {\n\t\t\t\tthis.prevClientLeftTimer.restart();\n\t\t\t} else {\n\t\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"noWaitOnDisconnected\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\tclientId: this._clientId,\n\t\t\t\t\t\tinQuorum: currentClientInQuorum,\n\t\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Report transition before we propagate event across layers\n\t\tthis.handler.connectionStateChanged(this._connectionState, oldState, reason);\n\t}\n\n\t// Helper method to switch between quorum and audience.\n\t// Old design was checking only quorum for \"write\" clients.\n\t// Latest change checks audience for all types of connections.\n\tprotected get membership(): IMembership | undefined {\n\t\t// We could always use audience here, and in practice it will probably be correct.\n\t\t// (including case when this.readClientsWaitForJoinSignal === false).\n\t\t// But only if it's superset of quorum, i.e. when filtered to \"write\" clients, they are always identical!\n\t\t// It's safer to assume that we have bugs and engaging kill-bit switch should bring us back to well-known\n\t\t// and tested state!\n\t\treturn this.readClientsWaitForJoinSignal ? this.protocol?.audience : this.protocol?.quorum;\n\t}\n\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\tthis.protocol = protocol;\n\n\t\tthis.membership?.on(\"addMember\", (clientId, details) => {\n\t\t\tassert(\n\t\t\t\t(details as IClient).mode === \"read\" ||\n\t\t\t\t\tprotocol.quorum.getMember(clientId) !== undefined,\n\t\t\t\t0x4b5 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedAddMemberEvent(clientId);\n\t\t});\n\n\t\tthis.membership?.on(\"removeMember\", (clientId) => {\n\t\t\tassert(\n\t\t\t\tprotocol.quorum.getMember(clientId) === undefined,\n\t\t\t\t0x4b6 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedRemoveMemberEvent(clientId);\n\t\t});\n\n\t\t/* There is a tiny tiny race possible, where these events happen in this order:\n 1. A connection is established (no \"cached\" mode is used, so it happens in parallel / faster than other steps)\n 2. Some other client produces a summary\n 3. We get \"lucky\" and load from that summary as our initial snapshot\n 4. ConnectionStateHandler.initProtocol is called, \"self\" is already in the quorum.\n We could avoid this sequence (and delete test case for it) if we move connection lower in Container.load()\n */\n\t\tif (this.hasMember(this.pendingClientId)) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tthis.receivedAddMemberEvent(this.pendingClientId!);\n\t\t}\n\n\t\t// if we have a clientId from a previous container we need to wait for its leave message\n\t\tif (this.clientId !== undefined && this.hasMember(this.clientId)) {\n\t\t\tthis.prevClientLeftTimer.restart();\n\t\t}\n\t}\n\n\tprotected hasMember(clientId?: string) {\n\t\treturn this.membership?.getMember(clientId ?? \"\") !== undefined;\n\t}\n}\n"]}
|
package/lib/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EACN,oBAAoB,EAEpB,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACN,WAAW,EAEX,SAAS,EAET,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,aAAa,EACb,iBAAiB,
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
|
package/lib/container.js
CHANGED
|
@@ -8,7 +8,7 @@ import { v4 as uuid } from "uuid";
|
|
|
8
8
|
import { TypedEventEmitter, assert, performance, unreachableCase, } from "@fluidframework/common-utils";
|
|
9
9
|
import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
|
|
10
10
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
11
|
-
import { readAndParse, OnlineStatus, isOnline, runWithRetry, isCombinedAppAndProtocolSummary, MessageType2,
|
|
11
|
+
import { readAndParse, OnlineStatus, isOnline, runWithRetry, isCombinedAppAndProtocolSummary, MessageType2, } from "@fluidframework/driver-utils";
|
|
12
12
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
13
13
|
import { createChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, connectedEventName, normalizeError, createChildMonitoringContext, wrapError, formatTick, } from "@fluidframework/telemetry-utils";
|
|
14
14
|
import { Audience } from "./audience";
|
|
@@ -186,7 +186,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
186
186
|
this.scope = scope;
|
|
187
187
|
this.detachedBlobStorage = detachedBlobStorage;
|
|
188
188
|
this.protocolHandlerBuilder =
|
|
189
|
-
protocolHandlerBuilder ??
|
|
189
|
+
protocolHandlerBuilder ??
|
|
190
|
+
((attributes, quorumSnapshot, sendProposal) => new ProtocolHandler(attributes, quorumSnapshot, sendProposal, new Audience(), (clientId) => this.clientsWhoShouldHaveLeft.has(clientId)));
|
|
190
191
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
191
192
|
this.clone = async (_loadProps, createParamOverrides) => {
|
|
192
193
|
return Container.load(_loadProps, {
|
|
@@ -239,11 +240,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
239
240
|
this._deltaManager = this.createDeltaManager();
|
|
240
241
|
this.connectionStateHandler = createConnectionStateHandler({
|
|
241
242
|
logger: this.mc.logger,
|
|
242
|
-
connectionStateChanged: (value, oldState, reason
|
|
243
|
+
connectionStateChanged: (value, oldState, reason) => {
|
|
243
244
|
if (value === ConnectionState.Connected) {
|
|
244
245
|
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
245
246
|
}
|
|
246
|
-
this.logConnectionStateChangeTelemetry(value, oldState, reason
|
|
247
|
+
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
247
248
|
if (this._lifecycleState === "loaded") {
|
|
248
249
|
this.propagateConnectionState(false /* initial transition */, value === ConnectionState.Disconnected
|
|
249
250
|
? reason
|
|
@@ -276,8 +277,9 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
276
277
|
// Other possible recovery path - move to connected state (i.e. ConnectionStateHandler.joinOpTimer
|
|
277
278
|
// to call this.applyForConnectedState("addMemberEvent") for "read" connections)
|
|
278
279
|
if (mode === "read") {
|
|
279
|
-
|
|
280
|
-
this.
|
|
280
|
+
const reason = { text: "NoJoinSignal" };
|
|
281
|
+
this.disconnectInternal(reason);
|
|
282
|
+
this.connectInternal({ reason, fetchOpsFromStorage: false });
|
|
281
283
|
}
|
|
282
284
|
},
|
|
283
285
|
clientShouldHaveLeft: (clientId) => {
|
|
@@ -612,7 +614,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
612
614
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
613
615
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
614
616
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
615
|
-
this.
|
|
617
|
+
this.disconnectInternal({ text: "closeAndGetPendingLocalState" }); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
616
618
|
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
617
619
|
this.close();
|
|
618
620
|
return pendingState;
|
|
@@ -746,7 +748,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
746
748
|
if (!this.closed) {
|
|
747
749
|
this.resumeInternal({
|
|
748
750
|
fetchOpsFromStorage: false,
|
|
749
|
-
reason: "createDetached",
|
|
751
|
+
reason: { text: "createDetached" },
|
|
750
752
|
});
|
|
751
753
|
}
|
|
752
754
|
}
|
|
@@ -762,7 +764,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
762
764
|
async request(path) {
|
|
763
765
|
return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
|
|
764
766
|
}
|
|
765
|
-
setAutoReconnectInternal(mode) {
|
|
767
|
+
setAutoReconnectInternal(mode, reason) {
|
|
766
768
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
767
769
|
if (currentMode === mode) {
|
|
768
770
|
return;
|
|
@@ -776,7 +778,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
776
778
|
connectionState: ConnectionState[this.connectionState],
|
|
777
779
|
duration,
|
|
778
780
|
});
|
|
779
|
-
this._deltaManager.connectionManager.setAutoReconnect(mode);
|
|
781
|
+
this._deltaManager.connectionManager.setAutoReconnect(mode, reason);
|
|
780
782
|
}
|
|
781
783
|
connect() {
|
|
782
784
|
if (this.closed) {
|
|
@@ -789,7 +791,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
789
791
|
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
790
792
|
// If there is gap, we will learn about it once connected, but the gap should be small (if any),
|
|
791
793
|
// assuming that connect() is called quickly after initial container boot.
|
|
792
|
-
this.connectInternal({
|
|
794
|
+
this.connectInternal({
|
|
795
|
+
reason: { text: "DocumentConnect" },
|
|
796
|
+
fetchOpsFromStorage: false,
|
|
797
|
+
});
|
|
793
798
|
}
|
|
794
799
|
}
|
|
795
800
|
connectInternal(args) {
|
|
@@ -799,21 +804,21 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
799
804
|
this.resumeInternal(args);
|
|
800
805
|
// Set Auto Reconnect Mode
|
|
801
806
|
const mode = ReconnectMode.Enabled;
|
|
802
|
-
this.setAutoReconnectInternal(mode);
|
|
807
|
+
this.setAutoReconnectInternal(mode, args.reason);
|
|
803
808
|
}
|
|
804
809
|
disconnect() {
|
|
805
810
|
if (this.closed) {
|
|
806
811
|
throw new UsageError(`The Container is closed and cannot be disconnected`);
|
|
807
812
|
}
|
|
808
813
|
else {
|
|
809
|
-
this.disconnectInternal();
|
|
814
|
+
this.disconnectInternal({ text: "DocumentDisconnect" });
|
|
810
815
|
}
|
|
811
816
|
}
|
|
812
|
-
disconnectInternal() {
|
|
817
|
+
disconnectInternal(reason) {
|
|
813
818
|
assert(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
|
|
814
819
|
// Set Auto Reconnect Mode
|
|
815
820
|
const mode = ReconnectMode.Disabled;
|
|
816
|
-
this.setAutoReconnectInternal(mode);
|
|
821
|
+
this.setAutoReconnectInternal(mode, reason);
|
|
817
822
|
}
|
|
818
823
|
resumeInternal(args) {
|
|
819
824
|
assert(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
|
|
@@ -915,7 +920,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
915
920
|
// A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
|
|
916
921
|
// B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
|
|
917
922
|
const connectionArgs = {
|
|
918
|
-
reason: "DocumentOpen",
|
|
923
|
+
reason: { text: "DocumentOpen" },
|
|
919
924
|
mode: "write",
|
|
920
925
|
fetchOpsFromStorage: false,
|
|
921
926
|
};
|
|
@@ -1277,10 +1282,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1277
1282
|
deltaManager.on("cancelEstablishingConnection", (reason) => {
|
|
1278
1283
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1279
1284
|
});
|
|
1280
|
-
deltaManager.on("disconnect", (reason
|
|
1285
|
+
deltaManager.on("disconnect", (reason) => {
|
|
1281
1286
|
this.noopHeuristic?.notifyDisconnect();
|
|
1282
1287
|
if (!this.closed) {
|
|
1283
|
-
this.connectionStateHandler.receivedDisconnectEvent(reason
|
|
1288
|
+
this.connectionStateHandler.receivedDisconnectEvent(reason);
|
|
1284
1289
|
}
|
|
1285
1290
|
});
|
|
1286
1291
|
deltaManager.on("throttled", (warning) => {
|
|
@@ -1312,7 +1317,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1312
1317
|
},
|
|
1313
1318
|
}, prefetchType);
|
|
1314
1319
|
}
|
|
1315
|
-
logConnectionStateChangeTelemetry(value, oldState, reason
|
|
1320
|
+
logConnectionStateChangeTelemetry(value, oldState, reason) {
|
|
1316
1321
|
// Log actual event
|
|
1317
1322
|
const time = performance.now();
|
|
1318
1323
|
this.connectionTransitionTimes[value] = time;
|
|
@@ -1347,7 +1352,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1347
1352
|
from: ConnectionState[oldState],
|
|
1348
1353
|
duration,
|
|
1349
1354
|
durationFromDisconnected,
|
|
1350
|
-
reason,
|
|
1355
|
+
reason: reason?.text,
|
|
1351
1356
|
connectionInitiationReason,
|
|
1352
1357
|
pendingClientId: this.connectionStateHandler.pendingClientId,
|
|
1353
1358
|
clientId: this.clientId,
|
|
@@ -1361,7 +1366,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1361
1366
|
quorumSize: this._protocolHandler?.quorum.getMembers().size,
|
|
1362
1367
|
isDirty: this.isDirty,
|
|
1363
1368
|
...this._deltaManager.connectionProps,
|
|
1364
|
-
}, error);
|
|
1369
|
+
}, reason?.error);
|
|
1365
1370
|
if (value === ConnectionState.Connected) {
|
|
1366
1371
|
this.firstConnection = false;
|
|
1367
1372
|
}
|
|
@@ -1379,7 +1384,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1379
1384
|
// Both protocol and context should not be undefined if we got so far.
|
|
1380
1385
|
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
1381
1386
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1382
|
-
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
1387
|
+
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason?.text);
|
|
1383
1388
|
}
|
|
1384
1389
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1385
1390
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
@@ -1429,23 +1434,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1429
1434
|
this.savedOps.push(message);
|
|
1430
1435
|
}
|
|
1431
1436
|
const local = this.clientId === message.clientId;
|
|
1432
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
1433
|
-
// flagged should have left, or from a client that's not in the quorum but should be
|
|
1434
|
-
if (message.clientId != null) {
|
|
1435
|
-
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
1436
|
-
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
1437
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1438
|
-
throw new Error("Remote message's clientId is missing from the quorum");
|
|
1439
|
-
}
|
|
1440
|
-
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
1441
|
-
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
1442
|
-
// document we don't need to blow up aggressively.
|
|
1443
|
-
if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
1444
|
-
!canBeCoalescedByService(message)) {
|
|
1445
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1446
|
-
throw new Error("Remote message's clientId already should have left");
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
1437
|
// Allow the protocol handler to process the message
|
|
1450
1438
|
const result = this.protocolHandler.processMessage(message, local);
|
|
1451
1439
|
// Forward messages to the loaded runtime for processing
|
|
@@ -1533,7 +1521,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1533
1521
|
const getSpecifiedCodeDetails = () => (this.protocolHandler.quorum.get("code") ??
|
|
1534
1522
|
this.protocolHandler.quorum.get("code2"));
|
|
1535
1523
|
const existing = snapshot !== undefined;
|
|
1536
|
-
const context = new ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1524
|
+
const context = new ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => this.resolvedUrl?.id, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1537
1525
|
this._runtime = await PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1538
1526
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1539
1527
|
this._loadedCodeDetails = codeDetails;
|