@fluidframework/container-loader 2.0.0-internal.6.0.0 → 2.0.0-internal.6.1.0
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/CHANGELOG.md +4 -0
- package/dist/connectionManager.d.ts +3 -3
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +33 -24
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +14 -14
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +17 -12
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +26 -38
- package/dist/container.js.map +1 -1
- package/dist/contracts.d.ts +11 -7
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +4 -4
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +5 -4
- package/dist/deltaManager.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 +4 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +23 -1
- package/dist/protocol.js.map +1 -1
- package/lib/connectionManager.d.ts +3 -3
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +33 -24
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +14 -14
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +17 -12
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +27 -39
- package/lib/container.js.map +1 -1
- package/lib/contracts.d.ts +11 -7
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +4 -4
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +5 -4
- package/lib/deltaManager.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 +4 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +23 -1
- package/lib/protocol.js.map +1 -1
- package/package.json +13 -17
- package/src/connectionManager.ts +46 -31
- package/src/connectionStateHandler.ts +28 -36
- package/src/container.ts +46 -51
- package/src/contracts.ts +12 -6
- package/src/deltaManager.ts +19 -13
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +33 -1
package/src/container.ts
CHANGED
|
@@ -40,17 +40,16 @@ import {
|
|
|
40
40
|
IProvideFluidCodeDetailsComparer,
|
|
41
41
|
IFluidCodeDetailsComparer,
|
|
42
42
|
IRuntime,
|
|
43
|
-
isFluidCodeDetails,
|
|
44
|
-
IThrottlingWarning,
|
|
45
43
|
ReadOnlyInfo,
|
|
44
|
+
isFluidCodeDetails,
|
|
46
45
|
} from "@fluidframework/container-definitions";
|
|
47
46
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
48
47
|
import {
|
|
49
|
-
IAnyDriverError,
|
|
50
48
|
IDocumentService,
|
|
51
49
|
IDocumentServiceFactory,
|
|
52
50
|
IDocumentStorageService,
|
|
53
51
|
IResolvedUrl,
|
|
52
|
+
IThrottlingWarning,
|
|
54
53
|
IUrlResolver,
|
|
55
54
|
} from "@fluidframework/driver-definitions";
|
|
56
55
|
import {
|
|
@@ -60,7 +59,6 @@ import {
|
|
|
60
59
|
runWithRetry,
|
|
61
60
|
isCombinedAppAndProtocolSummary,
|
|
62
61
|
MessageType2,
|
|
63
|
-
canBeCoalescedByService,
|
|
64
62
|
} from "@fluidframework/driver-utils";
|
|
65
63
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
66
64
|
import {
|
|
@@ -102,6 +100,7 @@ import {
|
|
|
102
100
|
IConnectionManagerFactoryArgs,
|
|
103
101
|
getPackageName,
|
|
104
102
|
IConnectionDetailsInternal,
|
|
103
|
+
IConnectionStateChangeReason,
|
|
105
104
|
} from "./contracts";
|
|
106
105
|
import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
107
106
|
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
|
|
@@ -761,7 +760,19 @@ export class Container
|
|
|
761
760
|
this.scope = scope;
|
|
762
761
|
this.detachedBlobStorage = detachedBlobStorage;
|
|
763
762
|
this.protocolHandlerBuilder =
|
|
764
|
-
protocolHandlerBuilder ??
|
|
763
|
+
protocolHandlerBuilder ??
|
|
764
|
+
((
|
|
765
|
+
attributes: IDocumentAttributes,
|
|
766
|
+
quorumSnapshot: IQuorumSnapshot,
|
|
767
|
+
sendProposal: (key: string, value: any) => number,
|
|
768
|
+
) =>
|
|
769
|
+
new ProtocolHandler(
|
|
770
|
+
attributes,
|
|
771
|
+
quorumSnapshot,
|
|
772
|
+
sendProposal,
|
|
773
|
+
new Audience(),
|
|
774
|
+
(clientId: string) => this.clientsWhoShouldHaveLeft.has(clientId),
|
|
775
|
+
));
|
|
765
776
|
|
|
766
777
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
767
778
|
this.clone = async (
|
|
@@ -827,11 +838,11 @@ export class Container
|
|
|
827
838
|
this.connectionStateHandler = createConnectionStateHandler(
|
|
828
839
|
{
|
|
829
840
|
logger: this.mc.logger,
|
|
830
|
-
connectionStateChanged: (value, oldState, reason
|
|
841
|
+
connectionStateChanged: (value, oldState, reason) => {
|
|
831
842
|
if (value === ConnectionState.Connected) {
|
|
832
843
|
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
833
844
|
}
|
|
834
|
-
this.logConnectionStateChangeTelemetry(value, oldState, reason
|
|
845
|
+
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
835
846
|
if (this._lifecycleState === "loaded") {
|
|
836
847
|
this.propagateConnectionState(
|
|
837
848
|
false /* initial transition */,
|
|
@@ -873,8 +884,9 @@ export class Container
|
|
|
873
884
|
// Other possible recovery path - move to connected state (i.e. ConnectionStateHandler.joinOpTimer
|
|
874
885
|
// to call this.applyForConnectedState("addMemberEvent") for "read" connections)
|
|
875
886
|
if (mode === "read") {
|
|
876
|
-
|
|
877
|
-
this.
|
|
887
|
+
const reason = { text: "NoJoinSignal" };
|
|
888
|
+
this.disconnectInternal(reason);
|
|
889
|
+
this.connectInternal({ reason, fetchOpsFromStorage: false });
|
|
878
890
|
}
|
|
879
891
|
},
|
|
880
892
|
clientShouldHaveLeft: (clientId: string) => {
|
|
@@ -1071,7 +1083,7 @@ export class Container
|
|
|
1071
1083
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
1072
1084
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
1073
1085
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
1074
|
-
this.
|
|
1086
|
+
this.disconnectInternal({ text: "closeAndGetPendingLocalState" }); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
1075
1087
|
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
1076
1088
|
this.close();
|
|
1077
1089
|
return pendingState;
|
|
@@ -1267,7 +1279,7 @@ export class Container
|
|
|
1267
1279
|
if (!this.closed) {
|
|
1268
1280
|
this.resumeInternal({
|
|
1269
1281
|
fetchOpsFromStorage: false,
|
|
1270
|
-
reason: "createDetached",
|
|
1282
|
+
reason: { text: "createDetached" },
|
|
1271
1283
|
});
|
|
1272
1284
|
}
|
|
1273
1285
|
} catch (error) {
|
|
@@ -1291,7 +1303,7 @@ export class Container
|
|
|
1291
1303
|
);
|
|
1292
1304
|
}
|
|
1293
1305
|
|
|
1294
|
-
private setAutoReconnectInternal(mode: ReconnectMode) {
|
|
1306
|
+
private setAutoReconnectInternal(mode: ReconnectMode, reason: IConnectionStateChangeReason) {
|
|
1295
1307
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
1296
1308
|
|
|
1297
1309
|
if (currentMode === mode) {
|
|
@@ -1310,7 +1322,7 @@ export class Container
|
|
|
1310
1322
|
duration,
|
|
1311
1323
|
});
|
|
1312
1324
|
|
|
1313
|
-
this._deltaManager.connectionManager.setAutoReconnect(mode);
|
|
1325
|
+
this._deltaManager.connectionManager.setAutoReconnect(mode, reason);
|
|
1314
1326
|
}
|
|
1315
1327
|
|
|
1316
1328
|
public connect() {
|
|
@@ -1322,7 +1334,10 @@ export class Container
|
|
|
1322
1334
|
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
1323
1335
|
// If there is gap, we will learn about it once connected, but the gap should be small (if any),
|
|
1324
1336
|
// assuming that connect() is called quickly after initial container boot.
|
|
1325
|
-
this.connectInternal({
|
|
1337
|
+
this.connectInternal({
|
|
1338
|
+
reason: { text: "DocumentConnect" },
|
|
1339
|
+
fetchOpsFromStorage: false,
|
|
1340
|
+
});
|
|
1326
1341
|
}
|
|
1327
1342
|
}
|
|
1328
1343
|
|
|
@@ -1338,23 +1353,23 @@ export class Container
|
|
|
1338
1353
|
|
|
1339
1354
|
// Set Auto Reconnect Mode
|
|
1340
1355
|
const mode = ReconnectMode.Enabled;
|
|
1341
|
-
this.setAutoReconnectInternal(mode);
|
|
1356
|
+
this.setAutoReconnectInternal(mode, args.reason);
|
|
1342
1357
|
}
|
|
1343
1358
|
|
|
1344
1359
|
public disconnect() {
|
|
1345
1360
|
if (this.closed) {
|
|
1346
1361
|
throw new UsageError(`The Container is closed and cannot be disconnected`);
|
|
1347
1362
|
} else {
|
|
1348
|
-
this.disconnectInternal();
|
|
1363
|
+
this.disconnectInternal({ text: "DocumentDisconnect" });
|
|
1349
1364
|
}
|
|
1350
1365
|
}
|
|
1351
1366
|
|
|
1352
|
-
private disconnectInternal() {
|
|
1367
|
+
private disconnectInternal(reason: IConnectionStateChangeReason) {
|
|
1353
1368
|
assert(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
|
|
1354
1369
|
|
|
1355
1370
|
// Set Auto Reconnect Mode
|
|
1356
1371
|
const mode = ReconnectMode.Disabled;
|
|
1357
|
-
this.setAutoReconnectInternal(mode);
|
|
1372
|
+
this.setAutoReconnectInternal(mode, reason);
|
|
1358
1373
|
}
|
|
1359
1374
|
|
|
1360
1375
|
private resumeInternal(args: IConnectionArgs) {
|
|
@@ -1506,7 +1521,7 @@ export class Container
|
|
|
1506
1521
|
// A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
|
|
1507
1522
|
// B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
|
|
1508
1523
|
const connectionArgs: IConnectionArgs = {
|
|
1509
|
-
reason: "DocumentOpen",
|
|
1524
|
+
reason: { text: "DocumentOpen" },
|
|
1510
1525
|
mode: "write",
|
|
1511
1526
|
fetchOpsFromStorage: false,
|
|
1512
1527
|
};
|
|
@@ -2009,18 +2024,18 @@ export class Container
|
|
|
2009
2024
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
2010
2025
|
});
|
|
2011
2026
|
|
|
2012
|
-
deltaManager.on("establishingConnection", (reason:
|
|
2027
|
+
deltaManager.on("establishingConnection", (reason: IConnectionStateChangeReason) => {
|
|
2013
2028
|
this.connectionStateHandler.establishingConnection(reason);
|
|
2014
2029
|
});
|
|
2015
2030
|
|
|
2016
|
-
deltaManager.on("cancelEstablishingConnection", (reason:
|
|
2031
|
+
deltaManager.on("cancelEstablishingConnection", (reason: IConnectionStateChangeReason) => {
|
|
2017
2032
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
2018
2033
|
});
|
|
2019
2034
|
|
|
2020
|
-
deltaManager.on("disconnect", (reason:
|
|
2035
|
+
deltaManager.on("disconnect", (reason: IConnectionStateChangeReason) => {
|
|
2021
2036
|
this.noopHeuristic?.notifyDisconnect();
|
|
2022
2037
|
if (!this.closed) {
|
|
2023
|
-
this.connectionStateHandler.receivedDisconnectEvent(reason
|
|
2038
|
+
this.connectionStateHandler.receivedDisconnectEvent(reason);
|
|
2024
2039
|
}
|
|
2025
2040
|
});
|
|
2026
2041
|
|
|
@@ -2073,8 +2088,7 @@ export class Container
|
|
|
2073
2088
|
private logConnectionStateChangeTelemetry(
|
|
2074
2089
|
value: ConnectionState,
|
|
2075
2090
|
oldState: ConnectionState,
|
|
2076
|
-
reason?:
|
|
2077
|
-
error?: IAnyDriverError,
|
|
2091
|
+
reason?: IConnectionStateChangeReason,
|
|
2078
2092
|
) {
|
|
2079
2093
|
// Log actual event
|
|
2080
2094
|
const time = performance.now();
|
|
@@ -2113,7 +2127,7 @@ export class Container
|
|
|
2113
2127
|
from: ConnectionState[oldState],
|
|
2114
2128
|
duration,
|
|
2115
2129
|
durationFromDisconnected,
|
|
2116
|
-
reason,
|
|
2130
|
+
reason: reason?.text,
|
|
2117
2131
|
connectionInitiationReason,
|
|
2118
2132
|
pendingClientId: this.connectionStateHandler.pendingClientId,
|
|
2119
2133
|
clientId: this.clientId,
|
|
@@ -2129,7 +2143,7 @@ export class Container
|
|
|
2129
2143
|
isDirty: this.isDirty,
|
|
2130
2144
|
...this._deltaManager.connectionProps,
|
|
2131
2145
|
},
|
|
2132
|
-
error,
|
|
2146
|
+
reason?.error,
|
|
2133
2147
|
);
|
|
2134
2148
|
|
|
2135
2149
|
if (value === ConnectionState.Connected) {
|
|
@@ -2137,7 +2151,10 @@ export class Container
|
|
|
2137
2151
|
}
|
|
2138
2152
|
}
|
|
2139
2153
|
|
|
2140
|
-
private propagateConnectionState(
|
|
2154
|
+
private propagateConnectionState(
|
|
2155
|
+
initialTransition: boolean,
|
|
2156
|
+
disconnectedReason?: IConnectionStateChangeReason,
|
|
2157
|
+
) {
|
|
2141
2158
|
// When container loaded, we want to propagate initial connection state.
|
|
2142
2159
|
// After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
2143
2160
|
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
@@ -2154,7 +2171,7 @@ export class Container
|
|
|
2154
2171
|
|
|
2155
2172
|
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
2156
2173
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
2157
|
-
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
2174
|
+
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason?.text);
|
|
2158
2175
|
}
|
|
2159
2176
|
|
|
2160
2177
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
@@ -2247,28 +2264,6 @@ export class Container
|
|
|
2247
2264
|
}
|
|
2248
2265
|
const local = this.clientId === message.clientId;
|
|
2249
2266
|
|
|
2250
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
2251
|
-
// flagged should have left, or from a client that's not in the quorum but should be
|
|
2252
|
-
if (message.clientId != null) {
|
|
2253
|
-
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
2254
|
-
|
|
2255
|
-
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
2256
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
2257
|
-
throw new Error("Remote message's clientId is missing from the quorum");
|
|
2258
|
-
}
|
|
2259
|
-
|
|
2260
|
-
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
2261
|
-
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
2262
|
-
// document we don't need to blow up aggressively.
|
|
2263
|
-
if (
|
|
2264
|
-
this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
2265
|
-
!canBeCoalescedByService(message)
|
|
2266
|
-
) {
|
|
2267
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
2268
|
-
throw new Error("Remote message's clientId already should have left");
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
2267
|
// Allow the protocol handler to process the message
|
|
2273
2268
|
const result = this.protocolHandler.processMessage(message, local);
|
|
2274
2269
|
|
package/src/contracts.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
IConnectionDetails,
|
|
9
9
|
ICriticalContainerError,
|
|
10
10
|
IDeltaQueue,
|
|
11
|
+
IErrorBase,
|
|
11
12
|
IFluidCodeDetails,
|
|
12
13
|
isFluidPackage,
|
|
13
14
|
ReadOnlyInfo,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
ISignalClient,
|
|
22
23
|
ISignalMessage,
|
|
23
24
|
} from "@fluidframework/protocol-definitions";
|
|
24
|
-
import {
|
|
25
|
+
import { IContainerPackageInfo } from "@fluidframework/driver-definitions";
|
|
25
26
|
|
|
26
27
|
export enum ReconnectMode {
|
|
27
28
|
Never = "Never",
|
|
@@ -29,6 +30,11 @@ export enum ReconnectMode {
|
|
|
29
30
|
Enabled = "Enabled",
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
export interface IConnectionStateChangeReason<T extends IErrorBase = IErrorBase> {
|
|
34
|
+
text: string;
|
|
35
|
+
error?: T;
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
/**
|
|
33
39
|
* Internal version of IConnectionDetails with props are only exposed internally
|
|
34
40
|
*/
|
|
@@ -36,7 +42,7 @@ export interface IConnectionDetailsInternal extends IConnectionDetails {
|
|
|
36
42
|
mode: ConnectionMode;
|
|
37
43
|
version: string;
|
|
38
44
|
initialClients: ISignalClient[];
|
|
39
|
-
reason:
|
|
45
|
+
reason: IConnectionStateChangeReason;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
/**
|
|
@@ -106,7 +112,7 @@ export interface IConnectionManager {
|
|
|
106
112
|
/**
|
|
107
113
|
* Initiates connection to relay service (noop if already connected).
|
|
108
114
|
*/
|
|
109
|
-
connect(reason:
|
|
115
|
+
connect(reason: IConnectionStateChangeReason, connectionMode?: ConnectionMode): void;
|
|
110
116
|
|
|
111
117
|
/**
|
|
112
118
|
* Disposed connection manager
|
|
@@ -150,7 +156,7 @@ export interface IConnectionManagerFactoryArgs {
|
|
|
150
156
|
/**
|
|
151
157
|
* Called whenever connection to relay service is lost.
|
|
152
158
|
*/
|
|
153
|
-
readonly disconnectHandler: (reason:
|
|
159
|
+
readonly disconnectHandler: (reason: IConnectionStateChangeReason) => void;
|
|
154
160
|
|
|
155
161
|
/**
|
|
156
162
|
* Called whenever new connection to rely service is established
|
|
@@ -180,12 +186,12 @@ export interface IConnectionManagerFactoryArgs {
|
|
|
180
186
|
/**
|
|
181
187
|
* Called whenever we try to start establishing a new connection.
|
|
182
188
|
*/
|
|
183
|
-
readonly establishConnectionHandler: (reason:
|
|
189
|
+
readonly establishConnectionHandler: (reason: IConnectionStateChangeReason) => void;
|
|
184
190
|
|
|
185
191
|
/**
|
|
186
192
|
* Called whenever we cancel the connection in progress.
|
|
187
193
|
*/
|
|
188
|
-
readonly cancelConnectionHandler: (reason:
|
|
194
|
+
readonly cancelConnectionHandler: (reason: IConnectionStateChangeReason) => void;
|
|
189
195
|
}
|
|
190
196
|
|
|
191
197
|
/**
|
package/src/deltaManager.ts
CHANGED
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
IDocumentDeltaStorageService,
|
|
26
26
|
IDocumentService,
|
|
27
27
|
DriverErrorType,
|
|
28
|
-
IAnyDriverError,
|
|
29
28
|
} from "@fluidframework/driver-definitions";
|
|
30
29
|
import {
|
|
31
30
|
IDocumentMessage,
|
|
@@ -46,6 +45,7 @@ import {
|
|
|
46
45
|
IConnectionDetailsInternal,
|
|
47
46
|
IConnectionManager,
|
|
48
47
|
IConnectionManagerFactoryArgs,
|
|
48
|
+
IConnectionStateChangeReason,
|
|
49
49
|
} from "./contracts";
|
|
50
50
|
import { DeltaQueue } from "./deltaQueue";
|
|
51
51
|
import { OnlyValidTermValue } from "./protocol";
|
|
@@ -53,7 +53,7 @@ import { OnlyValidTermValue } from "./protocol";
|
|
|
53
53
|
export interface IConnectionArgs {
|
|
54
54
|
mode?: ConnectionMode;
|
|
55
55
|
fetchOpsFromStorage?: boolean;
|
|
56
|
-
reason:
|
|
56
|
+
reason: IConnectionStateChangeReason;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
@@ -64,8 +64,11 @@ export interface IDeltaManagerInternalEvents extends IDeltaManagerEvents {
|
|
|
64
64
|
(event: "throttled", listener: (error: IThrottlingWarning) => void);
|
|
65
65
|
(event: "closed" | "disposed", listener: (error?: ICriticalContainerError) => void);
|
|
66
66
|
(event: "connect", listener: (details: IConnectionDetailsInternal, opsBehind?: number) => void);
|
|
67
|
-
(event: "establishingConnection", listener: (reason:
|
|
68
|
-
(
|
|
67
|
+
(event: "establishingConnection", listener: (reason: IConnectionStateChangeReason) => void);
|
|
68
|
+
(
|
|
69
|
+
event: "cancelEstablishingConnection",
|
|
70
|
+
listener: (reason: IConnectionStateChangeReason) => void,
|
|
71
|
+
);
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
/**
|
|
@@ -347,6 +350,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
347
350
|
return {
|
|
348
351
|
sequenceNumber: this.lastSequenceNumber,
|
|
349
352
|
opsSize: this.opsSize > 0 ? this.opsSize : undefined,
|
|
353
|
+
deltaManagerState: this._disposed ? "disposed" : this._closed ? "closed" : "open",
|
|
350
354
|
...this.connectionManager.connectionProps,
|
|
351
355
|
};
|
|
352
356
|
}
|
|
@@ -400,15 +404,17 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
400
404
|
reconnectionDelayHandler: (delayMs: number, error: unknown) =>
|
|
401
405
|
this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
|
|
402
406
|
closeHandler: (error: any) => this.close(error),
|
|
403
|
-
disconnectHandler: (reason:
|
|
404
|
-
this.disconnectHandler(reason
|
|
407
|
+
disconnectHandler: (reason: IConnectionStateChangeReason) =>
|
|
408
|
+
this.disconnectHandler(reason),
|
|
405
409
|
connectHandler: (connection: IConnectionDetailsInternal) =>
|
|
406
410
|
this.connectHandler(connection),
|
|
407
411
|
pongHandler: (latency: number) => this.emit("pong", latency),
|
|
408
412
|
readonlyChangeHandler: (readonly?: boolean) =>
|
|
409
413
|
safeRaiseEvent(this, this.logger, "readonly", readonly),
|
|
410
|
-
establishConnectionHandler: (reason:
|
|
411
|
-
|
|
414
|
+
establishConnectionHandler: (reason: IConnectionStateChangeReason) =>
|
|
415
|
+
this.establishingConnection(reason),
|
|
416
|
+
cancelConnectionHandler: (reason: IConnectionStateChangeReason) =>
|
|
417
|
+
this.cancelEstablishingConnection(reason),
|
|
412
418
|
};
|
|
413
419
|
|
|
414
420
|
this.connectionManager = createConnectionManager(props);
|
|
@@ -448,11 +454,11 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
448
454
|
// - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
|
|
449
455
|
}
|
|
450
456
|
|
|
451
|
-
private cancelEstablishingConnection(reason:
|
|
457
|
+
private cancelEstablishingConnection(reason: IConnectionStateChangeReason) {
|
|
452
458
|
this.emit("cancelEstablishingConnection", reason);
|
|
453
459
|
}
|
|
454
460
|
|
|
455
|
-
private establishingConnection(reason:
|
|
461
|
+
private establishingConnection(reason: IConnectionStateChangeReason) {
|
|
456
462
|
this.emit("establishingConnection", reason);
|
|
457
463
|
}
|
|
458
464
|
|
|
@@ -586,7 +592,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
586
592
|
// on the wire, we might be always behind.
|
|
587
593
|
// See comment at the end of "connect" handler
|
|
588
594
|
if (fetchOpsFromStorage) {
|
|
589
|
-
this.fetchMissingDeltas(args.reason);
|
|
595
|
+
this.fetchMissingDeltas(args.reason.text);
|
|
590
596
|
}
|
|
591
597
|
|
|
592
598
|
this.connectionManager.connect(args.reason, args.mode);
|
|
@@ -772,9 +778,9 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
772
778
|
}
|
|
773
779
|
}
|
|
774
780
|
|
|
775
|
-
private disconnectHandler(reason:
|
|
781
|
+
private disconnectHandler(reason: IConnectionStateChangeReason) {
|
|
776
782
|
this.messageBuffer.length = 0;
|
|
777
|
-
this.emit("disconnect", reason
|
|
783
|
+
this.emit("disconnect", reason);
|
|
778
784
|
}
|
|
779
785
|
|
|
780
786
|
/**
|
package/src/packageVersion.ts
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IAudienceOwner } from "@fluidframework/container-definitions";
|
|
7
|
+
import { canBeCoalescedByService } from "@fluidframework/driver-utils";
|
|
7
8
|
import {
|
|
8
9
|
IProtocolHandler as IBaseProtocolHandler,
|
|
9
10
|
IQuorumSnapshot,
|
|
@@ -11,8 +12,12 @@ import {
|
|
|
11
12
|
} from "@fluidframework/protocol-base";
|
|
12
13
|
import {
|
|
13
14
|
IDocumentAttributes,
|
|
15
|
+
IProcessMessageResult,
|
|
16
|
+
ISequencedClient,
|
|
17
|
+
ISequencedDocumentMessage,
|
|
14
18
|
ISignalClient,
|
|
15
19
|
ISignalMessage,
|
|
20
|
+
MessageType,
|
|
16
21
|
} from "@fluidframework/protocol-definitions";
|
|
17
22
|
|
|
18
23
|
// "term" was an experimental feature that is being removed. The only safe value to use is 1.
|
|
@@ -44,7 +49,8 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
|
|
|
44
49
|
attributes: IDocumentAttributes,
|
|
45
50
|
quorumSnapshot: IQuorumSnapshot,
|
|
46
51
|
sendProposal: (key: string, value: any) => number,
|
|
47
|
-
readonly audience: IAudienceOwner,
|
|
52
|
+
public readonly audience: IAudienceOwner,
|
|
53
|
+
private readonly shouldClientHaveLeft: (clientId: string) => boolean,
|
|
48
54
|
) {
|
|
49
55
|
super(
|
|
50
56
|
attributes.minimumSequenceNumber,
|
|
@@ -65,6 +71,32 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
|
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
|
|
74
|
+
public processMessage(
|
|
75
|
+
message: ISequencedDocumentMessage,
|
|
76
|
+
local: boolean,
|
|
77
|
+
): IProcessMessageResult {
|
|
78
|
+
const client: ISequencedClient | undefined = this.quorum.getMember(message.clientId);
|
|
79
|
+
|
|
80
|
+
// Check and report if we're getting messages from a clientId that we previously
|
|
81
|
+
// flagged as shouldHaveLeft, or from a client that's not in the quorum but should be
|
|
82
|
+
if (message.clientId != null) {
|
|
83
|
+
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
84
|
+
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
85
|
+
throw new Error("Remote message's clientId is missing from the quorum");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
89
|
+
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
90
|
+
// document we don't need to blow up aggressively.
|
|
91
|
+
if (this.shouldClientHaveLeft(message.clientId) && !canBeCoalescedByService(message)) {
|
|
92
|
+
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
93
|
+
throw new Error("Remote message's clientId already should have left");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return super.processMessage(message, local);
|
|
98
|
+
}
|
|
99
|
+
|
|
68
100
|
public processSignal(message: ISignalMessage) {
|
|
69
101
|
const innerContent = message.content as { content: any; type: string };
|
|
70
102
|
switch (innerContent.type) {
|