@fluidframework/container-loader 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.225277
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 +18 -21
- package/.mocharc.js +12 -0
- package/CHANGELOG.md +364 -0
- package/README.md +152 -56
- package/api-extractor-esm.json +4 -0
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/container-loader.api.md +143 -0
- package/dist/{audience.js → audience.cjs} +15 -13
- package/dist/audience.cjs.map +1 -0
- package/dist/audience.d.ts +4 -6
- package/dist/audience.d.ts.map +1 -1
- package/dist/catchUpMonitor.cjs +43 -0
- package/dist/catchUpMonitor.cjs.map +1 -0
- package/dist/catchUpMonitor.d.ts +29 -0
- package/dist/catchUpMonitor.d.ts.map +1 -0
- package/dist/{connectionManager.js → connectionManager.cjs} +397 -240
- package/dist/connectionManager.cjs.map +1 -0
- package/dist/connectionManager.d.ts +23 -33
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/{connectionState.js → connectionState.cjs} +5 -7
- package/dist/connectionState.cjs.map +1 -0
- package/dist/connectionState.d.ts +3 -5
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionStateHandler.cjs +474 -0
- package/dist/connectionStateHandler.cjs.map +1 -0
- package/dist/connectionStateHandler.d.ts +127 -29
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/container-loader-alpha.d.ts +274 -0
- package/dist/container-loader-beta.d.ts +75 -0
- package/dist/container-loader-public.d.ts +75 -0
- package/dist/container-loader-untrimmed.d.ts +331 -0
- package/dist/container.cjs +1585 -0
- package/dist/container.cjs.map +1 -0
- package/dist/container.d.ts +227 -83
- package/dist/container.d.ts.map +1 -1
- package/dist/containerContext.cjs +74 -0
- package/dist/containerContext.cjs.map +1 -0
- package/dist/containerContext.d.ts +33 -59
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerStorageAdapter.cjs +234 -0
- package/dist/containerStorageAdapter.cjs.map +1 -0
- package/dist/containerStorageAdapter.d.ts +48 -23
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/{contracts.js → contracts.cjs} +5 -5
- package/dist/contracts.cjs.map +1 -0
- package/dist/contracts.d.ts +45 -17
- package/dist/contracts.d.ts.map +1 -1
- package/dist/debugLogger.cjs +101 -0
- package/dist/debugLogger.cjs.map +1 -0
- package/dist/debugLogger.d.ts +30 -0
- package/dist/debugLogger.d.ts.map +1 -0
- package/dist/{deltaManager.js → deltaManager.cjs} +379 -186
- package/dist/deltaManager.cjs.map +1 -0
- package/dist/deltaManager.d.ts +54 -18
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/{deltaQueue.js → deltaQueue.cjs} +29 -28
- package/dist/deltaQueue.cjs.map +1 -0
- package/dist/deltaQueue.d.ts +3 -4
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/disposal.cjs +25 -0
- package/dist/disposal.cjs.map +1 -0
- package/dist/disposal.d.ts +13 -0
- package/dist/disposal.d.ts.map +1 -0
- package/dist/error.cjs +32 -0
- package/dist/error.cjs.map +1 -0
- package/dist/error.d.ts +23 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/index.cjs +19 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/loader.cjs +148 -0
- package/dist/loader.cjs.map +1 -0
- package/dist/loader.d.ts +38 -19
- package/dist/loader.d.ts.map +1 -1
- package/dist/location-redirection-utilities/index.cjs +11 -0
- package/dist/location-redirection-utilities/index.cjs.map +1 -0
- package/dist/location-redirection-utilities/index.d.ts +6 -0
- package/dist/location-redirection-utilities/index.d.ts.map +1 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.cjs +53 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.cjs.map +1 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +24 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
- package/dist/{collabWindowTracker.js → noopHeuristic.cjs} +37 -39
- package/dist/noopHeuristic.cjs.map +1 -0
- package/dist/noopHeuristic.d.ts +23 -0
- package/dist/noopHeuristic.d.ts.map +1 -0
- package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
- package/dist/packageVersion.cjs.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/protocol.cjs +99 -0
- package/dist/protocol.cjs.map +1 -0
- package/dist/protocol.d.ts +38 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/{protocolTreeDocumentStorageService.js → protocolTreeDocumentStorageService.cjs} +8 -5
- package/dist/protocolTreeDocumentStorageService.cjs.map +1 -0
- package/dist/protocolTreeDocumentStorageService.d.ts +8 -4
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/quorum.cjs +16 -0
- package/dist/quorum.cjs.map +1 -0
- package/dist/quorum.d.ts +1 -14
- package/dist/quorum.d.ts.map +1 -1
- package/dist/{retriableDocumentStorageService.js → retriableDocumentStorageService.cjs} +36 -21
- package/dist/retriableDocumentStorageService.cjs.map +1 -0
- package/dist/retriableDocumentStorageService.d.ts +7 -5
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/{utils.js → utils.cjs} +52 -14
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.ts +34 -1
- package/dist/utils.d.ts.map +1 -1
- package/lib/{audience.d.ts → audience.d.mts} +5 -11
- package/lib/audience.d.mts.map +1 -0
- package/lib/{audience.js → audience.mjs} +15 -17
- package/lib/audience.mjs.map +1 -0
- package/lib/catchUpMonitor.d.mts +29 -0
- package/lib/catchUpMonitor.d.mts.map +1 -0
- package/lib/catchUpMonitor.mjs +39 -0
- package/lib/catchUpMonitor.mjs.map +1 -0
- package/lib/{connectionManager.d.ts → connectionManager.d.mts} +24 -34
- package/lib/connectionManager.d.mts.map +1 -0
- package/lib/{connectionManager.js → connectionManager.mjs} +378 -218
- package/lib/connectionManager.mjs.map +1 -0
- package/lib/{connectionState.d.ts → connectionState.d.mts} +4 -6
- package/lib/connectionState.d.mts.map +1 -0
- package/lib/{connectionState.js → connectionState.mjs} +4 -6
- package/lib/connectionState.mjs.map +1 -0
- package/lib/connectionStateHandler.d.mts +179 -0
- package/lib/connectionStateHandler.d.mts.map +1 -0
- package/lib/connectionStateHandler.mjs +469 -0
- package/lib/connectionStateHandler.mjs.map +1 -0
- package/lib/container-loader-alpha.d.mts +274 -0
- package/lib/container-loader-beta.d.mts +75 -0
- package/lib/container-loader-public.d.mts +75 -0
- package/lib/container-loader-untrimmed.d.mts +331 -0
- package/lib/container.d.mts +382 -0
- package/lib/container.d.mts.map +1 -0
- package/lib/container.mjs +1579 -0
- package/lib/container.mjs.map +1 -0
- package/lib/containerContext.d.mts +58 -0
- package/lib/containerContext.d.mts.map +1 -0
- package/lib/containerContext.mjs +70 -0
- package/lib/containerContext.mjs.map +1 -0
- package/lib/containerStorageAdapter.d.mts +73 -0
- package/lib/containerStorageAdapter.d.mts.map +1 -0
- package/lib/containerStorageAdapter.mjs +228 -0
- package/lib/containerStorageAdapter.mjs.map +1 -0
- package/lib/{contracts.d.ts → contracts.d.mts} +46 -18
- package/lib/contracts.d.mts.map +1 -0
- package/lib/{contracts.js → contracts.mjs} +4 -4
- package/lib/contracts.mjs.map +1 -0
- package/lib/debugLogger.d.mts +30 -0
- package/lib/debugLogger.d.mts.map +1 -0
- package/lib/debugLogger.mjs +93 -0
- package/lib/debugLogger.mjs.map +1 -0
- package/lib/{deltaManager.d.ts → deltaManager.d.mts} +55 -19
- package/lib/deltaManager.d.mts.map +1 -0
- package/lib/{deltaManager.js → deltaManager.mjs} +361 -165
- package/lib/deltaManager.mjs.map +1 -0
- package/lib/{deltaQueue.d.ts → deltaQueue.d.mts} +4 -5
- package/lib/deltaQueue.d.mts.map +1 -0
- package/lib/{deltaQueue.js → deltaQueue.mjs} +25 -24
- package/lib/deltaQueue.mjs.map +1 -0
- package/lib/disposal.d.mts +13 -0
- package/lib/disposal.d.mts.map +1 -0
- package/lib/disposal.mjs +21 -0
- package/lib/disposal.mjs.map +1 -0
- package/lib/error.d.mts +23 -0
- package/lib/error.d.mts.map +1 -0
- package/lib/error.mjs +28 -0
- package/lib/error.mjs.map +1 -0
- package/lib/index.d.mts +11 -0
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +10 -0
- package/lib/index.mjs.map +1 -0
- package/lib/{loader.d.ts → loader.d.mts} +40 -21
- package/lib/loader.d.mts.map +1 -0
- package/lib/loader.mjs +143 -0
- package/lib/loader.mjs.map +1 -0
- package/lib/location-redirection-utilities/index.d.mts +6 -0
- package/lib/location-redirection-utilities/index.d.mts.map +1 -0
- package/lib/location-redirection-utilities/index.mjs +6 -0
- package/lib/location-redirection-utilities/index.mjs.map +1 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.mts +24 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.mts.map +1 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.mjs +48 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.mjs.map +1 -0
- package/lib/noopHeuristic.d.mts +23 -0
- package/lib/noopHeuristic.d.mts.map +1 -0
- package/lib/{collabWindowTracker.js → noopHeuristic.mjs} +33 -35
- package/lib/noopHeuristic.mjs.map +1 -0
- package/lib/{packageVersion.d.ts → packageVersion.d.mts} +2 -2
- package/lib/packageVersion.d.mts.map +1 -0
- package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
- package/lib/packageVersion.mjs.map +1 -0
- package/lib/protocol.d.mts +38 -0
- package/lib/protocol.d.mts.map +1 -0
- package/lib/protocol.mjs +94 -0
- package/lib/protocol.mjs.map +1 -0
- package/lib/{protocolTreeDocumentStorageService.d.ts → protocolTreeDocumentStorageService.d.mts} +9 -5
- package/lib/protocolTreeDocumentStorageService.d.mts.map +1 -0
- package/lib/{protocolTreeDocumentStorageService.js → protocolTreeDocumentStorageService.mjs} +8 -5
- package/lib/protocolTreeDocumentStorageService.mjs.map +1 -0
- package/lib/quorum.d.mts +4 -0
- package/lib/quorum.d.mts.map +1 -0
- package/lib/quorum.mjs +12 -0
- package/lib/quorum.mjs.map +1 -0
- package/lib/{retriableDocumentStorageService.d.ts → retriableDocumentStorageService.d.mts} +8 -6
- package/lib/retriableDocumentStorageService.d.mts.map +1 -0
- package/lib/{retriableDocumentStorageService.js → retriableDocumentStorageService.mjs} +35 -20
- package/lib/retriableDocumentStorageService.mjs.map +1 -0
- package/lib/utils.d.mts +67 -0
- package/lib/utils.d.mts.map +1 -0
- package/lib/{utils.js → utils.mjs} +47 -11
- package/lib/utils.mjs.map +1 -0
- package/package.json +189 -69
- package/prettier.config.cjs +8 -0
- package/src/audience.ts +59 -49
- package/src/catchUpMonitor.ts +61 -0
- package/src/connectionManager.ts +1154 -910
- package/src/connectionState.ts +22 -25
- package/src/connectionStateHandler.ts +689 -319
- package/src/container.ts +2476 -1792
- package/src/containerContext.ts +98 -330
- package/src/containerStorageAdapter.ts +301 -105
- package/src/contracts.ts +184 -146
- package/src/debugLogger.ts +123 -0
- package/src/deltaManager.ts +1165 -900
- package/src/deltaQueue.ts +156 -152
- package/src/disposal.ts +25 -0
- package/src/error.ts +44 -0
- package/src/index.ts +14 -15
- package/src/loader.ts +356 -427
- package/src/location-redirection-utilities/index.ts +9 -0
- package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +61 -0
- package/src/noopHeuristic.ts +107 -0
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +150 -0
- package/src/protocolTreeDocumentStorageService.ts +35 -35
- package/src/quorum.ts +11 -50
- package/src/retriableDocumentStorageService.ts +135 -95
- package/src/utils.ts +159 -86
- package/tsc-multi.test.json +4 -0
- package/tsconfig.json +10 -12
- package/dist/audience.js.map +0 -1
- package/dist/collabWindowTracker.d.ts +0 -19
- package/dist/collabWindowTracker.d.ts.map +0 -1
- package/dist/collabWindowTracker.js.map +0 -1
- package/dist/connectionManager.js.map +0 -1
- package/dist/connectionState.js.map +0 -1
- package/dist/connectionStateHandler.js +0 -280
- package/dist/connectionStateHandler.js.map +0 -1
- package/dist/container.js +0 -1284
- package/dist/container.js.map +0 -1
- package/dist/containerContext.js +0 -217
- package/dist/containerContext.js.map +0 -1
- package/dist/containerStorageAdapter.js +0 -104
- package/dist/containerStorageAdapter.js.map +0 -1
- package/dist/contracts.js.map +0 -1
- package/dist/deltaManager.js.map +0 -1
- package/dist/deltaManagerProxy.d.ts +0 -54
- package/dist/deltaManagerProxy.d.ts.map +0 -1
- package/dist/deltaManagerProxy.js +0 -115
- package/dist/deltaManagerProxy.js.map +0 -1
- package/dist/deltaQueue.js.map +0 -1
- package/dist/index.js +0 -16
- package/dist/index.js.map +0 -1
- package/dist/loader.js +0 -241
- package/dist/loader.js.map +0 -1
- package/dist/packageVersion.js.map +0 -1
- package/dist/protocolTreeDocumentStorageService.js.map +0 -1
- package/dist/quorum.js +0 -44
- package/dist/quorum.js.map +0 -1
- package/dist/retriableDocumentStorageService.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/lib/audience.d.ts.map +0 -1
- package/lib/audience.js.map +0 -1
- package/lib/collabWindowTracker.d.ts +0 -19
- package/lib/collabWindowTracker.d.ts.map +0 -1
- package/lib/collabWindowTracker.js.map +0 -1
- package/lib/connectionManager.d.ts.map +0 -1
- package/lib/connectionManager.js.map +0 -1
- package/lib/connectionState.d.ts.map +0 -1
- package/lib/connectionState.js.map +0 -1
- package/lib/connectionStateHandler.d.ts +0 -81
- package/lib/connectionStateHandler.d.ts.map +0 -1
- package/lib/connectionStateHandler.js +0 -276
- package/lib/connectionStateHandler.js.map +0 -1
- package/lib/container.d.ts +0 -238
- package/lib/container.d.ts.map +0 -1
- package/lib/container.js +0 -1276
- package/lib/container.js.map +0 -1
- package/lib/containerContext.d.ts +0 -84
- package/lib/containerContext.d.ts.map +0 -1
- package/lib/containerContext.js +0 -213
- package/lib/containerContext.js.map +0 -1
- package/lib/containerStorageAdapter.d.ts +0 -48
- package/lib/containerStorageAdapter.d.ts.map +0 -1
- package/lib/containerStorageAdapter.js +0 -99
- package/lib/containerStorageAdapter.js.map +0 -1
- package/lib/contracts.d.ts.map +0 -1
- package/lib/contracts.js.map +0 -1
- package/lib/deltaManager.d.ts.map +0 -1
- package/lib/deltaManager.js.map +0 -1
- package/lib/deltaManagerProxy.d.ts +0 -54
- package/lib/deltaManagerProxy.d.ts.map +0 -1
- package/lib/deltaManagerProxy.js +0 -110
- package/lib/deltaManagerProxy.js.map +0 -1
- package/lib/deltaQueue.d.ts.map +0 -1
- package/lib/deltaQueue.js.map +0 -1
- package/lib/index.d.ts +0 -8
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -8
- package/lib/index.js.map +0 -1
- package/lib/loader.d.ts.map +0 -1
- package/lib/loader.js +0 -236
- package/lib/loader.js.map +0 -1
- package/lib/packageVersion.d.ts.map +0 -1
- package/lib/packageVersion.js.map +0 -1
- package/lib/protocolTreeDocumentStorageService.d.ts.map +0 -1
- package/lib/protocolTreeDocumentStorageService.js.map +0 -1
- package/lib/quorum.d.ts +0 -21
- package/lib/quorum.d.ts.map +0 -1
- package/lib/quorum.js +0 -38
- package/lib/quorum.js.map +0 -1
- package/lib/retriableDocumentStorageService.d.ts.map +0 -1
- package/lib/retriableDocumentStorageService.js.map +0 -1
- package/lib/utils.d.ts +0 -34
- package/lib/utils.d.ts.map +0 -1
- package/lib/utils.js.map +0 -1
- package/src/collabWindowTracker.ts +0 -102
- package/src/deltaManagerProxy.ts +0 -158
- package/tsconfig.esnext.json +0 -7
|
@@ -3,21 +3,21 @@
|
|
|
3
3
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
-
};
|
|
9
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
7
|
exports.ConnectionManager = void 0;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
8
|
+
const core_interfaces_1 = require("@fluidframework/core-interfaces");
|
|
9
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
10
|
+
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
11
|
+
const driver_definitions_1 = require("@fluidframework/driver-definitions");
|
|
14
12
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
15
13
|
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
16
14
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
17
|
-
const contracts_1 = require("./contracts");
|
|
18
|
-
const deltaQueue_1 = require("./deltaQueue");
|
|
19
|
-
const
|
|
20
|
-
const
|
|
15
|
+
const contracts_1 = require("./contracts.cjs");
|
|
16
|
+
const deltaQueue_1 = require("./deltaQueue.cjs");
|
|
17
|
+
const protocol_1 = require("./protocol.cjs");
|
|
18
|
+
const utils_1 = require("./utils.cjs");
|
|
19
|
+
// We double this value in first try in when we calculate time to wait for in "calculateMaxWaitTime" function.
|
|
20
|
+
const InitialReconnectDelayInMs = 500;
|
|
21
21
|
const DefaultChunkSize = 16 * 1024;
|
|
22
22
|
const fatalConnectErrorProp = { fatalConnectError: true };
|
|
23
23
|
function getNackReconnectInfo(nackContent) {
|
|
@@ -30,10 +30,25 @@ function getNackReconnectInfo(nackContent) {
|
|
|
30
30
|
* Implementation of IDocumentDeltaConnection that does not support submitting
|
|
31
31
|
* or receiving ops. Used in storage-only mode.
|
|
32
32
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const clientNoDeltaStream = {
|
|
34
|
+
mode: "read",
|
|
35
|
+
details: { capabilities: { interactive: true } },
|
|
36
|
+
permission: [],
|
|
37
|
+
user: { id: "storage-only client" },
|
|
38
|
+
scopes: [],
|
|
39
|
+
};
|
|
40
|
+
const clientIdNoDeltaStream = "storage-only client";
|
|
41
|
+
class NoDeltaStream extends client_utils_1.TypedEventEmitter {
|
|
42
|
+
/**
|
|
43
|
+
* Connection which is not connected to socket.
|
|
44
|
+
* @param storageOnlyReason - Reason on why the connection to delta stream is not allowed.
|
|
45
|
+
* @param readonlyConnectionReason - reason/error if any which lead to using NoDeltaStream.
|
|
46
|
+
*/
|
|
47
|
+
constructor(storageOnlyReason, readonlyConnectionReason) {
|
|
48
|
+
super();
|
|
49
|
+
this.storageOnlyReason = storageOnlyReason;
|
|
50
|
+
this.readonlyConnectionReason = readonlyConnectionReason;
|
|
51
|
+
this.clientId = clientIdNoDeltaStream;
|
|
37
52
|
this.claims = {
|
|
38
53
|
scopes: [protocol_definitions_1.ScopeType.DocRead],
|
|
39
54
|
};
|
|
@@ -43,11 +58,12 @@ class NoDeltaStream extends common_utils_1.TypedEventEmitter {
|
|
|
43
58
|
this.version = "";
|
|
44
59
|
this.initialMessages = [];
|
|
45
60
|
this.initialSignals = [];
|
|
46
|
-
this.initialClients = [
|
|
61
|
+
this.initialClients = [
|
|
62
|
+
{ client: clientNoDeltaStream, clientId: clientIdNoDeltaStream },
|
|
63
|
+
];
|
|
47
64
|
this.serviceConfiguration = {
|
|
48
65
|
maxMessageSize: 0,
|
|
49
66
|
blockSize: 0,
|
|
50
|
-
summary: undefined,
|
|
51
67
|
};
|
|
52
68
|
this.checkpointSequenceNumber = undefined;
|
|
53
69
|
this._disposed = false;
|
|
@@ -66,85 +82,49 @@ class NoDeltaStream extends common_utils_1.TypedEventEmitter {
|
|
|
66
82
|
content: { message: "Cannot submit signal with storage-only connection", code: 403 },
|
|
67
83
|
});
|
|
68
84
|
}
|
|
69
|
-
get disposed() {
|
|
70
|
-
|
|
85
|
+
get disposed() {
|
|
86
|
+
return this._disposed;
|
|
87
|
+
}
|
|
88
|
+
dispose() {
|
|
89
|
+
this._disposed = true;
|
|
90
|
+
}
|
|
71
91
|
}
|
|
92
|
+
function isNoDeltaStreamConnection(connection) {
|
|
93
|
+
return connection instanceof NoDeltaStream;
|
|
94
|
+
}
|
|
95
|
+
const waitForOnline = async () => {
|
|
96
|
+
// Only wait if we have a strong signal that we're offline - otherwise assume we're online.
|
|
97
|
+
if (globalThis.navigator?.onLine === false && globalThis.addEventListener !== undefined) {
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
const resolveAndRemoveListener = () => {
|
|
100
|
+
resolve();
|
|
101
|
+
globalThis.removeEventListener("online", resolveAndRemoveListener);
|
|
102
|
+
};
|
|
103
|
+
globalThis.addEventListener("online", resolveAndRemoveListener);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
72
107
|
/**
|
|
73
108
|
* Implementation of IConnectionManager, used by Container class
|
|
74
|
-
* Implements constant connectivity to relay service, by reconnecting in case of
|
|
75
|
-
* Exposes various controls to
|
|
109
|
+
* Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
|
|
110
|
+
* Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
|
|
76
111
|
*/
|
|
77
112
|
class ConnectionManager {
|
|
78
|
-
|
|
79
|
-
this.
|
|
80
|
-
this.client = client;
|
|
81
|
-
this.logger = logger;
|
|
82
|
-
this.props = props;
|
|
83
|
-
/** tracks host requiring read-only mode. */
|
|
84
|
-
this._forceReadonly = false;
|
|
85
|
-
/** True if there is pending (async) reconnection from "read" to "write" */
|
|
86
|
-
this.pendingReconnect = false;
|
|
87
|
-
this.clientSequenceNumber = 0;
|
|
88
|
-
this.clientSequenceNumberObserved = 0;
|
|
89
|
-
/** Counts the number of noops sent by the client which may not be acked. */
|
|
90
|
-
this.trailingNoopCount = 0;
|
|
91
|
-
this.connectFirstConnection = true;
|
|
92
|
-
this._connectionVerboseProps = {};
|
|
93
|
-
this._connectionProps = {};
|
|
94
|
-
this.closed = false;
|
|
95
|
-
this.opHandler = (documentId, messagesArg) => {
|
|
96
|
-
const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
|
|
97
|
-
this.props.incomingOpHandler(messages, "opHandler");
|
|
98
|
-
};
|
|
99
|
-
// Always connect in write mode after getting nacked.
|
|
100
|
-
this.nackHandler = (documentId, messages) => {
|
|
101
|
-
const message = messages[0];
|
|
102
|
-
if (this._readonlyPermissions === true) {
|
|
103
|
-
this.props.closeHandler((0, driver_utils_1.createWriteError)("writeOnReadOnlyDocument", { driverVersion: undefined }));
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const reconnectInfo = getNackReconnectInfo(message.content);
|
|
107
|
-
// If the nack indicates we cannot retry, then close the container outright
|
|
108
|
-
if (!reconnectInfo.canRetry) {
|
|
109
|
-
this.props.closeHandler(reconnectInfo);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
this.reconnectOnError("write", reconnectInfo);
|
|
113
|
-
};
|
|
114
|
-
// Connection mode is always read on disconnect/error unless the system mode was write.
|
|
115
|
-
this.disconnectHandlerInternal = (disconnectReason) => {
|
|
116
|
-
// Note: we might get multiple disconnect calls on same socket, as early disconnect notification
|
|
117
|
-
// ("server_disconnect", ODSP-specific) is mapped to "disconnect"
|
|
118
|
-
this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
|
|
119
|
-
};
|
|
120
|
-
this.errorHandler = (error) => {
|
|
121
|
-
this.reconnectOnError(this.defaultReconnectionMode, error);
|
|
122
|
-
};
|
|
123
|
-
this.clientDetails = this.client.details;
|
|
124
|
-
this.defaultReconnectionMode = this.client.mode;
|
|
125
|
-
this._reconnectMode = reconnectAllowed ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Never;
|
|
126
|
-
// Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
|
|
127
|
-
// within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
|
|
128
|
-
this._outbound = new deltaQueue_1.DeltaQueue((messages) => {
|
|
129
|
-
if (this.connection === undefined) {
|
|
130
|
-
throw new Error("Attempted to submit an outbound message without connection");
|
|
131
|
-
}
|
|
132
|
-
this.connection.submit(messages);
|
|
133
|
-
});
|
|
134
|
-
this._outbound.on("error", (error) => {
|
|
135
|
-
this.props.closeHandler((0, telemetry_utils_1.normalizeError)(error));
|
|
136
|
-
});
|
|
113
|
+
get connectionVerboseProps() {
|
|
114
|
+
return this._connectionVerboseProps;
|
|
137
115
|
}
|
|
138
|
-
get connectionVerboseProps() { return this._connectionVerboseProps; }
|
|
139
116
|
/**
|
|
140
117
|
* The current connection mode, initially read.
|
|
141
118
|
*/
|
|
142
119
|
get connectionMode() {
|
|
143
|
-
|
|
144
|
-
|
|
120
|
+
return this.connection?.mode ?? "read";
|
|
121
|
+
}
|
|
122
|
+
get connected() {
|
|
123
|
+
return this.connection !== undefined;
|
|
124
|
+
}
|
|
125
|
+
get clientId() {
|
|
126
|
+
return this.connection?.clientId;
|
|
145
127
|
}
|
|
146
|
-
get connected() { return this.connection !== undefined; }
|
|
147
|
-
get clientId() { var _a; return (_a = this.connection) === null || _a === void 0 ? void 0 : _a.clientId; }
|
|
148
128
|
/**
|
|
149
129
|
* Automatic reconnecting enabled or disabled.
|
|
150
130
|
* If set to Never, then reconnecting will never be allowed.
|
|
@@ -153,8 +133,7 @@ class ConnectionManager {
|
|
|
153
133
|
return this._reconnectMode;
|
|
154
134
|
}
|
|
155
135
|
get maxMessageSize() {
|
|
156
|
-
|
|
157
|
-
return (_c = (_b = (_a = this.connection) === null || _a === void 0 ? void 0 : _a.serviceConfiguration) === null || _b === void 0 ? void 0 : _b.maxMessageSize) !== null && _c !== void 0 ? _c : DefaultChunkSize;
|
|
136
|
+
return this.connection?.serviceConfiguration?.maxMessageSize ?? DefaultChunkSize;
|
|
158
137
|
}
|
|
159
138
|
get version() {
|
|
160
139
|
if (this.connection === undefined) {
|
|
@@ -163,12 +142,10 @@ class ConnectionManager {
|
|
|
163
142
|
return this.connection.version;
|
|
164
143
|
}
|
|
165
144
|
get serviceConfiguration() {
|
|
166
|
-
|
|
167
|
-
return (_a = this.connection) === null || _a === void 0 ? void 0 : _a.serviceConfiguration;
|
|
145
|
+
return this.connection?.serviceConfiguration;
|
|
168
146
|
}
|
|
169
147
|
get scopes() {
|
|
170
|
-
|
|
171
|
-
return (_a = this.connection) === null || _a === void 0 ? void 0 : _a.claims.scopes;
|
|
148
|
+
return this.connection?.claims.scopes;
|
|
172
149
|
}
|
|
173
150
|
get outbound() {
|
|
174
151
|
return this._outbound;
|
|
@@ -176,20 +153,31 @@ class ConnectionManager {
|
|
|
176
153
|
/**
|
|
177
154
|
* Returns set of props that can be logged in telemetry that provide some insights / statistics
|
|
178
155
|
* about current or last connection (if there is no connection at the moment)
|
|
179
|
-
|
|
156
|
+
*/
|
|
180
157
|
get connectionProps() {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return Object.assign(Object.assign({}, this._connectionProps), {
|
|
158
|
+
return this.connection !== undefined
|
|
159
|
+
? this._connectionProps
|
|
160
|
+
: {
|
|
161
|
+
...this._connectionProps,
|
|
186
162
|
// Report how many ops this client sent in last disconnected session
|
|
187
|
-
sentOps: this.clientSequenceNumber
|
|
188
|
-
|
|
163
|
+
sentOps: this.clientSequenceNumber,
|
|
164
|
+
};
|
|
189
165
|
}
|
|
190
166
|
shouldJoinWrite() {
|
|
191
167
|
// We don't have to wait for ack for topmost NoOps. So subtract those.
|
|
192
|
-
|
|
168
|
+
const outstandingOps = this.clientSequenceNumberObserved < this.clientSequenceNumber - this.localOpsToIgnore;
|
|
169
|
+
// Previous behavior was to force write mode here only when there are outstanding ops (besides
|
|
170
|
+
// no-ops). The dirty signal from runtime should provide the same behavior, but also support
|
|
171
|
+
// stashed ops that weren't submitted to container layer yet. For safety, we want to retain the
|
|
172
|
+
// same behavior whenever dirty is false.
|
|
173
|
+
const isDirty = this.containerDirty();
|
|
174
|
+
if (outstandingOps !== isDirty) {
|
|
175
|
+
this.logger.sendTelemetryEvent({
|
|
176
|
+
eventName: "DesiredConnectionModeMismatch",
|
|
177
|
+
details: JSON.stringify({ outstandingOps, isDirty }),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return outstandingOps || isDirty;
|
|
193
181
|
}
|
|
194
182
|
/**
|
|
195
183
|
* Tells if container is in read-only mode.
|
|
@@ -201,82 +189,141 @@ class ConnectionManager {
|
|
|
201
189
|
* and do not know if user has write access to a file.
|
|
202
190
|
*/
|
|
203
191
|
get readonly() {
|
|
204
|
-
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
return this._readonlyPermissions;
|
|
192
|
+
return this.readOnlyInfo.readonly;
|
|
208
193
|
}
|
|
209
194
|
get readOnlyInfo() {
|
|
210
|
-
|
|
195
|
+
let storageOnly = false;
|
|
196
|
+
let storageOnlyReason;
|
|
197
|
+
if (isNoDeltaStreamConnection(this.connection)) {
|
|
198
|
+
storageOnly = true;
|
|
199
|
+
storageOnlyReason = this.connection.storageOnlyReason;
|
|
200
|
+
}
|
|
211
201
|
if (storageOnly || this._forceReadonly || this._readonlyPermissions === true) {
|
|
212
202
|
return {
|
|
213
203
|
readonly: true,
|
|
214
204
|
forced: this._forceReadonly,
|
|
215
205
|
permissions: this._readonlyPermissions,
|
|
216
206
|
storageOnly,
|
|
207
|
+
storageOnlyReason,
|
|
217
208
|
};
|
|
218
209
|
}
|
|
219
210
|
return { readonly: this._readonlyPermissions };
|
|
220
211
|
}
|
|
221
|
-
static detailsFromConnection(connection) {
|
|
212
|
+
static detailsFromConnection(connection, reason) {
|
|
222
213
|
return {
|
|
223
214
|
claims: connection.claims,
|
|
224
215
|
clientId: connection.clientId,
|
|
225
|
-
existing: connection.existing,
|
|
226
216
|
checkpointSequenceNumber: connection.checkpointSequenceNumber,
|
|
227
|
-
get initialClients() {
|
|
217
|
+
get initialClients() {
|
|
218
|
+
return connection.initialClients;
|
|
219
|
+
},
|
|
228
220
|
mode: connection.mode,
|
|
229
221
|
serviceConfiguration: connection.serviceConfiguration,
|
|
230
222
|
version: connection.version,
|
|
223
|
+
reason,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
constructor(serviceProvider, containerDirty, client, reconnectAllowed, logger, props) {
|
|
227
|
+
this.serviceProvider = serviceProvider;
|
|
228
|
+
this.containerDirty = containerDirty;
|
|
229
|
+
this.client = client;
|
|
230
|
+
this.logger = logger;
|
|
231
|
+
this.props = props;
|
|
232
|
+
/** tracks host requiring read-only mode. */
|
|
233
|
+
this._forceReadonly = false;
|
|
234
|
+
/** True if there is pending (async) reconnection from "read" to "write" */
|
|
235
|
+
this.pendingReconnect = false;
|
|
236
|
+
this.clientSequenceNumber = 0;
|
|
237
|
+
this.clientSequenceNumberObserved = 0;
|
|
238
|
+
/** Counts the number of non-runtime ops sent by the client which may not be acked. */
|
|
239
|
+
this.localOpsToIgnore = 0;
|
|
240
|
+
this.connectFirstConnection = true;
|
|
241
|
+
this._connectionVerboseProps = {};
|
|
242
|
+
this._connectionProps = {};
|
|
243
|
+
this._disposed = false;
|
|
244
|
+
this.opHandler = (documentId, messagesArg) => {
|
|
245
|
+
const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
|
|
246
|
+
this.props.incomingOpHandler(messages, "opHandler");
|
|
247
|
+
};
|
|
248
|
+
this.signalHandler = (signalsArg) => {
|
|
249
|
+
const signals = Array.isArray(signalsArg) ? signalsArg : [signalsArg];
|
|
250
|
+
this.props.signalHandler(signals);
|
|
251
|
+
};
|
|
252
|
+
// Always connect in write mode after getting nacked.
|
|
253
|
+
this.nackHandler = (documentId, messages) => {
|
|
254
|
+
const message = messages[0];
|
|
255
|
+
if (this._readonlyPermissions === true) {
|
|
256
|
+
this.props.closeHandler((0, driver_utils_1.createWriteError)("writeOnReadOnlyDocument", { driverVersion: undefined }));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const reconnectInfo = getNackReconnectInfo(message.content);
|
|
260
|
+
// If the nack indicates we cannot retry, then close the container outright
|
|
261
|
+
if (!reconnectInfo.canRetry) {
|
|
262
|
+
this.props.closeHandler(reconnectInfo);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
this.reconnectOnError("write", reconnectInfo);
|
|
231
266
|
};
|
|
267
|
+
// Connection mode is always read on disconnect/error unless the system mode was write.
|
|
268
|
+
this.disconnectHandlerInternal = (disconnectReason) => {
|
|
269
|
+
// Note: we might get multiple disconnect calls on same socket, as early disconnect notification
|
|
270
|
+
// ("server_disconnect", ODSP-specific) is mapped to "disconnect"
|
|
271
|
+
this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
|
|
272
|
+
};
|
|
273
|
+
this.errorHandler = (error) => {
|
|
274
|
+
this.reconnectOnError(this.defaultReconnectionMode, error);
|
|
275
|
+
};
|
|
276
|
+
this.clientDetails = this.client.details;
|
|
277
|
+
this.defaultReconnectionMode = this.client.mode;
|
|
278
|
+
this._reconnectMode = reconnectAllowed ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Never;
|
|
279
|
+
// Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
|
|
280
|
+
// within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
|
|
281
|
+
this._outbound = new deltaQueue_1.DeltaQueue((messages) => {
|
|
282
|
+
if (this.connection === undefined) {
|
|
283
|
+
throw new Error("Attempted to submit an outbound message without connection");
|
|
284
|
+
}
|
|
285
|
+
this.connection.submit(messages);
|
|
286
|
+
});
|
|
287
|
+
this._outbound.on("error", (error) => {
|
|
288
|
+
this.props.closeHandler((0, telemetry_utils_1.normalizeError)(error));
|
|
289
|
+
});
|
|
232
290
|
}
|
|
233
|
-
dispose(error) {
|
|
234
|
-
if (this.
|
|
291
|
+
dispose(error, switchToReadonly = true) {
|
|
292
|
+
if (this._disposed) {
|
|
235
293
|
return;
|
|
236
294
|
}
|
|
237
|
-
this.
|
|
238
|
-
this.pendingConnection = undefined;
|
|
295
|
+
this._disposed = true;
|
|
239
296
|
// Ensure that things like triggerConnect() will short circuit
|
|
240
297
|
this._reconnectMode = contracts_1.ReconnectMode.Never;
|
|
241
298
|
this._outbound.clear();
|
|
242
|
-
const disconnectReason =
|
|
243
|
-
|
|
244
|
-
|
|
299
|
+
const disconnectReason = {
|
|
300
|
+
text: "Closing DeltaManager",
|
|
301
|
+
error,
|
|
302
|
+
};
|
|
303
|
+
const oldReadonlyValue = this.readonly;
|
|
245
304
|
// This raises "disconnect" event if we have active connection.
|
|
246
305
|
this.disconnectFromDeltaStream(disconnectReason);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
306
|
+
if (switchToReadonly) {
|
|
307
|
+
// Notify everyone we are in read-only state.
|
|
308
|
+
// Useful for data stores in case we hit some critical error,
|
|
309
|
+
// to switch to a mode where user edits are not accepted
|
|
310
|
+
this.set_readonlyPermissions(true, oldReadonlyValue, disconnectReason);
|
|
311
|
+
}
|
|
251
312
|
}
|
|
252
313
|
/**
|
|
253
314
|
* Enables or disables automatic reconnecting.
|
|
254
315
|
* Will throw an error if reconnectMode set to Never.
|
|
255
|
-
|
|
256
|
-
setAutoReconnect(mode) {
|
|
257
|
-
(0,
|
|
316
|
+
*/
|
|
317
|
+
setAutoReconnect(mode, reason) {
|
|
318
|
+
(0, core_utils_1.assert)(mode !== contracts_1.ReconnectMode.Never && this._reconnectMode !== contracts_1.ReconnectMode.Never, 0x278 /* "API is not supported for non-connecting or closed container" */);
|
|
258
319
|
this._reconnectMode = mode;
|
|
259
320
|
if (mode !== contracts_1.ReconnectMode.Enabled) {
|
|
260
321
|
// immediately disconnect - do not rely on service eventually dropping connection.
|
|
261
|
-
this.disconnectFromDeltaStream(
|
|
322
|
+
this.disconnectFromDeltaStream(reason);
|
|
262
323
|
}
|
|
263
324
|
}
|
|
264
325
|
/**
|
|
265
|
-
*
|
|
266
|
-
* Hosts may have read only views, indicating to data stores that no edits are allowed.
|
|
267
|
-
* This is independent from this._readonlyPermissions (permissions) and this.connectionMode
|
|
268
|
-
* (server can return "write" mode even when asked for "read")
|
|
269
|
-
* Leveraging same "readonly" event as runtime & data stores should behave the same in such case
|
|
270
|
-
* as in read-only permissions.
|
|
271
|
-
* But this.active can be used by some DDSes to figure out if ops can be sent
|
|
272
|
-
* (for example, read-only view still participates in code proposals / upgrades decisions)
|
|
273
|
-
*
|
|
274
|
-
* Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour
|
|
275
|
-
* the readonly flag. If ops are generated, they will accumulate locally and not be sent. If
|
|
276
|
-
* there are pending in the outbound queue, it will stop sending until force readonly is
|
|
277
|
-
* cleared.
|
|
278
|
-
*
|
|
279
|
-
* @param readonly - set or clear force readonly.
|
|
326
|
+
* {@inheritDoc Container.forceReadonly}
|
|
280
327
|
*/
|
|
281
328
|
forceReadonly(readonly) {
|
|
282
329
|
if (readonly !== this._forceReadonly) {
|
|
@@ -289,7 +336,7 @@ class ConnectionManager {
|
|
|
289
336
|
this._forceReadonly = readonly;
|
|
290
337
|
if (oldValue !== this.readonly) {
|
|
291
338
|
if (this._reconnectMode === contracts_1.ReconnectMode.Never) {
|
|
292
|
-
throw new
|
|
339
|
+
throw new telemetry_utils_1.UsageError("API is not supported for non-connecting or closed container");
|
|
293
340
|
}
|
|
294
341
|
let reconnect = false;
|
|
295
342
|
if (this.readonly === true) {
|
|
@@ -301,42 +348,40 @@ class ConnectionManager {
|
|
|
301
348
|
// host logic error.
|
|
302
349
|
this.logger.sendErrorEvent({ eventName: "ForceReadonlyPendingChanged" });
|
|
303
350
|
}
|
|
304
|
-
reconnect = this.disconnectFromDeltaStream("Force readonly");
|
|
351
|
+
reconnect = this.disconnectFromDeltaStream({ text: "Force readonly" });
|
|
305
352
|
}
|
|
306
353
|
this.props.readonlyChangeHandler(this.readonly);
|
|
307
354
|
if (reconnect) {
|
|
308
355
|
// reconnect if we disconnected from before.
|
|
309
|
-
this.triggerConnect("read");
|
|
356
|
+
this.triggerConnect({ text: "Force Readonly" }, "read");
|
|
310
357
|
}
|
|
311
358
|
}
|
|
312
359
|
}
|
|
313
|
-
set_readonlyPermissions(
|
|
314
|
-
|
|
315
|
-
this.
|
|
316
|
-
|
|
317
|
-
this.props.readonlyChangeHandler(this.readonly);
|
|
360
|
+
set_readonlyPermissions(newReadonlyValue, oldReadonlyValue, readonlyConnectionReason) {
|
|
361
|
+
this._readonlyPermissions = newReadonlyValue;
|
|
362
|
+
if (oldReadonlyValue !== this.readonly) {
|
|
363
|
+
this.props.readonlyChangeHandler(this.readonly, readonlyConnectionReason);
|
|
318
364
|
}
|
|
319
365
|
}
|
|
320
|
-
connect(connectionMode) {
|
|
321
|
-
this.connectCore(connectionMode).catch((
|
|
322
|
-
const normalizedError = (0, telemetry_utils_1.normalizeError)(
|
|
366
|
+
connect(reason, connectionMode) {
|
|
367
|
+
this.connectCore(reason, connectionMode).catch((e) => {
|
|
368
|
+
const normalizedError = (0, telemetry_utils_1.normalizeError)(e, { props: fatalConnectErrorProp });
|
|
323
369
|
this.props.closeHandler(normalizedError);
|
|
324
370
|
});
|
|
325
371
|
}
|
|
326
|
-
async connectCore(connectionMode) {
|
|
327
|
-
|
|
328
|
-
(0, common_utils_1.assert)(!this.closed, 0x26a /* "not closed" */);
|
|
372
|
+
async connectCore(reason, connectionMode) {
|
|
373
|
+
(0, core_utils_1.assert)(!this._disposed, 0x26a /* "not closed" */);
|
|
329
374
|
if (this.connection !== undefined) {
|
|
330
375
|
return; // Connection attempt already completed successfully
|
|
331
376
|
}
|
|
332
377
|
let pendingConnectionMode;
|
|
333
378
|
if (this.pendingConnection !== undefined) {
|
|
334
379
|
pendingConnectionMode = this.pendingConnection.connectionMode;
|
|
335
|
-
this.cancelConnection(); // Throw out in-progress connection attempt in favor of new attempt
|
|
336
|
-
(0,
|
|
380
|
+
this.cancelConnection(reason); // Throw out in-progress connection attempt in favor of new attempt
|
|
381
|
+
(0, core_utils_1.assert)(this.pendingConnection === undefined, 0x344 /* this.pendingConnection should be undefined */);
|
|
337
382
|
}
|
|
338
383
|
// If there is no specified ConnectionMode, try the previous mode, if there is no previous mode use default
|
|
339
|
-
let requestedMode =
|
|
384
|
+
let requestedMode = connectionMode ?? pendingConnectionMode ?? this.defaultReconnectionMode;
|
|
340
385
|
// if we have any non-acked ops from last connection, reconnect as "write".
|
|
341
386
|
// without that we would connect in view-only mode, which will result in immediate
|
|
342
387
|
// firing of "connected" event from Container and switch of current clientId (as tracked
|
|
@@ -346,31 +391,37 @@ class ConnectionManager {
|
|
|
346
391
|
requestedMode = "write";
|
|
347
392
|
}
|
|
348
393
|
const docService = this.serviceProvider();
|
|
349
|
-
(0,
|
|
394
|
+
(0, core_utils_1.assert)(docService !== undefined, 0x2a7 /* "Container is not attached" */);
|
|
350
395
|
let connection;
|
|
351
|
-
if (
|
|
396
|
+
if (docService.policies?.storageOnly === true) {
|
|
352
397
|
connection = new NoDeltaStream();
|
|
353
|
-
this.setupNewSuccessfulConnection(connection, "read");
|
|
354
|
-
(0,
|
|
398
|
+
this.setupNewSuccessfulConnection(connection, "read", reason);
|
|
399
|
+
(0, core_utils_1.assert)(this.pendingConnection === undefined, 0x2b3 /* "logic error" */);
|
|
355
400
|
return;
|
|
356
401
|
}
|
|
357
402
|
let delayMs = InitialReconnectDelayInMs;
|
|
358
403
|
let connectRepeatCount = 0;
|
|
359
|
-
const connectStartTime =
|
|
404
|
+
const connectStartTime = client_utils_1.performance.now();
|
|
360
405
|
let lastError;
|
|
361
|
-
const abortController = new
|
|
406
|
+
const abortController = new AbortController();
|
|
362
407
|
const abortSignal = abortController.signal;
|
|
363
|
-
this.pendingConnection = {
|
|
408
|
+
this.pendingConnection = {
|
|
409
|
+
abort: () => {
|
|
410
|
+
abortController.abort();
|
|
411
|
+
},
|
|
412
|
+
connectionMode: requestedMode,
|
|
413
|
+
};
|
|
414
|
+
this.props.establishConnectionHandler(reason);
|
|
364
415
|
// This loop will keep trying to connect until successful, with a delay between each iteration.
|
|
365
416
|
while (connection === undefined) {
|
|
366
|
-
if (this.
|
|
417
|
+
if (this._disposed) {
|
|
367
418
|
throw new Error("Attempting to connect a closed DeltaManager");
|
|
368
419
|
}
|
|
369
420
|
if (abortSignal.aborted === true) {
|
|
370
421
|
this.logger.sendTelemetryEvent({
|
|
371
422
|
eventName: "ConnectionAttemptCancelled",
|
|
372
423
|
attempts: connectRepeatCount,
|
|
373
|
-
duration: telemetry_utils_1.
|
|
424
|
+
duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
|
|
374
425
|
connectionEstablished: false,
|
|
375
426
|
});
|
|
376
427
|
return;
|
|
@@ -378,17 +429,42 @@ class ConnectionManager {
|
|
|
378
429
|
connectRepeatCount++;
|
|
379
430
|
try {
|
|
380
431
|
this.client.mode = requestedMode;
|
|
381
|
-
connection = await docService.connectToDeltaStream(
|
|
432
|
+
connection = await docService.connectToDeltaStream({
|
|
433
|
+
...this.client,
|
|
434
|
+
mode: requestedMode,
|
|
435
|
+
});
|
|
382
436
|
if (connection.disposed) {
|
|
383
437
|
// Nobody observed this connection, so drop it on the floor and retry.
|
|
384
438
|
this.logger.sendTelemetryEvent({ eventName: "ReceivedClosedConnection" });
|
|
385
439
|
connection = undefined;
|
|
386
440
|
}
|
|
441
|
+
this.logger.sendTelemetryEvent({
|
|
442
|
+
eventName: "ConnectionReceived",
|
|
443
|
+
connected: connection !== undefined && connection.disposed === false,
|
|
444
|
+
}, undefined, core_interfaces_1.LogLevel.verbose);
|
|
387
445
|
}
|
|
388
446
|
catch (origError) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
connection
|
|
447
|
+
this.logger.sendTelemetryEvent({
|
|
448
|
+
eventName: "ConnectToDeltaStreamException",
|
|
449
|
+
connected: connection !== undefined && connection.disposed === false,
|
|
450
|
+
}, undefined, core_interfaces_1.LogLevel.verbose);
|
|
451
|
+
if ((0, utils_1.isDeltaStreamConnectionForbiddenError)(origError)) {
|
|
452
|
+
connection = new NoDeltaStream(origError.storageOnlyReason, {
|
|
453
|
+
text: origError.message,
|
|
454
|
+
error: origError,
|
|
455
|
+
});
|
|
456
|
+
requestedMode = "read";
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
else if ((0, telemetry_utils_1.isFluidError)(origError) &&
|
|
460
|
+
// eslint-disable-next-line import/no-deprecated
|
|
461
|
+
origError.errorType === driver_definitions_1.DriverErrorType.outOfStorageError) {
|
|
462
|
+
// If we get out of storage error from calling joinsession, then use the NoDeltaStream object so
|
|
463
|
+
// that user can at least load the container.
|
|
464
|
+
connection = new NoDeltaStream(undefined, {
|
|
465
|
+
text: origError.message,
|
|
466
|
+
error: origError,
|
|
467
|
+
});
|
|
392
468
|
requestedMode = "read";
|
|
393
469
|
break;
|
|
394
470
|
}
|
|
@@ -403,15 +479,36 @@ class ConnectionManager {
|
|
|
403
479
|
attempts: connectRepeatCount,
|
|
404
480
|
delay: delayMs,
|
|
405
481
|
eventName: "DeltaConnectionFailureToConnect",
|
|
406
|
-
duration: telemetry_utils_1.
|
|
482
|
+
duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
|
|
407
483
|
}, origError);
|
|
408
484
|
lastError = origError;
|
|
485
|
+
const waitStartTime = client_utils_1.performance.now();
|
|
409
486
|
const retryDelayFromError = (0, driver_utils_1.getRetryDelayFromError)(origError);
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
487
|
+
// If the error told us to wait or browser signals us that we are offline, then calculate the time we
|
|
488
|
+
// want to wait for before retrying. then we wait for that time. If the error didn't tell us to wait,
|
|
489
|
+
// let's still wait a little bit before retrying. We can skip this delay if we're confident we're offline,
|
|
490
|
+
// because we probably just need to wait to come back online. But we never have strong signal of being
|
|
491
|
+
// offline, so we at least wait for sometime.
|
|
492
|
+
if (retryDelayFromError !== undefined || globalThis.navigator?.onLine !== false) {
|
|
493
|
+
delayMs = (0, driver_utils_1.calculateMaxWaitTime)(delayMs, origError);
|
|
413
494
|
}
|
|
414
|
-
|
|
495
|
+
// Raise event in case the delay was there.
|
|
496
|
+
this.props.reconnectionDelayHandler(delayMs, origError);
|
|
497
|
+
await new Promise((resolve) => {
|
|
498
|
+
setTimeout(resolve, delayMs);
|
|
499
|
+
});
|
|
500
|
+
// If we believe we're offline, we assume there's no point in trying until we at least think we're online.
|
|
501
|
+
// NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
|
|
502
|
+
// should probably live in the driver.
|
|
503
|
+
await waitForOnline();
|
|
504
|
+
this.logger.sendPerformanceEvent({
|
|
505
|
+
eventName: "WaitBetweenConnectionAttempts",
|
|
506
|
+
duration: client_utils_1.performance.now() - waitStartTime,
|
|
507
|
+
details: JSON.stringify({
|
|
508
|
+
retryDelayFromError,
|
|
509
|
+
delayMs,
|
|
510
|
+
}),
|
|
511
|
+
});
|
|
415
512
|
}
|
|
416
513
|
}
|
|
417
514
|
// If we retried more than once, log an event about how long it took (this will not log to error table)
|
|
@@ -419,55 +516,61 @@ class ConnectionManager {
|
|
|
419
516
|
(0, driver_utils_1.logNetworkFailure)(this.logger, {
|
|
420
517
|
eventName: "MultipleDeltaConnectionFailures",
|
|
421
518
|
attempts: connectRepeatCount,
|
|
422
|
-
duration: telemetry_utils_1.
|
|
519
|
+
duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
|
|
423
520
|
}, lastError);
|
|
424
521
|
}
|
|
425
|
-
// Check for abort signal after while loop as well
|
|
426
|
-
if (abortSignal.aborted === true) {
|
|
522
|
+
// Check for abort signal after while loop as well or we've been disposed
|
|
523
|
+
if (abortSignal.aborted === true || this._disposed) {
|
|
427
524
|
connection.dispose();
|
|
428
525
|
this.logger.sendTelemetryEvent({
|
|
429
526
|
eventName: "ConnectionAttemptCancelled",
|
|
430
527
|
attempts: connectRepeatCount,
|
|
431
|
-
duration: telemetry_utils_1.
|
|
528
|
+
duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
|
|
432
529
|
connectionEstablished: true,
|
|
433
530
|
});
|
|
434
531
|
return;
|
|
435
532
|
}
|
|
436
|
-
this.setupNewSuccessfulConnection(connection, requestedMode);
|
|
533
|
+
this.setupNewSuccessfulConnection(connection, requestedMode, reason);
|
|
437
534
|
}
|
|
438
535
|
/**
|
|
439
|
-
* Start the connection. Any error should result in container being
|
|
440
|
-
* And report the error if it
|
|
536
|
+
* Start the connection. Any error should result in container being closed.
|
|
537
|
+
* And report the error if it escapes for any reason.
|
|
441
538
|
* @param args - The connection arguments
|
|
442
539
|
*/
|
|
443
|
-
triggerConnect(connectionMode) {
|
|
444
|
-
(
|
|
540
|
+
triggerConnect(reason, connectionMode) {
|
|
541
|
+
// reconnect() includes async awaits, and that causes potential race conditions
|
|
542
|
+
// where we might already have a connection. If it were to happen, it's possible that we will connect
|
|
543
|
+
// with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
|
|
544
|
+
// fine (if needed, reconnect flow will get triggered again). Places where new mode matters should encode it
|
|
545
|
+
// directly in connectCore - see this.shouldJoinWrite() test as an example.
|
|
546
|
+
// assert(this.connection === undefined, 0x239 /* "called only in disconnected state" */);
|
|
445
547
|
if (this.reconnectMode !== contracts_1.ReconnectMode.Enabled) {
|
|
446
548
|
return;
|
|
447
549
|
}
|
|
448
|
-
this.connect(connectionMode);
|
|
550
|
+
this.connect(reason, connectionMode);
|
|
449
551
|
}
|
|
450
552
|
/**
|
|
451
553
|
* Disconnect the current connection.
|
|
452
554
|
* @param reason - Text description of disconnect reason to emit with disconnect event
|
|
555
|
+
* @param error - Error causing the disconnect if any.
|
|
453
556
|
* @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect
|
|
454
557
|
*/
|
|
455
558
|
disconnectFromDeltaStream(reason) {
|
|
456
559
|
this.pendingReconnect = false;
|
|
457
560
|
if (this.connection === undefined) {
|
|
458
561
|
if (this.pendingConnection !== undefined) {
|
|
459
|
-
this.cancelConnection();
|
|
562
|
+
this.cancelConnection(reason);
|
|
460
563
|
return true;
|
|
461
564
|
}
|
|
462
565
|
return false;
|
|
463
566
|
}
|
|
464
|
-
(0,
|
|
567
|
+
(0, core_utils_1.assert)(this.pendingConnection === undefined, 0x27b /* "reentrancy may result in incorrect behavior" */);
|
|
465
568
|
const connection = this.connection;
|
|
466
569
|
// Avoid any re-entrancy - clear object reference
|
|
467
570
|
this.connection = undefined;
|
|
468
571
|
// Remove listeners first so we don't try to retrigger this flow accidentally through reconnectOnError
|
|
469
572
|
connection.off("op", this.opHandler);
|
|
470
|
-
connection.off("signal", this.
|
|
573
|
+
connection.off("signal", this.signalHandler);
|
|
471
574
|
connection.off("nack", this.nackHandler);
|
|
472
575
|
connection.off("disconnect", this.disconnectHandlerInternal);
|
|
473
576
|
connection.off("error", this.errorHandler);
|
|
@@ -475,48 +578,60 @@ class ConnectionManager {
|
|
|
475
578
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
476
579
|
this._outbound.pause();
|
|
477
580
|
this._outbound.clear();
|
|
478
|
-
this.props.disconnectHandler(reason);
|
|
479
581
|
connection.dispose();
|
|
582
|
+
this.props.disconnectHandler(reason);
|
|
480
583
|
this._connectionVerboseProps = {};
|
|
481
584
|
return true;
|
|
482
585
|
}
|
|
483
586
|
/**
|
|
484
587
|
* Cancel in-progress connection attempt.
|
|
485
588
|
*/
|
|
486
|
-
cancelConnection() {
|
|
487
|
-
(0,
|
|
589
|
+
cancelConnection(reason) {
|
|
590
|
+
(0, core_utils_1.assert)(this.pendingConnection !== undefined, 0x345 /* this.pendingConnection is undefined when trying to cancel */);
|
|
488
591
|
this.pendingConnection.abort();
|
|
489
592
|
this.pendingConnection = undefined;
|
|
490
593
|
this.logger.sendTelemetryEvent({ eventName: "ConnectionCancelReceived" });
|
|
594
|
+
this.props.cancelConnectionHandler({
|
|
595
|
+
text: `Cancel Pending Connection due to ${reason.text}`,
|
|
596
|
+
error: reason.error,
|
|
597
|
+
});
|
|
491
598
|
}
|
|
492
599
|
/**
|
|
493
600
|
* Once we've successfully gotten a connection, we need to set up state, attach event listeners, and process
|
|
494
601
|
* initial messages.
|
|
495
602
|
* @param connection - The newly established connection
|
|
496
603
|
*/
|
|
497
|
-
setupNewSuccessfulConnection(connection, requestedMode) {
|
|
604
|
+
setupNewSuccessfulConnection(connection, requestedMode, reason) {
|
|
498
605
|
// Old connection should have been cleaned up before establishing a new one
|
|
499
|
-
(0,
|
|
500
|
-
(0,
|
|
606
|
+
(0, core_utils_1.assert)(this.connection === undefined, 0x0e6 /* "old connection exists on new connection setup" */);
|
|
607
|
+
(0, core_utils_1.assert)(!connection.disposed, 0x28a /* "can't be disposed - Callers need to ensure that!" */);
|
|
501
608
|
this.pendingConnection = undefined;
|
|
609
|
+
const oldReadonlyValue = this.readonly;
|
|
502
610
|
this.connection = connection;
|
|
503
611
|
// Does information in scopes & mode matches?
|
|
504
612
|
// If we asked for "write" and got "read", then file is read-only
|
|
505
613
|
// But if we ask read, server can still give us write.
|
|
506
614
|
const readonly = !connection.claims.scopes.includes(protocol_definitions_1.ScopeType.DocWrite);
|
|
615
|
+
if (connection.mode !== requestedMode) {
|
|
616
|
+
this.logger.sendTelemetryEvent({
|
|
617
|
+
eventName: "ConnectionModeMismatch",
|
|
618
|
+
requestedMode,
|
|
619
|
+
mode: connection.mode,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
507
622
|
// This connection mode validation logic is moving to the driver layer in 0.44. These two asserts can be
|
|
508
623
|
// removed after those packages have released and become ubiquitous.
|
|
509
|
-
(0,
|
|
510
|
-
(0,
|
|
511
|
-
this.set_readonlyPermissions(readonly);
|
|
512
|
-
if (this.
|
|
624
|
+
(0, core_utils_1.assert)(requestedMode === "read" || readonly === (this.connectionMode === "read"), 0x0e7 /* "claims/connectionMode mismatch" */);
|
|
625
|
+
(0, core_utils_1.assert)(!readonly || this.connectionMode === "read", 0x0e8 /* "readonly perf with write connection" */);
|
|
626
|
+
this.set_readonlyPermissions(readonly, oldReadonlyValue, isNoDeltaStreamConnection(connection) ? connection.readonlyConnectionReason : undefined);
|
|
627
|
+
if (this._disposed) {
|
|
513
628
|
// Raise proper events, Log telemetry event and close connection.
|
|
514
|
-
this.disconnectFromDeltaStream("ConnectionManager already closed");
|
|
629
|
+
this.disconnectFromDeltaStream({ text: "ConnectionManager already closed" });
|
|
515
630
|
return;
|
|
516
631
|
}
|
|
517
632
|
this._outbound.resume();
|
|
518
633
|
connection.on("op", this.opHandler);
|
|
519
|
-
connection.on("signal", this.
|
|
634
|
+
connection.on("signal", this.signalHandler);
|
|
520
635
|
connection.on("nack", this.nackHandler);
|
|
521
636
|
connection.on("disconnect", this.disconnectHandlerInternal);
|
|
522
637
|
connection.on("error", this.errorHandler);
|
|
@@ -542,7 +657,8 @@ class ConnectionManager {
|
|
|
542
657
|
this._connectionProps.connectionMode = connection.mode;
|
|
543
658
|
let last = -1;
|
|
544
659
|
if (initialMessages.length !== 0) {
|
|
545
|
-
this._connectionVerboseProps.connectionInitialOpsFrom =
|
|
660
|
+
this._connectionVerboseProps.connectionInitialOpsFrom =
|
|
661
|
+
initialMessages[0].sequenceNumber;
|
|
546
662
|
last = initialMessages[initialMessages.length - 1].sequenceNumber;
|
|
547
663
|
this._connectionVerboseProps.connectionInitialOpsTo = last + 1;
|
|
548
664
|
// Update knowledge of how far we are behind, before raising "connect" event
|
|
@@ -553,15 +669,39 @@ class ConnectionManager {
|
|
|
553
669
|
}
|
|
554
670
|
}
|
|
555
671
|
this.props.incomingOpHandler(initialMessages, this.connectFirstConnection ? "InitialOps" : "ReconnectOps");
|
|
556
|
-
|
|
557
|
-
for (const signal of connection.initialSignals) {
|
|
558
|
-
this.props.signalHandler(signal);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
const details = ConnectionManager.detailsFromConnection(connection);
|
|
672
|
+
const details = ConnectionManager.detailsFromConnection(connection, reason);
|
|
562
673
|
details.checkpointSequenceNumber = checkpointSequenceNumber;
|
|
563
674
|
this.props.connectHandler(details);
|
|
564
675
|
this.connectFirstConnection = false;
|
|
676
|
+
// Synthesize clear & join signals out of initialClients state.
|
|
677
|
+
// This allows us to have single way to process signals, and makes it simpler to initialize
|
|
678
|
+
// protocol in Container.
|
|
679
|
+
const clearSignal = {
|
|
680
|
+
clientId: null,
|
|
681
|
+
content: JSON.stringify({
|
|
682
|
+
type: protocol_1.SignalType.Clear,
|
|
683
|
+
}),
|
|
684
|
+
};
|
|
685
|
+
// list of signals to process due to this new connection
|
|
686
|
+
let signalsToProcess = [clearSignal];
|
|
687
|
+
const clientJoinSignals = (connection.initialClients ?? []).map((priorClient) => ({
|
|
688
|
+
clientId: null,
|
|
689
|
+
content: JSON.stringify({
|
|
690
|
+
type: protocol_1.SignalType.ClientJoin,
|
|
691
|
+
content: priorClient, // ISignalClient
|
|
692
|
+
}),
|
|
693
|
+
}));
|
|
694
|
+
if (clientJoinSignals.length > 0) {
|
|
695
|
+
signalsToProcess = signalsToProcess.concat(clientJoinSignals);
|
|
696
|
+
}
|
|
697
|
+
// Unfortunately, there is no defined order between initialSignals (including join & leave signals)
|
|
698
|
+
// and connection.initialClients. In practice, connection.initialSignals quite often contains join signal
|
|
699
|
+
// for "self" and connection.initialClients does not contain "self", so we have to process them after
|
|
700
|
+
// "clear" signal above.
|
|
701
|
+
if (connection.initialSignals !== undefined && connection.initialSignals.length > 0) {
|
|
702
|
+
signalsToProcess = signalsToProcess.concat(connection.initialSignals);
|
|
703
|
+
}
|
|
704
|
+
this.props.signalHandler(signalsToProcess);
|
|
565
705
|
}
|
|
566
706
|
/**
|
|
567
707
|
* Disconnect the current connection and reconnect. Closes the container if it fails.
|
|
@@ -571,8 +711,7 @@ class ConnectionManager {
|
|
|
571
711
|
* @returns A promise that resolves when the connection is reestablished or we stop trying
|
|
572
712
|
*/
|
|
573
713
|
reconnectOnError(requestedMode, error) {
|
|
574
|
-
this.reconnect(requestedMode, error.message, error)
|
|
575
|
-
.catch(this.props.closeHandler);
|
|
714
|
+
this.reconnect(requestedMode, { text: error.message, error }).catch(this.props.closeHandler);
|
|
576
715
|
}
|
|
577
716
|
/**
|
|
578
717
|
* Disconnect the current connection and reconnect.
|
|
@@ -581,20 +720,20 @@ class ConnectionManager {
|
|
|
581
720
|
* @param error - Error reconnect information including whether or not to reconnect
|
|
582
721
|
* @returns A promise that resolves when the connection is reestablished or we stop trying
|
|
583
722
|
*/
|
|
584
|
-
async reconnect(requestedMode,
|
|
723
|
+
async reconnect(requestedMode, reason) {
|
|
585
724
|
// We quite often get protocol errors before / after observing nack/disconnect
|
|
586
725
|
// we do not want to run through same sequence twice.
|
|
587
726
|
// If we're already disconnected/disconnecting it's not appropriate to call this again.
|
|
588
|
-
(0,
|
|
589
|
-
this.disconnectFromDeltaStream(
|
|
727
|
+
(0, core_utils_1.assert)(this.connection !== undefined, 0x0eb /* "Missing connection for reconnect" */);
|
|
728
|
+
this.disconnectFromDeltaStream(reason);
|
|
590
729
|
// We will always trigger reconnect, even if canRetry is false.
|
|
591
730
|
// Any truly fatal error state will result in container close upon attempted reconnect,
|
|
592
731
|
// which is a preferable to closing abruptly when a live connection fails.
|
|
593
|
-
if (error
|
|
732
|
+
if (reason.error?.canRetry === false) {
|
|
594
733
|
this.logger.sendTelemetryEvent({
|
|
595
734
|
eventName: "reconnectingDespiteFatalError",
|
|
596
735
|
reconnectMode: this.reconnectMode,
|
|
597
|
-
}, error);
|
|
736
|
+
}, reason.error);
|
|
598
737
|
}
|
|
599
738
|
if (this.reconnectMode === contracts_1.ReconnectMode.Never) {
|
|
600
739
|
// Do not raise container error if we are closing just because we lost connection.
|
|
@@ -603,25 +742,37 @@ class ConnectionManager {
|
|
|
603
742
|
this.props.closeHandler();
|
|
604
743
|
}
|
|
605
744
|
// If closed then we can't reconnect
|
|
606
|
-
if (this.
|
|
745
|
+
if (this._disposed || this.reconnectMode !== contracts_1.ReconnectMode.Enabled) {
|
|
607
746
|
return;
|
|
608
747
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
748
|
+
// If the error tells us to wait before retrying, then do so.
|
|
749
|
+
const delayMs = (0, driver_utils_1.getRetryDelayFromError)(reason.error);
|
|
750
|
+
if (reason.error !== undefined && delayMs !== undefined) {
|
|
751
|
+
this.props.reconnectionDelayHandler(delayMs, reason.error);
|
|
752
|
+
await new Promise((resolve) => {
|
|
753
|
+
setTimeout(resolve, delayMs);
|
|
754
|
+
});
|
|
613
755
|
}
|
|
614
|
-
|
|
756
|
+
// If we believe we're offline, we assume there's no point in trying again until we at least think we're online.
|
|
757
|
+
// NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
|
|
758
|
+
// should probably live in the driver.
|
|
759
|
+
await waitForOnline();
|
|
760
|
+
this.triggerConnect({
|
|
761
|
+
text: reason.error !== undefined
|
|
762
|
+
? "Reconnecting due to Error"
|
|
763
|
+
: `Reconnecting due to: ${reason.text}`,
|
|
764
|
+
error: reason.error,
|
|
765
|
+
}, requestedMode);
|
|
615
766
|
}
|
|
616
767
|
prepareMessageToSend(message) {
|
|
617
|
-
var _a, _b;
|
|
618
768
|
if (this.readonly === true) {
|
|
619
|
-
(0,
|
|
620
|
-
const error = new
|
|
769
|
+
(0, core_utils_1.assert)(this.readOnlyInfo.readonly === true, 0x1f0 /* "Unexpected mismatch in readonly" */);
|
|
770
|
+
const error = new telemetry_utils_1.GenericError("deltaManagerReadonlySubmit", undefined /* error */, {
|
|
621
771
|
readonly: this.readOnlyInfo.readonly,
|
|
622
772
|
forcedReadonly: this.readOnlyInfo.forced,
|
|
623
773
|
readonlyPermissions: this.readOnlyInfo.permissions,
|
|
624
774
|
storageOnly: this.readOnlyInfo.storageOnly,
|
|
775
|
+
storageOnlyReason: this.readOnlyInfo.storageOnlyReason,
|
|
625
776
|
});
|
|
626
777
|
this.props.closeHandler(error);
|
|
627
778
|
return undefined;
|
|
@@ -629,30 +780,33 @@ class ConnectionManager {
|
|
|
629
780
|
// reset clientSequenceNumber if we are using new clientId.
|
|
630
781
|
// we keep info about old connection as long as possible to be able to account for all non-acked ops
|
|
631
782
|
// that we pick up on next connection.
|
|
632
|
-
(0,
|
|
633
|
-
if (this.lastSubmittedClientId !==
|
|
634
|
-
this.lastSubmittedClientId =
|
|
783
|
+
(0, core_utils_1.assert)(!!this.connection, 0x0e4 /* "Lost old connection!" */);
|
|
784
|
+
if (this.lastSubmittedClientId !== this.connection?.clientId) {
|
|
785
|
+
this.lastSubmittedClientId = this.connection?.clientId;
|
|
635
786
|
this.clientSequenceNumber = 0;
|
|
636
787
|
this.clientSequenceNumberObserved = 0;
|
|
637
788
|
}
|
|
638
|
-
if (
|
|
639
|
-
this.
|
|
789
|
+
if (!(0, driver_utils_1.isRuntimeMessage)(message)) {
|
|
790
|
+
this.localOpsToIgnore++;
|
|
640
791
|
}
|
|
641
792
|
else {
|
|
642
|
-
this.
|
|
793
|
+
this.localOpsToIgnore = 0;
|
|
643
794
|
}
|
|
644
|
-
return
|
|
795
|
+
return {
|
|
796
|
+
...message,
|
|
797
|
+
clientSequenceNumber: ++this.clientSequenceNumber,
|
|
798
|
+
};
|
|
645
799
|
}
|
|
646
|
-
submitSignal(content) {
|
|
800
|
+
submitSignal(content, targetClientId) {
|
|
647
801
|
if (this.connection !== undefined) {
|
|
648
|
-
this.connection.submitSignal(content);
|
|
802
|
+
this.connection.submitSignal(content, targetClientId);
|
|
649
803
|
}
|
|
650
804
|
else {
|
|
651
805
|
this.logger.sendErrorEvent({ eventName: "submitSignalDisconnected" });
|
|
652
806
|
}
|
|
653
807
|
}
|
|
654
808
|
sendMessages(messages) {
|
|
655
|
-
(0,
|
|
809
|
+
(0, core_utils_1.assert)(this.connected, 0x2b4 /* "not connected on sending ops!" */);
|
|
656
810
|
// If connection is "read" or implicit "read" (got leave op for "write" connection),
|
|
657
811
|
// then op can't make it through - we will get a nack if op is sent.
|
|
658
812
|
// We can short-circuit this process.
|
|
@@ -662,26 +816,29 @@ class ConnectionManager {
|
|
|
662
816
|
if (this.connectionMode === "read") {
|
|
663
817
|
if (!this.pendingReconnect) {
|
|
664
818
|
this.pendingReconnect = true;
|
|
665
|
-
Promise.resolve()
|
|
666
|
-
|
|
819
|
+
Promise.resolve()
|
|
820
|
+
.then(async () => {
|
|
821
|
+
if (this.pendingReconnect) {
|
|
822
|
+
// still valid?
|
|
667
823
|
await this.reconnect("write", // connectionMode
|
|
668
|
-
"Switch to write");
|
|
824
|
+
{ text: "Switch to write" });
|
|
669
825
|
}
|
|
670
826
|
})
|
|
671
827
|
.catch(() => { });
|
|
672
828
|
}
|
|
673
829
|
return;
|
|
674
830
|
}
|
|
675
|
-
(0,
|
|
831
|
+
(0, core_utils_1.assert)(!this.pendingReconnect, 0x2b5 /* "logic error" */);
|
|
676
832
|
this._outbound.push(messages);
|
|
677
833
|
}
|
|
678
834
|
beforeProcessingIncomingOp(message) {
|
|
679
835
|
// if we have connection, and message is local, then we better treat is as local!
|
|
680
|
-
(0,
|
|
681
|
-
if (this.lastSubmittedClientId !== undefined &&
|
|
836
|
+
(0, core_utils_1.assert)(this.clientId !== message.clientId || this.lastSubmittedClientId === message.clientId, 0x0ee /* "Not accounting local messages correctly" */);
|
|
837
|
+
if (this.lastSubmittedClientId !== undefined &&
|
|
838
|
+
this.lastSubmittedClientId === message.clientId) {
|
|
682
839
|
const clientSequenceNumber = message.clientSequenceNumber;
|
|
683
|
-
(0,
|
|
684
|
-
(0,
|
|
840
|
+
(0, core_utils_1.assert)(this.clientSequenceNumberObserved < clientSequenceNumber, 0x0ef /* "client seq# not growing" */);
|
|
841
|
+
(0, core_utils_1.assert)(clientSequenceNumber <= this.clientSequenceNumber, 0x0f0 /* "Incoming local client seq# > generated by this client" */);
|
|
685
842
|
this.clientSequenceNumberObserved = clientSequenceNumber;
|
|
686
843
|
}
|
|
687
844
|
if (message.type === protocol_definitions_1.MessageType.ClientLeave) {
|
|
@@ -697,7 +854,7 @@ class ConnectionManager {
|
|
|
697
854
|
// Clients need to be able to transition to "read" state after some time of inactivity!
|
|
698
855
|
// Note - this may close container!
|
|
699
856
|
this.reconnect("read", // connectionMode
|
|
700
|
-
"Switch to read").catch((error) => {
|
|
857
|
+
{ text: "Switch to read" }).catch((error) => {
|
|
701
858
|
this.logger.sendErrorEvent({ eventName: "SwitchToReadConnection" }, error);
|
|
702
859
|
});
|
|
703
860
|
}
|
|
@@ -705,4 +862,4 @@ class ConnectionManager {
|
|
|
705
862
|
}
|
|
706
863
|
}
|
|
707
864
|
exports.ConnectionManager = ConnectionManager;
|
|
708
|
-
//# sourceMappingURL=connectionManager.
|
|
865
|
+
//# sourceMappingURL=connectionManager.cjs.map
|