@fluidframework/container-loader 1.4.0-115997 → 2.0.0-dev-rc.1.0.0.224419
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-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 +3 -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} +3 -10
- 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} +23 -33
- 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} +3 -5
- 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} +45 -17
- 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} +54 -18
- 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} +3 -4
- 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} +39 -20
- 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} +1 -1
- package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
- 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} +8 -4
- 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} +7 -5
- 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 +163 -70
- 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.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,102 +3,66 @@
|
|
|
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.DeltaManager = void 0;
|
|
11
|
-
const abort_controller_1 = __importDefault(require("abort-controller"));
|
|
12
8
|
const uuid_1 = require("uuid");
|
|
13
|
-
const
|
|
9
|
+
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
10
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
14
11
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
15
12
|
const driver_definitions_1 = require("@fluidframework/driver-definitions");
|
|
16
13
|
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
17
14
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
18
|
-
const
|
|
19
|
-
const
|
|
15
|
+
const deltaQueue_1 = require("./deltaQueue.cjs");
|
|
16
|
+
const error_1 = require("./error.cjs");
|
|
17
|
+
/**
|
|
18
|
+
* Determines if message was sent by client, not service
|
|
19
|
+
*/
|
|
20
|
+
function isClientMessage(message) {
|
|
21
|
+
if ((0, driver_utils_1.isRuntimeMessage)(message)) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
switch (message.type) {
|
|
25
|
+
case protocol_definitions_1.MessageType.Propose:
|
|
26
|
+
case protocol_definitions_1.MessageType.Reject:
|
|
27
|
+
case protocol_definitions_1.MessageType.NoOp:
|
|
28
|
+
case driver_utils_1.MessageType2.Accept:
|
|
29
|
+
case protocol_definitions_1.MessageType.Summarize:
|
|
30
|
+
return true;
|
|
31
|
+
default:
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Like assert, but logs only if the condition is false, rather than throwing
|
|
37
|
+
* @param condition - The condition to attest too
|
|
38
|
+
* @param logger - The logger to log with
|
|
39
|
+
* @param event - The string or event to log
|
|
40
|
+
* @returns The outcome of the condition
|
|
41
|
+
*/
|
|
42
|
+
function logIfFalse(condition, logger, event) {
|
|
43
|
+
if (condition) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
const newEvent = typeof event === "string"
|
|
47
|
+
? { eventName: event, category: "error" }
|
|
48
|
+
: { category: "error", ...event };
|
|
49
|
+
logger.send(newEvent);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
20
52
|
/**
|
|
21
53
|
* Manages the flow of both inbound and outbound messages. This class ensures that shared objects receive delta
|
|
22
54
|
* messages in order regardless of possible network conditions or timings causing out of order delivery.
|
|
23
55
|
*/
|
|
24
|
-
class DeltaManager extends
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this
|
|
33
|
-
// There are three numbers we track
|
|
34
|
-
// * lastQueuedSequenceNumber is the last queued sequence number. If there are gaps in seq numbers, then this number
|
|
35
|
-
// is not updated until we cover that gap, so it increases each time by 1.
|
|
36
|
-
// * lastObservedSeqNumber is an estimation of last known sequence number for container in storage. It's initially
|
|
37
|
-
// populated at web socket connection time (if storage provides that info) and is updated once ops shows up.
|
|
38
|
-
// It's never less than lastQueuedSequenceNumber
|
|
39
|
-
// * lastProcessedSequenceNumber - last processed sequence number
|
|
40
|
-
this.lastQueuedSequenceNumber = 0;
|
|
41
|
-
this.lastObservedSeqNumber = 0;
|
|
42
|
-
this.lastProcessedSequenceNumber = 0;
|
|
43
|
-
this.baseTerm = 0;
|
|
44
|
-
/**
|
|
45
|
-
* Track down the ops size.
|
|
46
|
-
*/
|
|
47
|
-
this.opsSize = 0;
|
|
48
|
-
// The sequence number we initially loaded from
|
|
49
|
-
this.initSequenceNumber = 0;
|
|
50
|
-
this.closed = false;
|
|
51
|
-
this.throttlingIdSet = new Set();
|
|
52
|
-
this.timeTillThrottling = 0;
|
|
53
|
-
this.closeAbortController = new abort_controller_1.default();
|
|
54
|
-
this.deltaStorageDelayId = (0, uuid_1.v4)();
|
|
55
|
-
this.deltaStreamDelayId = (0, uuid_1.v4)();
|
|
56
|
-
this.messageBuffer = [];
|
|
57
|
-
const props = {
|
|
58
|
-
incomingOpHandler: (messages, reason) => {
|
|
59
|
-
try {
|
|
60
|
-
this.enqueueMessages(messages, reason);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
this.logger.sendErrorEvent({ eventName: "EnqueueMessages_Exception" }, error);
|
|
64
|
-
this.close((0, telemetry_utils_1.normalizeError)(error));
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
signalHandler: (message) => this._inboundSignal.push(message),
|
|
68
|
-
reconnectionDelayHandler: (delayMs, error) => this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
|
|
69
|
-
closeHandler: (error) => this.close(error),
|
|
70
|
-
disconnectHandler: (reason) => this.disconnectHandler(reason),
|
|
71
|
-
connectHandler: (connection) => this.connectHandler(connection),
|
|
72
|
-
pongHandler: (latency) => this.emit("pong", latency),
|
|
73
|
-
readonlyChangeHandler: (readonly) => (0, telemetry_utils_1.safeRaiseEvent)(this, this.logger, "readonly", readonly),
|
|
74
|
-
};
|
|
75
|
-
this.connectionManager = createConnectionManager(props);
|
|
76
|
-
this._inbound = new deltaQueue_1.DeltaQueue((op) => {
|
|
77
|
-
this.processInboundMessage(op);
|
|
78
|
-
});
|
|
79
|
-
this._inbound.on("error", (error) => {
|
|
80
|
-
this.close(container_utils_1.DataProcessingError.wrapIfUnrecognized(error, "deltaManagerInboundErrorHandler", this.lastMessage));
|
|
81
|
-
});
|
|
82
|
-
// Inbound signal queue
|
|
83
|
-
this._inboundSignal = new deltaQueue_1.DeltaQueue((message) => {
|
|
84
|
-
if (this.handler === undefined) {
|
|
85
|
-
throw new Error("Attempted to process an inbound signal without a handler attached");
|
|
86
|
-
}
|
|
87
|
-
this.handler.processSignal({
|
|
88
|
-
clientId: message.clientId,
|
|
89
|
-
content: JSON.parse(message.content),
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
this._inboundSignal.on("error", (error) => {
|
|
93
|
-
this.close((0, telemetry_utils_1.normalizeError)(error));
|
|
94
|
-
});
|
|
95
|
-
// Initially, all queues are created paused.
|
|
96
|
-
// - outbound is flipped back and forth in setupNewSuccessfulConnection / disconnectFromDeltaStream
|
|
97
|
-
// - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
|
|
56
|
+
class DeltaManager extends client_utils_1.TypedEventEmitter {
|
|
57
|
+
get active() {
|
|
58
|
+
return this._active();
|
|
59
|
+
}
|
|
60
|
+
get disposed() {
|
|
61
|
+
return this._closed;
|
|
62
|
+
}
|
|
63
|
+
get IDeltaSender() {
|
|
64
|
+
return this;
|
|
98
65
|
}
|
|
99
|
-
get active() { return this._active(); }
|
|
100
|
-
get disposed() { return this.closed; }
|
|
101
|
-
get IDeltaSender() { return this; }
|
|
102
66
|
get inbound() {
|
|
103
67
|
return this._inbound;
|
|
104
68
|
}
|
|
@@ -117,9 +81,6 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
117
81
|
get lastKnownSeqNumber() {
|
|
118
82
|
return this.lastObservedSeqNumber;
|
|
119
83
|
}
|
|
120
|
-
get referenceTerm() {
|
|
121
|
-
return this.baseTerm;
|
|
122
|
-
}
|
|
123
84
|
get minimumSequenceNumber() {
|
|
124
85
|
return this.minSequenceNumber;
|
|
125
86
|
}
|
|
@@ -129,22 +90,37 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
129
90
|
*/
|
|
130
91
|
get hasCheckpointSequenceNumber() {
|
|
131
92
|
// Valid to be called only if we have active connection.
|
|
132
|
-
(0,
|
|
93
|
+
(0, core_utils_1.assert)(this.connectionManager.connected, 0x0df /* "Missing active connection" */);
|
|
133
94
|
return this._checkpointSequenceNumber !== undefined;
|
|
134
95
|
}
|
|
135
96
|
// Forwarding connection manager properties / IDeltaManager implementation
|
|
136
|
-
get maxMessageSize() {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
get
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
97
|
+
get maxMessageSize() {
|
|
98
|
+
return this.connectionManager.maxMessageSize;
|
|
99
|
+
}
|
|
100
|
+
get version() {
|
|
101
|
+
return this.connectionManager.version;
|
|
102
|
+
}
|
|
103
|
+
get serviceConfiguration() {
|
|
104
|
+
return this.connectionManager.serviceConfiguration;
|
|
105
|
+
}
|
|
106
|
+
get outbound() {
|
|
107
|
+
return this.connectionManager.outbound;
|
|
108
|
+
}
|
|
109
|
+
get readOnlyInfo() {
|
|
110
|
+
return this.connectionManager.readOnlyInfo;
|
|
111
|
+
}
|
|
112
|
+
get clientDetails() {
|
|
113
|
+
return this.connectionManager.clientDetails;
|
|
114
|
+
}
|
|
115
|
+
submit(type, contents, batch = false, metadata, compression, referenceSequenceNumber) {
|
|
116
|
+
// Back-compat ADO:3455
|
|
117
|
+
const backCompatRefSeqNum = referenceSequenceNumber ?? this.lastProcessedSequenceNumber;
|
|
143
118
|
const messagePartial = {
|
|
144
|
-
contents
|
|
119
|
+
contents,
|
|
145
120
|
metadata,
|
|
146
|
-
referenceSequenceNumber:
|
|
121
|
+
referenceSequenceNumber: backCompatRefSeqNum,
|
|
147
122
|
type,
|
|
123
|
+
compression,
|
|
148
124
|
};
|
|
149
125
|
if (!batch) {
|
|
150
126
|
this.flush();
|
|
@@ -153,26 +129,48 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
153
129
|
if (message === undefined) {
|
|
154
130
|
return -1;
|
|
155
131
|
}
|
|
156
|
-
|
|
132
|
+
(0, core_utils_1.assert)(isClientMessage(message), 0x419 /* client sends non-client message */);
|
|
133
|
+
if (contents !== undefined) {
|
|
134
|
+
this.opsSize += contents.length;
|
|
135
|
+
}
|
|
157
136
|
this.messageBuffer.push(message);
|
|
137
|
+
if (message.type === protocol_definitions_1.MessageType.NoOp) {
|
|
138
|
+
this.noOpCount++;
|
|
139
|
+
}
|
|
158
140
|
this.emit("submitOp", message);
|
|
159
141
|
if (!batch) {
|
|
160
142
|
this.flush();
|
|
161
143
|
}
|
|
162
144
|
return message.clientSequenceNumber;
|
|
163
145
|
}
|
|
164
|
-
submitSignal(content) {
|
|
146
|
+
submitSignal(content, targetClientId) {
|
|
147
|
+
return this.connectionManager.submitSignal(content, targetClientId);
|
|
148
|
+
}
|
|
165
149
|
flush() {
|
|
166
|
-
|
|
150
|
+
const batch = this.messageBuffer;
|
|
151
|
+
if (batch.length === 0) {
|
|
167
152
|
return;
|
|
168
153
|
}
|
|
169
|
-
// The prepareFlush event allows listeners to append metadata to the batch prior to submission.
|
|
170
|
-
this.emit("prepareSend", this.messageBuffer);
|
|
171
|
-
this.connectionManager.sendMessages(this.messageBuffer);
|
|
172
154
|
this.messageBuffer = [];
|
|
155
|
+
// The prepareFlush event allows listeners to append metadata to the batch prior to submission.
|
|
156
|
+
this.emit("prepareSend", batch);
|
|
157
|
+
if (batch.length === 1) {
|
|
158
|
+
(0, core_utils_1.assert)(batch[0].metadata?.batch === undefined, 0x3c9 /* no batch markup on single message */);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
(0, core_utils_1.assert)(batch[0].metadata?.batch === true, 0x3ca /* no start batch markup */);
|
|
162
|
+
(0, core_utils_1.assert)(batch[batch.length - 1].metadata?.batch === false, 0x3cb /* no end batch markup */);
|
|
163
|
+
}
|
|
164
|
+
this.connectionManager.sendMessages(batch);
|
|
165
|
+
(0, core_utils_1.assert)(this.messageBuffer.length === 0, 0x3cc /* reentrancy */);
|
|
173
166
|
}
|
|
174
167
|
get connectionProps() {
|
|
175
|
-
return
|
|
168
|
+
return {
|
|
169
|
+
sequenceNumber: this.lastSequenceNumber,
|
|
170
|
+
opsSize: this.opsSize > 0 ? this.opsSize : undefined,
|
|
171
|
+
deltaManagerState: this._disposed ? "disposed" : this._closed ? "closed" : "open",
|
|
172
|
+
...this.connectionManager.connectionProps,
|
|
173
|
+
};
|
|
176
174
|
}
|
|
177
175
|
/**
|
|
178
176
|
* Log error event with a bunch of internal to DeltaManager information about state of op processing
|
|
@@ -181,15 +179,121 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
181
179
|
* @param event - Event to log.
|
|
182
180
|
*/
|
|
183
181
|
logConnectionIssue(event) {
|
|
184
|
-
|
|
185
|
-
(0, common_utils_1.assert)(this.connectionManager.connected, 0x238 /* "called only in connected state" */);
|
|
182
|
+
(0, core_utils_1.assert)(this.connectionManager.connected, 0x238 /* "called only in connected state" */);
|
|
186
183
|
const pendingSorted = this.pending.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
|
|
187
|
-
this.logger.
|
|
184
|
+
this.logger.sendTelemetryEvent({
|
|
185
|
+
...event,
|
|
188
186
|
// This directly tells us if fetching ops is in flight, and thus likely the reason of
|
|
189
187
|
// stalled op processing
|
|
190
|
-
fetchReason: this.fetchReason,
|
|
188
|
+
fetchReason: this.fetchReason,
|
|
191
189
|
// A bunch of useful sequence numbers to understand if we are holding some ops from processing
|
|
192
|
-
lastQueuedSequenceNumber: this.lastQueuedSequenceNumber,
|
|
190
|
+
lastQueuedSequenceNumber: this.lastQueuedSequenceNumber,
|
|
191
|
+
lastProcessedSequenceNumber: this.lastProcessedSequenceNumber,
|
|
192
|
+
lastObserved: this.lastObservedSeqNumber,
|
|
193
|
+
// connection info
|
|
194
|
+
...this.connectionManager.connectionVerboseProps,
|
|
195
|
+
pendingOps: this.pending.length,
|
|
196
|
+
pendingFirst: pendingSorted[0]?.sequenceNumber,
|
|
197
|
+
haveHandler: this.handler !== undefined,
|
|
198
|
+
inboundLength: this.inbound.length,
|
|
199
|
+
inboundPaused: this.inbound.paused,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
constructor(serviceProvider, logger, _active, createConnectionManager) {
|
|
203
|
+
super();
|
|
204
|
+
this.serviceProvider = serviceProvider;
|
|
205
|
+
this.logger = logger;
|
|
206
|
+
this._active = _active;
|
|
207
|
+
this.pending = [];
|
|
208
|
+
// A boolean used to assert that ops are not being sent while processing another op.
|
|
209
|
+
this.currentlyProcessingOps = false;
|
|
210
|
+
// The minimum sequence number and last sequence number received from the server
|
|
211
|
+
this.minSequenceNumber = 0;
|
|
212
|
+
// There are three numbers we track
|
|
213
|
+
// * lastQueuedSequenceNumber is the last queued sequence number. If there are gaps in seq numbers, then this number
|
|
214
|
+
// is not updated until we cover that gap, so it increases each time by 1.
|
|
215
|
+
// * lastObservedSeqNumber is an estimation of last known sequence number for container in storage. It's initially
|
|
216
|
+
// populated at web socket connection time (if storage provides that info) and is updated once ops shows up.
|
|
217
|
+
// It's never less than lastQueuedSequenceNumber
|
|
218
|
+
// * lastProcessedSequenceNumber - last processed sequence number
|
|
219
|
+
this.lastQueuedSequenceNumber = 0;
|
|
220
|
+
this.lastObservedSeqNumber = 0;
|
|
221
|
+
this.lastProcessedSequenceNumber = 0;
|
|
222
|
+
/** count number of noops sent by the client which may not be acked */
|
|
223
|
+
this.noOpCount = 0;
|
|
224
|
+
/** Track clientSequenceNumber of the last op */
|
|
225
|
+
this.lastClientSequenceNumber = 0;
|
|
226
|
+
/**
|
|
227
|
+
* Track down the ops size.
|
|
228
|
+
*/
|
|
229
|
+
this.opsSize = 0;
|
|
230
|
+
// The sequence number we initially loaded from
|
|
231
|
+
// In case of reading from a snapshot or pending state, its value will be equal to
|
|
232
|
+
// the last message that got serialized.
|
|
233
|
+
this.initSequenceNumber = 0;
|
|
234
|
+
this._closed = false;
|
|
235
|
+
this._disposed = false;
|
|
236
|
+
this.throttlingIdSet = new Set();
|
|
237
|
+
this.timeTillThrottling = 0;
|
|
238
|
+
this.closeAbortController = new AbortController();
|
|
239
|
+
this.deltaStorageDelayId = (0, uuid_1.v4)();
|
|
240
|
+
this.deltaStreamDelayId = (0, uuid_1.v4)();
|
|
241
|
+
this.messageBuffer = [];
|
|
242
|
+
const props = {
|
|
243
|
+
incomingOpHandler: (messages, reason) => {
|
|
244
|
+
try {
|
|
245
|
+
this.enqueueMessages(messages, reason);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
this.logger.sendErrorEvent({ eventName: "EnqueueMessages_Exception" }, error);
|
|
249
|
+
this.close((0, telemetry_utils_1.normalizeError)(error));
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
signalHandler: (signals) => {
|
|
253
|
+
for (const signal of signals) {
|
|
254
|
+
this._inboundSignal.push(signal);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
reconnectionDelayHandler: (delayMs, error) => this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
|
|
258
|
+
closeHandler: (error) => this.close(error),
|
|
259
|
+
disconnectHandler: (reason) => this.disconnectHandler(reason),
|
|
260
|
+
connectHandler: (connection) => this.connectHandler(connection),
|
|
261
|
+
pongHandler: (latency) => this.emit("pong", latency),
|
|
262
|
+
readonlyChangeHandler: (readonly, readonlyConnectionReason) => {
|
|
263
|
+
(0, telemetry_utils_1.safeRaiseEvent)(this, this.logger, "readonly", readonly, readonlyConnectionReason);
|
|
264
|
+
},
|
|
265
|
+
establishConnectionHandler: (reason) => this.establishingConnection(reason),
|
|
266
|
+
cancelConnectionHandler: (reason) => this.cancelEstablishingConnection(reason),
|
|
267
|
+
};
|
|
268
|
+
this.connectionManager = createConnectionManager(props);
|
|
269
|
+
this._inbound = new deltaQueue_1.DeltaQueue((op) => {
|
|
270
|
+
this.processInboundMessage(op);
|
|
271
|
+
});
|
|
272
|
+
this._inbound.on("error", (error) => {
|
|
273
|
+
this.close(telemetry_utils_1.DataProcessingError.wrapIfUnrecognized(error, "deltaManagerInboundErrorHandler", this.lastMessage));
|
|
274
|
+
});
|
|
275
|
+
// Inbound signal queue
|
|
276
|
+
this._inboundSignal = new deltaQueue_1.DeltaQueue((message) => {
|
|
277
|
+
if (this.handler === undefined) {
|
|
278
|
+
throw new Error("Attempted to process an inbound signal without a handler attached");
|
|
279
|
+
}
|
|
280
|
+
this.handler.processSignal({
|
|
281
|
+
clientId: message.clientId,
|
|
282
|
+
content: JSON.parse(message.content),
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
this._inboundSignal.on("error", (error) => {
|
|
286
|
+
this.close((0, telemetry_utils_1.normalizeError)(error));
|
|
287
|
+
});
|
|
288
|
+
// Initially, all queues are created paused.
|
|
289
|
+
// - outbound is flipped back and forth in setupNewSuccessfulConnection / disconnectFromDeltaStream
|
|
290
|
+
// - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
|
|
291
|
+
}
|
|
292
|
+
cancelEstablishingConnection(reason) {
|
|
293
|
+
this.emit("cancelEstablishingConnection", reason);
|
|
294
|
+
}
|
|
295
|
+
establishingConnection(reason) {
|
|
296
|
+
this.emit("establishingConnection", reason);
|
|
193
297
|
}
|
|
194
298
|
connectHandler(connection) {
|
|
195
299
|
this.refreshDelayInfo(this.deltaStreamDelayId);
|
|
@@ -206,10 +310,12 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
206
310
|
// but it's safe to assume (until better design is put into place) that batches should not exist
|
|
207
311
|
// across multiple connections. Right now we assume runtime will not submit any ops in disconnected
|
|
208
312
|
// state. As requirements change, so should these checks.
|
|
209
|
-
(0,
|
|
313
|
+
(0, core_utils_1.assert)(this.messageBuffer.length === 0, 0x0e9 /* "messageBuffer is not empty on new connection" */);
|
|
210
314
|
this.opsSize = 0;
|
|
211
|
-
this.
|
|
212
|
-
|
|
315
|
+
this.noOpCount = 0;
|
|
316
|
+
this.emit("connect", connection, checkpointSequenceNumber !== undefined
|
|
317
|
+
? this.lastObservedSeqNumber - this.lastSequenceNumber
|
|
318
|
+
: undefined);
|
|
213
319
|
// If we got some initial ops, then we know the gap and call above fetched ops to fill it.
|
|
214
320
|
// Same is true for "write" mode even if we have no ops - we will get "join" own op very very soon.
|
|
215
321
|
// However if we are connecting as view-only, then there is no good signal to realize if client is behind.
|
|
@@ -225,31 +331,27 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
225
331
|
this.fetchMissingDeltas("AfterReadConnection");
|
|
226
332
|
}
|
|
227
333
|
}
|
|
228
|
-
dispose() {
|
|
229
|
-
throw new Error("Not implemented.");
|
|
230
|
-
}
|
|
231
334
|
/**
|
|
232
335
|
* Sets the sequence number from which inbound messages should be returned
|
|
233
336
|
*/
|
|
234
|
-
async attachOpHandler(minSequenceNumber, sequenceNumber,
|
|
337
|
+
async attachOpHandler(minSequenceNumber, sequenceNumber, handler, prefetchType = "none") {
|
|
235
338
|
this.initSequenceNumber = sequenceNumber;
|
|
236
339
|
this.lastProcessedSequenceNumber = sequenceNumber;
|
|
237
|
-
this.baseTerm = term;
|
|
238
340
|
this.minSequenceNumber = minSequenceNumber;
|
|
239
341
|
this.lastQueuedSequenceNumber = sequenceNumber;
|
|
240
342
|
this.lastObservedSeqNumber = sequenceNumber;
|
|
241
343
|
// We will use same check in other places to make sure all the seq number above are set properly.
|
|
242
|
-
(0,
|
|
344
|
+
(0, core_utils_1.assert)(this.handler === undefined, 0x0e2 /* "DeltaManager already has attached op handler!" */);
|
|
243
345
|
this.handler = handler;
|
|
244
346
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
245
|
-
(0,
|
|
347
|
+
(0, core_utils_1.assert)(!!this.handler, 0x0e3 /* "Newly set op handler is null/undefined!" */);
|
|
246
348
|
// There should be no pending fetch!
|
|
247
349
|
// This API is called right after attachOpHandler by Container.load().
|
|
248
350
|
// We might have connection already and it might have called fetchMissingDeltas() from
|
|
249
351
|
// setupNewSuccessfulConnection. But it should do nothing, because there is no way to fetch ops before
|
|
250
352
|
// we know snapshot sequence number that is set in attachOpHandler. So all such calls should be noop.
|
|
251
|
-
(0,
|
|
252
|
-
if (this.
|
|
353
|
+
(0, core_utils_1.assert)(this.fetchReason === undefined, 0x268 /* "There can't be pending fetch that early in boot sequence!" */);
|
|
354
|
+
if (this._closed) {
|
|
253
355
|
return;
|
|
254
356
|
}
|
|
255
357
|
this._inbound.resume();
|
|
@@ -267,12 +369,11 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
267
369
|
}
|
|
268
370
|
}
|
|
269
371
|
// Ensure there is no need to call this.processPendingOps() at the end of boot sequence
|
|
270
|
-
(0,
|
|
372
|
+
(0, core_utils_1.assert)(this.fetchReason !== undefined || this.pending.length === 0, 0x269 /* "pending ops are not dropped" */);
|
|
271
373
|
}
|
|
272
374
|
connect(args) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
(0, telemetry_utils_1.logIfFalse)(this.handler !== undefined || !fetchOpsFromStorage, this.logger, "CantFetchWithoutBaseline"); // can't fetch if no baseline
|
|
375
|
+
const fetchOpsFromStorage = args.fetchOpsFromStorage ?? true;
|
|
376
|
+
logIfFalse(this.handler !== undefined || !fetchOpsFromStorage, this.logger, "CantFetchWithoutBaseline"); // can't fetch if no baseline
|
|
276
377
|
// Note: There is race condition here.
|
|
277
378
|
// We want to issue request to storage as soon as possible, to
|
|
278
379
|
// reduce latency of becoming current, thus this code here.
|
|
@@ -284,9 +385,9 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
284
385
|
// on the wire, we might be always behind.
|
|
285
386
|
// See comment at the end of "connect" handler
|
|
286
387
|
if (fetchOpsFromStorage) {
|
|
287
|
-
this.fetchMissingDeltas(args.reason);
|
|
388
|
+
this.fetchMissingDeltas(args.reason.text);
|
|
288
389
|
}
|
|
289
|
-
this.connectionManager.connect(args.mode);
|
|
390
|
+
this.connectionManager.connect(args.reason, args.mode);
|
|
290
391
|
}
|
|
291
392
|
async getDeltas(from, // inclusive
|
|
292
393
|
to, // exclusive
|
|
@@ -304,8 +405,14 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
304
405
|
// It is possible that due to asynchrony (including await above), required ops were already
|
|
305
406
|
// received through delta stream. Validate that before moving forward.
|
|
306
407
|
if (this.lastQueuedSequenceNumber >= lastExpectedOp) {
|
|
307
|
-
this.logger.sendPerformanceEvent(
|
|
308
|
-
|
|
408
|
+
this.logger.sendPerformanceEvent({
|
|
409
|
+
reason: fetchReason,
|
|
410
|
+
eventName: "ExtraStorageCall",
|
|
411
|
+
early: true,
|
|
412
|
+
from,
|
|
413
|
+
to,
|
|
414
|
+
...this.connectionManager.connectionVerboseProps,
|
|
415
|
+
});
|
|
309
416
|
return;
|
|
310
417
|
}
|
|
311
418
|
// Be prepared for the case where webSocket would receive the ops that we are trying to fill through
|
|
@@ -322,22 +429,22 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
322
429
|
// That said, if we have socket connection, make sure we got ops up to checkpointSequenceNumber!
|
|
323
430
|
cancelFetch = (op) => op.sequenceNumber >= this.lastObservedSeqNumber;
|
|
324
431
|
}
|
|
325
|
-
const controller = new
|
|
432
|
+
const controller = new AbortController();
|
|
326
433
|
let opsFromFetch = false;
|
|
327
434
|
const opListener = (op) => {
|
|
328
|
-
(0,
|
|
435
|
+
(0, core_utils_1.assert)(op.sequenceNumber === this.lastQueuedSequenceNumber, 0x23a /* "seq#'s" */);
|
|
329
436
|
// Ops that are coming from this request should not cancel itself.
|
|
330
437
|
// This is useless for known ranges (to is defined) as it means request is over either way.
|
|
331
438
|
// And it will cancel unbound request too early, not allowing us to learn where the end of the file is.
|
|
332
439
|
if (!opsFromFetch && cancelFetch(op)) {
|
|
333
|
-
controller.abort();
|
|
440
|
+
controller.abort("DeltaManager getDeltas fetch cancelled");
|
|
334
441
|
this._inbound.off("push", opListener);
|
|
335
442
|
}
|
|
336
443
|
};
|
|
337
444
|
try {
|
|
338
445
|
this._inbound.on("push", opListener);
|
|
339
|
-
(0,
|
|
340
|
-
this.closeAbortController.signal.onabort = () => controller.abort();
|
|
446
|
+
(0, core_utils_1.assert)(this.closeAbortController.signal.onabort === null, 0x1e8 /* "reentrancy" */);
|
|
447
|
+
this.closeAbortController.signal.onabort = () => controller.abort(this.closeAbortController.signal.reason);
|
|
341
448
|
const stream = this.deltaStorage.fetchMessages(from, // inclusive
|
|
342
449
|
to, // exclusive
|
|
343
450
|
controller.signal, cacheOnly, fetchReason);
|
|
@@ -357,21 +464,61 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
357
464
|
}
|
|
358
465
|
}
|
|
359
466
|
finally {
|
|
467
|
+
if (controller.signal.aborted) {
|
|
468
|
+
this.logger.sendTelemetryEvent({
|
|
469
|
+
eventName: "DeltaManager_GetDeltasAborted",
|
|
470
|
+
fetchReason,
|
|
471
|
+
reason: controller.signal.reason,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
360
474
|
this.closeAbortController.signal.onabort = null;
|
|
361
475
|
this._inbound.off("push", opListener);
|
|
362
|
-
(0,
|
|
476
|
+
(0, core_utils_1.assert)(!opsFromFetch, 0x289 /* "logic error" */);
|
|
363
477
|
}
|
|
364
478
|
}
|
|
365
479
|
/**
|
|
366
480
|
* Closes the connection and clears inbound & outbound queues.
|
|
481
|
+
*
|
|
482
|
+
* Differences from dispose:
|
|
483
|
+
* - close will trigger readonly notification
|
|
484
|
+
* - close emits "closed"
|
|
485
|
+
* - close cannot be called after dispose
|
|
367
486
|
*/
|
|
368
487
|
close(error) {
|
|
369
|
-
if (this.
|
|
488
|
+
if (this._closed) {
|
|
370
489
|
return;
|
|
371
490
|
}
|
|
372
|
-
this.
|
|
373
|
-
this.connectionManager.dispose(error);
|
|
374
|
-
this.
|
|
491
|
+
this._closed = true;
|
|
492
|
+
this.connectionManager.dispose(error, true /* switchToReadonly */);
|
|
493
|
+
this.clearQueues();
|
|
494
|
+
this.emit("closed", error);
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Disposes the connection and clears the inbound & outbound queues.
|
|
498
|
+
*
|
|
499
|
+
* Differences from close:
|
|
500
|
+
* - dispose will emit "disposed"
|
|
501
|
+
* - dispose will remove all listeners
|
|
502
|
+
* - dispose can be called after closure
|
|
503
|
+
*/
|
|
504
|
+
dispose(error) {
|
|
505
|
+
if (this._disposed) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (error !== undefined && !(0, telemetry_utils_1.isFluidError)(error)) {
|
|
509
|
+
throw new telemetry_utils_1.UsageError("Error must be a Fluid error");
|
|
510
|
+
}
|
|
511
|
+
this._disposed = true;
|
|
512
|
+
this._closed = true; // We consider "disposed" as a further state than "closed"
|
|
513
|
+
this.connectionManager.dispose(error, false /* switchToReadonly */);
|
|
514
|
+
this.clearQueues();
|
|
515
|
+
// This needs to be the last thing we do (before removing listeners), as it causes
|
|
516
|
+
// Container to dispose context and break ability of data stores / runtime to "hear" from delta manager.
|
|
517
|
+
this.emit("disposed", error);
|
|
518
|
+
this.removeAllListeners();
|
|
519
|
+
}
|
|
520
|
+
clearQueues() {
|
|
521
|
+
this.closeAbortController.abort("DeltaManager is closed");
|
|
375
522
|
this._inbound.clear();
|
|
376
523
|
this._inboundSignal.clear();
|
|
377
524
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
@@ -380,11 +527,6 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
380
527
|
this._inboundSignal.pause();
|
|
381
528
|
// Drop pending messages - this will ensure catchUp() does not go into infinite loop
|
|
382
529
|
this.pending = [];
|
|
383
|
-
// This needs to be the last thing we do (before removing listeners), as it causes
|
|
384
|
-
// Container to dispose context and break ability of data stores / runtime to "hear"
|
|
385
|
-
// from delta manager, including notification (above) about readonly state.
|
|
386
|
-
this.emit("closed", error);
|
|
387
|
-
this.removeAllListeners();
|
|
388
530
|
}
|
|
389
531
|
refreshDelayInfo(id) {
|
|
390
532
|
this.throttlingIdSet.delete(id);
|
|
@@ -405,9 +547,9 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
405
547
|
emitDelayInfo(id, delayMs, error) {
|
|
406
548
|
const timeNow = Date.now();
|
|
407
549
|
this.throttlingIdSet.add(id);
|
|
408
|
-
if (delayMs > 0 &&
|
|
550
|
+
if (delayMs > 0 && timeNow + delayMs > this.timeTillThrottling) {
|
|
409
551
|
this.timeTillThrottling = timeNow + delayMs;
|
|
410
|
-
const throttlingWarning =
|
|
552
|
+
const throttlingWarning = error_1.ThrottlingWarning.wrap(error, delayMs / 1000 /* retryAfterSeconds */, this.logger);
|
|
411
553
|
this.emit("throttled", throttlingWarning);
|
|
412
554
|
}
|
|
413
555
|
}
|
|
@@ -416,13 +558,12 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
416
558
|
// for example, it's not clear if serverMetadata or timestamp property is a property of message or server state.
|
|
417
559
|
// We only extract the most obvious fields that are sufficient (with high probability) to detect sequence number
|
|
418
560
|
// reuse.
|
|
419
|
-
// Also payload goes to telemetry, so no
|
|
561
|
+
// Also payload goes to telemetry, so no content or anything else that shouldn't be logged for privacy reasons
|
|
420
562
|
// Note: It's possible for a duplicate op to be broadcasted and have everything the same except the timestamp.
|
|
421
563
|
comparableMessagePayload(m) {
|
|
422
564
|
return `${m.clientId}-${m.type}-${m.minimumSequenceNumber}-${m.referenceSequenceNumber}-${m.timestamp}`;
|
|
423
565
|
}
|
|
424
566
|
enqueueMessages(messages, reason, allowGaps = false) {
|
|
425
|
-
var _a, _b;
|
|
426
567
|
if (this.handler === undefined) {
|
|
427
568
|
// We did not setup handler yet.
|
|
428
569
|
// This happens when we connect to web socket faster than we get attributes for container
|
|
@@ -438,7 +579,7 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
438
579
|
// It's responsibility of
|
|
439
580
|
// - attachOpHandler()
|
|
440
581
|
// - fetchMissingDeltas() after it's done with querying storage
|
|
441
|
-
(0,
|
|
582
|
+
(0, core_utils_1.assert)(this.pending.length === 0 || this.fetchReason !== undefined, 0x1e9 /* "Pending ops" */);
|
|
442
583
|
if (messages.length === 0) {
|
|
443
584
|
return;
|
|
444
585
|
}
|
|
@@ -469,31 +610,48 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
469
610
|
}
|
|
470
611
|
let eventName;
|
|
471
612
|
// Report if we found some issues
|
|
472
|
-
if (duplicate !== 0 ||
|
|
613
|
+
if (duplicate !== 0 ||
|
|
614
|
+
(gap !== 0 && !allowGaps) ||
|
|
615
|
+
(initialGap > 0 && this.fetchReason === undefined)) {
|
|
473
616
|
eventName = "enqueueMessages";
|
|
474
617
|
// Also report if we are fetching ops, and same range comes in, thus making this fetch obsolete.
|
|
475
618
|
}
|
|
476
|
-
else if (this.fetchReason !== undefined &&
|
|
477
|
-
|
|
619
|
+
else if (this.fetchReason !== undefined &&
|
|
620
|
+
this.fetchReason !== reason &&
|
|
621
|
+
from <= this.lastQueuedSequenceNumber + 1 &&
|
|
622
|
+
last > this.lastQueuedSequenceNumber) {
|
|
478
623
|
eventName = "enqueueMessagesExtraFetch";
|
|
479
624
|
}
|
|
480
625
|
// Report if there is something to report
|
|
481
626
|
// Do not report when pending fetch is in progress, as such reporting will not
|
|
482
627
|
// correctly take into account pending ops.
|
|
483
628
|
if (eventName !== undefined) {
|
|
484
|
-
this.logger.sendPerformanceEvent(
|
|
485
|
-
|
|
629
|
+
this.logger.sendPerformanceEvent({
|
|
630
|
+
eventName,
|
|
631
|
+
reason,
|
|
632
|
+
previousReason: this.prevEnqueueMessagesReason,
|
|
633
|
+
from,
|
|
634
|
+
to: last + 1,
|
|
635
|
+
length: messages.length,
|
|
636
|
+
fetchReason: this.fetchReason,
|
|
637
|
+
duplicate: duplicate > 0 ? duplicate : undefined,
|
|
638
|
+
initialGap: initialGap !== 0 ? initialGap : undefined,
|
|
639
|
+
gap: gap > 0 ? gap : undefined,
|
|
640
|
+
firstMissing,
|
|
641
|
+
dmInitialSeqNumber: this.initialSequenceNumber,
|
|
642
|
+
...this.connectionManager.connectionVerboseProps,
|
|
643
|
+
});
|
|
486
644
|
}
|
|
487
645
|
}
|
|
488
646
|
this.updateLatestKnownOpSeqNumber(messages[messages.length - 1].sequenceNumber);
|
|
489
|
-
const n =
|
|
490
|
-
(0,
|
|
647
|
+
const n = this.previouslyProcessedMessage?.sequenceNumber;
|
|
648
|
+
(0, core_utils_1.assert)(n === undefined || n === this.lastQueuedSequenceNumber, 0x0ec /* "Unexpected value for previously processed message's sequence number" */);
|
|
491
649
|
for (const message of messages) {
|
|
492
650
|
// Check that the messages are arriving in the expected order
|
|
493
651
|
if (message.sequenceNumber <= this.lastQueuedSequenceNumber) {
|
|
494
652
|
// Validate that we do not have data loss, i.e. sequencing is reset and started again
|
|
495
653
|
// with numbers that this client already observed before.
|
|
496
|
-
if (
|
|
654
|
+
if (this.previouslyProcessedMessage?.sequenceNumber === message.sequenceNumber) {
|
|
497
655
|
const message1 = this.comparableMessagePayload(this.previouslyProcessedMessage);
|
|
498
656
|
const message2 = this.comparableMessagePayload(message);
|
|
499
657
|
if (message1 !== message2) {
|
|
@@ -505,8 +663,8 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
505
663
|
// hit. One example is that some clients could be submitting ops to two different service
|
|
506
664
|
// instances such that the same sequence number is reused for two different ops.
|
|
507
665
|
// pre-0.58 error message: twoMessagesWithSameSeqNumAndDifferentPayload
|
|
508
|
-
"Found two messages with the same sequenceNumber but different payloads. Likely to be a "
|
|
509
|
-
|
|
666
|
+
"Found two messages with the same sequenceNumber but different payloads. Likely to be a " +
|
|
667
|
+
"service issue", driver_definitions_1.DriverErrorTypes.fileOverwrittenInStorage, {
|
|
510
668
|
clientId: this.connectionManager.clientId,
|
|
511
669
|
sequenceNumber: message.sequenceNumber,
|
|
512
670
|
message1,
|
|
@@ -534,40 +692,73 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
534
692
|
}
|
|
535
693
|
processInboundMessage(message) {
|
|
536
694
|
const startTime = Date.now();
|
|
695
|
+
(0, core_utils_1.assert)(!this.currentlyProcessingOps, 0x3af /* Already processing ops. */);
|
|
696
|
+
this.currentlyProcessingOps = true;
|
|
537
697
|
this.lastProcessedMessage = message;
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
698
|
+
const isString = typeof message.clientId === "string";
|
|
699
|
+
(0, core_utils_1.assert)(message.clientId === null || isString, 0x41a /* undefined or string */);
|
|
700
|
+
// All client messages are coming from some client, and should have clientId,
|
|
701
|
+
// and non-client message should not have clientId. But, there are two exceptions:
|
|
702
|
+
// 1. (Legacy) We can see message.type === "attach" or "chunkedOp" for legacy files before RTM
|
|
703
|
+
// 2. Non-immediate noops (contents: null) can be sent by service without clientId
|
|
704
|
+
if (!isString && isClientMessage(message) && message.type !== protocol_definitions_1.MessageType.NoOp) {
|
|
705
|
+
throw new telemetry_utils_1.DataCorruptionError("Mismatch in clientId", {
|
|
706
|
+
...(0, telemetry_utils_1.extractSafePropertiesFromMessage)(message),
|
|
707
|
+
messageType: message.type,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
542
710
|
// TODO Remove after SPO picks up the latest build.
|
|
543
|
-
if (typeof message.contents === "string"
|
|
544
|
-
|
|
545
|
-
|
|
711
|
+
if (typeof message.contents === "string" &&
|
|
712
|
+
message.contents !== "" &&
|
|
713
|
+
message.type !== protocol_definitions_1.MessageType.ClientLeave) {
|
|
546
714
|
message.contents = JSON.parse(message.contents);
|
|
547
715
|
}
|
|
716
|
+
// Validate client sequence number has no gap. Decrement the noOpCount by gap
|
|
717
|
+
// If the count ends up negative, that means we have a real gap and throw error
|
|
718
|
+
if (this.connectionManager.clientId !== undefined &&
|
|
719
|
+
this.connectionManager.clientId === message.clientId) {
|
|
720
|
+
if (message.type === protocol_definitions_1.MessageType.NoOp) {
|
|
721
|
+
this.noOpCount--;
|
|
722
|
+
}
|
|
723
|
+
const clientSeqNumGap = message.clientSequenceNumber - this.lastClientSequenceNumber - 1;
|
|
724
|
+
this.noOpCount -= clientSeqNumGap;
|
|
725
|
+
if (this.noOpCount < 0) {
|
|
726
|
+
throw new Error(`gap in client sequence number: ${clientSeqNumGap}`);
|
|
727
|
+
}
|
|
728
|
+
this.lastClientSequenceNumber = message.clientSequenceNumber;
|
|
729
|
+
}
|
|
548
730
|
this.connectionManager.beforeProcessingIncomingOp(message);
|
|
549
731
|
// Watch the minimum sequence number and be ready to update as needed
|
|
550
732
|
if (this.minSequenceNumber > message.minimumSequenceNumber) {
|
|
551
733
|
// pre-0.58 error message: msnMovesBackwards
|
|
552
|
-
throw new
|
|
734
|
+
throw new telemetry_utils_1.DataCorruptionError("Found a lower minimumSequenceNumber (msn) than previously recorded", {
|
|
735
|
+
...(0, telemetry_utils_1.extractSafePropertiesFromMessage)(message),
|
|
736
|
+
clientId: this.connectionManager.clientId,
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
// Client ops: MSN has to be lower than sequence #, as client can continue to send ops with same
|
|
740
|
+
// reference sequence number as this op.
|
|
741
|
+
// System ops (when no clients are connected) are the only ops where equation is possible.
|
|
742
|
+
const diff = message.sequenceNumber - message.minimumSequenceNumber;
|
|
743
|
+
if (diff < 0 || (diff === 0 && message.clientId !== null)) {
|
|
744
|
+
throw new telemetry_utils_1.DataCorruptionError("MSN has to be lower than sequence #", (0, telemetry_utils_1.extractSafePropertiesFromMessage)(message));
|
|
553
745
|
}
|
|
554
746
|
this.minSequenceNumber = message.minimumSequenceNumber;
|
|
555
747
|
if (message.sequenceNumber !== this.lastProcessedSequenceNumber + 1) {
|
|
556
748
|
// pre-0.58 error message: nonSequentialSequenceNumber
|
|
557
|
-
throw new
|
|
749
|
+
throw new telemetry_utils_1.DataCorruptionError("Found a non-Sequential sequenceNumber", {
|
|
750
|
+
...(0, telemetry_utils_1.extractSafePropertiesFromMessage)(message),
|
|
751
|
+
clientId: this.connectionManager.clientId,
|
|
752
|
+
});
|
|
558
753
|
}
|
|
559
754
|
this.lastProcessedSequenceNumber = message.sequenceNumber;
|
|
560
755
|
// a bunch of code assumes that this is true
|
|
561
|
-
(0,
|
|
562
|
-
// Back-compat for older server with no term
|
|
563
|
-
if (message.term === undefined) {
|
|
564
|
-
message.term = 1;
|
|
565
|
-
}
|
|
566
|
-
this.baseTerm = message.term;
|
|
756
|
+
(0, core_utils_1.assert)(this.lastProcessedSequenceNumber <= this.lastObservedSeqNumber, 0x267 /* "lastObservedSeqNumber should be updated first" */);
|
|
567
757
|
if (this.handler === undefined) {
|
|
568
758
|
throw new Error("Attempted to process an inbound message without a handler attached");
|
|
569
759
|
}
|
|
570
760
|
this.handler.process(message);
|
|
761
|
+
this.currentlyProcessingOps = false;
|
|
571
762
|
const endTime = Date.now();
|
|
572
763
|
// Should be last, after changing this.lastProcessedSequenceNumber above, as many callers
|
|
573
764
|
// test this.lastProcessedSequenceNumber instead of using op.sequenceNumber itself.
|
|
@@ -582,34 +773,36 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
582
773
|
});
|
|
583
774
|
}
|
|
584
775
|
/**
|
|
585
|
-
|
|
586
|
-
|
|
776
|
+
* Retrieves the missing deltas between the given sequence numbers
|
|
777
|
+
*/
|
|
587
778
|
async fetchMissingDeltasCore(reason, cacheOnly, to) {
|
|
588
|
-
var _a;
|
|
589
779
|
// Exit out early if we're already fetching deltas
|
|
590
780
|
if (this.fetchReason !== undefined) {
|
|
591
781
|
return;
|
|
592
782
|
}
|
|
593
|
-
if (this.
|
|
594
|
-
this.logger.sendTelemetryEvent({
|
|
783
|
+
if (this._closed) {
|
|
784
|
+
this.logger.sendTelemetryEvent({
|
|
785
|
+
eventName: "fetchMissingDeltasClosedConnection",
|
|
786
|
+
reason,
|
|
787
|
+
});
|
|
595
788
|
return;
|
|
596
789
|
}
|
|
597
790
|
if (this.handler === undefined) {
|
|
598
791
|
// We do not poses yet any information
|
|
599
|
-
(0,
|
|
792
|
+
(0, core_utils_1.assert)(this.lastQueuedSequenceNumber === 0, 0x26b /* "initial state" */);
|
|
600
793
|
return;
|
|
601
794
|
}
|
|
602
795
|
try {
|
|
603
796
|
let from = this.lastQueuedSequenceNumber + 1;
|
|
604
|
-
const n =
|
|
797
|
+
const n = this.previouslyProcessedMessage?.sequenceNumber;
|
|
605
798
|
if (n !== undefined) {
|
|
606
799
|
// If we already processed at least one op, then we have this.previouslyProcessedMessage populated
|
|
607
800
|
// and can use it to validate that we are operating on same file, i.e. it was not overwritten.
|
|
608
801
|
// Knowing about this mechanism, we could ask for op we already observed to increase validation.
|
|
609
802
|
// This is especially useful when coming out of offline mode or loading from
|
|
610
803
|
// very old cached (by client / driver) snapshot.
|
|
611
|
-
(0,
|
|
612
|
-
(0,
|
|
804
|
+
(0, core_utils_1.assert)(n === this.lastQueuedSequenceNumber, 0x0f2 /* "previouslyProcessedMessage" */);
|
|
805
|
+
(0, core_utils_1.assert)(from > 1, 0x0f3 /* "not positive" */);
|
|
613
806
|
from--;
|
|
614
807
|
}
|
|
615
808
|
const fetchReason = `${reason}_fetch`;
|
|
@@ -633,10 +826,10 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
633
826
|
* Sorts pending ops and attempts to apply them
|
|
634
827
|
*/
|
|
635
828
|
processPendingOps(reason) {
|
|
636
|
-
if (this.
|
|
829
|
+
if (this._closed) {
|
|
637
830
|
return;
|
|
638
831
|
}
|
|
639
|
-
(0,
|
|
832
|
+
(0, core_utils_1.assert)(this.handler !== undefined, 0x26c /* "handler should be installed" */);
|
|
640
833
|
const pendingSorted = this.pending.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
|
|
641
834
|
this.pending = [];
|
|
642
835
|
// Given that we do not track where these ops came from any more, it's not very
|
|
@@ -667,4 +860,4 @@ class DeltaManager extends common_utils_1.TypedEventEmitter {
|
|
|
667
860
|
}
|
|
668
861
|
}
|
|
669
862
|
exports.DeltaManager = DeltaManager;
|
|
670
|
-
//# sourceMappingURL=deltaManager.
|
|
863
|
+
//# sourceMappingURL=deltaManager.cjs.map
|