@fluidframework/container-loader 1.2.7 → 2.0.0-dev.1.3.0.96595
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/.mocharc.js +12 -0
- package/dist/audience.d.ts +2 -6
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +6 -11
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts +29 -0
- package/dist/catchUpMonitor.d.ts.map +1 -0
- package/dist/catchUpMonitor.js +43 -0
- package/dist/catchUpMonitor.js.map +1 -0
- package/dist/collabWindowTracker.d.ts +1 -1
- package/dist/collabWindowTracker.d.ts.map +1 -1
- package/dist/collabWindowTracker.js +12 -4
- package/dist/collabWindowTracker.js.map +1 -1
- package/dist/connectionManager.d.ts +5 -5
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +43 -22
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.d.ts +0 -5
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionState.js +0 -5
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +84 -22
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +172 -59
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +29 -17
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +181 -171
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +18 -7
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +18 -8
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +11 -25
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +51 -17
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +5 -5
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +4 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +39 -12
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts +4 -1
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaQueue.d.ts +9 -2
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +31 -26
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +8 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +4 -3
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +27 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +79 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +2 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +2 -2
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/lib/audience.d.ts +2 -6
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +6 -11
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +29 -0
- package/lib/catchUpMonitor.d.ts.map +1 -0
- package/lib/catchUpMonitor.js +39 -0
- package/lib/catchUpMonitor.js.map +1 -0
- package/lib/collabWindowTracker.d.ts +1 -1
- package/lib/collabWindowTracker.d.ts.map +1 -1
- package/lib/collabWindowTracker.js +13 -5
- package/lib/collabWindowTracker.js.map +1 -1
- package/lib/connectionManager.d.ts +5 -5
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +44 -25
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionState.d.ts +0 -5
- package/lib/connectionState.d.ts.map +1 -1
- package/lib/connectionState.js +0 -5
- package/lib/connectionState.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +84 -22
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +171 -59
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +29 -17
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +184 -174
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +18 -7
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +19 -9
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +11 -25
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +51 -16
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +5 -5
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +4 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +41 -14
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts +4 -1
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaQueue.d.ts +9 -2
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +32 -27
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +8 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +4 -3
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +27 -0
- package/lib/protocol.d.ts.map +1 -0
- package/lib/protocol.js +75 -0
- package/lib/protocol.js.map +1 -0
- package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +2 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +2 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/package.json +27 -19
- package/src/audience.ts +8 -14
- package/src/catchUpMonitor.ts +59 -0
- package/src/collabWindowTracker.ts +15 -6
- package/src/connectionManager.ts +56 -33
- package/src/connectionState.ts +0 -6
- package/src/connectionStateHandler.ts +235 -70
- package/src/container.ts +241 -218
- package/src/containerContext.ts +22 -8
- package/src/containerStorageAdapter.ts +71 -16
- package/src/contracts.ts +7 -7
- package/src/deltaManager.ts +48 -15
- package/src/deltaQueue.ts +34 -28
- package/src/index.ts +4 -0
- package/src/loader.ts +14 -3
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +120 -0
- package/src/retriableDocumentStorageService.ts +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retriableDocumentStorageService.js","sourceRoot":"","sources":["../src/retriableDocumentStorageService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"retriableDocumentStorageService.js","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/common-utils\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport {\n FetchSource,\n IDocumentStorageService,\n IDocumentStorageServicePolicies,\n ISummaryContext,\n} from \"@fluidframework/driver-definitions\";\nimport {\n ICreateBlobResponse,\n ISnapshotTree,\n ISummaryHandle,\n ISummaryTree,\n IVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport { IDisposable, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { runWithRetry } from \"@fluidframework/driver-utils\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n private _disposed = false;\n constructor(\n private readonly internalStorageService: IDocumentStorageService,\n private readonly logger: ITelemetryLogger,\n ) {\n }\n\n public get policies(): IDocumentStorageServicePolicies | undefined {\n return this.internalStorageService.policies;\n }\n public get disposed() { return this._disposed; }\n public dispose() {\n this._disposed = true;\n }\n\n public get repositoryUrl(): string {\n return this.internalStorageService.repositoryUrl;\n }\n\n public async getSnapshotTree(version?: IVersion, scenarioName?: string): Promise<ISnapshotTree | null> {\n return this.runWithRetry(\n async () => this.internalStorageService.getSnapshotTree(version, scenarioName),\n \"storage_getSnapshotTree\",\n );\n }\n\n public async readBlob(id: string): Promise<ArrayBufferLike> {\n return this.runWithRetry(\n async () => this.internalStorageService.readBlob(id),\n \"storage_readBlob\",\n );\n }\n\n public async getVersions(\n versionId: string | null,\n count: number,\n scenarioName?: string,\n fetchSource?: FetchSource,\n ): Promise<IVersion[]> {\n return this.runWithRetry(\n async () => this.internalStorageService.getVersions(versionId, count, scenarioName, fetchSource),\n \"storage_getVersions\",\n );\n }\n\n public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {\n // Not using retry loop here. Couple reasons:\n // 1. If client lost connectivity, then retry loop will result in uploading stale summary\n // by stale summarizer after connectivity comes back. It will cause failures for this client and for\n // real (new) summarizer. This problem in particular should be solved in future by supplying abort handle\n // on all APIs and caller (ContainerRuntime.submitSummary) aborting call on loss of connectivity\n // 2. Similar, if we get 429 with retryAfter = 10 minutes, it's likely not the right call to retry summary\n // upload in 10 minutes - it's better to keep processing ops and retry later. Though caller needs to take\n // retryAfter into account!\n // But retry loop is required for creation flow (Container.attach)\n assert((context.referenceSequenceNumber === 0) === (context.ackHandle === undefined),\n 0x251 /* \"creation summary has to have seq=0 && handle === undefined\" */);\n if (context.referenceSequenceNumber !== 0) {\n return this.internalStorageService.uploadSummaryWithContext(summary, context);\n }\n\n // Creation flow with attachment blobs - need to do retries!\n return this.runWithRetry(\n async () => this.internalStorageService.uploadSummaryWithContext(summary, context),\n \"storage_uploadSummaryWithContext\",\n );\n }\n\n public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {\n return this.runWithRetry(\n async () => this.internalStorageService.downloadSummary(handle),\n \"storage_downloadSummary\",\n );\n }\n\n public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {\n return this.runWithRetry(\n async () => this.internalStorageService.createBlob(file),\n \"storage_createBlob\",\n );\n }\n\n private checkStorageDisposed() {\n if (this._disposed) {\n // pre-0.58 error message: storageServiceDisposedCannotRetry\n throw new GenericError(\"Storage Service is disposed. Cannot retry\", { canRetry: false });\n }\n return undefined;\n }\n\n private async runWithRetry<T>(api: () => Promise<T>, callName: string): Promise<T> {\n return runWithRetry(\n api,\n callName,\n this.logger,\n {\n onRetry: () => this.checkStorageDisposed(),\n },\n );\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-loader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.0.0-dev.1.3.0.96595",
|
|
4
4
|
"description": "Fluid container loader",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -62,27 +62,28 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
65
|
-
"@fluidframework/common-utils": "^0.
|
|
66
|
-
"@fluidframework/container-definitions": "
|
|
67
|
-
"@fluidframework/container-utils": "
|
|
68
|
-
"@fluidframework/core-interfaces": "
|
|
69
|
-
"@fluidframework/driver-definitions": "
|
|
70
|
-
"@fluidframework/driver-utils": "
|
|
71
|
-
"@fluidframework/protocol-base": "^0.
|
|
72
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
73
|
-
"@fluidframework/telemetry-utils": "
|
|
65
|
+
"@fluidframework/common-utils": "^1.0.0",
|
|
66
|
+
"@fluidframework/container-definitions": "2.0.0-dev.1.3.0.96595",
|
|
67
|
+
"@fluidframework/container-utils": "2.0.0-dev.1.3.0.96595",
|
|
68
|
+
"@fluidframework/core-interfaces": "2.0.0-dev.1.3.0.96595",
|
|
69
|
+
"@fluidframework/driver-definitions": "2.0.0-dev.1.3.0.96595",
|
|
70
|
+
"@fluidframework/driver-utils": "2.0.0-dev.1.3.0.96595",
|
|
71
|
+
"@fluidframework/protocol-base": "^0.1037.2001",
|
|
72
|
+
"@fluidframework/protocol-definitions": "^1.0.0",
|
|
73
|
+
"@fluidframework/telemetry-utils": "2.0.0-dev.1.3.0.96595",
|
|
74
74
|
"abort-controller": "^3.0.0",
|
|
75
75
|
"double-ended-queue": "^2.1.0-0",
|
|
76
76
|
"lodash": "^4.17.21",
|
|
77
|
+
"url": "^0.11.0",
|
|
77
78
|
"uuid": "^8.3.1"
|
|
78
79
|
},
|
|
79
80
|
"devDependencies": {
|
|
80
|
-
"@fluidframework/build-common": "^0.
|
|
81
|
-
"@fluidframework/build-tools": "^0.
|
|
82
|
-
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader
|
|
83
|
-
"@fluidframework/eslint-config-fluid": "^0.
|
|
84
|
-
"@fluidframework/mocha-test-setup": "
|
|
85
|
-
"@fluidframework/test-loader-utils": "
|
|
81
|
+
"@fluidframework/build-common": "^1.0.0",
|
|
82
|
+
"@fluidframework/build-tools": "^0.4.6000",
|
|
83
|
+
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@^1.0.0",
|
|
84
|
+
"@fluidframework/eslint-config-fluid": "^1.0.0",
|
|
85
|
+
"@fluidframework/mocha-test-setup": "2.0.0-dev.1.3.0.96595",
|
|
86
|
+
"@fluidframework/test-loader-utils": "2.0.0-dev.1.3.0.96595",
|
|
86
87
|
"@microsoft/api-extractor": "^7.22.2",
|
|
87
88
|
"@rushstack/eslint-config": "^2.5.1",
|
|
88
89
|
"@types/double-ended-queue": "^2.1.0",
|
|
@@ -91,7 +92,7 @@
|
|
|
91
92
|
"@types/node": "^14.18.0",
|
|
92
93
|
"@types/sinon": "^7.0.13",
|
|
93
94
|
"concurrently": "^6.2.0",
|
|
94
|
-
"copyfiles": "^2.1
|
|
95
|
+
"copyfiles": "^2.4.1",
|
|
95
96
|
"cross-env": "^7.0.2",
|
|
96
97
|
"eslint": "~8.6.0",
|
|
97
98
|
"mocha": "^10.0.0",
|
|
@@ -102,7 +103,14 @@
|
|
|
102
103
|
"typescript-formatter": "7.1.0"
|
|
103
104
|
},
|
|
104
105
|
"typeValidation": {
|
|
105
|
-
"version": "
|
|
106
|
-
"broken": {
|
|
106
|
+
"version": "2.0.0",
|
|
107
|
+
"broken": {
|
|
108
|
+
"EnumDeclaration_ConnectionState": {
|
|
109
|
+
"forwardCompat": false
|
|
110
|
+
},
|
|
111
|
+
"ClassDeclaration_Container": {
|
|
112
|
+
"forwardCompat": false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
107
115
|
}
|
|
108
116
|
}
|
package/src/audience.ts
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { EventEmitter } from "events";
|
|
6
|
-
import {
|
|
6
|
+
import { IAudienceOwner } from "@fluidframework/container-definitions";
|
|
7
7
|
import { IClient } from "@fluidframework/protocol-definitions";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Audience represents all clients connected to the op stream.
|
|
11
11
|
*/
|
|
12
|
-
export class Audience extends EventEmitter implements
|
|
12
|
+
export class Audience extends EventEmitter implements IAudienceOwner {
|
|
13
13
|
private readonly members = new Map<string, IClient>();
|
|
14
14
|
|
|
15
15
|
public on(event: "addMember" | "removeMember", listener: (clientId: string, client: IClient) => void): this;
|
|
@@ -21,8 +21,12 @@ export class Audience extends EventEmitter implements IAudience {
|
|
|
21
21
|
* Adds a new client to the audience
|
|
22
22
|
*/
|
|
23
23
|
public addMember(clientId: string, details: IClient) {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
// Given that signal delivery is unreliable process, we might observe same client being added twice
|
|
25
|
+
// In such case we should see exactly same payload (IClient), and should not raise event twice!
|
|
26
|
+
if (!this.members.has(clientId)) {
|
|
27
|
+
this.members.set(clientId, details);
|
|
28
|
+
this.emit("addMember", clientId, details);
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
/**
|
|
@@ -53,14 +57,4 @@ export class Audience extends EventEmitter implements IAudience {
|
|
|
53
57
|
public getMember(clientId: string): IClient | undefined {
|
|
54
58
|
return this.members.get(clientId);
|
|
55
59
|
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Clears the audience
|
|
59
|
-
*/
|
|
60
|
-
public clear(): void {
|
|
61
|
-
const clientIds = this.members.keys();
|
|
62
|
-
for (const clientId of clientIds) {
|
|
63
|
-
this.removeMember(clientId);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
60
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IDisposable } from "@fluidframework/common-definitions";
|
|
7
|
+
import { assert } from "@fluidframework/common-utils";
|
|
8
|
+
import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
9
|
+
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
10
|
+
|
|
11
|
+
/** @see CatchUpMonitor for usage */
|
|
12
|
+
type CaughtUpListener = () => void;
|
|
13
|
+
|
|
14
|
+
/** Monitor that emits an event when a Container has caught up to a given point in the op stream */
|
|
15
|
+
export type ICatchUpMonitor = IDisposable;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Monitors a Container's DeltaManager, notifying listeners when all ops have been processed
|
|
19
|
+
* that were known at the time the monitor was created.
|
|
20
|
+
*/
|
|
21
|
+
export class CatchUpMonitor implements ICatchUpMonitor {
|
|
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(this.targetSeqNumber >= this.deltaManager.lastSequenceNumber,
|
|
42
|
+
0x37c /* Cannot wait for seqNumber below last processed sequence number */);
|
|
43
|
+
|
|
44
|
+
this.deltaManager.on("op", this.opHandler);
|
|
45
|
+
|
|
46
|
+
// Simulate the last processed op to set caughtUp in case we already are
|
|
47
|
+
this.opHandler({ sequenceNumber: this.deltaManager.lastSequenceNumber });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public disposed: boolean = false;
|
|
51
|
+
public dispose() {
|
|
52
|
+
if (this.disposed) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.disposed = true;
|
|
56
|
+
|
|
57
|
+
this.deltaManager.off("op", this.opHandler);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert, Timer } from "@fluidframework/common-utils";
|
|
7
7
|
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import { isRuntimeMessage } from "@fluidframework/driver-utils";
|
|
8
|
+
import { isRuntimeMessage, MessageType2 } from "@fluidframework/driver-utils";
|
|
9
9
|
|
|
10
10
|
const defaultNoopTimeFrequency = 2000;
|
|
11
11
|
const defaultNoopCountFrequency = 50;
|
|
@@ -34,7 +34,7 @@ export class CollabWindowTracker {
|
|
|
34
34
|
private readonly timer: Timer | undefined;
|
|
35
35
|
|
|
36
36
|
constructor(
|
|
37
|
-
private readonly submit: (type: MessageType
|
|
37
|
+
private readonly submit: (type: MessageType) => void,
|
|
38
38
|
NoopTimeFrequency: number = defaultNoopTimeFrequency,
|
|
39
39
|
private readonly NoopCountFrequency: number = defaultNoopCountFrequency,
|
|
40
40
|
) {
|
|
@@ -69,9 +69,17 @@ export class CollabWindowTracker {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
this.opsCountSinceNoop++;
|
|
72
|
-
if (this.opsCountSinceNoop
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
if (this.opsCountSinceNoop === this.NoopCountFrequency) {
|
|
73
|
+
// Ensure we only send noop after a batch of many ops is processed
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
75
|
+
Promise.resolve().then(() => {
|
|
76
|
+
assert(this.opsCountSinceNoop >= this.NoopCountFrequency,
|
|
77
|
+
0x3ae /* not enough ops were sent to reach the noop frequency */);
|
|
78
|
+
this.submitNoop(false /* immediate */);
|
|
79
|
+
// reset count now that all ops are processed
|
|
80
|
+
this.opsCountSinceNoop = 0;
|
|
81
|
+
return;
|
|
82
|
+
});
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
if (this.timer !== undefined) {
|
|
@@ -85,7 +93,8 @@ export class CollabWindowTracker {
|
|
|
85
93
|
|
|
86
94
|
private submitNoop(immediate: boolean) {
|
|
87
95
|
// Anything other than null is immediate noop
|
|
88
|
-
|
|
96
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
97
|
+
this.submit(immediate ? (MessageType2.Accept as unknown as MessageType) : MessageType.NoOp);
|
|
89
98
|
assert(this.opsCountSinceNoop === 0,
|
|
90
99
|
0x243 /* "stopSequenceNumberUpdate should be called as result of sending any op!" */);
|
|
91
100
|
}
|
package/src/connectionManager.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
waitForConnectedState,
|
|
32
32
|
DeltaStreamConnectionForbiddenError,
|
|
33
33
|
logNetworkFailure,
|
|
34
|
-
|
|
34
|
+
isRuntimeMessage,
|
|
35
35
|
} from "@fluidframework/driver-utils";
|
|
36
36
|
import {
|
|
37
37
|
ConnectionMode,
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
IConnectionManagerFactoryArgs,
|
|
60
60
|
} from "./contracts";
|
|
61
61
|
import { DeltaQueue } from "./deltaQueue";
|
|
62
|
+
import { SignalType } from "./protocol";
|
|
62
63
|
|
|
63
64
|
const MaxReconnectDelayInMs = 8000;
|
|
64
65
|
const InitialReconnectDelayInMs = 1000;
|
|
@@ -97,7 +98,6 @@ class NoDeltaStream
|
|
|
97
98
|
serviceConfiguration: IClientConfiguration = {
|
|
98
99
|
maxMessageSize: 0,
|
|
99
100
|
blockSize: 0,
|
|
100
|
-
summary: undefined as any,
|
|
101
101
|
};
|
|
102
102
|
checkpointSequenceNumber?: number | undefined = undefined;
|
|
103
103
|
submit(messages: IDocumentMessage[]): void {
|
|
@@ -137,8 +137,8 @@ interface IPendingConnection {
|
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* Implementation of IConnectionManager, used by Container class
|
|
140
|
-
* Implements constant connectivity to relay service, by reconnecting in case of
|
|
141
|
-
* Exposes various controls to
|
|
140
|
+
* Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
|
|
141
|
+
* Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
|
|
142
142
|
*/
|
|
143
143
|
export class ConnectionManager implements IConnectionManager {
|
|
144
144
|
/** Connection mode used when reconnecting on error or disconnect. */
|
|
@@ -169,7 +169,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
169
169
|
private clientSequenceNumber = 0;
|
|
170
170
|
private clientSequenceNumberObserved = 0;
|
|
171
171
|
/** Counts the number of noops sent by the client which may not be acked. */
|
|
172
|
-
private
|
|
172
|
+
private localOpsToIgnore = 0;
|
|
173
173
|
|
|
174
174
|
/** track clientId used last time when we sent any ops */
|
|
175
175
|
private lastSubmittedClientId: string | undefined;
|
|
@@ -202,7 +202,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
202
202
|
* Automatic reconnecting enabled or disabled.
|
|
203
203
|
* If set to Never, then reconnecting will never be allowed.
|
|
204
204
|
*/
|
|
205
|
-
|
|
205
|
+
public get reconnectMode(): ReconnectMode {
|
|
206
206
|
return this._reconnectMode;
|
|
207
207
|
}
|
|
208
208
|
|
|
@@ -234,21 +234,19 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
234
234
|
* Returns set of props that can be logged in telemetry that provide some insights / statistics
|
|
235
235
|
* about current or last connection (if there is no connection at the moment)
|
|
236
236
|
*/
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return {
|
|
237
|
+
public get connectionProps(): ITelemetryProperties {
|
|
238
|
+
return this.connection !== undefined
|
|
239
|
+
? this._connectionProps
|
|
240
|
+
: {
|
|
242
241
|
...this._connectionProps,
|
|
243
242
|
// Report how many ops this client sent in last disconnected session
|
|
244
243
|
sentOps: this.clientSequenceNumber,
|
|
245
244
|
};
|
|
246
|
-
}
|
|
247
245
|
}
|
|
248
246
|
|
|
249
247
|
public shouldJoinWrite(): boolean {
|
|
250
248
|
// We don't have to wait for ack for topmost NoOps. So subtract those.
|
|
251
|
-
return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.
|
|
249
|
+
return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.localOpsToIgnore);
|
|
252
250
|
}
|
|
253
251
|
|
|
254
252
|
/**
|
|
@@ -379,7 +377,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
379
377
|
*
|
|
380
378
|
* @param readonly - set or clear force readonly.
|
|
381
379
|
*/
|
|
382
|
-
|
|
380
|
+
public forceReadonly(readonly: boolean) {
|
|
383
381
|
if (readonly !== this._forceReadonly) {
|
|
384
382
|
this.logger.sendTelemetryEvent({
|
|
385
383
|
eventName: "ForceReadOnly",
|
|
@@ -568,11 +566,11 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
568
566
|
}
|
|
569
567
|
|
|
570
568
|
/**
|
|
571
|
-
* Start the connection. Any error should result in container being
|
|
572
|
-
* And report the error if it
|
|
569
|
+
* Start the connection. Any error should result in container being closed.
|
|
570
|
+
* And report the error if it escapes for any reason.
|
|
573
571
|
* @param args - The connection arguments
|
|
574
572
|
*/
|
|
575
|
-
|
|
573
|
+
private triggerConnect(connectionMode: ConnectionMode) {
|
|
576
574
|
assert(this.connection === undefined, 0x239 /* "called only in disconnected state" */);
|
|
577
575
|
if (this.reconnectMode !== ReconnectMode.Enabled) {
|
|
578
576
|
return;
|
|
@@ -585,7 +583,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
585
583
|
* @param reason - Text description of disconnect reason to emit with disconnect event
|
|
586
584
|
* @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect
|
|
587
585
|
*/
|
|
588
|
-
|
|
586
|
+
private disconnectFromDeltaStream(reason: string): boolean {
|
|
589
587
|
this.pendingReconnect = false;
|
|
590
588
|
|
|
591
589
|
if (this.connection === undefined) {
|
|
@@ -638,7 +636,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
638
636
|
* initial messages.
|
|
639
637
|
* @param connection - The newly established connection
|
|
640
638
|
*/
|
|
641
|
-
|
|
639
|
+
private setupNewSuccessfulConnection(connection: IDocumentDeltaConnection, requestedMode: ConnectionMode) {
|
|
642
640
|
// Old connection should have been cleaned up before establishing a new one
|
|
643
641
|
assert(this.connection === undefined, 0x0e6 /* "old connection exists on new connection setup" */);
|
|
644
642
|
assert(!connection.disposed, 0x28a /* "can't be disposed - Callers need to ensure that!" */);
|
|
@@ -716,17 +714,43 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
716
714
|
initialMessages,
|
|
717
715
|
this.connectFirstConnection ? "InitialOps" : "ReconnectOps");
|
|
718
716
|
|
|
719
|
-
if (connection.initialSignals !== undefined) {
|
|
720
|
-
for (const signal of connection.initialSignals) {
|
|
721
|
-
this.props.signalHandler(signal);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
717
|
const details = ConnectionManager.detailsFromConnection(connection);
|
|
726
718
|
details.checkpointSequenceNumber = checkpointSequenceNumber;
|
|
727
719
|
this.props.connectHandler(details);
|
|
728
720
|
|
|
729
721
|
this.connectFirstConnection = false;
|
|
722
|
+
|
|
723
|
+
// Synthesize clear & join signals out of initialClients state.
|
|
724
|
+
// This allows us to have single way to process signals, and makes it simpler to initialize
|
|
725
|
+
// protocol in Container.
|
|
726
|
+
const clearSignal: ISignalMessage = {
|
|
727
|
+
clientId: null, // system message
|
|
728
|
+
content: JSON.stringify({
|
|
729
|
+
type: SignalType.Clear,
|
|
730
|
+
}),
|
|
731
|
+
};
|
|
732
|
+
this.props.signalHandler(clearSignal);
|
|
733
|
+
|
|
734
|
+
for (const priorClient of connection.initialClients ?? []) {
|
|
735
|
+
const joinSignal: ISignalMessage = {
|
|
736
|
+
clientId: null, // system signal
|
|
737
|
+
content: JSON.stringify({
|
|
738
|
+
type: SignalType.ClientJoin,
|
|
739
|
+
content: priorClient, // ISignalClient
|
|
740
|
+
}),
|
|
741
|
+
};
|
|
742
|
+
this.props.signalHandler(joinSignal);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Unfortunately, there is no defined order between initialSignals (including join & leave signals)
|
|
746
|
+
// and connection.initialClients. In practice, connection.initialSignals quite often contains join signal
|
|
747
|
+
// for "self" and connection.initialClients does not contain "self", so we have to process them after
|
|
748
|
+
// "clear" signal above.
|
|
749
|
+
if (connection.initialSignals !== undefined) {
|
|
750
|
+
for (const signal of connection.initialSignals) {
|
|
751
|
+
this.props.signalHandler(signal);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
730
754
|
}
|
|
731
755
|
|
|
732
756
|
/**
|
|
@@ -736,7 +760,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
736
760
|
* @param error - Error reconnect information including whether or not to reconnect
|
|
737
761
|
* @returns A promise that resolves when the connection is reestablished or we stop trying
|
|
738
762
|
*/
|
|
739
|
-
|
|
763
|
+
private reconnectOnError(
|
|
740
764
|
requestedMode: ConnectionMode,
|
|
741
765
|
error: IAnyDriverError,
|
|
742
766
|
) {
|
|
@@ -744,7 +768,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
744
768
|
requestedMode,
|
|
745
769
|
error.message,
|
|
746
770
|
error)
|
|
747
|
-
|
|
771
|
+
.catch(this.props.closeHandler);
|
|
748
772
|
}
|
|
749
773
|
|
|
750
774
|
/**
|
|
@@ -773,7 +797,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
773
797
|
this.logger.sendTelemetryEvent({
|
|
774
798
|
eventName: "reconnectingDespiteFatalError",
|
|
775
799
|
reconnectMode: this.reconnectMode,
|
|
776
|
-
|
|
800
|
+
}, error);
|
|
777
801
|
}
|
|
778
802
|
|
|
779
803
|
if (this.reconnectMode === ReconnectMode.Never) {
|
|
@@ -820,10 +844,10 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
820
844
|
this.clientSequenceNumberObserved = 0;
|
|
821
845
|
}
|
|
822
846
|
|
|
823
|
-
if (message
|
|
824
|
-
this.
|
|
847
|
+
if (!isRuntimeMessage(message)) {
|
|
848
|
+
this.localOpsToIgnore++;
|
|
825
849
|
} else {
|
|
826
|
-
this.
|
|
850
|
+
this.localOpsToIgnore = 0;
|
|
827
851
|
}
|
|
828
852
|
|
|
829
853
|
return {
|
|
@@ -858,8 +882,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
858
882
|
"Switch to write", // message
|
|
859
883
|
);
|
|
860
884
|
}
|
|
861
|
-
})
|
|
862
|
-
.catch(() => {});
|
|
885
|
+
}).catch(() => { });
|
|
863
886
|
}
|
|
864
887
|
return;
|
|
865
888
|
}
|
package/src/connectionState.ts
CHANGED
|
@@ -17,12 +17,6 @@ export enum ConnectionState {
|
|
|
17
17
|
*/
|
|
18
18
|
EstablishingConnection = 3,
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* See {@link ConnectionState.CatchingUp}, which is the new name for this state.
|
|
22
|
-
* @deprecated - This state itself is not gone, just being renamed. Please use {@link ConnectionState.CatchingUp}.
|
|
23
|
-
*/
|
|
24
|
-
Connecting = 1,
|
|
25
|
-
|
|
26
20
|
/**
|
|
27
21
|
* The container has an inbound connection only, and is catching up to the latest known state from the service.
|
|
28
22
|
*/
|