@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
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
exports.NoopHeuristic = void 0;
|
|
8
|
+
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
9
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
10
10
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
11
11
|
const defaultNoopTimeFrequency = 2000;
|
|
12
12
|
const defaultNoopCountFrequency = 50;
|
|
@@ -29,17 +29,17 @@ const defaultNoopCountFrequency = 50;
|
|
|
29
29
|
// server timeout of 2000ms should be reconsidered to be increased.
|
|
30
30
|
// 2. If there are more than 50 ops received without sending any ops, send noop to keep collab window small.
|
|
31
31
|
// Note that system ops (including noops themselves) are excluded, so it's 1 noop per 50 real ops.
|
|
32
|
-
class
|
|
33
|
-
constructor(
|
|
34
|
-
|
|
32
|
+
class NoopHeuristic extends client_utils_1.TypedEventEmitter {
|
|
33
|
+
constructor(NoopTimeFrequency = defaultNoopTimeFrequency, NoopCountFrequency = defaultNoopCountFrequency) {
|
|
34
|
+
super();
|
|
35
35
|
this.NoopCountFrequency = NoopCountFrequency;
|
|
36
|
-
this.
|
|
36
|
+
this.opsProcessedSinceOpSent = 0;
|
|
37
37
|
if (NoopTimeFrequency !== Infinity) {
|
|
38
|
-
this.timer = new
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
if (this.
|
|
42
|
-
this.
|
|
38
|
+
this.timer = new core_utils_1.Timer(NoopTimeFrequency, () => {
|
|
39
|
+
// We allow the timer to expire even if an op is sent or we disconnect.
|
|
40
|
+
// This condition is to guard against trying to send a noop anyway in that case.
|
|
41
|
+
if (this.opsProcessedSinceOpSent !== 0) {
|
|
42
|
+
this.emit("wantsNoop");
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
}
|
|
@@ -47,13 +47,7 @@ class CollabWindowTracker {
|
|
|
47
47
|
/**
|
|
48
48
|
* Schedules as ack to the server to update the reference sequence number
|
|
49
49
|
*/
|
|
50
|
-
|
|
51
|
-
// While processing a message, an immediate no-op can be requested.
|
|
52
|
-
// i.e. to expedite approve or commit phase of quorum.
|
|
53
|
-
if (immediateNoOp) {
|
|
54
|
-
this.submitNoop(true /* immediate */);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
50
|
+
notifyMessageProcessed(message) {
|
|
57
51
|
// We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN
|
|
58
52
|
// update, which updates the MSN, then ack the update, etc...).
|
|
59
53
|
// Intent here is for runtime (and DDSes) not to keep too much tracking state / memory
|
|
@@ -61,32 +55,36 @@ class CollabWindowTracker {
|
|
|
61
55
|
if (!(0, driver_utils_1.isRuntimeMessage)(message)) {
|
|
62
56
|
return;
|
|
63
57
|
}
|
|
64
|
-
this.
|
|
65
|
-
if (this.
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
this.opsProcessedSinceOpSent++;
|
|
59
|
+
if (this.opsProcessedSinceOpSent === this.NoopCountFrequency) {
|
|
60
|
+
// Wait to send a noop if we are still synchronously processing ops. This guards against two things:
|
|
61
|
+
// 1. If we're processing many ops, we may pass the frequency threshold many times. We only need to send one noop at the very end in this case.
|
|
62
|
+
// 2. We may send another (non-noop) op in response to processing those ops, e.g. an Accept op.
|
|
63
|
+
queueMicrotask(() => {
|
|
64
|
+
if (this.opsProcessedSinceOpSent >= this.NoopCountFrequency) {
|
|
65
|
+
this.emit("wantsNoop");
|
|
66
|
+
(0, core_utils_1.assert)(this.opsProcessedSinceOpSent === 0, 0x243 /* "Expected a noop to be synchronously sent" */);
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
});
|
|
68
70
|
}
|
|
69
71
|
if (this.timer !== undefined) {
|
|
70
|
-
if
|
|
72
|
+
// Start the timer if we newly have ops that want a noop.
|
|
73
|
+
// If the timer was already running (e.g. we surpassed the op count and sent a noop) this will reset it to its full duration.
|
|
74
|
+
if (this.opsProcessedSinceOpSent === 1) {
|
|
71
75
|
this.timer.restart();
|
|
72
76
|
}
|
|
73
|
-
(0,
|
|
77
|
+
(0, core_utils_1.assert)(this.timer.hasTimer, 0x242 /* "has timer" */);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
this.
|
|
79
|
-
(0, common_utils_1.assert)(this.opsCountSinceNoop === 0, 0x243 /* "stopSequenceNumberUpdate should be called as result of sending any op!" */);
|
|
80
|
+
notifyDisconnect() {
|
|
81
|
+
// No need to noop for any ops processed prior to disconnect - we are already removed from MSN calculation.
|
|
82
|
+
this.opsProcessedSinceOpSent = 0;
|
|
80
83
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// keeps sending ops. In most cases it's actually better to let it expire (at most - 4 times per second)
|
|
85
|
-
// for nothing, then have a ton of set/reset cycles.
|
|
86
|
-
// Note that Timer.restart() is smart and will not change timer expiration if we keep extending timer
|
|
87
|
-
// expiration - it will restart the timer instead when it fires with adjusted expiration.
|
|
88
|
-
// this.timer.clear();
|
|
84
|
+
notifyMessageSent() {
|
|
85
|
+
// Sending any message is as good as a noop for updating MSN.
|
|
86
|
+
this.opsProcessedSinceOpSent = 0;
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
|
-
exports.
|
|
92
|
-
//# sourceMappingURL=
|
|
89
|
+
exports.NoopHeuristic = NoopHeuristic;
|
|
90
|
+
//# sourceMappingURL=noopHeuristic.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noopHeuristic.cjs","sourceRoot":"","sources":["../src/noopHeuristic.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAAiE;AACjE,2DAA2D;AAE3D,+DAAgE;AAGhE,MAAM,wBAAwB,GAAG,IAAI,CAAC;AACtC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAMrC,6FAA6F;AAC7F,4GAA4G;AAC5G,yGAAyG;AACzG,2CAA2C;AAC3C,oHAAoH;AACpH,2FAA2F;AAC3F,kHAAkH;AAClH,+CAA+C;AAC/C,gHAAgH;AAChH,yFAAyF;AACzF,qHAAqH;AACrH,oDAAoD;AACpD,EAAE;AACF,kDAAkD;AAClD,oGAAoG;AACpG,iHAAiH;AACjH,sEAAsE;AACtE,4GAA4G;AAC5G,qGAAqG;AACrG,MAAa,aAAc,SAAQ,gCAAoC;IAItE,YACC,oBAA4B,wBAAwB,EACnC,qBAA6B,yBAAyB;QAEvE,KAAK,EAAE,CAAC;QAFS,uBAAkB,GAAlB,kBAAkB,CAAoC;QALhE,4BAAuB,GAAG,CAAC,CAAC;QAQnC,IAAI,iBAAiB,KAAK,QAAQ,EAAE;YACnC,IAAI,CAAC,KAAK,GAAG,IAAI,kBAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE;gBAC9C,uEAAuE;gBACvE,gFAAgF;gBAChF,IAAI,IAAI,CAAC,uBAAuB,KAAK,CAAC,EAAE;oBACvC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;iBACvB;YACF,CAAC,CAAC,CAAC;SACH;IACF,CAAC;IAED;;OAEG;IACI,sBAAsB,CAAC,OAAkC;QAC/D,gFAAgF;QAChF,+DAA+D;QAC/D,sFAAsF;QACtF,yCAAyC;QACzC,IAAI,CAAC,IAAA,+BAAgB,EAAC,OAAO,CAAC,EAAE;YAC/B,OAAO;SACP;QAED,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,uBAAuB,KAAK,IAAI,CAAC,kBAAkB,EAAE;YAC7D,qGAAqG;YACrG,gJAAgJ;YAChJ,+FAA+F;YAC/F,cAAc,CAAC,GAAG,EAAE;gBACnB,IAAI,IAAI,CAAC,uBAAuB,IAAI,IAAI,CAAC,kBAAkB,EAAE;oBAC5D,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;oBACvB,IAAA,mBAAM,EACL,IAAI,CAAC,uBAAuB,KAAK,CAAC,EAClC,KAAK,CAAC,gDAAgD,CACtD,CAAC;iBACF;gBACD,OAAO;YACR,CAAC,CAAC,CAAC;SACH;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE;YAC7B,yDAAyD;YACzD,6HAA6H;YAC7H,IAAI,IAAI,CAAC,uBAAuB,KAAK,CAAC,EAAE;gBACvC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;aACrB;YAED,IAAA,mBAAM,EAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;SACrD;IACF,CAAC;IAEM,gBAAgB;QACtB,2GAA2G;QAC3G,IAAI,CAAC,uBAAuB,GAAG,CAAC,CAAC;IAClC,CAAC;IAEM,iBAAiB;QACvB,6DAA6D;QAC7D,IAAI,CAAC,uBAAuB,GAAG,CAAC,CAAC;IAClC,CAAC;CACD;AArED,sCAqEC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport { assert, Timer } from \"@fluidframework/core-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils\";\nimport { IEvent } from \"@fluidframework/core-interfaces\";\n\nconst defaultNoopTimeFrequency = 2000;\nconst defaultNoopCountFrequency = 50;\n\nexport interface INoopSenderEvents extends IEvent {\n\t(event: \"wantsNoop\", listener: () => void);\n}\n\n// Here are key considerations when deciding conditions for when to send non-immediate noops:\n// 1. Sending them too often results in increase in file size and bandwidth, as well as catch up performance\n// 2. Sending too infrequently ensures that collab window is large, and as result Sequence DDS would have\n// large catchUp blobs - see Issue #6364\n// 3. Similarly, processes that rely on \"core\" snapshot (and can't parse trailing ops, including above), like search\n// parser in SPO, will result in non-accurate results due to presence of catch up blobs.\n// 4. Ordering service used 250ms timeout to coalesce non-immediate noops. It was changed to 2000 ms to allow more\n// aggressive noop sending from client side.\n// 5. Number of ops sent by all clients is proportional to number of \"write\" clients (every client sends noops),\n// but number of sequenced noops is a function of time (one op per 2 seconds at most).\n// We should consider impact to both outbound traffic (might be huge, depends on number of clients) and file size.\n// Please also see Issue #5629 for more discussions.\n//\n// With that, the current algorithm is as follows:\n// 1. Sent noop 2000 ms of receiving an op if no ops were sent by this client within this timeframe.\n// This will ensure that MSN moves forward with reasonable speed. If that results in too many sequenced noops,\n// server timeout of 2000ms should be reconsidered to be increased.\n// 2. If there are more than 50 ops received without sending any ops, send noop to keep collab window small.\n// Note that system ops (including noops themselves) are excluded, so it's 1 noop per 50 real ops.\nexport class NoopHeuristic extends TypedEventEmitter<INoopSenderEvents> {\n\tprivate opsProcessedSinceOpSent = 0;\n\tprivate readonly timer: Timer | undefined;\n\n\tconstructor(\n\t\tNoopTimeFrequency: number = defaultNoopTimeFrequency,\n\t\tprivate readonly NoopCountFrequency: number = defaultNoopCountFrequency,\n\t) {\n\t\tsuper();\n\t\tif (NoopTimeFrequency !== Infinity) {\n\t\t\tthis.timer = new Timer(NoopTimeFrequency, () => {\n\t\t\t\t// We allow the timer to expire even if an op is sent or we disconnect.\n\t\t\t\t// This condition is to guard against trying to send a noop anyway in that case.\n\t\t\t\tif (this.opsProcessedSinceOpSent !== 0) {\n\t\t\t\t\tthis.emit(\"wantsNoop\");\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Schedules as ack to the server to update the reference sequence number\n\t */\n\tpublic notifyMessageProcessed(message: ISequencedDocumentMessage): void {\n\t\t// We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN\n\t\t// update, which updates the MSN, then ack the update, etc...).\n\t\t// Intent here is for runtime (and DDSes) not to keep too much tracking state / memory\n\t\t// due to runtime ops from other clients.\n\t\tif (!isRuntimeMessage(message)) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.opsProcessedSinceOpSent++;\n\t\tif (this.opsProcessedSinceOpSent === this.NoopCountFrequency) {\n\t\t\t// Wait to send a noop if we are still synchronously processing ops. This guards against two things:\n\t\t\t// 1. If we're processing many ops, we may pass the frequency threshold many times. We only need to send one noop at the very end in this case.\n\t\t\t// 2. We may send another (non-noop) op in response to processing those ops, e.g. an Accept op.\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (this.opsProcessedSinceOpSent >= this.NoopCountFrequency) {\n\t\t\t\t\tthis.emit(\"wantsNoop\");\n\t\t\t\t\tassert(\n\t\t\t\t\t\tthis.opsProcessedSinceOpSent === 0,\n\t\t\t\t\t\t0x243 /* \"Expected a noop to be synchronously sent\" */,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t});\n\t\t}\n\n\t\tif (this.timer !== undefined) {\n\t\t\t// Start the timer if we newly have ops that want a noop.\n\t\t\t// If the timer was already running (e.g. we surpassed the op count and sent a noop) this will reset it to its full duration.\n\t\t\tif (this.opsProcessedSinceOpSent === 1) {\n\t\t\t\tthis.timer.restart();\n\t\t\t}\n\n\t\t\tassert(this.timer.hasTimer, 0x242 /* \"has timer\" */);\n\t\t}\n\t}\n\n\tpublic notifyDisconnect(): void {\n\t\t// No need to noop for any ops processed prior to disconnect - we are already removed from MSN calculation.\n\t\tthis.opsProcessedSinceOpSent = 0;\n\t}\n\n\tpublic notifyMessageSent(): void {\n\t\t// Sending any message is as good as a noop for updating MSN.\n\t\tthis.opsProcessedSinceOpSent = 0;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
6
|
+
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
7
|
+
import { IEvent } from "@fluidframework/core-interfaces";
|
|
8
|
+
export interface INoopSenderEvents extends IEvent {
|
|
9
|
+
(event: "wantsNoop", listener: () => void): any;
|
|
10
|
+
}
|
|
11
|
+
export declare class NoopHeuristic extends TypedEventEmitter<INoopSenderEvents> {
|
|
12
|
+
private readonly NoopCountFrequency;
|
|
13
|
+
private opsProcessedSinceOpSent;
|
|
14
|
+
private readonly timer;
|
|
15
|
+
constructor(NoopTimeFrequency?: number, NoopCountFrequency?: number);
|
|
16
|
+
/**
|
|
17
|
+
* Schedules as ack to the server to update the reference sequence number
|
|
18
|
+
*/
|
|
19
|
+
notifyMessageProcessed(message: ISequencedDocumentMessage): void;
|
|
20
|
+
notifyDisconnect(): void;
|
|
21
|
+
notifyMessageSent(): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=noopHeuristic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noopHeuristic.d.ts","sourceRoot":"","sources":["../src/noopHeuristic.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAKzD,MAAM,WAAW,iBAAkB,SAAQ,MAAM;IAChD,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,OAAE;CAC3C;AAqBD,qBAAa,aAAc,SAAQ,iBAAiB,CAAC,iBAAiB,CAAC;IAMrE,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IALpC,OAAO,CAAC,uBAAuB,CAAK;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAGzC,iBAAiB,GAAE,MAAiC,EACnC,kBAAkB,GAAE,MAAkC;IAcxE;;OAEG;IACI,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI;IAqChE,gBAAgB,IAAI,IAAI;IAKxB,iBAAiB,IAAI,IAAI;CAIhC"}
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.pkgVersion = exports.pkgName = void 0;
|
|
10
10
|
exports.pkgName = "@fluidframework/container-loader";
|
|
11
|
-
exports.pkgVersion = "1.
|
|
12
|
-
//# sourceMappingURL=packageVersion.
|
|
11
|
+
exports.pkgVersion = "2.0.0-dev-rc.1.0.0.224419";
|
|
12
|
+
//# sourceMappingURL=packageVersion.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageVersion.cjs","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,kCAAkC,CAAC;AAC7C,QAAA,UAAU,GAAG,2BAA2B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-loader\";\nexport const pkgVersion = \"2.0.0-dev-rc.1.0.0.224419\";\n"]}
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/container-loader";
|
|
8
|
-
export declare const pkgVersion = "1.
|
|
8
|
+
export declare const pkgVersion = "2.0.0-dev-rc.1.0.0.224419";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,qCAAqC,CAAC;AAC1D,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,qCAAqC,CAAC;AAC1D,eAAO,MAAM,UAAU,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.protocolHandlerShouldProcessSignal = exports.ProtocolHandler = exports.SignalType = void 0;
|
|
8
|
+
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
9
|
+
const protocol_base_1 = require("@fluidframework/protocol-base");
|
|
10
|
+
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
11
|
+
// ADO: #1986: Start using enum from protocol-base.
|
|
12
|
+
var SignalType;
|
|
13
|
+
(function (SignalType) {
|
|
14
|
+
SignalType["ClientJoin"] = "join";
|
|
15
|
+
SignalType["ClientLeave"] = "leave";
|
|
16
|
+
SignalType["Clear"] = "clear";
|
|
17
|
+
})(SignalType || (exports.SignalType = SignalType = {}));
|
|
18
|
+
class ProtocolHandler extends protocol_base_1.ProtocolOpHandler {
|
|
19
|
+
constructor(attributes, quorumSnapshot, sendProposal, audience, shouldClientHaveLeft) {
|
|
20
|
+
super(attributes.minimumSequenceNumber, attributes.sequenceNumber, quorumSnapshot.members, quorumSnapshot.proposals, quorumSnapshot.values, sendProposal);
|
|
21
|
+
this.audience = audience;
|
|
22
|
+
this.shouldClientHaveLeft = shouldClientHaveLeft;
|
|
23
|
+
// Join / leave signals are ignored for "write" clients in favor of join / leave ops
|
|
24
|
+
this.quorum.on("addMember", (clientId, details) => audience.addMember(clientId, details.client));
|
|
25
|
+
this.quorum.on("removeMember", (clientId) => audience.removeMember(clientId));
|
|
26
|
+
for (const [clientId, details] of this.quorum.getMembers()) {
|
|
27
|
+
this.audience.addMember(clientId, details.client);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
processMessage(message, local) {
|
|
31
|
+
// Check and report if we're getting messages from a clientId that we previously
|
|
32
|
+
// flagged as shouldHaveLeft, or from a client that's not in the quorum but should be
|
|
33
|
+
if (message.clientId != null) {
|
|
34
|
+
const client = this.quorum.getMember(message.clientId);
|
|
35
|
+
if (client === undefined && message.type !== protocol_definitions_1.MessageType.ClientJoin) {
|
|
36
|
+
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
37
|
+
throw new Error("Remote message's clientId is missing from the quorum");
|
|
38
|
+
}
|
|
39
|
+
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
40
|
+
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
41
|
+
// document we don't need to blow up aggressively.
|
|
42
|
+
if (this.shouldClientHaveLeft(message.clientId) && !(0, driver_utils_1.canBeCoalescedByService)(message)) {
|
|
43
|
+
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
44
|
+
throw new Error("Remote message's clientId already should have left");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return super.processMessage(message, local);
|
|
48
|
+
}
|
|
49
|
+
processSignal(message) {
|
|
50
|
+
const innerContent = message.content;
|
|
51
|
+
switch (innerContent.type) {
|
|
52
|
+
case SignalType.Clear: {
|
|
53
|
+
const members = this.audience.getMembers();
|
|
54
|
+
for (const [clientId, client] of members) {
|
|
55
|
+
if (client.mode === "read") {
|
|
56
|
+
this.audience.removeMember(clientId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case SignalType.ClientJoin: {
|
|
62
|
+
const newClient = innerContent.content;
|
|
63
|
+
// Ignore write clients - quorum will control such clients.
|
|
64
|
+
if (newClient.client.mode === "read") {
|
|
65
|
+
this.audience.addMember(newClient.clientId, newClient.client);
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case SignalType.ClientLeave: {
|
|
70
|
+
const leftClientId = innerContent.content;
|
|
71
|
+
// Ignore write clients - quorum will control such clients.
|
|
72
|
+
if (this.audience.getMember(leftClientId)?.mode === "read") {
|
|
73
|
+
this.audience.removeMember(leftClientId);
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.ProtocolHandler = ProtocolHandler;
|
|
83
|
+
/**
|
|
84
|
+
* Function to check whether the protocol handler should process the Signal.
|
|
85
|
+
* The protocol handler should strictly handle only ClientJoin, ClientLeave
|
|
86
|
+
* and Clear signal types.
|
|
87
|
+
*/
|
|
88
|
+
function protocolHandlerShouldProcessSignal(message) {
|
|
89
|
+
// Signal originates from server
|
|
90
|
+
if (message.clientId === null) {
|
|
91
|
+
const innerContent = message.content;
|
|
92
|
+
return (innerContent.type === SignalType.Clear ||
|
|
93
|
+
innerContent.type === SignalType.ClientJoin ||
|
|
94
|
+
innerContent.type === SignalType.ClientLeave);
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
exports.protocolHandlerShouldProcessSignal = protocolHandlerShouldProcessSignal;
|
|
99
|
+
//# sourceMappingURL=protocol.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.cjs","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IAudienceOwner } from \"@fluidframework/container-definitions\";\nimport { canBeCoalescedByService } from \"@fluidframework/driver-utils\";\nimport {\n\tIProtocolHandler as IBaseProtocolHandler,\n\tIQuorumSnapshot,\n\tProtocolOpHandler,\n} from \"@fluidframework/protocol-base\";\nimport {\n\tIDocumentAttributes,\n\tIProcessMessageResult,\n\tISequencedDocumentMessage,\n\tISignalClient,\n\tISignalMessage,\n\tMessageType,\n} from \"@fluidframework/protocol-definitions\";\n\n// ADO: #1986: Start using enum from protocol-base.\nexport enum SignalType {\n\tClientJoin = \"join\", // same value as MessageType.ClientJoin,\n\tClientLeave = \"leave\", // same value as MessageType.ClientLeave,\n\tClear = \"clear\", // used only by client for synthetic signals\n}\n\n/**\n * Function to be used for creating a protocol handler.\n * @alpha\n */\nexport type ProtocolHandlerBuilder = (\n\tattributes: IDocumentAttributes,\n\tsnapshot: IQuorumSnapshot,\n\tsendProposal: (key: string, value: any) => number,\n) => IProtocolHandler;\n\n/**\n * @alpha\n */\nexport interface IProtocolHandler extends IBaseProtocolHandler {\n\treadonly audience: IAudienceOwner;\n\tprocessSignal(message: ISignalMessage);\n}\n\nexport class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandler {\n\tconstructor(\n\t\tattributes: IDocumentAttributes,\n\t\tquorumSnapshot: IQuorumSnapshot,\n\t\tsendProposal: (key: string, value: any) => number,\n\t\tpublic readonly audience: IAudienceOwner,\n\t\tprivate readonly shouldClientHaveLeft: (clientId: string) => boolean,\n\t) {\n\t\tsuper(\n\t\t\tattributes.minimumSequenceNumber,\n\t\t\tattributes.sequenceNumber,\n\t\t\tquorumSnapshot.members,\n\t\t\tquorumSnapshot.proposals,\n\t\t\tquorumSnapshot.values,\n\t\t\tsendProposal,\n\t\t);\n\n\t\t// Join / leave signals are ignored for \"write\" clients in favor of join / leave ops\n\t\tthis.quorum.on(\"addMember\", (clientId, details) =>\n\t\t\taudience.addMember(clientId, details.client),\n\t\t);\n\t\tthis.quorum.on(\"removeMember\", (clientId) => audience.removeMember(clientId));\n\t\tfor (const [clientId, details] of this.quorum.getMembers()) {\n\t\t\tthis.audience.addMember(clientId, details.client);\n\t\t}\n\t}\n\n\tpublic processMessage(\n\t\tmessage: ISequencedDocumentMessage,\n\t\tlocal: boolean,\n\t): IProcessMessageResult {\n\t\t// Check and report if we're getting messages from a clientId that we previously\n\t\t// flagged as shouldHaveLeft, or from a client that's not in the quorum but should be\n\t\tif (message.clientId != null) {\n\t\t\tconst client = this.quorum.getMember(message.clientId);\n\n\t\t\tif (client === undefined && message.type !== MessageType.ClientJoin) {\n\t\t\t\t// pre-0.58 error message: messageClientIdMissingFromQuorum\n\t\t\t\tthrow new Error(\"Remote message's clientId is missing from the quorum\");\n\t\t\t}\n\n\t\t\t// Here checking canBeCoalescedByService is used as an approximation of \"is benign to process despite being unexpected\".\n\t\t\t// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the\n\t\t\t// document we don't need to blow up aggressively.\n\t\t\tif (this.shouldClientHaveLeft(message.clientId) && !canBeCoalescedByService(message)) {\n\t\t\t\t// pre-0.58 error message: messageClientIdShouldHaveLeft\n\t\t\t\tthrow new Error(\"Remote message's clientId already should have left\");\n\t\t\t}\n\t\t}\n\n\t\treturn super.processMessage(message, local);\n\t}\n\n\tpublic processSignal(message: ISignalMessage) {\n\t\tconst innerContent = message.content as { content: any; type: string };\n\t\tswitch (innerContent.type) {\n\t\t\tcase SignalType.Clear: {\n\t\t\t\tconst members = this.audience.getMembers();\n\t\t\t\tfor (const [clientId, client] of members) {\n\t\t\t\t\tif (client.mode === \"read\") {\n\t\t\t\t\t\tthis.audience.removeMember(clientId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SignalType.ClientJoin: {\n\t\t\t\tconst newClient = innerContent.content as ISignalClient;\n\t\t\t\t// Ignore write clients - quorum will control such clients.\n\t\t\t\tif (newClient.client.mode === \"read\") {\n\t\t\t\t\tthis.audience.addMember(newClient.clientId, newClient.client);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SignalType.ClientLeave: {\n\t\t\t\tconst leftClientId = innerContent.content as string;\n\t\t\t\t// Ignore write clients - quorum will control such clients.\n\t\t\t\tif (this.audience.getMember(leftClientId)?.mode === \"read\") {\n\t\t\t\t\tthis.audience.removeMember(leftClientId);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/**\n * Function to check whether the protocol handler should process the Signal.\n * The protocol handler should strictly handle only ClientJoin, ClientLeave\n * and Clear signal types.\n */\nexport function protocolHandlerShouldProcessSignal(message: ISignalMessage) {\n\t// Signal originates from server\n\tif (message.clientId === null) {\n\t\tconst innerContent = message.content as { content: unknown; type: string };\n\t\treturn (\n\t\t\tinnerContent.type === SignalType.Clear ||\n\t\t\tinnerContent.type === SignalType.ClientJoin ||\n\t\t\tinnerContent.type === SignalType.ClientLeave\n\t\t);\n\t}\n\treturn false;\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { IAudienceOwner } from "@fluidframework/container-definitions";
|
|
6
|
+
import { IProtocolHandler as IBaseProtocolHandler, IQuorumSnapshot, ProtocolOpHandler } from "@fluidframework/protocol-base";
|
|
7
|
+
import { IDocumentAttributes, IProcessMessageResult, ISequencedDocumentMessage, ISignalMessage } from "@fluidframework/protocol-definitions";
|
|
8
|
+
export declare enum SignalType {
|
|
9
|
+
ClientJoin = "join",
|
|
10
|
+
ClientLeave = "leave",
|
|
11
|
+
Clear = "clear"
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Function to be used for creating a protocol handler.
|
|
15
|
+
* @alpha
|
|
16
|
+
*/
|
|
17
|
+
export type ProtocolHandlerBuilder = (attributes: IDocumentAttributes, snapshot: IQuorumSnapshot, sendProposal: (key: string, value: any) => number) => IProtocolHandler;
|
|
18
|
+
/**
|
|
19
|
+
* @alpha
|
|
20
|
+
*/
|
|
21
|
+
export interface IProtocolHandler extends IBaseProtocolHandler {
|
|
22
|
+
readonly audience: IAudienceOwner;
|
|
23
|
+
processSignal(message: ISignalMessage): any;
|
|
24
|
+
}
|
|
25
|
+
export declare class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandler {
|
|
26
|
+
readonly audience: IAudienceOwner;
|
|
27
|
+
private readonly shouldClientHaveLeft;
|
|
28
|
+
constructor(attributes: IDocumentAttributes, quorumSnapshot: IQuorumSnapshot, sendProposal: (key: string, value: any) => number, audience: IAudienceOwner, shouldClientHaveLeft: (clientId: string) => boolean);
|
|
29
|
+
processMessage(message: ISequencedDocumentMessage, local: boolean): IProcessMessageResult;
|
|
30
|
+
processSignal(message: ISignalMessage): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Function to check whether the protocol handler should process the Signal.
|
|
34
|
+
* The protocol handler should strictly handle only ClientJoin, ClientLeave
|
|
35
|
+
* and Clear signal types.
|
|
36
|
+
*/
|
|
37
|
+
export declare function protocolHandlerShouldProcessSignal(message: ISignalMessage): boolean;
|
|
38
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAEvE,OAAO,EACN,gBAAgB,IAAI,oBAAoB,EACxC,eAAe,EACf,iBAAiB,EACjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACN,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EAEzB,cAAc,EAEd,MAAM,sCAAsC,CAAC;AAG9C,oBAAY,UAAU;IACrB,UAAU,SAAS;IACnB,WAAW,UAAU;IACrB,KAAK,UAAU;CACf;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACpC,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,eAAe,EACzB,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,MAAM,KAC7C,gBAAgB,CAAC;AAEtB;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC7D,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,aAAa,CAAC,OAAO,EAAE,cAAc,OAAE;CACvC;AAED,qBAAa,eAAgB,SAAQ,iBAAkB,YAAW,gBAAgB;aAKhE,QAAQ,EAAE,cAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,oBAAoB;gBAJrC,UAAU,EAAE,mBAAmB,EAC/B,cAAc,EAAE,eAAe,EAC/B,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,MAAM,EACjC,QAAQ,EAAE,cAAc,EACvB,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO;IAqB9D,cAAc,CACpB,OAAO,EAAE,yBAAyB,EAClC,KAAK,EAAE,OAAO,GACZ,qBAAqB;IAuBjB,aAAa,CAAC,OAAO,EAAE,cAAc;CAgC5C;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,cAAc,WAWzE"}
|
package/dist/{protocolTreeDocumentStorageService.js → protocolTreeDocumentStorageService.cjs}
RENAMED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.ProtocolTreeStorageService = void 0;
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* A storage service wrapper whose sole job is to intercept calls to uploadSummaryWithContext and ensure they include
|
|
10
|
+
* the protocol summary, using the provided callback to add it if necessary.
|
|
11
|
+
*/
|
|
9
12
|
class ProtocolTreeStorageService {
|
|
10
|
-
constructor(internalStorageService,
|
|
13
|
+
constructor(internalStorageService, addProtocolSummaryIfMissing) {
|
|
11
14
|
this.internalStorageService = internalStorageService;
|
|
12
|
-
this.
|
|
15
|
+
this.addProtocolSummaryIfMissing = addProtocolSummaryIfMissing;
|
|
13
16
|
this.getSnapshotTree = this.internalStorageService.getSnapshotTree.bind(this.internalStorageService);
|
|
14
17
|
this.getVersions = this.internalStorageService.getVersions.bind(this.internalStorageService);
|
|
15
18
|
this.createBlob = this.internalStorageService.createBlob.bind(this.internalStorageService);
|
|
@@ -27,8 +30,8 @@ class ProtocolTreeStorageService {
|
|
|
27
30
|
return this.internalStorageService.disposed;
|
|
28
31
|
}
|
|
29
32
|
async uploadSummaryWithContext(summary, context) {
|
|
30
|
-
return this.internalStorageService.uploadSummaryWithContext(
|
|
33
|
+
return this.internalStorageService.uploadSummaryWithContext(this.addProtocolSummaryIfMissing(summary), context);
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
exports.ProtocolTreeStorageService = ProtocolTreeStorageService;
|
|
34
|
-
//# sourceMappingURL=protocolTreeDocumentStorageService.
|
|
37
|
+
//# sourceMappingURL=protocolTreeDocumentStorageService.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocolTreeDocumentStorageService.cjs","sourceRoot":"","sources":["../src/protocolTreeDocumentStorageService.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAMH;;;GAGG;AACH,MAAa,0BAA0B;IACtC,YACkB,sBAA6D,EAC7D,2BAAwE;QADxE,2BAAsB,GAAtB,sBAAsB,CAAuC;QAC7D,gCAA2B,GAA3B,2BAA2B,CAA6C;QAY1F,oBAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAChG,gBAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACxF,eAAU,GAAG,IAAI,CAAC,sBAAsB,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACtF,aAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAClF,oBAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAChG,YAAO,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAhB7E,CAAC;IACJ,IAAW,QAAQ;QAClB,OAAO,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC;IAC7C,CAAC;IACD,IAAW,aAAa;QACvB,OAAO,IAAI,CAAC,sBAAsB,CAAC,aAAa,CAAC;IAClD,CAAC;IACD,IAAW,QAAQ;QAClB,OAAO,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC;IAC7C,CAAC;IASD,KAAK,CAAC,wBAAwB,CAC7B,OAAqB,EACrB,OAAwB;QAExB,OAAO,IAAI,CAAC,sBAAsB,CAAC,wBAAwB,CAC1D,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,EACzC,OAAO,CACP,CAAC;IACH,CAAC;CACD;AA/BD,gEA+BC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { IDocumentStorageService, ISummaryContext } from \"@fluidframework/driver-definitions\";\nimport { ISummaryTree } from \"@fluidframework/protocol-definitions\";\n\n/**\n * A storage service wrapper whose sole job is to intercept calls to uploadSummaryWithContext and ensure they include\n * the protocol summary, using the provided callback to add it if necessary.\n */\nexport class ProtocolTreeStorageService implements IDocumentStorageService, IDisposable {\n\tconstructor(\n\t\tprivate readonly internalStorageService: IDocumentStorageService & IDisposable,\n\t\tprivate readonly addProtocolSummaryIfMissing: (summaryTree: ISummaryTree) => ISummaryTree,\n\t) {}\n\tpublic get policies() {\n\t\treturn this.internalStorageService.policies;\n\t}\n\tpublic get repositoryUrl() {\n\t\treturn this.internalStorageService.repositoryUrl;\n\t}\n\tpublic get disposed() {\n\t\treturn this.internalStorageService.disposed;\n\t}\n\n\tgetSnapshotTree = this.internalStorageService.getSnapshotTree.bind(this.internalStorageService);\n\tgetVersions = this.internalStorageService.getVersions.bind(this.internalStorageService);\n\tcreateBlob = this.internalStorageService.createBlob.bind(this.internalStorageService);\n\treadBlob = this.internalStorageService.readBlob.bind(this.internalStorageService);\n\tdownloadSummary = this.internalStorageService.downloadSummary.bind(this.internalStorageService);\n\tdispose = this.internalStorageService.dispose.bind(this.internalStorageService);\n\n\tasync uploadSummaryWithContext(\n\t\tsummary: ISummaryTree,\n\t\tcontext: ISummaryContext,\n\t): Promise<string> {\n\t\treturn this.internalStorageService.uploadSummaryWithContext(\n\t\t\tthis.addProtocolSummaryIfMissing(summary),\n\t\t\tcontext,\n\t\t);\n\t}\n}\n"]}
|
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { IDisposable } from "@fluidframework/
|
|
5
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
6
6
|
import { IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
|
|
7
7
|
import { ISummaryTree } from "@fluidframework/protocol-definitions";
|
|
8
|
+
/**
|
|
9
|
+
* A storage service wrapper whose sole job is to intercept calls to uploadSummaryWithContext and ensure they include
|
|
10
|
+
* the protocol summary, using the provided callback to add it if necessary.
|
|
11
|
+
*/
|
|
8
12
|
export declare class ProtocolTreeStorageService implements IDocumentStorageService, IDisposable {
|
|
9
13
|
private readonly internalStorageService;
|
|
10
|
-
private readonly
|
|
11
|
-
constructor(internalStorageService: IDocumentStorageService & IDisposable,
|
|
14
|
+
private readonly addProtocolSummaryIfMissing;
|
|
15
|
+
constructor(internalStorageService: IDocumentStorageService & IDisposable, addProtocolSummaryIfMissing: (summaryTree: ISummaryTree) => ISummaryTree);
|
|
12
16
|
get policies(): import("@fluidframework/driver-definitions").IDocumentStorageServicePolicies | undefined;
|
|
13
17
|
get repositoryUrl(): string;
|
|
14
18
|
get disposed(): boolean;
|
|
15
19
|
getSnapshotTree: (version?: import("@fluidframework/protocol-definitions").IVersion | undefined, scenarioName?: string | undefined) => Promise<import("@fluidframework/protocol-definitions").ISnapshotTree | null>;
|
|
16
|
-
getVersions: (versionId: string | null, count: number, scenarioName?: string | undefined) => Promise<import("@fluidframework/protocol-definitions").IVersion[]>;
|
|
20
|
+
getVersions: (versionId: string | null, count: number, scenarioName?: string | undefined, fetchSource?: import("@fluidframework/driver-definitions").FetchSource | undefined) => Promise<import("@fluidframework/protocol-definitions").IVersion[]>;
|
|
17
21
|
createBlob: (file: ArrayBufferLike) => Promise<import("@fluidframework/protocol-definitions").ICreateBlobResponse>;
|
|
18
22
|
readBlob: (id: string) => Promise<ArrayBufferLike>;
|
|
19
23
|
downloadSummary: (handle: import("@fluidframework/protocol-definitions").ISummaryHandle) => Promise<ISummaryTree>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocolTreeDocumentStorageService.d.ts","sourceRoot":"","sources":["../src/protocolTreeDocumentStorageService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"protocolTreeDocumentStorageService.d.ts","sourceRoot":"","sources":["../src/protocolTreeDocumentStorageService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEpE;;;GAGG;AACH,qBAAa,0BAA2B,YAAW,uBAAuB,EAAE,WAAW;IAErF,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,2BAA2B;gBAD3B,sBAAsB,EAAE,uBAAuB,GAAG,WAAW,EAC7D,2BAA2B,EAAE,CAAC,WAAW,EAAE,YAAY,KAAK,YAAY;IAE1F,IAAW,QAAQ,6FAElB;IACD,IAAW,aAAa,WAEvB;IACD,IAAW,QAAQ,YAElB;IAED,eAAe,qMAAiF;IAChG,WAAW,yOAA6E;IACxF,UAAU,yGAA4E;IACtF,QAAQ,2CAA0E;IAClF,eAAe,mGAAiF;IAChG,OAAO,sCAAyE;IAE1E,wBAAwB,CAC7B,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,MAAM,CAAC;CAMlB"}
|
package/dist/quorum.cjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initQuorumValuesFromCodeDetails = void 0;
|
|
4
|
+
function initQuorumValuesFromCodeDetails(source) {
|
|
5
|
+
// Seed the base quorum to be an empty list with a code quorum set
|
|
6
|
+
const committedCodeProposal = {
|
|
7
|
+
key: "code",
|
|
8
|
+
value: source,
|
|
9
|
+
approvalSequenceNumber: 0,
|
|
10
|
+
commitSequenceNumber: 0,
|
|
11
|
+
sequenceNumber: 0,
|
|
12
|
+
};
|
|
13
|
+
return [["code", committedCodeProposal]];
|
|
14
|
+
}
|
|
15
|
+
exports.initQuorumValuesFromCodeDetails = initQuorumValuesFromCodeDetails;
|
|
16
|
+
//# sourceMappingURL=quorum.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quorum.cjs","sourceRoot":"","sources":["../src/quorum.ts"],"names":[],"mappings":";;;AAOA,SAAgB,+BAA+B,CAC9C,MAAyB;IAEzB,kEAAkE;IAClE,MAAM,qBAAqB,GAAuB;QACjD,GAAG,EAAE,MAAM;QACX,KAAK,EAAE,MAAM;QACb,sBAAsB,EAAE,CAAC;QACzB,oBAAoB,EAAE,CAAC;QACvB,cAAc,EAAE,CAAC;KACjB,CAAC;IACF,OAAO,CAAC,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC;AAC1C,CAAC;AAZD,0EAYC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { IFluidCodeDetails } from \"@fluidframework/core-interfaces\";\nimport { ICommittedProposal } from \"@fluidframework/protocol-definitions\";\n\nexport function initQuorumValuesFromCodeDetails(\n\tsource: IFluidCodeDetails,\n): [string, ICommittedProposal][] {\n\t// Seed the base quorum to be an empty list with a code quorum set\n\tconst committedCodeProposal: ICommittedProposal = {\n\t\tkey: \"code\",\n\t\tvalue: source,\n\t\tapprovalSequenceNumber: 0,\n\t\tcommitSequenceNumber: 0,\n\t\tsequenceNumber: 0,\n\t};\n\treturn [[\"code\", committedCodeProposal]];\n}\n"]}
|
package/dist/quorum.d.ts
CHANGED
|
@@ -2,20 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { EventForwarder } from "@fluidframework/common-utils";
|
|
6
5
|
import { IFluidCodeDetails } from "@fluidframework/core-interfaces";
|
|
7
|
-
import { ICommittedProposal
|
|
8
|
-
/**
|
|
9
|
-
* Proxies Quorum events.
|
|
10
|
-
*/
|
|
11
|
-
export declare class QuorumProxy extends EventForwarder<IQuorumEvents> implements IQuorum {
|
|
12
|
-
readonly propose: (key: string, value: any) => Promise<void>;
|
|
13
|
-
readonly has: (key: string) => boolean;
|
|
14
|
-
readonly get: (key: string) => any;
|
|
15
|
-
readonly getMembers: () => Map<string, ISequencedClient>;
|
|
16
|
-
readonly getMember: (clientId: string) => ISequencedClient | undefined;
|
|
17
|
-
constructor(quorum: IQuorum);
|
|
18
|
-
}
|
|
19
|
-
export declare function getCodeDetailsFromQuorumValues(quorumValues: [string, ICommittedProposal][]): IFluidCodeDetails;
|
|
6
|
+
import { ICommittedProposal } from "@fluidframework/protocol-definitions";
|
|
20
7
|
export declare function initQuorumValuesFromCodeDetails(source: IFluidCodeDetails): [string, ICommittedProposal][];
|
|
21
8
|
//# sourceMappingURL=quorum.d.ts.map
|
package/dist/quorum.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quorum.d.ts","sourceRoot":"","sources":["../src/quorum.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"quorum.d.ts","sourceRoot":"","sources":["../src/quorum.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAE1E,wBAAgB,+BAA+B,CAC9C,MAAM,EAAE,iBAAiB,GACvB,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,CAUhC"}
|
|
@@ -5,33 +5,42 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.RetriableDocumentStorageService = void 0;
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
9
|
+
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
10
10
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
11
11
|
class RetriableDocumentStorageService {
|
|
12
|
-
constructor(
|
|
13
|
-
this.
|
|
12
|
+
constructor(internalStorageServiceP, logger) {
|
|
13
|
+
this.internalStorageServiceP = internalStorageServiceP;
|
|
14
14
|
this.logger = logger;
|
|
15
15
|
this._disposed = false;
|
|
16
|
+
this.internalStorageServiceP.then((s) => (this.internalStorageService = s)).catch(() => { });
|
|
16
17
|
}
|
|
17
18
|
get policies() {
|
|
18
|
-
|
|
19
|
+
if (this.internalStorageService) {
|
|
20
|
+
return this.internalStorageService.policies;
|
|
21
|
+
}
|
|
22
|
+
throw new Error("storage service not yet instantiated");
|
|
23
|
+
}
|
|
24
|
+
get disposed() {
|
|
25
|
+
return this._disposed;
|
|
19
26
|
}
|
|
20
|
-
get disposed() { return this._disposed; }
|
|
21
27
|
dispose() {
|
|
22
28
|
this._disposed = true;
|
|
23
29
|
}
|
|
24
30
|
get repositoryUrl() {
|
|
25
|
-
|
|
31
|
+
if (this.internalStorageService) {
|
|
32
|
+
return this.internalStorageService.repositoryUrl;
|
|
33
|
+
}
|
|
34
|
+
throw new Error("storage service not yet instantiated");
|
|
26
35
|
}
|
|
27
36
|
async getSnapshotTree(version, scenarioName) {
|
|
28
|
-
return this.runWithRetry(async () => this.
|
|
37
|
+
return this.runWithRetry(async () => this.internalStorageServiceP.then(async (s) => s.getSnapshotTree(version, scenarioName)), "storage_getSnapshotTree");
|
|
29
38
|
}
|
|
30
39
|
async readBlob(id) {
|
|
31
|
-
return this.runWithRetry(async () => this.
|
|
40
|
+
return this.runWithRetry(async () => this.internalStorageServiceP.then(async (s) => s.readBlob(id)), "storage_readBlob");
|
|
32
41
|
}
|
|
33
|
-
async getVersions(versionId, count, scenarioName) {
|
|
34
|
-
return this.runWithRetry(async () => this.
|
|
42
|
+
async getVersions(versionId, count, scenarioName, fetchSource) {
|
|
43
|
+
return this.runWithRetry(async () => this.internalStorageServiceP.then(async (s) => s.getVersions(versionId, count, scenarioName, fetchSource)), "storage_getVersions");
|
|
35
44
|
}
|
|
36
45
|
async uploadSummaryWithContext(summary, context) {
|
|
37
46
|
// Not using retry loop here. Couple reasons:
|
|
@@ -43,31 +52,37 @@ class RetriableDocumentStorageService {
|
|
|
43
52
|
// upload in 10 minutes - it's better to keep processing ops and retry later. Though caller needs to take
|
|
44
53
|
// retryAfter into account!
|
|
45
54
|
// But retry loop is required for creation flow (Container.attach)
|
|
46
|
-
(0,
|
|
55
|
+
(0, core_utils_1.assert)((context.referenceSequenceNumber === 0) === (context.ackHandle === undefined), 0x251 /* "creation summary has to have seq=0 && handle === undefined" */);
|
|
47
56
|
if (context.referenceSequenceNumber !== 0) {
|
|
48
|
-
return this.
|
|
57
|
+
return this.internalStorageServiceP.then(async (s) => s.uploadSummaryWithContext(summary, context));
|
|
49
58
|
}
|
|
50
59
|
// Creation flow with attachment blobs - need to do retries!
|
|
51
|
-
return this.runWithRetry(async () => this.
|
|
60
|
+
return this.runWithRetry(async () => this.internalStorageServiceP.then(async (s) => s.uploadSummaryWithContext(summary, context)), "storage_uploadSummaryWithContext");
|
|
52
61
|
}
|
|
53
62
|
async downloadSummary(handle) {
|
|
54
|
-
return this.runWithRetry(async () => this.
|
|
63
|
+
return this.runWithRetry(async () => this.internalStorageServiceP.then(async (s) => s.downloadSummary(handle)), "storage_downloadSummary");
|
|
55
64
|
}
|
|
56
65
|
async createBlob(file) {
|
|
57
|
-
return this.runWithRetry(async () => this.
|
|
66
|
+
return this.runWithRetry(async () => this.internalStorageServiceP.then(async (s) => s.createBlob(file)), "storage_createBlob");
|
|
58
67
|
}
|
|
59
|
-
checkStorageDisposed() {
|
|
68
|
+
checkStorageDisposed(callName, error) {
|
|
60
69
|
if (this._disposed) {
|
|
70
|
+
this.logger.sendTelemetryEvent({
|
|
71
|
+
eventName: `${callName}_abortedStorageDisposed`,
|
|
72
|
+
fetchCallName: callName, // fetchCallName matches logs in runWithRetry.ts
|
|
73
|
+
}, error);
|
|
61
74
|
// pre-0.58 error message: storageServiceDisposedCannotRetry
|
|
62
|
-
throw new
|
|
75
|
+
throw new telemetry_utils_1.GenericError("Storage Service is disposed. Cannot retry", {
|
|
76
|
+
canRetry: false,
|
|
77
|
+
});
|
|
63
78
|
}
|
|
64
|
-
return
|
|
79
|
+
return;
|
|
65
80
|
}
|
|
66
81
|
async runWithRetry(api, callName) {
|
|
67
82
|
return (0, driver_utils_1.runWithRetry)(api, callName, this.logger, {
|
|
68
|
-
onRetry: () => this.checkStorageDisposed(),
|
|
83
|
+
onRetry: (_delayInMs, error) => this.checkStorageDisposed(callName, error),
|
|
69
84
|
});
|
|
70
85
|
}
|
|
71
86
|
}
|
|
72
87
|
exports.RetriableDocumentStorageService = RetriableDocumentStorageService;
|
|
73
|
-
//# sourceMappingURL=retriableDocumentStorageService.
|
|
88
|
+
//# sourceMappingURL=retriableDocumentStorageService.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retriableDocumentStorageService.cjs","sourceRoot":"","sources":["../src/retriableDocumentStorageService.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils\";\nimport {\n\tFetchSource,\n\tIDocumentStorageService,\n\tIDocumentStorageServicePolicies,\n\tISummaryContext,\n} from \"@fluidframework/driver-definitions\";\nimport {\n\tICreateBlobResponse,\n\tISnapshotTree,\n\tISummaryHandle,\n\tISummaryTree,\n\tIVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { GenericError, ITelemetryLoggerExt } from \"@fluidframework/telemetry-utils\";\nimport { runWithRetry } from \"@fluidframework/driver-utils\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n\tprivate _disposed = false;\n\tprivate internalStorageService: IDocumentStorageService | undefined;\n\tconstructor(\n\t\tprivate readonly internalStorageServiceP: Promise<IDocumentStorageService>,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tthis.internalStorageServiceP.then((s) => (this.internalStorageService = s)).catch(() => {});\n\t}\n\n\tpublic get policies(): IDocumentStorageServicePolicies | undefined {\n\t\tif (this.internalStorageService) {\n\t\t\treturn this.internalStorageService.policies;\n\t\t}\n\t\tthrow new Error(\"storage service not yet instantiated\");\n\t}\n\tpublic get disposed() {\n\t\treturn this._disposed;\n\t}\n\tpublic dispose() {\n\t\tthis._disposed = true;\n\t}\n\n\tpublic get repositoryUrl(): string {\n\t\tif (this.internalStorageService) {\n\t\t\treturn this.internalStorageService.repositoryUrl;\n\t\t}\n\t\tthrow new Error(\"storage service not yet instantiated\");\n\t}\n\n\tpublic async getSnapshotTree(\n\t\tversion?: IVersion,\n\t\tscenarioName?: string,\n\t): Promise<ISnapshotTree | null> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getSnapshotTree(version, scenarioName),\n\t\t\t\t),\n\t\t\t\"storage_getSnapshotTree\",\n\t\t);\n\t}\n\n\tpublic async readBlob(id: string): Promise<ArrayBufferLike> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.readBlob(id)),\n\t\t\t\"storage_readBlob\",\n\t\t);\n\t}\n\n\tpublic async getVersions(\n\t\tversionId: string | null,\n\t\tcount: number,\n\t\tscenarioName?: string,\n\t\tfetchSource?: FetchSource,\n\t): Promise<IVersion[]> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getVersions(versionId, count, scenarioName, fetchSource),\n\t\t\t\t),\n\t\t\t\"storage_getVersions\",\n\t\t);\n\t}\n\n\tpublic async uploadSummaryWithContext(\n\t\tsummary: ISummaryTree,\n\t\tcontext: ISummaryContext,\n\t): Promise<string> {\n\t\t// Not using retry loop here. Couple reasons:\n\t\t// 1. If client lost connectivity, then retry loop will result in uploading stale summary\n\t\t// by stale summarizer after connectivity comes back. It will cause failures for this client and for\n\t\t// real (new) summarizer. This problem in particular should be solved in future by supplying abort handle\n\t\t// on all APIs and caller (ContainerRuntime.submitSummary) aborting call on loss of connectivity\n\t\t// 2. Similar, if we get 429 with retryAfter = 10 minutes, it's likely not the right call to retry summary\n\t\t// upload in 10 minutes - it's better to keep processing ops and retry later. Though caller needs to take\n\t\t// retryAfter into account!\n\t\t// But retry loop is required for creation flow (Container.attach)\n\t\tassert(\n\t\t\t(context.referenceSequenceNumber === 0) === (context.ackHandle === undefined),\n\t\t\t0x251 /* \"creation summary has to have seq=0 && handle === undefined\" */,\n\t\t);\n\t\tif (context.referenceSequenceNumber !== 0) {\n\t\t\treturn this.internalStorageServiceP.then(async (s) =>\n\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t);\n\t\t}\n\n\t\t// Creation flow with attachment blobs - need to do retries!\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t\t),\n\t\t\t\"storage_uploadSummaryWithContext\",\n\t\t);\n\t}\n\n\tpublic async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.downloadSummary(handle)),\n\t\t\t\"storage_downloadSummary\",\n\t\t);\n\t}\n\n\tpublic async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.createBlob(file)),\n\t\t\t\"storage_createBlob\",\n\t\t);\n\t}\n\n\tprivate checkStorageDisposed(callName: string, error: unknown) {\n\t\tif (this._disposed) {\n\t\t\tthis.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: `${callName}_abortedStorageDisposed`,\n\t\t\t\t\tfetchCallName: callName, // fetchCallName matches logs in runWithRetry.ts\n\t\t\t\t},\n\t\t\t\terror,\n\t\t\t);\n\t\t\t// pre-0.58 error message: storageServiceDisposedCannotRetry\n\t\t\tthrow new GenericError(\"Storage Service is disposed. Cannot retry\", {\n\t\t\t\tcanRetry: false,\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\n\tprivate async runWithRetry<T>(api: () => Promise<T>, callName: string): Promise<T> {\n\t\treturn runWithRetry(api, callName, this.logger, {\n\t\t\tonRetry: (_delayInMs: number, error: unknown) =>\n\t\t\t\tthis.checkStorageDisposed(callName, error),\n\t\t});\n\t}\n}\n"]}
|