@fluidframework/container-loader 2.0.0-internal.3.0.5 → 2.0.0-internal.3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +18 -21
- package/.mocharc.js +2 -2
- package/README.md +45 -43
- package/api-extractor.json +2 -2
- package/closeAndGetPendingLocalState.md +51 -0
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/collabWindowTracker.d.ts.map +1 -1
- package/dist/collabWindowTracker.js.map +1 -1
- package/dist/connectionManager.d.ts +2 -2
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +51 -24
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +35 -16
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +1 -10
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +89 -44
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +6 -2
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +2 -4
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +3 -3
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +56 -27
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaManagerProxy.js.map +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +4 -2
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +3 -3
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +18 -15
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +2 -1
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +6 -2
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -4
- package/dist/utils.js.map +1 -1
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/collabWindowTracker.d.ts.map +1 -1
- package/lib/collabWindowTracker.js.map +1 -1
- package/lib/connectionManager.d.ts +2 -2
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +53 -26
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionState.d.ts.map +1 -1
- package/lib/connectionState.js.map +1 -1
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +35 -16
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +1 -10
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +93 -48
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +6 -2
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +2 -4
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +3 -3
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +58 -29
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaManagerProxy.js.map +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +4 -2
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +3 -3
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +18 -15
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +2 -1
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +6 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +6 -4
- package/lib/utils.js.map +1 -1
- package/package.json +115 -114
- package/prettier.config.cjs +1 -1
- package/src/audience.ts +51 -46
- package/src/catchUpMonitor.ts +39 -37
- package/src/collabWindowTracker.ts +75 -70
- package/src/connectionManager.ts +1006 -944
- package/src/connectionState.ts +19 -19
- package/src/connectionStateHandler.ts +544 -465
- package/src/container.ts +2056 -1909
- package/src/containerContext.ts +350 -340
- package/src/containerStorageAdapter.ts +163 -153
- package/src/contracts.ts +155 -153
- package/src/deltaManager.ts +1069 -992
- package/src/deltaManagerProxy.ts +143 -137
- package/src/deltaQueue.ts +155 -151
- package/src/index.ts +14 -17
- package/src/loader.ts +428 -430
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +93 -87
- package/src/protocolTreeDocumentStorageService.ts +30 -33
- package/src/quorum.ts +34 -34
- package/src/retriableDocumentStorageService.ts +118 -102
- package/src/utils.ts +89 -82
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -12
package/package.json
CHANGED
|
@@ -1,116 +1,117 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
2
|
+
"name": "@fluidframework/container-loader",
|
|
3
|
+
"version": "2.0.0-internal.3.1.1",
|
|
4
|
+
"description": "Fluid container loader",
|
|
5
|
+
"homepage": "https://fluidframework.com",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/microsoft/FluidFramework.git",
|
|
9
|
+
"directory": "packages/loader/container-loader"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "Microsoft and contributors",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"module": "lib/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "npm run build:genver && concurrently npm:build:compile npm:lint && npm run build:docs",
|
|
19
|
+
"build:commonjs": "npm run tsc && npm run typetests:gen && npm run build:test",
|
|
20
|
+
"build:compile": "concurrently npm:build:commonjs npm:build:esnext",
|
|
21
|
+
"build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
|
|
22
|
+
"build:esnext": "tsc --project ./tsconfig.esnext.json",
|
|
23
|
+
"build:full": "npm run build",
|
|
24
|
+
"build:full:compile": "npm run build:compile",
|
|
25
|
+
"build:genver": "gen-version",
|
|
26
|
+
"build:test": "tsc --project ./src/test/tsconfig.json",
|
|
27
|
+
"ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/",
|
|
28
|
+
"clean": "rimraf dist lib *.tsbuildinfo *.build.log",
|
|
29
|
+
"eslint": "eslint --format stylish src",
|
|
30
|
+
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
31
|
+
"format": "npm run prettier:fix",
|
|
32
|
+
"lint": "npm run prettier && npm run eslint",
|
|
33
|
+
"lint:fix": "npm run prettier:fix && npm run eslint:fix",
|
|
34
|
+
"prettier": "prettier --check . --ignore-path ../../../.prettierignore",
|
|
35
|
+
"prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
|
|
36
|
+
"test": "npm run test:mocha",
|
|
37
|
+
"test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
|
|
38
|
+
"test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
|
|
39
|
+
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
40
|
+
"tsc": "tsc",
|
|
41
|
+
"tsc:watch": "tsc --watch",
|
|
42
|
+
"typetests:gen": "flub generate typetests --generate --dir .",
|
|
43
|
+
"typetests:prepare": "flub generate typetests --prepare --dir . --pin"
|
|
44
|
+
},
|
|
45
|
+
"nyc": {
|
|
46
|
+
"all": true,
|
|
47
|
+
"cache-dir": "nyc/.cache",
|
|
48
|
+
"exclude": [
|
|
49
|
+
"src/test/**/*.ts",
|
|
50
|
+
"dist/test/**/*.js"
|
|
51
|
+
],
|
|
52
|
+
"exclude-after-remap": false,
|
|
53
|
+
"include": [
|
|
54
|
+
"src/**/*.ts",
|
|
55
|
+
"dist/**/*.js"
|
|
56
|
+
],
|
|
57
|
+
"report-dir": "nyc/report",
|
|
58
|
+
"reporter": [
|
|
59
|
+
"cobertura",
|
|
60
|
+
"html",
|
|
61
|
+
"text"
|
|
62
|
+
],
|
|
63
|
+
"temp-directory": "nyc/.nyc_output"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@fluidframework/common-definitions": "^0.20.1",
|
|
67
|
+
"@fluidframework/common-utils": "^1.0.0",
|
|
68
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
69
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
70
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
71
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
72
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
73
|
+
"@fluidframework/protocol-base": "^0.1038.2000",
|
|
74
|
+
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
75
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
76
|
+
"abort-controller": "^3.0.0",
|
|
77
|
+
"double-ended-queue": "^2.1.0-0",
|
|
78
|
+
"events": "^3.1.0",
|
|
79
|
+
"lodash": "^4.17.21",
|
|
80
|
+
"url": "^0.11.0",
|
|
81
|
+
"uuid": "^8.3.1"
|
|
82
|
+
},
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@fluid-tools/build-cli": "^0.9.0",
|
|
85
|
+
"@fluidframework/build-common": "^1.1.0",
|
|
86
|
+
"@fluidframework/build-tools": "^0.9.0",
|
|
87
|
+
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.3.0.1",
|
|
88
|
+
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
89
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
90
|
+
"@fluidframework/test-loader-utils": ">=2.0.0-internal.3.1.1 <2.0.0-internal.4.0.0",
|
|
91
|
+
"@microsoft/api-extractor": "^7.22.2",
|
|
92
|
+
"@rushstack/eslint-config": "^2.5.1",
|
|
93
|
+
"@types/double-ended-queue": "^2.1.0",
|
|
94
|
+
"@types/events": "^3.0.0",
|
|
95
|
+
"@types/lodash": "^4.14.118",
|
|
96
|
+
"@types/mocha": "^9.1.1",
|
|
97
|
+
"@types/node": "^14.18.36",
|
|
98
|
+
"@types/sinon": "^7.0.13",
|
|
99
|
+
"concurrently": "^6.2.0",
|
|
100
|
+
"copyfiles": "^2.4.1",
|
|
101
|
+
"cross-env": "^7.0.2",
|
|
102
|
+
"eslint": "~8.6.0",
|
|
103
|
+
"mocha": "^10.0.0",
|
|
104
|
+
"nyc": "^15.0.0",
|
|
105
|
+
"prettier": "~2.6.2",
|
|
106
|
+
"rimraf": "^2.6.2",
|
|
107
|
+
"sinon": "^7.4.2",
|
|
108
|
+
"typescript": "~4.5.5"
|
|
109
|
+
},
|
|
110
|
+
"typeValidation": {
|
|
111
|
+
"version": "2.0.0-internal.3.1.1",
|
|
112
|
+
"previousVersionStyle": "~previousMinor",
|
|
113
|
+
"baselineRange": ">=2.0.0-internal.3.0.0 <2.0.0-internal.3.1.0",
|
|
114
|
+
"baselineVersion": "2.0.0-internal.3.0.1",
|
|
115
|
+
"broken": {}
|
|
116
|
+
}
|
|
116
117
|
}
|
package/prettier.config.cjs
CHANGED
package/src/audience.ts
CHANGED
|
@@ -11,55 +11,60 @@ import { IClient } from "@fluidframework/protocol-definitions";
|
|
|
11
11
|
* Audience represents all clients connected to the op stream.
|
|
12
12
|
*/
|
|
13
13
|
export class Audience extends EventEmitter implements IAudienceOwner {
|
|
14
|
-
|
|
14
|
+
private readonly members = new Map<string, IClient>();
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
public on(
|
|
17
|
+
event: "addMember" | "removeMember",
|
|
18
|
+
listener: (clientId: string, client: IClient) => void,
|
|
19
|
+
): this;
|
|
20
|
+
public on(event: string, listener: (...args: any[]) => void): this {
|
|
21
|
+
return super.on(event, listener);
|
|
22
|
+
}
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Adds a new client to the audience
|
|
26
|
+
*/
|
|
27
|
+
public addMember(clientId: string, details: IClient) {
|
|
28
|
+
// Given that signal delivery is unreliable process, we might observe same client being added twice
|
|
29
|
+
// In such case we should see exactly same payload (IClient), and should not raise event twice!
|
|
30
|
+
if (this.members.has(clientId)) {
|
|
31
|
+
const client = this.members.get(clientId);
|
|
32
|
+
assert(
|
|
33
|
+
JSON.stringify(client) === JSON.stringify(details),
|
|
34
|
+
0x4b2 /* new client has different payload from existing one */,
|
|
35
|
+
);
|
|
36
|
+
} else {
|
|
37
|
+
this.members.set(clientId, details);
|
|
38
|
+
this.emit("addMember", clientId, details);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Removes a client from the audience. Only emits an event if a client is actually removed
|
|
44
|
+
* @returns if a client was removed from the audience
|
|
45
|
+
*/
|
|
46
|
+
public removeMember(clientId: string): boolean {
|
|
47
|
+
const removedClient = this.members.get(clientId);
|
|
48
|
+
if (removedClient !== undefined) {
|
|
49
|
+
this.members.delete(clientId);
|
|
50
|
+
this.emit("removeMember", clientId, removedClient);
|
|
51
|
+
return true;
|
|
52
|
+
} else {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves all the members in the audience
|
|
59
|
+
*/
|
|
60
|
+
public getMembers(): Map<string, IClient> {
|
|
61
|
+
return new Map(this.members);
|
|
62
|
+
}
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves a specific member of the audience
|
|
66
|
+
*/
|
|
67
|
+
public getMember(clientId: string): IClient | undefined {
|
|
68
|
+
return this.members.get(clientId);
|
|
69
|
+
}
|
|
65
70
|
}
|
package/src/catchUpMonitor.ts
CHANGED
|
@@ -19,41 +19,43 @@ export type ICatchUpMonitor = IDisposable;
|
|
|
19
19
|
* that were known at the time the monitor was created.
|
|
20
20
|
*/
|
|
21
21
|
export class CatchUpMonitor implements ICatchUpMonitor {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
22
|
+
private readonly targetSeqNumber: number;
|
|
23
|
+
private caughtUp: boolean = false;
|
|
24
|
+
|
|
25
|
+
private readonly opHandler = (message: Pick<ISequencedDocumentMessage, "sequenceNumber">) => {
|
|
26
|
+
if (!this.caughtUp && message.sequenceNumber >= this.targetSeqNumber) {
|
|
27
|
+
this.caughtUp = true;
|
|
28
|
+
this.listener();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create the CatchUpMonitor, setting the target sequence number to wait for based on DeltaManager's current state.
|
|
34
|
+
*/
|
|
35
|
+
constructor(
|
|
36
|
+
private readonly deltaManager: IDeltaManager<any, any>,
|
|
37
|
+
private readonly listener: CaughtUpListener,
|
|
38
|
+
) {
|
|
39
|
+
this.targetSeqNumber = this.deltaManager.lastKnownSeqNumber;
|
|
40
|
+
|
|
41
|
+
assert(
|
|
42
|
+
this.targetSeqNumber >= this.deltaManager.lastSequenceNumber,
|
|
43
|
+
0x37c /* Cannot wait for seqNumber below last processed sequence number */,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
this.deltaManager.on("op", this.opHandler);
|
|
47
|
+
|
|
48
|
+
// Simulate the last processed op to set caughtUp in case we already are
|
|
49
|
+
this.opHandler({ sequenceNumber: this.deltaManager.lastSequenceNumber });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public disposed: boolean = false;
|
|
53
|
+
public dispose() {
|
|
54
|
+
if (this.disposed) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.disposed = true;
|
|
58
|
+
|
|
59
|
+
this.deltaManager.off("op", this.opHandler);
|
|
60
|
+
}
|
|
59
61
|
}
|
|
@@ -30,82 +30,87 @@ const defaultNoopCountFrequency = 50;
|
|
|
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
32
|
export class CollabWindowTracker {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
private opsCountSinceNoop = 0;
|
|
34
|
+
private readonly timer: Timer | undefined;
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
constructor(
|
|
37
|
+
private readonly submit: (type: MessageType) => void,
|
|
38
|
+
NoopTimeFrequency: number = defaultNoopTimeFrequency,
|
|
39
|
+
private readonly NoopCountFrequency: number = defaultNoopCountFrequency,
|
|
40
|
+
) {
|
|
41
|
+
if (NoopTimeFrequency !== Infinity) {
|
|
42
|
+
this.timer = new Timer(NoopTimeFrequency, () => {
|
|
43
|
+
// Can get here due to this.stopSequenceNumberUpdate() not resetting timer.
|
|
44
|
+
// Also timer callback can fire even after timer cancellation if it was queued before cancellation.
|
|
45
|
+
if (this.opsCountSinceNoop !== 0) {
|
|
46
|
+
this.submitNoop(false /* immediate */);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Schedules as ack to the server to update the reference sequence number
|
|
54
|
+
*/
|
|
55
|
+
public scheduleSequenceNumberUpdate(
|
|
56
|
+
message: ISequencedDocumentMessage,
|
|
57
|
+
immediateNoOp: boolean,
|
|
58
|
+
): void {
|
|
59
|
+
// While processing a message, an immediate no-op can be requested.
|
|
60
|
+
// i.e. to expedite approve or commit phase of quorum.
|
|
61
|
+
if (immediateNoOp) {
|
|
62
|
+
this.submitNoop(true /* immediate */);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
// We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN
|
|
67
|
+
// update, which updates the MSN, then ack the update, etc...).
|
|
68
|
+
// Intent here is for runtime (and DDSes) not to keep too much tracking state / memory
|
|
69
|
+
// due to runtime ops from other clients.
|
|
70
|
+
if (!isRuntimeMessage(message)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
this.opsCountSinceNoop++;
|
|
75
|
+
if (this.opsCountSinceNoop === this.NoopCountFrequency) {
|
|
76
|
+
// Ensure we only send noop after a batch of many ops is processed
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
78
|
+
Promise.resolve().then(() => {
|
|
79
|
+
if (this.opsCountSinceNoop >= this.NoopCountFrequency) {
|
|
80
|
+
this.submitNoop(false /* immediate */);
|
|
81
|
+
// reset count now that all ops are processed
|
|
82
|
+
this.opsCountSinceNoop = 0;
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
if (this.timer !== undefined) {
|
|
89
|
+
if (this.opsCountSinceNoop === 1) {
|
|
90
|
+
this.timer.restart();
|
|
91
|
+
}
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
assert(this.timer.hasTimer, 0x242 /* "has timer" */);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
private submitNoop(immediate: boolean) {
|
|
98
|
+
// Anything other than null is immediate noop
|
|
99
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
100
|
+
this.submit(immediate ? (MessageType2.Accept as unknown as MessageType) : MessageType.NoOp);
|
|
101
|
+
assert(
|
|
102
|
+
this.opsCountSinceNoop === 0,
|
|
103
|
+
0x243 /* "stopSequenceNumberUpdate should be called as result of sending any op!" */,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
public stopSequenceNumberUpdate(): void {
|
|
108
|
+
this.opsCountSinceNoop = 0;
|
|
109
|
+
// Ideally, we cancel timer here. But that will result in too often set/reset cycle if this client
|
|
110
|
+
// keeps sending ops. In most cases it's actually better to let it expire (at most - 4 times per second)
|
|
111
|
+
// for nothing, then have a ton of set/reset cycles.
|
|
112
|
+
// Note that Timer.restart() is smart and will not change timer expiration if we keep extending timer
|
|
113
|
+
// expiration - it will restart the timer instead when it fires with adjusted expiration.
|
|
114
|
+
// this.timer.clear();
|
|
115
|
+
}
|
|
111
116
|
}
|