@fluidframework/container-loader 2.0.0-internal.2.1.1 → 2.0.0-internal.2.2.0
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 +21 -11
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +6 -1
- package/dist/audience.js.map +1 -1
- package/dist/collabWindowTracker.js +5 -4
- package/dist/collabWindowTracker.js.map +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +13 -2
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +20 -11
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +65 -36
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +8 -0
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +23 -7
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +5 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/contracts.d.ts +8 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +1 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +4 -3
- 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/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +6 -1
- package/lib/audience.js.map +1 -1
- package/lib/collabWindowTracker.js +5 -4
- package/lib/collabWindowTracker.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +13 -2
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +20 -11
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +65 -36
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +8 -0
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +22 -7
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +5 -1
- package/lib/containerContext.js.map +1 -1
- package/lib/contracts.d.ts +8 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +1 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +4 -3
- 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/package.json +22 -15
- package/prettier.config.cjs +8 -0
- package/src/audience.ts +6 -1
- package/src/collabWindowTracker.ts +5 -5
- package/src/connectionManager.ts +15 -3
- package/src/connectionStateHandler.ts +87 -39
- package/src/container.ts +26 -6
- package/src/containerContext.ts +8 -2
- package/src/contracts.ts +8 -0
- package/src/deltaManager.ts +4 -3
- package/src/packageVersion.ts +1 -1
package/.eslintrc.js
CHANGED
package/README.md
CHANGED
|
@@ -147,7 +147,7 @@ In normal circumstances, container will attempt to reconnect back to ordering se
|
|
|
147
147
|
|
|
148
148
|
Container will also not attempt to reconnect on lost connection if `Container.disconnect()` was called prior to loss of connection. This can be useful if the hosting application implements "user away" type of experience to reduce cost on both client and server of maintaining connection while user is away. Calling `Container.connect()` will reenable automatic reconnections, but the host might need to allow extra time for reconnection as it likely involves token fetch and processing of a lot of Ops generated by other clients while it was not connected.
|
|
149
149
|
|
|
150
|
-
Data stores should almost never listen to these events (see more on [Readonly states](#Readonly-states), and should use consensus DDSes if they need to synchronize activity across clients. DDSes listen for these events to know when to resubmit pending Ops.
|
|
150
|
+
Data stores should almost never listen to these events (see more on [Readonly states](#Readonly-states)), and should use consensus DDSes if they need to synchronize activity across clients. DDSes listen for these events to know when to resubmit pending Ops.
|
|
151
151
|
|
|
152
152
|
Hosting application can use these events in order to indicate to user when user changes are not propagating through the system, and thus can be lost (on browser tab being closed). It's advised to use some delay (like 5 seconds) before showing such UI, as network connectivity might be intermittent. Also if container was offline for very long period of time due to `Container.disconnect()` being called, it might take a while to get connected and current.
|
|
153
153
|
|
|
@@ -156,33 +156,43 @@ Please note that hosts can implement various strategies on how to handle disconn
|
|
|
156
156
|
It's worth pointing out that being connected does not mean all user edits are preserved on container closure. There is latency in the system, and loader layer does not provide any guarantees here. Not every implementation needs a solution here (games likely do not care), and thus solving this problem is pushed to framework level (i.e. having a data store that can expose `'dirtyDocument'` signal from ContainerRuntime and request route that can return such data store).
|
|
157
157
|
|
|
158
158
|
## Readonly states
|
|
159
|
+
|
|
159
160
|
User permissions can change over lifetime of Container. They can't change during single connection session (in other words, change in permissions causes disconnect and reconnect). Hosts are advised to recheck this property on every reconnect.
|
|
160
161
|
|
|
161
162
|
DeltaManager will emit a `"readonly"` event when transitioning to a read-only state. Readonly events are accessible by data stores and DDSes (through ContainerRuntime.deltaManager). It's expected that data stores adhere to requirements and expose read-only (or rather 'no edit') experiences.
|
|
162
163
|
|
|
163
|
-
`Container.readOnlyInfo` (and `DeltaManager.readOnlyInfo`) indicates to host if file is writable or not.
|
|
164
|
+
`Container.readOnlyInfo` (and `DeltaManager.readOnlyInfo`) indicates to the host if the file is writable or not.
|
|
165
|
+
It contains the following properties:
|
|
166
|
+
|
|
164
167
|
### `readonly`
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
-
|
|
168
|
+
|
|
169
|
+
One of the following:
|
|
170
|
+
|
|
171
|
+
- `true`: Container is read-only. One or more of the additional properties listed below will be `true`.
|
|
172
|
+
- `undefined`: Runtime does not know yet if file is writable or not. Currently we get a signal here only when websocket connection is made to the server.
|
|
173
|
+
- `false`: Container.forceReadonly() was never called or last call was with false, plus it's known that user has write permissions to a file.
|
|
169
174
|
|
|
170
175
|
### `permissions`
|
|
171
|
-
There are two cases when it's true:
|
|
172
176
|
|
|
173
|
-
|
|
177
|
+
There are two cases when it's `true`:
|
|
178
|
+
|
|
179
|
+
1. User has no write permissions to modify this container (which usually maps to file in storage, and lack of write permissions by a given user)
|
|
174
180
|
2. Container was closed, either due to critical error, or due to host closing container. See [Container Lifetime](#Container-lifetime) and [Error Handling](#Error-handling) for more details.
|
|
175
181
|
|
|
176
182
|
### `forced`
|
|
177
|
-
Hosts can also force readonly-mode for a container via calling `Container.forceReadonly(true)`. This can be useful in scenarios like:
|
|
178
183
|
|
|
179
|
-
|
|
184
|
+
`true` if the Container is in read-only mode due to the host calling `Container.forceReadonly(true)`.
|
|
185
|
+
This can be useful in scenarios like:
|
|
186
|
+
|
|
187
|
+
- Loss of connectivity, in scenarios where host chooses method of preventing user edits over (or in addition to) showing disconnected UX and warning user of potential data loss on closure of container.
|
|
180
188
|
- Special view-only mode in host. For example can be used by hosts for previewing container content in-place with other host content, and leveraging full-screen / separate window experience for editing.
|
|
181
189
|
|
|
182
190
|
### `storageOnly`
|
|
183
|
-
|
|
191
|
+
|
|
192
|
+
Storage-only mode is a read-only mode in which the container does not connect to the delta stream and is unable to submit or receive ops. This is useful for viewing a specific version of a document.
|
|
184
193
|
|
|
185
194
|
## Dirty events
|
|
195
|
+
|
|
186
196
|
The Container runtime can communicate with the container to get the container's current state. In response, the container will raise two events - `"dirty"` and `"saved"` events. Transitions between these two events signify presence (or lack of) user changes that were not saved to storage. In other words, if container is dirty, closing it at that moment will result in data loss from user perspective, because not all user changes made it to storage.
|
|
187
197
|
This information can be used by a host to build appropriate UX that allows user to be confident in the platform. For example, a host may chose to show a dialog asking the user if they want to save their changes before closing. Instead of, or in addition to this, the host may choose to show "Saving..." and "Saved" text somewhere in UX. Coupled with lack of connectivity to ordering service (and appropriate notification to the user) that may create enough continuous notification to user not to require a blocking dialog on closing.
|
|
188
198
|
Note that when an active connection is in place, it's just a matter of time before changes will be flushed to storage unless there is some source of continuous local changes being generated that prevents container from ever being fully saved. But if there is no active connection, because the user is offline, for example, then a document may stay in a dirty state for very long time.
|
package/dist/audience.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audience.d.ts","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"audience.d.ts","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAE/D;;GAEG;AACH,qBAAa,QAAS,SAAQ,YAAa,YAAW,cAAc;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IAE/C,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAK3G;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAanD;;;OAGG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAW9C;;OAEG;IACI,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAIzC;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;CAG1D"}
|
package/dist/audience.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.Audience = void 0;
|
|
|
6
6
|
* Licensed under the MIT License.
|
|
7
7
|
*/
|
|
8
8
|
const events_1 = require("events");
|
|
9
|
+
const common_utils_1 = require("@fluidframework/common-utils");
|
|
9
10
|
/**
|
|
10
11
|
* Audience represents all clients connected to the op stream.
|
|
11
12
|
*/
|
|
@@ -23,7 +24,11 @@ class Audience extends events_1.EventEmitter {
|
|
|
23
24
|
addMember(clientId, details) {
|
|
24
25
|
// Given that signal delivery is unreliable process, we might observe same client being added twice
|
|
25
26
|
// In such case we should see exactly same payload (IClient), and should not raise event twice!
|
|
26
|
-
if (
|
|
27
|
+
if (this.members.has(clientId)) {
|
|
28
|
+
const client = this.members.get(clientId);
|
|
29
|
+
(0, common_utils_1.assert)(JSON.stringify(client) === JSON.stringify(details), 0x4b2 /* new client has different payload from existing one */);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
27
32
|
this.members.set(clientId, details);
|
|
28
33
|
this.emit("addMember", clientId, details);
|
|
29
34
|
}
|
package/dist/audience.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audience.js","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,mCAAsC;
|
|
1
|
+
{"version":3,"file":"audience.js","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,mCAAsC;AACtC,+DAAsD;AAItD;;GAEG;AACH,MAAa,QAAS,SAAQ,qBAAY;IAA1C;;QACqB,YAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAmD1D,CAAC;IAhDU,EAAE,CAAC,KAAa,EAAE,QAAkC;QACvD,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,QAAgB,EAAE,OAAgB;QAC/C,mGAAmG;QACnG,+FAA+F;QAC/F,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAA,qBAAM,EAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,wDAAwD,CAAC,CAAC;SAC9H;aACI;YACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;SAC7C;IACL,CAAC;IAED;;;OAGG;IACI,YAAY,CAAC,QAAgB;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;SACf;aAAM;YACH,OAAO,KAAK,CAAC;SAChB;IACL,CAAC;IAED;;OAEG;IACI,UAAU;QACb,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;CACJ;AApDD,4BAoDC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { EventEmitter } from \"events\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IAudienceOwner } from \"@fluidframework/container-definitions\";\nimport { IClient } from \"@fluidframework/protocol-definitions\";\n\n/**\n * Audience represents all clients connected to the op stream.\n */\nexport class Audience extends EventEmitter implements IAudienceOwner {\n private readonly members = new Map<string, IClient>();\n\n public on(event: \"addMember\" | \"removeMember\", listener: (clientId: string, client: IClient) => void): this;\n public on(event: string, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n /**\n * Adds a new client to the audience\n */\n public addMember(clientId: string, details: IClient) {\n // Given that signal delivery is unreliable process, we might observe same client being added twice\n // In such case we should see exactly same payload (IClient), and should not raise event twice!\n if (this.members.has(clientId)) {\n const client = this.members.get(clientId);\n assert(JSON.stringify(client) === JSON.stringify(details), 0x4b2 /* new client has different payload from existing one */);\n }\n else {\n this.members.set(clientId, details);\n this.emit(\"addMember\", clientId, details);\n }\n }\n\n /**\n * Removes a client from the audience. Only emits an event if a client is actually removed\n * @returns if a client was removed from the audience\n */\n public removeMember(clientId: string): boolean {\n const removedClient = this.members.get(clientId);\n if (removedClient !== undefined) {\n this.members.delete(clientId);\n this.emit(\"removeMember\", clientId, removedClient);\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * Retrieves all the members in the audience\n */\n public getMembers(): Map<string, IClient> {\n return new Map(this.members);\n }\n\n /**\n * Retrieves a specific member of the audience\n */\n public getMember(clientId: string): IClient | undefined {\n return this.members.get(clientId);\n }\n}\n"]}
|
|
@@ -66,10 +66,11 @@ class CollabWindowTracker {
|
|
|
66
66
|
// Ensure we only send noop after a batch of many ops is processed
|
|
67
67
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
68
68
|
Promise.resolve().then(() => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
if (this.opsCountSinceNoop >= this.NoopCountFrequency) {
|
|
70
|
+
this.submitNoop(false /* immediate */);
|
|
71
|
+
// reset count now that all ops are processed
|
|
72
|
+
this.opsCountSinceNoop = 0;
|
|
73
|
+
}
|
|
73
74
|
return;
|
|
74
75
|
});
|
|
75
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collabWindowTracker.js","sourceRoot":"","sources":["../src/collabWindowTracker.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAA6D;AAC7D,+EAA8F;AAC9F,+DAA8E;AAE9E,MAAM,wBAAwB,GAAG,IAAI,CAAC;AACtC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAErC,6FAA6F;AAC7F,4GAA4G;AAC5G,yGAAyG;AACzG,2CAA2C;AAC3C,oHAAoH;AACpH,2FAA2F;AAC3F,kHAAkH;AAClH,+CAA+C;AAC/C,gHAAgH;AAChH,yFAAyF;AACzF,qHAAqH;AACrH,oDAAoD;AACpD,EAAE;AACF,kDAAkD;AAClD,oGAAoG;AACpG,iHAAiH;AACjH,sEAAsE;AACtE,4GAA4G;AAC5G,qGAAqG;AACrG,MAAa,mBAAmB;IAI5B,YACqB,MAAmC,EACpD,oBAA4B,wBAAwB,EACnC,qBAA6B,yBAAyB;QAFtD,WAAM,GAAN,MAAM,CAA6B;QAEnC,uBAAkB,GAAlB,kBAAkB,CAAoC;QANnE,sBAAiB,GAAG,CAAC,CAAC;QAQ1B,IAAI,iBAAiB,KAAK,QAAQ,EAAE;YAChC,IAAI,CAAC,KAAK,GAAG,IAAI,oBAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE;gBAC3C,2EAA2E;gBAC3E,mGAAmG;gBACnG,IAAI,IAAI,CAAC,iBAAiB,KAAK,CAAC,EAAE;oBAC9B,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;iBAC1C;YACL,CAAC,CAAC,CAAC;SACN;IACL,CAAC;IAED;;OAEG;IACI,4BAA4B,CAAC,OAAkC,EAAE,aAAsB;QAC1F,mEAAmE;QACnE,sDAAsD;QACtD,IAAI,aAAa,EAAE;YACf,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACtC,OAAO;SACV;QAED,gFAAgF;QAChF,+DAA+D;QAC/D,sFAAsF;QACtF,yCAAyC;QACzC,IAAI,CAAC,IAAA,+BAAgB,EAAC,OAAO,CAAC,EAAE;YAC5B,OAAO;SACV;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,CAAC,kBAAkB,EAAE;YACpD,kEAAkE;YAClE,mEAAmE;YACnE,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;gBACxB,
|
|
1
|
+
{"version":3,"file":"collabWindowTracker.js","sourceRoot":"","sources":["../src/collabWindowTracker.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 { assert, Timer } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport { isRuntimeMessage, MessageType2 } from \"@fluidframework/driver-utils\";\n\nconst defaultNoopTimeFrequency = 2000;\nconst defaultNoopCountFrequency = 50;\n\n// Here are key considerations when deciding conditions for when to send non-immediate noops:\n// 1. Sending them too often results in increase in file size and bandwidth, as well as catch up performance\n// 2. Sending too infrequently ensures that collab window is large, and as result Sequence DDS would have\n// large catchUp blobs - see Issue #6364\n// 3. Similarly, processes that rely on \"core\" snapshot (and can't parse trailing ops, including above), like search\n// parser in SPO, will result in non-accurate results due to presence of catch up blobs.\n// 4. Ordering service used 250ms timeout to coalesce non-immediate noops. It was changed to 2000 ms to allow more\n// aggressive noop sending from client side.\n// 5. Number of ops sent by all clients is proportional to number of \"write\" clients (every client sends noops),\n// but number of sequenced noops is a function of time (one op per 2 seconds at most).\n// We should consider impact to both outbound traffic (might be huge, depends on number of clients) and file size.\n// Please also see Issue #5629 for more discussions.\n//\n// With that, the current algorithm is as follows:\n// 1. Sent noop 2000 ms of receiving an op if no ops were sent by this client within this timeframe.\n// This will ensure that MSN moves forward with reasonable speed. If that results in too many sequenced noops,\n// server timeout of 2000ms should be reconsidered to be increased.\n// 2. If there are more than 50 ops received without sending any ops, send noop to keep collab window small.\n// Note that system ops (including noops themselves) are excluded, so it's 1 noop per 50 real ops.\nexport class CollabWindowTracker {\n private opsCountSinceNoop = 0;\n private readonly timer: Timer | undefined;\n\n constructor(\n private readonly submit: (type: MessageType) => void,\n NoopTimeFrequency: number = defaultNoopTimeFrequency,\n private readonly NoopCountFrequency: number = defaultNoopCountFrequency,\n ) {\n if (NoopTimeFrequency !== Infinity) {\n this.timer = new Timer(NoopTimeFrequency, () => {\n // Can get here due to this.stopSequenceNumberUpdate() not resetting timer.\n // Also timer callback can fire even after timer cancellation if it was queued before cancellation.\n if (this.opsCountSinceNoop !== 0) {\n this.submitNoop(false /* immediate */);\n }\n });\n }\n }\n\n /**\n * Schedules as ack to the server to update the reference sequence number\n */\n public scheduleSequenceNumberUpdate(message: ISequencedDocumentMessage, immediateNoOp: boolean): void {\n // While processing a message, an immediate no-op can be requested.\n // i.e. to expedite approve or commit phase of quorum.\n if (immediateNoOp) {\n this.submitNoop(true /* immediate */);\n return;\n }\n\n // We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN\n // update, which updates the MSN, then ack the update, etc...).\n // Intent here is for runtime (and DDSes) not to keep too much tracking state / memory\n // due to runtime ops from other clients.\n if (!isRuntimeMessage(message)) {\n return;\n }\n\n this.opsCountSinceNoop++;\n if (this.opsCountSinceNoop === this.NoopCountFrequency) {\n // Ensure we only send noop after a batch of many ops is processed\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.resolve().then(() => {\n if (this.opsCountSinceNoop >= this.NoopCountFrequency) {\n this.submitNoop(false /* immediate */);\n // reset count now that all ops are processed\n this.opsCountSinceNoop = 0;\n }\n return;\n });\n }\n\n if (this.timer !== undefined) {\n if (this.opsCountSinceNoop === 1) {\n this.timer.restart();\n }\n\n assert(this.timer.hasTimer, 0x242 /* \"has timer\" */);\n }\n }\n\n private submitNoop(immediate: boolean) {\n // Anything other than null is immediate noop\n // ADO:1385: Remove cast and use MessageType once definition changes propagate\n this.submit(immediate ? (MessageType2.Accept as unknown as MessageType) : MessageType.NoOp);\n assert(this.opsCountSinceNoop === 0,\n 0x243 /* \"stopSequenceNumberUpdate should be called as result of sending any op!\" */);\n }\n\n public stopSequenceNumberUpdate(): void {\n this.opsCountSinceNoop = 0;\n // Ideally, we cancel timer here. But that will result in too often set/reset cycle if this client\n // keeps sending ops. In most cases it's actually better to let it expire (at most - 4 times per second)\n // for nothing, then have a ton of set/reset cycles.\n // Note that Timer.restart() is smart and will not change timer expiration if we keep extending timer\n // expiration - it will restart the timer instead when it fires with adjusted expiration.\n // this.timer.clear();\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionManager.d.ts","sourceRoot":"","sources":["../src/connectionManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEH,gBAAgB,EAChB,oBAAoB,EACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACH,WAAW,EACX,YAAY,EAEZ,uBAAuB,EAC1B,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EAEH,gBAAgB,EAGnB,MAAM,oCAAoC,CAAC;AAW5C,OAAO,EACH,cAAc,EACd,OAAO,EACP,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAGhB,yBAAyB,EAO5B,MAAM,sCAAsC,CAAC;AAK9C,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,6BAA6B,EAChC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"connectionManager.d.ts","sourceRoot":"","sources":["../src/connectionManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,10 +31,18 @@ function getNackReconnectInfo(nackContent) {
|
|
|
31
31
|
* Implementation of IDocumentDeltaConnection that does not support submitting
|
|
32
32
|
* or receiving ops. Used in storage-only mode.
|
|
33
33
|
*/
|
|
34
|
+
const clientNoDeltaStream = {
|
|
35
|
+
mode: "read",
|
|
36
|
+
details: { capabilities: { interactive: true } },
|
|
37
|
+
permission: [],
|
|
38
|
+
user: { id: "storage-only client" },
|
|
39
|
+
scopes: [],
|
|
40
|
+
};
|
|
41
|
+
const clientIdNoDeltaStream = "storage-only client";
|
|
34
42
|
class NoDeltaStream extends common_utils_1.TypedEventEmitter {
|
|
35
43
|
constructor() {
|
|
36
44
|
super(...arguments);
|
|
37
|
-
this.clientId =
|
|
45
|
+
this.clientId = clientIdNoDeltaStream;
|
|
38
46
|
this.claims = {
|
|
39
47
|
scopes: [protocol_definitions_1.ScopeType.DocRead],
|
|
40
48
|
};
|
|
@@ -44,7 +52,7 @@ class NoDeltaStream extends common_utils_1.TypedEventEmitter {
|
|
|
44
52
|
this.version = "";
|
|
45
53
|
this.initialMessages = [];
|
|
46
54
|
this.initialSignals = [];
|
|
47
|
-
this.initialClients = [];
|
|
55
|
+
this.initialClients = [{ client: clientNoDeltaStream, clientId: clientIdNoDeltaStream }];
|
|
48
56
|
this.serviceConfiguration = {
|
|
49
57
|
maxMessageSize: 0,
|
|
50
58
|
blockSize: 0,
|
|
@@ -507,6 +515,9 @@ class ConnectionManager {
|
|
|
507
515
|
// If we asked for "write" and got "read", then file is read-only
|
|
508
516
|
// But if we ask read, server can still give us write.
|
|
509
517
|
const readonly = !connection.claims.scopes.includes(protocol_definitions_1.ScopeType.DocWrite);
|
|
518
|
+
if (connection.mode !== requestedMode) {
|
|
519
|
+
this.logger.sendTelemetryEvent({ eventName: "ConnectionModeMismatch", requestedMode, mode: connection.mode });
|
|
520
|
+
}
|
|
510
521
|
// This connection mode validation logic is moving to the driver layer in 0.44. These two asserts can be
|
|
511
522
|
// removed after those packages have released and become ubiquitous.
|
|
512
523
|
(0, common_utils_1.assert)(requestedMode === "read" || readonly === (this.connectionMode === "read"), 0x0e7 /* "claims/connectionMode mismatch" */);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionManager.js","sourceRoot":"","sources":["../src/connectionManager.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 { default as AbortController } from \"abort-controller\";\nimport {\n IDisposable,\n ITelemetryLogger,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { assert, performance, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport {\n IDeltaQueue,\n ReadOnlyInfo,\n IConnectionDetails,\n ICriticalContainerError,\n} from \"@fluidframework/container-definitions\";\nimport { GenericError, UsageError } from \"@fluidframework/container-utils\";\nimport {\n IAnyDriverError,\n IDocumentService,\n IDocumentDeltaConnection,\n IDocumentDeltaConnectionEvents,\n} from \"@fluidframework/driver-definitions\";\nimport {\n canRetryOnError,\n createWriteError,\n createGenericNetworkError,\n getRetryDelayFromError,\n waitForConnectedState,\n DeltaStreamConnectionForbiddenError,\n logNetworkFailure,\n isRuntimeMessage,\n} from \"@fluidframework/driver-utils\";\nimport {\n ConnectionMode,\n IClient,\n IClientConfiguration,\n IClientDetails,\n IDocumentMessage,\n INack,\n INackContent,\n ISequencedDocumentMessage,\n ISignalClient,\n ISignalMessage,\n ITokenClaims,\n MessageType,\n ScopeType,\n ISequencedDocumentSystemMessage,\n} from \"@fluidframework/protocol-definitions\";\nimport {\n TelemetryLogger,\n normalizeError,\n} from \"@fluidframework/telemetry-utils\";\nimport {\n ReconnectMode,\n IConnectionManager,\n IConnectionManagerFactoryArgs,\n} from \"./contracts\";\nimport { DeltaQueue } from \"./deltaQueue\";\nimport { SignalType } from \"./protocol\";\n\nconst MaxReconnectDelayInMs = 8000;\nconst InitialReconnectDelayInMs = 1000;\nconst DefaultChunkSize = 16 * 1024;\n\nconst fatalConnectErrorProp = { fatalConnectError: true };\n\nfunction getNackReconnectInfo(nackContent: INackContent) {\n const message = `Nack (${nackContent.type}): ${nackContent.message}`;\n const canRetry = nackContent.code !== 403;\n const retryAfterMs = nackContent.retryAfter !== undefined ? nackContent.retryAfter * 1000 : undefined;\n return createGenericNetworkError(\n message,\n { canRetry, retryAfterMs },\n { statusCode: nackContent.code, driverVersion: undefined });\n}\n\n/**\n * Implementation of IDocumentDeltaConnection that does not support submitting\n * or receiving ops. Used in storage-only mode.\n */\nclass NoDeltaStream\n extends TypedEventEmitter<IDocumentDeltaConnectionEvents>\n implements IDocumentDeltaConnection, IDisposable {\n clientId: string = \"storage-only client\";\n claims: ITokenClaims = {\n scopes: [ScopeType.DocRead],\n } as any;\n mode: ConnectionMode = \"read\";\n existing: boolean = true;\n maxMessageSize: number = 0;\n version: string = \"\";\n initialMessages: ISequencedDocumentMessage[] = [];\n initialSignals: ISignalMessage[] = [];\n initialClients: ISignalClient[] = [];\n serviceConfiguration: IClientConfiguration = {\n maxMessageSize: 0,\n blockSize: 0,\n };\n checkpointSequenceNumber?: number | undefined = undefined;\n submit(messages: IDocumentMessage[]): void {\n this.emit(\"nack\", this.clientId, messages.map((operation) => {\n return {\n operation,\n content: { message: \"Cannot submit with storage-only connection\", code: 403 },\n };\n }));\n }\n submitSignal(message: any): void {\n this.emit(\"nack\", this.clientId, {\n operation: message,\n content: { message: \"Cannot submit signal with storage-only connection\", code: 403 },\n });\n }\n\n private _disposed = false;\n public get disposed() { return this._disposed; }\n public dispose() { this._disposed = true; }\n}\n\n/**\n * Interface to track the current in-progress connection attempt.\n */\ninterface IPendingConnection {\n /**\n * Used to cancel an in-progress connection attempt.\n */\n abort(): void;\n\n /**\n * Desired ConnectionMode of this in-progress connection attempt.\n */\n connectionMode: ConnectionMode;\n}\n\n/**\n * Implementation of IConnectionManager, used by Container class\n * Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.\n * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.\n */\nexport class ConnectionManager implements IConnectionManager {\n /** Connection mode used when reconnecting on error or disconnect. */\n private readonly defaultReconnectionMode: ConnectionMode;\n\n /**\n * Tracks the current in-progress connection attempt. Undefined if there is none.\n * Note: Once the connection attempt fires and the code becomes asynchronous, its possible that a new connection\n * attempt was fired and this.pendingConnection was overwritten to reflect the new attempt.\n */\n private pendingConnection: IPendingConnection | undefined;\n private connection: IDocumentDeltaConnection | undefined;\n\n /** file ACL - whether user has only read-only access to a file */\n private _readonlyPermissions: boolean | undefined;\n\n /** tracks host requiring read-only mode. */\n private _forceReadonly = false;\n\n /**\n * Controls whether the DeltaManager will automatically reconnect to the delta stream after receiving a disconnect.\n */\n private _reconnectMode: ReconnectMode;\n\n /** True if there is pending (async) reconnection from \"read\" to \"write\" */\n private pendingReconnect = false;\n\n private clientSequenceNumber = 0;\n private clientSequenceNumberObserved = 0;\n /** Counts the number of noops sent by the client which may not be acked. */\n private localOpsToIgnore = 0;\n\n /** track clientId used last time when we sent any ops */\n private lastSubmittedClientId: string | undefined;\n\n private connectFirstConnection = true;\n\n private _connectionVerboseProps: Record<string, string | number> = {};\n\n private _connectionProps: ITelemetryProperties = {};\n\n private closed = false;\n\n private readonly _outbound: DeltaQueue<IDocumentMessage[]>;\n\n public get connectionVerboseProps() { return this._connectionVerboseProps; }\n\n public readonly clientDetails: IClientDetails;\n\n /**\n * The current connection mode, initially read.\n */\n public get connectionMode(): ConnectionMode {\n return this.connection?.mode ?? \"read\";\n }\n\n public get connected() { return this.connection !== undefined; }\n\n public get clientId() { return this.connection?.clientId; }\n /**\n * Automatic reconnecting enabled or disabled.\n * If set to Never, then reconnecting will never be allowed.\n */\n public get reconnectMode(): ReconnectMode {\n return this._reconnectMode;\n }\n\n public get maxMessageSize(): number {\n return this.connection?.serviceConfiguration?.maxMessageSize\n ?? DefaultChunkSize;\n }\n\n public get version(): string {\n if (this.connection === undefined) {\n throw new Error(\"Cannot check version without a connection\");\n }\n return this.connection.version;\n }\n\n public get serviceConfiguration(): IClientConfiguration | undefined {\n return this.connection?.serviceConfiguration;\n }\n\n public get scopes(): string[] | undefined {\n return this.connection?.claims.scopes;\n }\n\n public get outbound(): IDeltaQueue<IDocumentMessage[]> {\n return this._outbound;\n }\n\n /**\n * Returns set of props that can be logged in telemetry that provide some insights / statistics\n * about current or last connection (if there is no connection at the moment)\n */\n public get connectionProps(): ITelemetryProperties {\n return this.connection !== undefined\n ? this._connectionProps\n : {\n ...this._connectionProps,\n // Report how many ops this client sent in last disconnected session\n sentOps: this.clientSequenceNumber,\n };\n }\n\n public shouldJoinWrite(): boolean {\n // We don't have to wait for ack for topmost NoOps. So subtract those.\n return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.localOpsToIgnore);\n }\n\n /**\n * Tells if container is in read-only mode.\n * Data stores should listen for \"readonly\" notifications and disallow user\n * making changes to data stores.\n * Readonly state can be because of no storage write permission,\n * or due to host forcing readonly mode for container.\n * It is undefined if we have not yet established websocket connection\n * and do not know if user has write access to a file.\n */\n private get readonly() {\n if (this._forceReadonly) {\n return true;\n }\n return this._readonlyPermissions;\n }\n\n public get readOnlyInfo(): ReadOnlyInfo {\n const storageOnly = this.connection !== undefined && this.connection instanceof NoDeltaStream;\n if (storageOnly || this._forceReadonly || this._readonlyPermissions === true) {\n return {\n readonly: true,\n forced: this._forceReadonly,\n permissions: this._readonlyPermissions,\n storageOnly,\n };\n }\n\n return { readonly: this._readonlyPermissions };\n }\n\n private static detailsFromConnection(connection: IDocumentDeltaConnection): IConnectionDetails {\n return {\n claims: connection.claims,\n clientId: connection.clientId,\n existing: connection.existing,\n checkpointSequenceNumber: connection.checkpointSequenceNumber,\n get initialClients() { return connection.initialClients; },\n mode: connection.mode,\n serviceConfiguration: connection.serviceConfiguration,\n version: connection.version,\n };\n }\n\n constructor(\n private readonly serviceProvider: () => IDocumentService | undefined,\n private client: IClient,\n reconnectAllowed: boolean,\n private readonly logger: ITelemetryLogger,\n private readonly props: IConnectionManagerFactoryArgs,\n ) {\n this.clientDetails = this.client.details;\n this.defaultReconnectionMode = this.client.mode;\n this._reconnectMode = reconnectAllowed ? ReconnectMode.Enabled : ReconnectMode.Never;\n\n // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained\n // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.\n this._outbound = new DeltaQueue<IDocumentMessage[]>(\n (messages) => {\n if (this.connection === undefined) {\n throw new Error(\"Attempted to submit an outbound message without connection\");\n }\n this.connection.submit(messages);\n });\n\n this._outbound.on(\"error\", (error) => {\n this.props.closeHandler(normalizeError(error));\n });\n }\n\n public dispose(error?: ICriticalContainerError) {\n if (this.closed) {\n return;\n }\n this.closed = true;\n\n this.pendingConnection = undefined;\n\n // Ensure that things like triggerConnect() will short circuit\n this._reconnectMode = ReconnectMode.Never;\n\n this._outbound.clear();\n\n const disconnectReason = error !== undefined\n ? `Closing DeltaManager (${error.message})`\n : \"Closing DeltaManager\";\n\n // This raises \"disconnect\" event if we have active connection.\n this.disconnectFromDeltaStream(disconnectReason);\n\n // Notify everyone we are in read-only state.\n // Useful for data stores in case we hit some critical error,\n // to switch to a mode where user edits are not accepted\n this.set_readonlyPermissions(true);\n }\n\n /**\n * Enables or disables automatic reconnecting.\n * Will throw an error if reconnectMode set to Never.\n */\n public setAutoReconnect(mode: ReconnectMode): void {\n assert(mode !== ReconnectMode.Never && this._reconnectMode !== ReconnectMode.Never,\n 0x278 /* \"API is not supported for non-connecting or closed container\" */);\n\n this._reconnectMode = mode;\n\n if (mode !== ReconnectMode.Enabled) {\n // immediately disconnect - do not rely on service eventually dropping connection.\n this.disconnectFromDeltaStream(\"setAutoReconnect\");\n }\n }\n\n /**\n * Sends signal to runtime (and data stores) to be read-only.\n * Hosts may have read only views, indicating to data stores that no edits are allowed.\n * This is independent from this._readonlyPermissions (permissions) and this.connectionMode\n * (server can return \"write\" mode even when asked for \"read\")\n * Leveraging same \"readonly\" event as runtime & data stores should behave the same in such case\n * as in read-only permissions.\n * But this.active can be used by some DDSes to figure out if ops can be sent\n * (for example, read-only view still participates in code proposals / upgrades decisions)\n *\n * Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour\n * the readonly flag. If ops are generated, they will accumulate locally and not be sent. If\n * there are pending in the outbound queue, it will stop sending until force readonly is\n * cleared.\n *\n * @param readonly - set or clear force readonly.\n */\n public forceReadonly(readonly: boolean) {\n if (readonly !== this._forceReadonly) {\n this.logger.sendTelemetryEvent({\n eventName: \"ForceReadOnly\",\n value: readonly,\n });\n }\n const oldValue = this.readonly;\n this._forceReadonly = readonly;\n\n if (oldValue !== this.readonly) {\n if (this._reconnectMode === ReconnectMode.Never) {\n throw new UsageError(\"API is not supported for non-connecting or closed container\");\n }\n let reconnect = false;\n if (this.readonly === true) {\n // If we switch to readonly while connected, we should disconnect first\n // See comment in the \"readonly\" event handler to deltaManager set up by\n // the ContainerRuntime constructor\n\n if (this.shouldJoinWrite()) {\n // If we have pending changes, then we will never send them - it smells like\n // host logic error.\n this.logger.sendErrorEvent({ eventName: \"ForceReadonlyPendingChanged\" });\n }\n\n reconnect = this.disconnectFromDeltaStream(\"Force readonly\");\n }\n this.props.readonlyChangeHandler(this.readonly);\n if (reconnect) {\n // reconnect if we disconnected from before.\n this.triggerConnect(\"read\");\n }\n }\n }\n\n private set_readonlyPermissions(readonly: boolean) {\n const oldValue = this.readonly;\n this._readonlyPermissions = readonly;\n if (oldValue !== this.readonly) {\n this.props.readonlyChangeHandler(this.readonly);\n }\n }\n\n public connect(connectionMode?: ConnectionMode) {\n this.connectCore(connectionMode).catch((error) => {\n const normalizedError = normalizeError(error, { props: fatalConnectErrorProp });\n this.props.closeHandler(normalizedError);\n });\n }\n\n private async connectCore(connectionMode?: ConnectionMode): Promise<void> {\n assert(!this.closed, 0x26a /* \"not closed\" */);\n\n if (this.connection !== undefined) {\n return; // Connection attempt already completed successfully\n }\n\n let pendingConnectionMode;\n if (this.pendingConnection !== undefined) {\n pendingConnectionMode = this.pendingConnection.connectionMode;\n this.cancelConnection(); // Throw out in-progress connection attempt in favor of new attempt\n assert(this.pendingConnection === undefined, 0x344 /* this.pendingConnection should be undefined */);\n }\n // If there is no specified ConnectionMode, try the previous mode, if there is no previous mode use default\n let requestedMode = connectionMode ?? pendingConnectionMode ?? this.defaultReconnectionMode;\n\n // if we have any non-acked ops from last connection, reconnect as \"write\".\n // without that we would connect in view-only mode, which will result in immediate\n // firing of \"connected\" event from Container and switch of current clientId (as tracked\n // by all DDSes). This will make it impossible to figure out if ops actually made it through,\n // so DDSes will immediately resubmit all pending ops, and some of them will be duplicates, corrupting document\n if (this.shouldJoinWrite()) {\n requestedMode = \"write\";\n }\n\n const docService = this.serviceProvider();\n assert(docService !== undefined, 0x2a7 /* \"Container is not attached\" */);\n\n let connection: IDocumentDeltaConnection | undefined;\n\n if (docService.policies?.storageOnly === true) {\n connection = new NoDeltaStream();\n this.setupNewSuccessfulConnection(connection, \"read\");\n assert(this.pendingConnection === undefined, 0x2b3 /* \"logic error\" */);\n return;\n }\n\n let delayMs = InitialReconnectDelayInMs;\n let connectRepeatCount = 0;\n const connectStartTime = performance.now();\n let lastError: any;\n\n const abortController = new AbortController();\n const abortSignal = abortController.signal;\n this.pendingConnection = { abort: () => { abortController.abort(); }, connectionMode: requestedMode };\n\n // This loop will keep trying to connect until successful, with a delay between each iteration.\n while (connection === undefined) {\n if (this.closed) {\n throw new Error(\"Attempting to connect a closed DeltaManager\");\n }\n if (abortSignal.aborted === true) {\n this.logger.sendTelemetryEvent({\n eventName: \"ConnectionAttemptCancelled\",\n attempts: connectRepeatCount,\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n connectionEstablished: false,\n });\n return;\n }\n connectRepeatCount++;\n\n try {\n this.client.mode = requestedMode;\n connection = await docService.connectToDeltaStream({ ...this.client, mode: requestedMode });\n\n if (connection.disposed) {\n // Nobody observed this connection, so drop it on the floor and retry.\n this.logger.sendTelemetryEvent({ eventName: \"ReceivedClosedConnection\" });\n connection = undefined;\n }\n } catch (origError: any) {\n if (typeof origError === \"object\" && origError !== null &&\n origError?.errorType === DeltaStreamConnectionForbiddenError.errorType) {\n connection = new NoDeltaStream();\n requestedMode = \"read\";\n break;\n }\n\n // Socket.io error when we connect to wrong socket, or hit some multiplexing bug\n if (!canRetryOnError(origError)) {\n const error = normalizeError(origError, { props: fatalConnectErrorProp });\n this.props.closeHandler(error);\n throw error;\n }\n\n // Since the error is retryable this will not log to the error table\n logNetworkFailure(\n this.logger,\n {\n attempts: connectRepeatCount,\n delay: delayMs, // milliseconds\n eventName: \"DeltaConnectionFailureToConnect\",\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n },\n origError);\n\n lastError = origError;\n\n const retryDelayFromError = getRetryDelayFromError(origError);\n delayMs = retryDelayFromError ?? Math.min(delayMs * 2, MaxReconnectDelayInMs);\n\n if (retryDelayFromError !== undefined) {\n this.props.reconnectionDelayHandler(retryDelayFromError, origError);\n }\n await waitForConnectedState(delayMs);\n }\n }\n\n // If we retried more than once, log an event about how long it took (this will not log to error table)\n if (connectRepeatCount > 1) {\n logNetworkFailure(\n this.logger,\n {\n eventName: \"MultipleDeltaConnectionFailures\",\n attempts: connectRepeatCount,\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n },\n lastError,\n );\n }\n\n // Check for abort signal after while loop as well\n if (abortSignal.aborted === true) {\n connection.dispose();\n this.logger.sendTelemetryEvent({\n eventName: \"ConnectionAttemptCancelled\",\n attempts: connectRepeatCount,\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n connectionEstablished: true,\n });\n return;\n }\n\n this.setupNewSuccessfulConnection(connection, requestedMode);\n }\n\n /**\n * Start the connection. Any error should result in container being closed.\n * And report the error if it escapes for any reason.\n * @param args - The connection arguments\n */\n private triggerConnect(connectionMode: ConnectionMode) {\n // reconnect() has async await of waitForConnectedState(), and that causes potential race conditions\n // where we might already have a connection. If it were to happen, it's possible that we will connect\n // with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be\n // fine (if needed, reconnect flow will get triggered again). Places where new mode matters should encode it\n // directly in connectCore - see this.shouldJoinWrite() test as an example.\n // assert(this.connection === undefined, 0x239 /* \"called only in disconnected state\" */);\n\n if (this.reconnectMode !== ReconnectMode.Enabled) {\n return;\n }\n this.connect(connectionMode);\n }\n\n /**\n * Disconnect the current connection.\n * @param reason - Text description of disconnect reason to emit with disconnect event\n * @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect\n */\n private disconnectFromDeltaStream(reason: string): boolean {\n this.pendingReconnect = false;\n\n if (this.connection === undefined) {\n if (this.pendingConnection !== undefined) {\n this.cancelConnection();\n return true;\n }\n return false;\n }\n\n assert(this.pendingConnection === undefined, 0x27b /* \"reentrancy may result in incorrect behavior\" */);\n\n const connection = this.connection;\n // Avoid any re-entrancy - clear object reference\n this.connection = undefined;\n\n // Remove listeners first so we don't try to retrigger this flow accidentally through reconnectOnError\n connection.off(\"op\", this.opHandler);\n connection.off(\"signal\", this.props.signalHandler);\n connection.off(\"nack\", this.nackHandler);\n connection.off(\"disconnect\", this.disconnectHandlerInternal);\n connection.off(\"error\", this.errorHandler);\n connection.off(\"pong\", this.props.pongHandler);\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._outbound.pause();\n this._outbound.clear();\n this.props.disconnectHandler(reason);\n\n connection.dispose();\n\n this._connectionVerboseProps = {};\n\n return true;\n }\n\n /**\n * Cancel in-progress connection attempt.\n */\n private cancelConnection() {\n assert(this.pendingConnection !== undefined,\n 0x345 /* this.pendingConnection is undefined when trying to cancel */);\n this.pendingConnection.abort();\n this.pendingConnection = undefined;\n this.logger.sendTelemetryEvent({ eventName: \"ConnectionCancelReceived\" });\n }\n\n /**\n * Once we've successfully gotten a connection, we need to set up state, attach event listeners, and process\n * initial messages.\n * @param connection - The newly established connection\n */\n private setupNewSuccessfulConnection(connection: IDocumentDeltaConnection, requestedMode: ConnectionMode) {\n // Old connection should have been cleaned up before establishing a new one\n assert(this.connection === undefined, 0x0e6 /* \"old connection exists on new connection setup\" */);\n assert(!connection.disposed, 0x28a /* \"can't be disposed - Callers need to ensure that!\" */);\n\n this.pendingConnection = undefined;\n\n this.connection = connection;\n\n // Does information in scopes & mode matches?\n // If we asked for \"write\" and got \"read\", then file is read-only\n // But if we ask read, server can still give us write.\n const readonly = !connection.claims.scopes.includes(ScopeType.DocWrite);\n\n // This connection mode validation logic is moving to the driver layer in 0.44. These two asserts can be\n // removed after those packages have released and become ubiquitous.\n assert(requestedMode === \"read\" || readonly === (this.connectionMode === \"read\"),\n 0x0e7 /* \"claims/connectionMode mismatch\" */);\n assert(!readonly || this.connectionMode === \"read\", 0x0e8 /* \"readonly perf with write connection\" */);\n\n this.set_readonlyPermissions(readonly);\n\n if (this.closed) {\n // Raise proper events, Log telemetry event and close connection.\n this.disconnectFromDeltaStream(\"ConnectionManager already closed\");\n return;\n }\n\n this._outbound.resume();\n\n connection.on(\"op\", this.opHandler);\n connection.on(\"signal\", this.props.signalHandler);\n connection.on(\"nack\", this.nackHandler);\n connection.on(\"disconnect\", this.disconnectHandlerInternal);\n connection.on(\"error\", this.errorHandler);\n connection.on(\"pong\", this.props.pongHandler);\n\n // Initial messages are always sorted. However, due to early op handler installed by drivers and appending those\n // ops to initialMessages, resulting set is no longer sorted, which would result in client hitting storage to\n // fill in gap. We will recover by cancelling this request once we process remaining ops, but it's a waste that\n // we could avoid\n const initialMessages = connection.initialMessages.sort((a, b) => a.sequenceNumber - b.sequenceNumber);\n\n // Some storages may provide checkpointSequenceNumber to identify how far client is behind.\n let checkpointSequenceNumber = connection.checkpointSequenceNumber;\n\n this._connectionVerboseProps = {\n clientId: connection.clientId,\n mode: connection.mode,\n };\n\n // reset connection props\n this._connectionProps = {};\n\n if (connection.relayServiceAgent !== undefined) {\n this._connectionVerboseProps.relayServiceAgent = connection.relayServiceAgent;\n this._connectionProps.relayServiceAgent = connection.relayServiceAgent;\n }\n this._connectionProps.socketDocumentId = connection.claims.documentId;\n this._connectionProps.connectionMode = connection.mode;\n\n let last = -1;\n if (initialMessages.length !== 0) {\n this._connectionVerboseProps.connectionInitialOpsFrom = initialMessages[0].sequenceNumber;\n last = initialMessages[initialMessages.length - 1].sequenceNumber;\n this._connectionVerboseProps.connectionInitialOpsTo = last + 1;\n // Update knowledge of how far we are behind, before raising \"connect\" event\n // This is duplication of what incomingOpHandler() does, but we have to raise event before we get there,\n // so duplicating update logic here as well.\n if (checkpointSequenceNumber === undefined || checkpointSequenceNumber < last) {\n checkpointSequenceNumber = last;\n }\n }\n\n this.props.incomingOpHandler(\n initialMessages,\n this.connectFirstConnection ? \"InitialOps\" : \"ReconnectOps\");\n\n const details = ConnectionManager.detailsFromConnection(connection);\n details.checkpointSequenceNumber = checkpointSequenceNumber;\n this.props.connectHandler(details);\n\n this.connectFirstConnection = false;\n\n // Synthesize clear & join signals out of initialClients state.\n // This allows us to have single way to process signals, and makes it simpler to initialize\n // protocol in Container.\n const clearSignal: ISignalMessage = {\n clientId: null, // system message\n content: JSON.stringify({\n type: SignalType.Clear,\n }),\n };\n this.props.signalHandler(clearSignal);\n\n for (const priorClient of connection.initialClients ?? []) {\n const joinSignal: ISignalMessage = {\n clientId: null, // system signal\n content: JSON.stringify({\n type: SignalType.ClientJoin,\n content: priorClient, // ISignalClient\n }),\n };\n this.props.signalHandler(joinSignal);\n }\n\n // Unfortunately, there is no defined order between initialSignals (including join & leave signals)\n // and connection.initialClients. In practice, connection.initialSignals quite often contains join signal\n // for \"self\" and connection.initialClients does not contain \"self\", so we have to process them after\n // \"clear\" signal above.\n if (connection.initialSignals !== undefined) {\n for (const signal of connection.initialSignals) {\n this.props.signalHandler(signal);\n }\n }\n }\n\n /**\n * Disconnect the current connection and reconnect. Closes the container if it fails.\n * @param connection - The connection that wants to reconnect - no-op if it's different from this.connection\n * @param requestedMode - Read or write\n * @param error - Error reconnect information including whether or not to reconnect\n * @returns A promise that resolves when the connection is reestablished or we stop trying\n */\n private reconnectOnError(\n requestedMode: ConnectionMode,\n error: IAnyDriverError,\n ) {\n this.reconnect(\n requestedMode,\n error.message,\n error)\n .catch(this.props.closeHandler);\n }\n\n /**\n * Disconnect the current connection and reconnect.\n * @param connection - The connection that wants to reconnect - no-op if it's different from this.connection\n * @param requestedMode - Read or write\n * @param error - Error reconnect information including whether or not to reconnect\n * @returns A promise that resolves when the connection is reestablished or we stop trying\n */\n private async reconnect(\n requestedMode: ConnectionMode,\n disconnectMessage: string,\n error?: IAnyDriverError,\n ) {\n // We quite often get protocol errors before / after observing nack/disconnect\n // we do not want to run through same sequence twice.\n // If we're already disconnected/disconnecting it's not appropriate to call this again.\n assert(this.connection !== undefined, 0x0eb /* \"Missing connection for reconnect\" */);\n\n this.disconnectFromDeltaStream(disconnectMessage);\n\n // We will always trigger reconnect, even if canRetry is false.\n // Any truly fatal error state will result in container close upon attempted reconnect,\n // which is a preferable to closing abruptly when a live connection fails.\n if (error !== undefined && !error.canRetry) {\n this.logger.sendTelemetryEvent({\n eventName: \"reconnectingDespiteFatalError\",\n reconnectMode: this.reconnectMode,\n }, error);\n }\n\n if (this.reconnectMode === ReconnectMode.Never) {\n // Do not raise container error if we are closing just because we lost connection.\n // Those errors (like IdleDisconnect) would show up in telemetry dashboards and\n // are very misleading, as first initial reaction - some logic is broken.\n this.props.closeHandler();\n }\n\n // If closed then we can't reconnect\n if (this.closed || this.reconnectMode !== ReconnectMode.Enabled) {\n return;\n }\n\n const delayMs = getRetryDelayFromError(error);\n if (error !== undefined && delayMs !== undefined) {\n this.props.reconnectionDelayHandler(delayMs, error);\n await waitForConnectedState(delayMs);\n }\n\n this.triggerConnect(requestedMode);\n }\n\n public prepareMessageToSend(message: Omit<IDocumentMessage, \"clientSequenceNumber\">): IDocumentMessage | undefined {\n if (this.readonly === true) {\n assert(this.readOnlyInfo.readonly === true, 0x1f0 /* \"Unexpected mismatch in readonly\" */);\n const error = new GenericError(\"deltaManagerReadonlySubmit\", undefined /* error */, {\n readonly: this.readOnlyInfo.readonly,\n forcedReadonly: this.readOnlyInfo.forced,\n readonlyPermissions: this.readOnlyInfo.permissions,\n storageOnly: this.readOnlyInfo.storageOnly,\n });\n this.props.closeHandler(error);\n return undefined;\n }\n\n // reset clientSequenceNumber if we are using new clientId.\n // we keep info about old connection as long as possible to be able to account for all non-acked ops\n // that we pick up on next connection.\n assert(!!this.connection, 0x0e4 /* \"Lost old connection!\" */);\n if (this.lastSubmittedClientId !== this.connection?.clientId) {\n this.lastSubmittedClientId = this.connection?.clientId;\n this.clientSequenceNumber = 0;\n this.clientSequenceNumberObserved = 0;\n }\n\n if (!isRuntimeMessage(message)) {\n this.localOpsToIgnore++;\n } else {\n this.localOpsToIgnore = 0;\n }\n\n return {\n ...message,\n clientSequenceNumber: ++this.clientSequenceNumber,\n };\n }\n\n public submitSignal(content: any) {\n if (this.connection !== undefined) {\n this.connection.submitSignal(content);\n } else {\n this.logger.sendErrorEvent({ eventName: \"submitSignalDisconnected\" });\n }\n }\n\n public sendMessages(messages: IDocumentMessage[]) {\n assert(this.connected, 0x2b4 /* \"not connected on sending ops!\" */);\n // If connection is \"read\" or implicit \"read\" (got leave op for \"write\" connection),\n // then op can't make it through - we will get a nack if op is sent.\n // We can short-circuit this process.\n // Note that we also want nacks to be rare and be treated as catastrophic failures.\n // Be careful with reentrancy though - disconnected event should not be be raised in the\n // middle of the current workflow, but rather on clean stack!\n if (this.connectionMode === \"read\") {\n if (!this.pendingReconnect) {\n this.pendingReconnect = true;\n Promise.resolve().then(async () => {\n if (this.pendingReconnect) { // still valid?\n await this.reconnect(\n \"write\", // connectionMode\n \"Switch to write\", // message\n );\n }\n }).catch(() => { });\n }\n return;\n }\n\n assert(!this.pendingReconnect, 0x2b5 /* \"logic error\" */);\n\n this._outbound.push(messages);\n }\n\n public beforeProcessingIncomingOp(message: ISequencedDocumentMessage) {\n // if we have connection, and message is local, then we better treat is as local!\n assert(this.clientId !== message.clientId || this.lastSubmittedClientId === message.clientId,\n 0x0ee /* \"Not accounting local messages correctly\" */,\n );\n\n if (this.lastSubmittedClientId !== undefined && this.lastSubmittedClientId === message.clientId) {\n const clientSequenceNumber = message.clientSequenceNumber;\n\n assert(this.clientSequenceNumberObserved < clientSequenceNumber, 0x0ef /* \"client seq# not growing\" */);\n assert(clientSequenceNumber <= this.clientSequenceNumber,\n 0x0f0 /* \"Incoming local client seq# > generated by this client\" */);\n\n this.clientSequenceNumberObserved = clientSequenceNumber;\n }\n\n if (message.type === MessageType.ClientLeave) {\n const systemLeaveMessage = message as ISequencedDocumentSystemMessage;\n const clientId = JSON.parse(systemLeaveMessage.data) as string;\n if (clientId === this.clientId) {\n // We have been kicked out from quorum\n this.logger.sendPerformanceEvent({ eventName: \"ReadConnectionTransition\" });\n\n // Please see #8483 for more details on why maintaining connection further as is would not work.\n // Short story - connection properties are immutable, and many processes (consensus DDSes, summarizer)\n // assume that connection stays \"write\" connection until disconnect, and act accordingly, which may\n // not work well with de-facto \"read\" connection we are in after receiving own leave op on timeout.\n // Clients need to be able to transition to \"read\" state after some time of inactivity!\n // Note - this may close container!\n this.reconnect(\n \"read\", // connectionMode\n \"Switch to read\", // message\n ).catch((error) => {\n this.logger.sendErrorEvent({ eventName: \"SwitchToReadConnection\" }, error);\n });\n }\n }\n }\n\n private readonly opHandler = (documentId: string, messagesArg: ISequencedDocumentMessage[]) => {\n const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];\n this.props.incomingOpHandler(messages, \"opHandler\");\n };\n\n // Always connect in write mode after getting nacked.\n private readonly nackHandler = (documentId: string, messages: INack[]) => {\n const message = messages[0];\n if (this._readonlyPermissions === true) {\n this.props.closeHandler(createWriteError(\"writeOnReadOnlyDocument\", { driverVersion: undefined }));\n return;\n }\n\n const reconnectInfo = getNackReconnectInfo(message.content);\n\n // If the nack indicates we cannot retry, then close the container outright\n if (!reconnectInfo.canRetry) {\n this.props.closeHandler(reconnectInfo);\n return;\n }\n\n this.reconnectOnError(\n \"write\",\n reconnectInfo,\n );\n };\n\n // Connection mode is always read on disconnect/error unless the system mode was write.\n private readonly disconnectHandlerInternal = (disconnectReason: IAnyDriverError) => {\n // Note: we might get multiple disconnect calls on same socket, as early disconnect notification\n // (\"server_disconnect\", ODSP-specific) is mapped to \"disconnect\"\n this.reconnectOnError(\n this.defaultReconnectionMode,\n disconnectReason,\n );\n };\n\n private readonly errorHandler = (error: IAnyDriverError) => {\n this.reconnectOnError(\n this.defaultReconnectionMode,\n error,\n );\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connectionManager.js","sourceRoot":"","sources":["../src/connectionManager.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 { default as AbortController } from \"abort-controller\";\nimport {\n IDisposable,\n ITelemetryLogger,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { assert, performance, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport {\n IDeltaQueue,\n ReadOnlyInfo,\n IConnectionDetails,\n ICriticalContainerError,\n} from \"@fluidframework/container-definitions\";\nimport { GenericError, UsageError } from \"@fluidframework/container-utils\";\nimport {\n IAnyDriverError,\n IDocumentService,\n IDocumentDeltaConnection,\n IDocumentDeltaConnectionEvents,\n} from \"@fluidframework/driver-definitions\";\nimport {\n canRetryOnError,\n createWriteError,\n createGenericNetworkError,\n getRetryDelayFromError,\n waitForConnectedState,\n DeltaStreamConnectionForbiddenError,\n logNetworkFailure,\n isRuntimeMessage,\n} from \"@fluidframework/driver-utils\";\nimport {\n ConnectionMode,\n IClient,\n IClientConfiguration,\n IClientDetails,\n IDocumentMessage,\n INack,\n INackContent,\n ISequencedDocumentMessage,\n ISignalClient,\n ISignalMessage,\n ITokenClaims,\n MessageType,\n ScopeType,\n ISequencedDocumentSystemMessage,\n} from \"@fluidframework/protocol-definitions\";\nimport {\n TelemetryLogger,\n normalizeError,\n} from \"@fluidframework/telemetry-utils\";\nimport {\n ReconnectMode,\n IConnectionManager,\n IConnectionManagerFactoryArgs,\n} from \"./contracts\";\nimport { DeltaQueue } from \"./deltaQueue\";\nimport { SignalType } from \"./protocol\";\n\nconst MaxReconnectDelayInMs = 8000;\nconst InitialReconnectDelayInMs = 1000;\nconst DefaultChunkSize = 16 * 1024;\n\nconst fatalConnectErrorProp = { fatalConnectError: true };\n\nfunction getNackReconnectInfo(nackContent: INackContent) {\n const message = `Nack (${nackContent.type}): ${nackContent.message}`;\n const canRetry = nackContent.code !== 403;\n const retryAfterMs = nackContent.retryAfter !== undefined ? nackContent.retryAfter * 1000 : undefined;\n return createGenericNetworkError(\n message,\n { canRetry, retryAfterMs },\n { statusCode: nackContent.code, driverVersion: undefined });\n}\n\n/**\n * Implementation of IDocumentDeltaConnection that does not support submitting\n * or receiving ops. Used in storage-only mode.\n */\nconst clientNoDeltaStream: IClient = {\n mode: \"read\",\n details: { capabilities: { interactive: true } },\n permission: [],\n user: { id: \"storage-only client\" }, // we need some \"fake\" ID here.\n scopes: [],\n};\nconst clientIdNoDeltaStream: string = \"storage-only client\";\n\nclass NoDeltaStream\n extends TypedEventEmitter<IDocumentDeltaConnectionEvents>\n implements IDocumentDeltaConnection, IDisposable {\n clientId = clientIdNoDeltaStream;\n claims: ITokenClaims = {\n scopes: [ScopeType.DocRead],\n } as any;\n mode: ConnectionMode = \"read\";\n existing: boolean = true;\n maxMessageSize: number = 0;\n version: string = \"\";\n initialMessages: ISequencedDocumentMessage[] = [];\n initialSignals: ISignalMessage[] = [];\n initialClients: ISignalClient[] = [{ client: clientNoDeltaStream, clientId: clientIdNoDeltaStream }];\n serviceConfiguration: IClientConfiguration = {\n maxMessageSize: 0,\n blockSize: 0,\n };\n checkpointSequenceNumber?: number | undefined = undefined;\n submit(messages: IDocumentMessage[]): void {\n this.emit(\"nack\", this.clientId, messages.map((operation) => {\n return {\n operation,\n content: { message: \"Cannot submit with storage-only connection\", code: 403 },\n };\n }));\n }\n submitSignal(message: any): void {\n this.emit(\"nack\", this.clientId, {\n operation: message,\n content: { message: \"Cannot submit signal with storage-only connection\", code: 403 },\n });\n }\n\n private _disposed = false;\n public get disposed() { return this._disposed; }\n public dispose() { this._disposed = true; }\n}\n\n/**\n * Interface to track the current in-progress connection attempt.\n */\ninterface IPendingConnection {\n /**\n * Used to cancel an in-progress connection attempt.\n */\n abort(): void;\n\n /**\n * Desired ConnectionMode of this in-progress connection attempt.\n */\n connectionMode: ConnectionMode;\n}\n\n/**\n * Implementation of IConnectionManager, used by Container class\n * Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.\n * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.\n */\nexport class ConnectionManager implements IConnectionManager {\n /** Connection mode used when reconnecting on error or disconnect. */\n private readonly defaultReconnectionMode: ConnectionMode;\n\n /**\n * Tracks the current in-progress connection attempt. Undefined if there is none.\n * Note: Once the connection attempt fires and the code becomes asynchronous, its possible that a new connection\n * attempt was fired and this.pendingConnection was overwritten to reflect the new attempt.\n */\n private pendingConnection: IPendingConnection | undefined;\n private connection: IDocumentDeltaConnection | undefined;\n\n /** file ACL - whether user has only read-only access to a file */\n private _readonlyPermissions: boolean | undefined;\n\n /** tracks host requiring read-only mode. */\n private _forceReadonly = false;\n\n /**\n * Controls whether the DeltaManager will automatically reconnect to the delta stream after receiving a disconnect.\n */\n private _reconnectMode: ReconnectMode;\n\n /** True if there is pending (async) reconnection from \"read\" to \"write\" */\n private pendingReconnect = false;\n\n private clientSequenceNumber = 0;\n private clientSequenceNumberObserved = 0;\n /** Counts the number of noops sent by the client which may not be acked. */\n private localOpsToIgnore = 0;\n\n /** track clientId used last time when we sent any ops */\n private lastSubmittedClientId: string | undefined;\n\n private connectFirstConnection = true;\n\n private _connectionVerboseProps: Record<string, string | number> = {};\n\n private _connectionProps: ITelemetryProperties = {};\n\n private closed = false;\n\n private readonly _outbound: DeltaQueue<IDocumentMessage[]>;\n\n public get connectionVerboseProps() { return this._connectionVerboseProps; }\n\n public readonly clientDetails: IClientDetails;\n\n /**\n * The current connection mode, initially read.\n */\n public get connectionMode(): ConnectionMode {\n return this.connection?.mode ?? \"read\";\n }\n\n public get connected() { return this.connection !== undefined; }\n\n public get clientId() { return this.connection?.clientId; }\n /**\n * Automatic reconnecting enabled or disabled.\n * If set to Never, then reconnecting will never be allowed.\n */\n public get reconnectMode(): ReconnectMode {\n return this._reconnectMode;\n }\n\n public get maxMessageSize(): number {\n return this.connection?.serviceConfiguration?.maxMessageSize\n ?? DefaultChunkSize;\n }\n\n public get version(): string {\n if (this.connection === undefined) {\n throw new Error(\"Cannot check version without a connection\");\n }\n return this.connection.version;\n }\n\n public get serviceConfiguration(): IClientConfiguration | undefined {\n return this.connection?.serviceConfiguration;\n }\n\n public get scopes(): string[] | undefined {\n return this.connection?.claims.scopes;\n }\n\n public get outbound(): IDeltaQueue<IDocumentMessage[]> {\n return this._outbound;\n }\n\n /**\n * Returns set of props that can be logged in telemetry that provide some insights / statistics\n * about current or last connection (if there is no connection at the moment)\n */\n public get connectionProps(): ITelemetryProperties {\n return this.connection !== undefined\n ? this._connectionProps\n : {\n ...this._connectionProps,\n // Report how many ops this client sent in last disconnected session\n sentOps: this.clientSequenceNumber,\n };\n }\n\n public shouldJoinWrite(): boolean {\n // We don't have to wait for ack for topmost NoOps. So subtract those.\n return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.localOpsToIgnore);\n }\n\n /**\n * Tells if container is in read-only mode.\n * Data stores should listen for \"readonly\" notifications and disallow user\n * making changes to data stores.\n * Readonly state can be because of no storage write permission,\n * or due to host forcing readonly mode for container.\n * It is undefined if we have not yet established websocket connection\n * and do not know if user has write access to a file.\n */\n private get readonly(): boolean | undefined {\n if (this._forceReadonly) {\n return true;\n }\n return this._readonlyPermissions;\n }\n\n public get readOnlyInfo(): ReadOnlyInfo {\n const storageOnly = this.connection !== undefined && this.connection instanceof NoDeltaStream;\n if (storageOnly || this._forceReadonly || this._readonlyPermissions === true) {\n return {\n readonly: true,\n forced: this._forceReadonly,\n permissions: this._readonlyPermissions,\n storageOnly,\n };\n }\n\n return { readonly: this._readonlyPermissions };\n }\n\n private static detailsFromConnection(connection: IDocumentDeltaConnection): IConnectionDetails {\n return {\n claims: connection.claims,\n clientId: connection.clientId,\n existing: connection.existing,\n checkpointSequenceNumber: connection.checkpointSequenceNumber,\n get initialClients() { return connection.initialClients; },\n mode: connection.mode,\n serviceConfiguration: connection.serviceConfiguration,\n version: connection.version,\n };\n }\n\n constructor(\n private readonly serviceProvider: () => IDocumentService | undefined,\n private client: IClient,\n reconnectAllowed: boolean,\n private readonly logger: ITelemetryLogger,\n private readonly props: IConnectionManagerFactoryArgs,\n ) {\n this.clientDetails = this.client.details;\n this.defaultReconnectionMode = this.client.mode;\n this._reconnectMode = reconnectAllowed ? ReconnectMode.Enabled : ReconnectMode.Never;\n\n // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained\n // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.\n this._outbound = new DeltaQueue<IDocumentMessage[]>(\n (messages) => {\n if (this.connection === undefined) {\n throw new Error(\"Attempted to submit an outbound message without connection\");\n }\n this.connection.submit(messages);\n });\n\n this._outbound.on(\"error\", (error) => {\n this.props.closeHandler(normalizeError(error));\n });\n }\n\n public dispose(error?: ICriticalContainerError) {\n if (this.closed) {\n return;\n }\n this.closed = true;\n\n this.pendingConnection = undefined;\n\n // Ensure that things like triggerConnect() will short circuit\n this._reconnectMode = ReconnectMode.Never;\n\n this._outbound.clear();\n\n const disconnectReason = error !== undefined\n ? `Closing DeltaManager (${error.message})`\n : \"Closing DeltaManager\";\n\n // This raises \"disconnect\" event if we have active connection.\n this.disconnectFromDeltaStream(disconnectReason);\n\n // Notify everyone we are in read-only state.\n // Useful for data stores in case we hit some critical error,\n // to switch to a mode where user edits are not accepted\n this.set_readonlyPermissions(true);\n }\n\n /**\n * Enables or disables automatic reconnecting.\n * Will throw an error if reconnectMode set to Never.\n */\n public setAutoReconnect(mode: ReconnectMode): void {\n assert(mode !== ReconnectMode.Never && this._reconnectMode !== ReconnectMode.Never,\n 0x278 /* \"API is not supported for non-connecting or closed container\" */);\n\n this._reconnectMode = mode;\n\n if (mode !== ReconnectMode.Enabled) {\n // immediately disconnect - do not rely on service eventually dropping connection.\n this.disconnectFromDeltaStream(\"setAutoReconnect\");\n }\n }\n\n /**\n * Sends signal to runtime (and data stores) to be read-only.\n * Hosts may have read only views, indicating to data stores that no edits are allowed.\n * This is independent from this._readonlyPermissions (permissions) and this.connectionMode\n * (server can return \"write\" mode even when asked for \"read\")\n * Leveraging same \"readonly\" event as runtime & data stores should behave the same in such case\n * as in read-only permissions.\n * But this.active can be used by some DDSes to figure out if ops can be sent\n * (for example, read-only view still participates in code proposals / upgrades decisions)\n *\n * Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour\n * the readonly flag. If ops are generated, they will accumulate locally and not be sent. If\n * there are pending in the outbound queue, it will stop sending until force readonly is\n * cleared.\n *\n * @param readonly - set or clear force readonly.\n */\n public forceReadonly(readonly: boolean) {\n if (readonly !== this._forceReadonly) {\n this.logger.sendTelemetryEvent({\n eventName: \"ForceReadOnly\",\n value: readonly,\n });\n }\n const oldValue = this.readonly;\n this._forceReadonly = readonly;\n\n if (oldValue !== this.readonly) {\n if (this._reconnectMode === ReconnectMode.Never) {\n throw new UsageError(\"API is not supported for non-connecting or closed container\");\n }\n let reconnect = false;\n if (this.readonly === true) {\n // If we switch to readonly while connected, we should disconnect first\n // See comment in the \"readonly\" event handler to deltaManager set up by\n // the ContainerRuntime constructor\n\n if (this.shouldJoinWrite()) {\n // If we have pending changes, then we will never send them - it smells like\n // host logic error.\n this.logger.sendErrorEvent({ eventName: \"ForceReadonlyPendingChanged\" });\n }\n\n reconnect = this.disconnectFromDeltaStream(\"Force readonly\");\n }\n this.props.readonlyChangeHandler(this.readonly);\n if (reconnect) {\n // reconnect if we disconnected from before.\n this.triggerConnect(\"read\");\n }\n }\n }\n\n private set_readonlyPermissions(readonly: boolean) {\n const oldValue = this.readonly;\n this._readonlyPermissions = readonly;\n if (oldValue !== this.readonly) {\n this.props.readonlyChangeHandler(this.readonly);\n }\n }\n\n public connect(connectionMode?: ConnectionMode) {\n this.connectCore(connectionMode).catch((error) => {\n const normalizedError = normalizeError(error, { props: fatalConnectErrorProp });\n this.props.closeHandler(normalizedError);\n });\n }\n\n private async connectCore(connectionMode?: ConnectionMode): Promise<void> {\n assert(!this.closed, 0x26a /* \"not closed\" */);\n\n if (this.connection !== undefined) {\n return; // Connection attempt already completed successfully\n }\n\n let pendingConnectionMode;\n if (this.pendingConnection !== undefined) {\n pendingConnectionMode = this.pendingConnection.connectionMode;\n this.cancelConnection(); // Throw out in-progress connection attempt in favor of new attempt\n assert(this.pendingConnection === undefined, 0x344 /* this.pendingConnection should be undefined */);\n }\n // If there is no specified ConnectionMode, try the previous mode, if there is no previous mode use default\n let requestedMode = connectionMode ?? pendingConnectionMode ?? this.defaultReconnectionMode;\n\n // if we have any non-acked ops from last connection, reconnect as \"write\".\n // without that we would connect in view-only mode, which will result in immediate\n // firing of \"connected\" event from Container and switch of current clientId (as tracked\n // by all DDSes). This will make it impossible to figure out if ops actually made it through,\n // so DDSes will immediately resubmit all pending ops, and some of them will be duplicates, corrupting document\n if (this.shouldJoinWrite()) {\n requestedMode = \"write\";\n }\n\n const docService = this.serviceProvider();\n assert(docService !== undefined, 0x2a7 /* \"Container is not attached\" */);\n\n let connection: IDocumentDeltaConnection | undefined;\n\n if (docService.policies?.storageOnly === true) {\n connection = new NoDeltaStream();\n this.setupNewSuccessfulConnection(connection, \"read\");\n assert(this.pendingConnection === undefined, 0x2b3 /* \"logic error\" */);\n return;\n }\n\n let delayMs = InitialReconnectDelayInMs;\n let connectRepeatCount = 0;\n const connectStartTime = performance.now();\n let lastError: any;\n\n const abortController = new AbortController();\n const abortSignal = abortController.signal;\n this.pendingConnection = { abort: () => { abortController.abort(); }, connectionMode: requestedMode };\n\n // This loop will keep trying to connect until successful, with a delay between each iteration.\n while (connection === undefined) {\n if (this.closed) {\n throw new Error(\"Attempting to connect a closed DeltaManager\");\n }\n if (abortSignal.aborted === true) {\n this.logger.sendTelemetryEvent({\n eventName: \"ConnectionAttemptCancelled\",\n attempts: connectRepeatCount,\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n connectionEstablished: false,\n });\n return;\n }\n connectRepeatCount++;\n\n try {\n this.client.mode = requestedMode;\n connection = await docService.connectToDeltaStream({ ...this.client, mode: requestedMode });\n\n if (connection.disposed) {\n // Nobody observed this connection, so drop it on the floor and retry.\n this.logger.sendTelemetryEvent({ eventName: \"ReceivedClosedConnection\" });\n connection = undefined;\n }\n } catch (origError: any) {\n if (typeof origError === \"object\" && origError !== null &&\n origError?.errorType === DeltaStreamConnectionForbiddenError.errorType) {\n connection = new NoDeltaStream();\n requestedMode = \"read\";\n break;\n }\n\n // Socket.io error when we connect to wrong socket, or hit some multiplexing bug\n if (!canRetryOnError(origError)) {\n const error = normalizeError(origError, { props: fatalConnectErrorProp });\n this.props.closeHandler(error);\n throw error;\n }\n\n // Since the error is retryable this will not log to the error table\n logNetworkFailure(\n this.logger,\n {\n attempts: connectRepeatCount,\n delay: delayMs, // milliseconds\n eventName: \"DeltaConnectionFailureToConnect\",\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n },\n origError);\n\n lastError = origError;\n\n const retryDelayFromError = getRetryDelayFromError(origError);\n delayMs = retryDelayFromError ?? Math.min(delayMs * 2, MaxReconnectDelayInMs);\n\n if (retryDelayFromError !== undefined) {\n this.props.reconnectionDelayHandler(retryDelayFromError, origError);\n }\n await waitForConnectedState(delayMs);\n }\n }\n\n // If we retried more than once, log an event about how long it took (this will not log to error table)\n if (connectRepeatCount > 1) {\n logNetworkFailure(\n this.logger,\n {\n eventName: \"MultipleDeltaConnectionFailures\",\n attempts: connectRepeatCount,\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n },\n lastError,\n );\n }\n\n // Check for abort signal after while loop as well\n if (abortSignal.aborted === true) {\n connection.dispose();\n this.logger.sendTelemetryEvent({\n eventName: \"ConnectionAttemptCancelled\",\n attempts: connectRepeatCount,\n duration: TelemetryLogger.formatTick(performance.now() - connectStartTime),\n connectionEstablished: true,\n });\n return;\n }\n\n this.setupNewSuccessfulConnection(connection, requestedMode);\n }\n\n /**\n * Start the connection. Any error should result in container being closed.\n * And report the error if it escapes for any reason.\n * @param args - The connection arguments\n */\n private triggerConnect(connectionMode: ConnectionMode) {\n // reconnect() has async await of waitForConnectedState(), and that causes potential race conditions\n // where we might already have a connection. If it were to happen, it's possible that we will connect\n // with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be\n // fine (if needed, reconnect flow will get triggered again). Places where new mode matters should encode it\n // directly in connectCore - see this.shouldJoinWrite() test as an example.\n // assert(this.connection === undefined, 0x239 /* \"called only in disconnected state\" */);\n\n if (this.reconnectMode !== ReconnectMode.Enabled) {\n return;\n }\n this.connect(connectionMode);\n }\n\n /**\n * Disconnect the current connection.\n * @param reason - Text description of disconnect reason to emit with disconnect event\n * @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect\n */\n private disconnectFromDeltaStream(reason: string): boolean {\n this.pendingReconnect = false;\n\n if (this.connection === undefined) {\n if (this.pendingConnection !== undefined) {\n this.cancelConnection();\n return true;\n }\n return false;\n }\n\n assert(this.pendingConnection === undefined, 0x27b /* \"reentrancy may result in incorrect behavior\" */);\n\n const connection = this.connection;\n // Avoid any re-entrancy - clear object reference\n this.connection = undefined;\n\n // Remove listeners first so we don't try to retrigger this flow accidentally through reconnectOnError\n connection.off(\"op\", this.opHandler);\n connection.off(\"signal\", this.props.signalHandler);\n connection.off(\"nack\", this.nackHandler);\n connection.off(\"disconnect\", this.disconnectHandlerInternal);\n connection.off(\"error\", this.errorHandler);\n connection.off(\"pong\", this.props.pongHandler);\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._outbound.pause();\n this._outbound.clear();\n this.props.disconnectHandler(reason);\n\n connection.dispose();\n\n this._connectionVerboseProps = {};\n\n return true;\n }\n\n /**\n * Cancel in-progress connection attempt.\n */\n private cancelConnection() {\n assert(this.pendingConnection !== undefined,\n 0x345 /* this.pendingConnection is undefined when trying to cancel */);\n this.pendingConnection.abort();\n this.pendingConnection = undefined;\n this.logger.sendTelemetryEvent({ eventName: \"ConnectionCancelReceived\" });\n }\n\n /**\n * Once we've successfully gotten a connection, we need to set up state, attach event listeners, and process\n * initial messages.\n * @param connection - The newly established connection\n */\n private setupNewSuccessfulConnection(connection: IDocumentDeltaConnection, requestedMode: ConnectionMode) {\n // Old connection should have been cleaned up before establishing a new one\n assert(this.connection === undefined, 0x0e6 /* \"old connection exists on new connection setup\" */);\n assert(!connection.disposed, 0x28a /* \"can't be disposed - Callers need to ensure that!\" */);\n\n this.pendingConnection = undefined;\n\n this.connection = connection;\n\n // Does information in scopes & mode matches?\n // If we asked for \"write\" and got \"read\", then file is read-only\n // But if we ask read, server can still give us write.\n const readonly = !connection.claims.scopes.includes(ScopeType.DocWrite);\n\n if (connection.mode !== requestedMode) {\n this.logger.sendTelemetryEvent({ eventName: \"ConnectionModeMismatch\", requestedMode, mode: connection.mode });\n }\n // This connection mode validation logic is moving to the driver layer in 0.44. These two asserts can be\n // removed after those packages have released and become ubiquitous.\n assert(requestedMode === \"read\" || readonly === (this.connectionMode === \"read\"),\n 0x0e7 /* \"claims/connectionMode mismatch\" */);\n assert(!readonly || this.connectionMode === \"read\", 0x0e8 /* \"readonly perf with write connection\" */);\n\n this.set_readonlyPermissions(readonly);\n\n if (this.closed) {\n // Raise proper events, Log telemetry event and close connection.\n this.disconnectFromDeltaStream(\"ConnectionManager already closed\");\n return;\n }\n\n this._outbound.resume();\n\n connection.on(\"op\", this.opHandler);\n connection.on(\"signal\", this.props.signalHandler);\n connection.on(\"nack\", this.nackHandler);\n connection.on(\"disconnect\", this.disconnectHandlerInternal);\n connection.on(\"error\", this.errorHandler);\n connection.on(\"pong\", this.props.pongHandler);\n\n // Initial messages are always sorted. However, due to early op handler installed by drivers and appending those\n // ops to initialMessages, resulting set is no longer sorted, which would result in client hitting storage to\n // fill in gap. We will recover by cancelling this request once we process remaining ops, but it's a waste that\n // we could avoid\n const initialMessages = connection.initialMessages.sort((a, b) => a.sequenceNumber - b.sequenceNumber);\n\n // Some storages may provide checkpointSequenceNumber to identify how far client is behind.\n let checkpointSequenceNumber = connection.checkpointSequenceNumber;\n\n this._connectionVerboseProps = {\n clientId: connection.clientId,\n mode: connection.mode,\n };\n\n // reset connection props\n this._connectionProps = {};\n\n if (connection.relayServiceAgent !== undefined) {\n this._connectionVerboseProps.relayServiceAgent = connection.relayServiceAgent;\n this._connectionProps.relayServiceAgent = connection.relayServiceAgent;\n }\n this._connectionProps.socketDocumentId = connection.claims.documentId;\n this._connectionProps.connectionMode = connection.mode;\n\n let last = -1;\n if (initialMessages.length !== 0) {\n this._connectionVerboseProps.connectionInitialOpsFrom = initialMessages[0].sequenceNumber;\n last = initialMessages[initialMessages.length - 1].sequenceNumber;\n this._connectionVerboseProps.connectionInitialOpsTo = last + 1;\n // Update knowledge of how far we are behind, before raising \"connect\" event\n // This is duplication of what incomingOpHandler() does, but we have to raise event before we get there,\n // so duplicating update logic here as well.\n if (checkpointSequenceNumber === undefined || checkpointSequenceNumber < last) {\n checkpointSequenceNumber = last;\n }\n }\n\n this.props.incomingOpHandler(\n initialMessages,\n this.connectFirstConnection ? \"InitialOps\" : \"ReconnectOps\");\n\n const details = ConnectionManager.detailsFromConnection(connection);\n details.checkpointSequenceNumber = checkpointSequenceNumber;\n this.props.connectHandler(details);\n\n this.connectFirstConnection = false;\n\n // Synthesize clear & join signals out of initialClients state.\n // This allows us to have single way to process signals, and makes it simpler to initialize\n // protocol in Container.\n const clearSignal: ISignalMessage = {\n clientId: null, // system message\n content: JSON.stringify({\n type: SignalType.Clear,\n }),\n };\n this.props.signalHandler(clearSignal);\n\n for (const priorClient of connection.initialClients ?? []) {\n const joinSignal: ISignalMessage = {\n clientId: null, // system signal\n content: JSON.stringify({\n type: SignalType.ClientJoin,\n content: priorClient, // ISignalClient\n }),\n };\n this.props.signalHandler(joinSignal);\n }\n\n // Unfortunately, there is no defined order between initialSignals (including join & leave signals)\n // and connection.initialClients. In practice, connection.initialSignals quite often contains join signal\n // for \"self\" and connection.initialClients does not contain \"self\", so we have to process them after\n // \"clear\" signal above.\n if (connection.initialSignals !== undefined) {\n for (const signal of connection.initialSignals) {\n this.props.signalHandler(signal);\n }\n }\n }\n\n /**\n * Disconnect the current connection and reconnect. Closes the container if it fails.\n * @param connection - The connection that wants to reconnect - no-op if it's different from this.connection\n * @param requestedMode - Read or write\n * @param error - Error reconnect information including whether or not to reconnect\n * @returns A promise that resolves when the connection is reestablished or we stop trying\n */\n private reconnectOnError(\n requestedMode: ConnectionMode,\n error: IAnyDriverError,\n ) {\n this.reconnect(\n requestedMode,\n error.message,\n error)\n .catch(this.props.closeHandler);\n }\n\n /**\n * Disconnect the current connection and reconnect.\n * @param connection - The connection that wants to reconnect - no-op if it's different from this.connection\n * @param requestedMode - Read or write\n * @param error - Error reconnect information including whether or not to reconnect\n * @returns A promise that resolves when the connection is reestablished or we stop trying\n */\n private async reconnect(\n requestedMode: ConnectionMode,\n disconnectMessage: string,\n error?: IAnyDriverError,\n ) {\n // We quite often get protocol errors before / after observing nack/disconnect\n // we do not want to run through same sequence twice.\n // If we're already disconnected/disconnecting it's not appropriate to call this again.\n assert(this.connection !== undefined, 0x0eb /* \"Missing connection for reconnect\" */);\n\n this.disconnectFromDeltaStream(disconnectMessage);\n\n // We will always trigger reconnect, even if canRetry is false.\n // Any truly fatal error state will result in container close upon attempted reconnect,\n // which is a preferable to closing abruptly when a live connection fails.\n if (error !== undefined && !error.canRetry) {\n this.logger.sendTelemetryEvent({\n eventName: \"reconnectingDespiteFatalError\",\n reconnectMode: this.reconnectMode,\n }, error);\n }\n\n if (this.reconnectMode === ReconnectMode.Never) {\n // Do not raise container error if we are closing just because we lost connection.\n // Those errors (like IdleDisconnect) would show up in telemetry dashboards and\n // are very misleading, as first initial reaction - some logic is broken.\n this.props.closeHandler();\n }\n\n // If closed then we can't reconnect\n if (this.closed || this.reconnectMode !== ReconnectMode.Enabled) {\n return;\n }\n\n const delayMs = getRetryDelayFromError(error);\n if (error !== undefined && delayMs !== undefined) {\n this.props.reconnectionDelayHandler(delayMs, error);\n await waitForConnectedState(delayMs);\n }\n\n this.triggerConnect(requestedMode);\n }\n\n public prepareMessageToSend(message: Omit<IDocumentMessage, \"clientSequenceNumber\">): IDocumentMessage | undefined {\n if (this.readonly === true) {\n assert(this.readOnlyInfo.readonly === true, 0x1f0 /* \"Unexpected mismatch in readonly\" */);\n const error = new GenericError(\"deltaManagerReadonlySubmit\", undefined /* error */, {\n readonly: this.readOnlyInfo.readonly,\n forcedReadonly: this.readOnlyInfo.forced,\n readonlyPermissions: this.readOnlyInfo.permissions,\n storageOnly: this.readOnlyInfo.storageOnly,\n });\n this.props.closeHandler(error);\n return undefined;\n }\n\n // reset clientSequenceNumber if we are using new clientId.\n // we keep info about old connection as long as possible to be able to account for all non-acked ops\n // that we pick up on next connection.\n assert(!!this.connection, 0x0e4 /* \"Lost old connection!\" */);\n if (this.lastSubmittedClientId !== this.connection?.clientId) {\n this.lastSubmittedClientId = this.connection?.clientId;\n this.clientSequenceNumber = 0;\n this.clientSequenceNumberObserved = 0;\n }\n\n if (!isRuntimeMessage(message)) {\n this.localOpsToIgnore++;\n } else {\n this.localOpsToIgnore = 0;\n }\n\n return {\n ...message,\n clientSequenceNumber: ++this.clientSequenceNumber,\n };\n }\n\n public submitSignal(content: any) {\n if (this.connection !== undefined) {\n this.connection.submitSignal(content);\n } else {\n this.logger.sendErrorEvent({ eventName: \"submitSignalDisconnected\" });\n }\n }\n\n public sendMessages(messages: IDocumentMessage[]) {\n assert(this.connected, 0x2b4 /* \"not connected on sending ops!\" */);\n // If connection is \"read\" or implicit \"read\" (got leave op for \"write\" connection),\n // then op can't make it through - we will get a nack if op is sent.\n // We can short-circuit this process.\n // Note that we also want nacks to be rare and be treated as catastrophic failures.\n // Be careful with reentrancy though - disconnected event should not be be raised in the\n // middle of the current workflow, but rather on clean stack!\n if (this.connectionMode === \"read\") {\n if (!this.pendingReconnect) {\n this.pendingReconnect = true;\n Promise.resolve().then(async () => {\n if (this.pendingReconnect) { // still valid?\n await this.reconnect(\n \"write\", // connectionMode\n \"Switch to write\", // message\n );\n }\n }).catch(() => { });\n }\n return;\n }\n\n assert(!this.pendingReconnect, 0x2b5 /* \"logic error\" */);\n\n this._outbound.push(messages);\n }\n\n public beforeProcessingIncomingOp(message: ISequencedDocumentMessage) {\n // if we have connection, and message is local, then we better treat is as local!\n assert(this.clientId !== message.clientId || this.lastSubmittedClientId === message.clientId,\n 0x0ee /* \"Not accounting local messages correctly\" */,\n );\n\n if (this.lastSubmittedClientId !== undefined && this.lastSubmittedClientId === message.clientId) {\n const clientSequenceNumber = message.clientSequenceNumber;\n\n assert(this.clientSequenceNumberObserved < clientSequenceNumber, 0x0ef /* \"client seq# not growing\" */);\n assert(clientSequenceNumber <= this.clientSequenceNumber,\n 0x0f0 /* \"Incoming local client seq# > generated by this client\" */);\n\n this.clientSequenceNumberObserved = clientSequenceNumber;\n }\n\n if (message.type === MessageType.ClientLeave) {\n const systemLeaveMessage = message as ISequencedDocumentSystemMessage;\n const clientId = JSON.parse(systemLeaveMessage.data) as string;\n if (clientId === this.clientId) {\n // We have been kicked out from quorum\n this.logger.sendPerformanceEvent({ eventName: \"ReadConnectionTransition\" });\n\n // Please see #8483 for more details on why maintaining connection further as is would not work.\n // Short story - connection properties are immutable, and many processes (consensus DDSes, summarizer)\n // assume that connection stays \"write\" connection until disconnect, and act accordingly, which may\n // not work well with de-facto \"read\" connection we are in after receiving own leave op on timeout.\n // Clients need to be able to transition to \"read\" state after some time of inactivity!\n // Note - this may close container!\n this.reconnect(\n \"read\", // connectionMode\n \"Switch to read\", // message\n ).catch((error) => {\n this.logger.sendErrorEvent({ eventName: \"SwitchToReadConnection\" }, error);\n });\n }\n }\n }\n\n private readonly opHandler = (documentId: string, messagesArg: ISequencedDocumentMessage[]) => {\n const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];\n this.props.incomingOpHandler(messages, \"opHandler\");\n };\n\n // Always connect in write mode after getting nacked.\n private readonly nackHandler = (documentId: string, messages: INack[]) => {\n const message = messages[0];\n if (this._readonlyPermissions === true) {\n this.props.closeHandler(createWriteError(\"writeOnReadOnlyDocument\", { driverVersion: undefined }));\n return;\n }\n\n const reconnectInfo = getNackReconnectInfo(message.content);\n\n // If the nack indicates we cannot retry, then close the container outright\n if (!reconnectInfo.canRetry) {\n this.props.closeHandler(reconnectInfo);\n return;\n }\n\n this.reconnectOnError(\n \"write\",\n reconnectInfo,\n );\n };\n\n // Connection mode is always read on disconnect/error unless the system mode was write.\n private readonly disconnectHandlerInternal = (disconnectReason: IAnyDriverError) => {\n // Note: we might get multiple disconnect calls on same socket, as early disconnect notification\n // (\"server_disconnect\", ODSP-specific) is mapped to \"disconnect\"\n this.reconnectOnError(\n this.defaultReconnectionMode,\n disconnectReason,\n );\n };\n\n private readonly errorHandler = (error: IAnyDriverError) => {\n this.reconnectOnError(\n this.defaultReconnectionMode,\n error,\n );\n };\n}\n"]}
|