@fluidframework/container-loader 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.4.1.0.148229
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 +65 -44
- package/api-extractor.json +2 -2
- package/closeAndGetPendingLocalState.md +51 -0
- package/dist/audience.d.ts +0 -1
- 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 +5 -5
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +107 -44
- 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 +7 -7
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +50 -21
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +64 -5
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +329 -137
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +19 -8
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +58 -14
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +41 -2
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +88 -14
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +3 -3
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +21 -8
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +112 -37
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts +10 -22
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaManagerProxy.js +14 -50
- 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 +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +13 -4
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +38 -24
- 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 +6 -2
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +7 -4
- 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 +8 -5
- package/dist/utils.js.map +1 -1
- package/lib/audience.d.ts +0 -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 +5 -5
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +110 -47
- 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 +7 -7
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +50 -21
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +64 -5
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +336 -144
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +19 -8
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +59 -15
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +41 -2
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +86 -14
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +3 -3
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +21 -8
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +114 -39
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts +10 -22
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaManagerProxy.js +14 -50
- 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 +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +13 -4
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +37 -24
- 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 +6 -2
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js +7 -4
- 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 +8 -5
- package/lib/utils.js.map +1 -1
- package/package.json +67 -56
- 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 +1040 -941
- package/src/connectionState.ts +19 -19
- package/src/connectionStateHandler.ts +557 -463
- package/src/container.ts +2147 -1784
- package/src/containerContext.ts +417 -345
- package/src/containerStorageAdapter.ts +268 -154
- package/src/contracts.ts +155 -153
- package/src/deltaManager.ts +1074 -945
- package/src/deltaManagerProxy.ts +88 -137
- package/src/deltaQueue.ts +155 -151
- package/src/index.ts +13 -17
- package/src/loader.ts +434 -427
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +93 -87
- package/src/protocolTreeDocumentStorageService.ts +34 -34
- package/src/quorum.ts +34 -34
- package/src/retriableDocumentStorageService.ts +118 -102
- package/src/utils.ts +93 -83
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -12
package/lib/connectionManager.js
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
import { default as AbortController } from "abort-controller";
|
|
6
6
|
import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
7
7
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
8
|
-
import {
|
|
8
|
+
import { DriverErrorType, } from "@fluidframework/driver-definitions";
|
|
9
|
+
import { canRetryOnError, createWriteError, createGenericNetworkError, getRetryDelayFromError, logNetworkFailure, isRuntimeMessage, } from "@fluidframework/driver-utils";
|
|
9
10
|
import { MessageType, ScopeType, } from "@fluidframework/protocol-definitions";
|
|
10
|
-
import { TelemetryLogger, normalizeError
|
|
11
|
-
import { ReconnectMode
|
|
11
|
+
import { TelemetryLogger, normalizeError } from "@fluidframework/telemetry-utils";
|
|
12
|
+
import { ReconnectMode } from "./contracts";
|
|
12
13
|
import { DeltaQueue } from "./deltaQueue";
|
|
13
14
|
import { SignalType } from "./protocol";
|
|
14
15
|
const MaxReconnectDelayInMs = 8000;
|
|
@@ -46,7 +47,9 @@ class NoDeltaStream extends TypedEventEmitter {
|
|
|
46
47
|
this.version = "";
|
|
47
48
|
this.initialMessages = [];
|
|
48
49
|
this.initialSignals = [];
|
|
49
|
-
this.initialClients = [
|
|
50
|
+
this.initialClients = [
|
|
51
|
+
{ client: clientNoDeltaStream, clientId: clientIdNoDeltaStream },
|
|
52
|
+
];
|
|
50
53
|
this.serviceConfiguration = {
|
|
51
54
|
maxMessageSize: 0,
|
|
52
55
|
blockSize: 0,
|
|
@@ -68,9 +71,26 @@ class NoDeltaStream extends TypedEventEmitter {
|
|
|
68
71
|
content: { message: "Cannot submit signal with storage-only connection", code: 403 },
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
|
-
get disposed() {
|
|
72
|
-
|
|
74
|
+
get disposed() {
|
|
75
|
+
return this._disposed;
|
|
76
|
+
}
|
|
77
|
+
dispose() {
|
|
78
|
+
this._disposed = true;
|
|
79
|
+
}
|
|
73
80
|
}
|
|
81
|
+
const waitForOnline = async () => {
|
|
82
|
+
var _a;
|
|
83
|
+
// Only wait if we have a strong signal that we're offline - otherwise assume we're online.
|
|
84
|
+
if (((_a = globalThis.navigator) === null || _a === void 0 ? void 0 : _a.onLine) === false && globalThis.addEventListener !== undefined) {
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
const resolveAndRemoveListener = () => {
|
|
87
|
+
resolve();
|
|
88
|
+
globalThis.removeEventListener("online", resolveAndRemoveListener);
|
|
89
|
+
};
|
|
90
|
+
globalThis.addEventListener("online", resolveAndRemoveListener);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
74
94
|
/**
|
|
75
95
|
* Implementation of IConnectionManager, used by Container class
|
|
76
96
|
* Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
|
|
@@ -88,12 +108,12 @@ export class ConnectionManager {
|
|
|
88
108
|
this.pendingReconnect = false;
|
|
89
109
|
this.clientSequenceNumber = 0;
|
|
90
110
|
this.clientSequenceNumberObserved = 0;
|
|
91
|
-
/** Counts the number of
|
|
111
|
+
/** Counts the number of non-runtime ops sent by the client which may not be acked. */
|
|
92
112
|
this.localOpsToIgnore = 0;
|
|
93
113
|
this.connectFirstConnection = true;
|
|
94
114
|
this._connectionVerboseProps = {};
|
|
95
115
|
this._connectionProps = {};
|
|
96
|
-
this.
|
|
116
|
+
this._disposed = false;
|
|
97
117
|
this.opHandler = (documentId, messagesArg) => {
|
|
98
118
|
const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
|
|
99
119
|
this.props.incomingOpHandler(messages, "opHandler");
|
|
@@ -137,7 +157,9 @@ export class ConnectionManager {
|
|
|
137
157
|
this.props.closeHandler(normalizeError(error));
|
|
138
158
|
});
|
|
139
159
|
}
|
|
140
|
-
get connectionVerboseProps() {
|
|
160
|
+
get connectionVerboseProps() {
|
|
161
|
+
return this._connectionVerboseProps;
|
|
162
|
+
}
|
|
141
163
|
/**
|
|
142
164
|
* The current connection mode, initially read.
|
|
143
165
|
*/
|
|
@@ -145,8 +167,13 @@ export class ConnectionManager {
|
|
|
145
167
|
var _a, _b;
|
|
146
168
|
return (_b = (_a = this.connection) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : "read";
|
|
147
169
|
}
|
|
148
|
-
get connected() {
|
|
149
|
-
|
|
170
|
+
get connected() {
|
|
171
|
+
return this.connection !== undefined;
|
|
172
|
+
}
|
|
173
|
+
get clientId() {
|
|
174
|
+
var _a;
|
|
175
|
+
return (_a = this.connection) === null || _a === void 0 ? void 0 : _a.clientId;
|
|
176
|
+
}
|
|
150
177
|
/**
|
|
151
178
|
* Automatic reconnecting enabled or disabled.
|
|
152
179
|
* If set to Never, then reconnecting will never be allowed.
|
|
@@ -178,7 +205,7 @@ export class ConnectionManager {
|
|
|
178
205
|
/**
|
|
179
206
|
* Returns set of props that can be logged in telemetry that provide some insights / statistics
|
|
180
207
|
* about current or last connection (if there is no connection at the moment)
|
|
181
|
-
|
|
208
|
+
*/
|
|
182
209
|
get connectionProps() {
|
|
183
210
|
return this.connection !== undefined
|
|
184
211
|
? this._connectionProps
|
|
@@ -188,7 +215,7 @@ export class ConnectionManager {
|
|
|
188
215
|
}
|
|
189
216
|
shouldJoinWrite() {
|
|
190
217
|
// We don't have to wait for ack for topmost NoOps. So subtract those.
|
|
191
|
-
return this.clientSequenceNumberObserved <
|
|
218
|
+
return (this.clientSequenceNumberObserved < this.clientSequenceNumber - this.localOpsToIgnore);
|
|
192
219
|
}
|
|
193
220
|
/**
|
|
194
221
|
* Tells if container is in read-only mode.
|
|
@@ -221,37 +248,38 @@ export class ConnectionManager {
|
|
|
221
248
|
return {
|
|
222
249
|
claims: connection.claims,
|
|
223
250
|
clientId: connection.clientId,
|
|
224
|
-
existing: connection.existing,
|
|
225
251
|
checkpointSequenceNumber: connection.checkpointSequenceNumber,
|
|
226
|
-
get initialClients() {
|
|
252
|
+
get initialClients() {
|
|
253
|
+
return connection.initialClients;
|
|
254
|
+
},
|
|
227
255
|
mode: connection.mode,
|
|
228
256
|
serviceConfiguration: connection.serviceConfiguration,
|
|
229
257
|
version: connection.version,
|
|
230
258
|
};
|
|
231
259
|
}
|
|
232
|
-
dispose(error) {
|
|
233
|
-
if (this.
|
|
260
|
+
dispose(error, switchToReadonly = true) {
|
|
261
|
+
if (this._disposed) {
|
|
234
262
|
return;
|
|
235
263
|
}
|
|
236
|
-
this.
|
|
264
|
+
this._disposed = true;
|
|
237
265
|
this.pendingConnection = undefined;
|
|
238
266
|
// Ensure that things like triggerConnect() will short circuit
|
|
239
267
|
this._reconnectMode = ReconnectMode.Never;
|
|
240
268
|
this._outbound.clear();
|
|
241
|
-
const disconnectReason =
|
|
242
|
-
? `Closing DeltaManager (${error.message})`
|
|
243
|
-
: "Closing DeltaManager";
|
|
269
|
+
const disconnectReason = "Closing DeltaManager";
|
|
244
270
|
// This raises "disconnect" event if we have active connection.
|
|
245
271
|
this.disconnectFromDeltaStream(disconnectReason);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
272
|
+
if (switchToReadonly) {
|
|
273
|
+
// Notify everyone we are in read-only state.
|
|
274
|
+
// Useful for data stores in case we hit some critical error,
|
|
275
|
+
// to switch to a mode where user edits are not accepted
|
|
276
|
+
this.set_readonlyPermissions(true);
|
|
277
|
+
}
|
|
250
278
|
}
|
|
251
279
|
/**
|
|
252
280
|
* Enables or disables automatic reconnecting.
|
|
253
281
|
* Will throw an error if reconnectMode set to Never.
|
|
254
|
-
|
|
282
|
+
*/
|
|
255
283
|
setAutoReconnect(mode) {
|
|
256
284
|
assert(mode !== ReconnectMode.Never && this._reconnectMode !== ReconnectMode.Never, 0x278 /* "API is not supported for non-connecting or closed container" */);
|
|
257
285
|
this._reconnectMode = mode;
|
|
@@ -323,8 +351,8 @@ export class ConnectionManager {
|
|
|
323
351
|
});
|
|
324
352
|
}
|
|
325
353
|
async connectCore(connectionMode) {
|
|
326
|
-
var _a, _b;
|
|
327
|
-
assert(!this.
|
|
354
|
+
var _a, _b, _c;
|
|
355
|
+
assert(!this._disposed, 0x26a /* "not closed" */);
|
|
328
356
|
if (this.connection !== undefined) {
|
|
329
357
|
return; // Connection attempt already completed successfully
|
|
330
358
|
}
|
|
@@ -359,10 +387,15 @@ export class ConnectionManager {
|
|
|
359
387
|
let lastError;
|
|
360
388
|
const abortController = new AbortController();
|
|
361
389
|
const abortSignal = abortController.signal;
|
|
362
|
-
this.pendingConnection = {
|
|
390
|
+
this.pendingConnection = {
|
|
391
|
+
abort: () => {
|
|
392
|
+
abortController.abort();
|
|
393
|
+
},
|
|
394
|
+
connectionMode: requestedMode,
|
|
395
|
+
};
|
|
363
396
|
// This loop will keep trying to connect until successful, with a delay between each iteration.
|
|
364
397
|
while (connection === undefined) {
|
|
365
|
-
if (this.
|
|
398
|
+
if (this._disposed) {
|
|
366
399
|
throw new Error("Attempting to connect a closed DeltaManager");
|
|
367
400
|
}
|
|
368
401
|
if (abortSignal.aborted === true) {
|
|
@@ -385,8 +418,9 @@ export class ConnectionManager {
|
|
|
385
418
|
}
|
|
386
419
|
}
|
|
387
420
|
catch (origError) {
|
|
388
|
-
if (typeof origError === "object" &&
|
|
389
|
-
|
|
421
|
+
if (typeof origError === "object" &&
|
|
422
|
+
origError !== null &&
|
|
423
|
+
(origError === null || origError === void 0 ? void 0 : origError.errorType) === DriverErrorType.deltaStreamConnectionForbidden) {
|
|
390
424
|
connection = new NoDeltaStream();
|
|
391
425
|
requestedMode = "read";
|
|
392
426
|
break;
|
|
@@ -406,11 +440,25 @@ export class ConnectionManager {
|
|
|
406
440
|
}, origError);
|
|
407
441
|
lastError = origError;
|
|
408
442
|
const retryDelayFromError = getRetryDelayFromError(origError);
|
|
409
|
-
delayMs = retryDelayFromError !== null && retryDelayFromError !== void 0 ? retryDelayFromError : Math.min(delayMs * 2, MaxReconnectDelayInMs);
|
|
410
443
|
if (retryDelayFromError !== undefined) {
|
|
444
|
+
// If the error told us to wait, then we wait.
|
|
411
445
|
this.props.reconnectionDelayHandler(retryDelayFromError, origError);
|
|
446
|
+
await new Promise((resolve) => {
|
|
447
|
+
setTimeout(resolve, retryDelayFromError);
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
else if (((_c = globalThis.navigator) === null || _c === void 0 ? void 0 : _c.onLine) !== false) {
|
|
451
|
+
// If the error didn't tell us to wait, let's still wait a little bit before retrying.
|
|
452
|
+
// We skip this delay if we're confident we're offline, because we probably just need to wait to come back online.
|
|
453
|
+
await new Promise((resolve) => {
|
|
454
|
+
setTimeout(resolve, delayMs);
|
|
455
|
+
delayMs = Math.min(delayMs * 2, MaxReconnectDelayInMs);
|
|
456
|
+
});
|
|
412
457
|
}
|
|
413
|
-
|
|
458
|
+
// If we believe we're offline, we assume there's no point in trying until we at least think we're online.
|
|
459
|
+
// NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
|
|
460
|
+
// should probably live in the driver.
|
|
461
|
+
await waitForOnline();
|
|
414
462
|
}
|
|
415
463
|
}
|
|
416
464
|
// If we retried more than once, log an event about how long it took (this will not log to error table)
|
|
@@ -440,7 +488,7 @@ export class ConnectionManager {
|
|
|
440
488
|
* @param args - The connection arguments
|
|
441
489
|
*/
|
|
442
490
|
triggerConnect(connectionMode) {
|
|
443
|
-
// reconnect()
|
|
491
|
+
// reconnect() includes async awaits, and that causes potential race conditions
|
|
444
492
|
// where we might already have a connection. If it were to happen, it's possible that we will connect
|
|
445
493
|
// with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
|
|
446
494
|
// fine (if needed, reconnect flow will get triggered again). Places where new mode matters should encode it
|
|
@@ -479,8 +527,8 @@ export class ConnectionManager {
|
|
|
479
527
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
480
528
|
this._outbound.pause();
|
|
481
529
|
this._outbound.clear();
|
|
482
|
-
this.props.disconnectHandler(reason);
|
|
483
530
|
connection.dispose();
|
|
531
|
+
this.props.disconnectHandler(reason);
|
|
484
532
|
this._connectionVerboseProps = {};
|
|
485
533
|
return true;
|
|
486
534
|
}
|
|
@@ -510,14 +558,18 @@ export class ConnectionManager {
|
|
|
510
558
|
// But if we ask read, server can still give us write.
|
|
511
559
|
const readonly = !connection.claims.scopes.includes(ScopeType.DocWrite);
|
|
512
560
|
if (connection.mode !== requestedMode) {
|
|
513
|
-
this.logger.sendTelemetryEvent({
|
|
561
|
+
this.logger.sendTelemetryEvent({
|
|
562
|
+
eventName: "ConnectionModeMismatch",
|
|
563
|
+
requestedMode,
|
|
564
|
+
mode: connection.mode,
|
|
565
|
+
});
|
|
514
566
|
}
|
|
515
567
|
// This connection mode validation logic is moving to the driver layer in 0.44. These two asserts can be
|
|
516
568
|
// removed after those packages have released and become ubiquitous.
|
|
517
569
|
assert(requestedMode === "read" || readonly === (this.connectionMode === "read"), 0x0e7 /* "claims/connectionMode mismatch" */);
|
|
518
570
|
assert(!readonly || this.connectionMode === "read", 0x0e8 /* "readonly perf with write connection" */);
|
|
519
571
|
this.set_readonlyPermissions(readonly);
|
|
520
|
-
if (this.
|
|
572
|
+
if (this._disposed) {
|
|
521
573
|
// Raise proper events, Log telemetry event and close connection.
|
|
522
574
|
this.disconnectFromDeltaStream("ConnectionManager already closed");
|
|
523
575
|
return;
|
|
@@ -550,7 +602,8 @@ export class ConnectionManager {
|
|
|
550
602
|
this._connectionProps.connectionMode = connection.mode;
|
|
551
603
|
let last = -1;
|
|
552
604
|
if (initialMessages.length !== 0) {
|
|
553
|
-
this._connectionVerboseProps.connectionInitialOpsFrom =
|
|
605
|
+
this._connectionVerboseProps.connectionInitialOpsFrom =
|
|
606
|
+
initialMessages[0].sequenceNumber;
|
|
554
607
|
last = initialMessages[initialMessages.length - 1].sequenceNumber;
|
|
555
608
|
this._connectionVerboseProps.connectionInitialOpsTo = last + 1;
|
|
556
609
|
// Update knowledge of how far we are behind, before raising "connect" event
|
|
@@ -603,8 +656,7 @@ export class ConnectionManager {
|
|
|
603
656
|
* @returns A promise that resolves when the connection is reestablished or we stop trying
|
|
604
657
|
*/
|
|
605
658
|
reconnectOnError(requestedMode, error) {
|
|
606
|
-
this.reconnect(requestedMode, error.message, error)
|
|
607
|
-
.catch(this.props.closeHandler);
|
|
659
|
+
this.reconnect(requestedMode, error.message, error).catch(this.props.closeHandler);
|
|
608
660
|
}
|
|
609
661
|
/**
|
|
610
662
|
* Disconnect the current connection and reconnect.
|
|
@@ -635,14 +687,21 @@ export class ConnectionManager {
|
|
|
635
687
|
this.props.closeHandler();
|
|
636
688
|
}
|
|
637
689
|
// If closed then we can't reconnect
|
|
638
|
-
if (this.
|
|
690
|
+
if (this._disposed || this.reconnectMode !== ReconnectMode.Enabled) {
|
|
639
691
|
return;
|
|
640
692
|
}
|
|
693
|
+
// If the error tells us to wait before retrying, then do so.
|
|
641
694
|
const delayMs = getRetryDelayFromError(error);
|
|
642
695
|
if (error !== undefined && delayMs !== undefined) {
|
|
643
696
|
this.props.reconnectionDelayHandler(delayMs, error);
|
|
644
|
-
await
|
|
697
|
+
await new Promise((resolve) => {
|
|
698
|
+
setTimeout(resolve, delayMs);
|
|
699
|
+
});
|
|
645
700
|
}
|
|
701
|
+
// If we believe we're offline, we assume there's no point in trying again until we at least think we're online.
|
|
702
|
+
// NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
|
|
703
|
+
// should probably live in the driver.
|
|
704
|
+
await waitForOnline();
|
|
646
705
|
this.triggerConnect(requestedMode);
|
|
647
706
|
}
|
|
648
707
|
prepareMessageToSend(message) {
|
|
@@ -694,12 +753,15 @@ export class ConnectionManager {
|
|
|
694
753
|
if (this.connectionMode === "read") {
|
|
695
754
|
if (!this.pendingReconnect) {
|
|
696
755
|
this.pendingReconnect = true;
|
|
697
|
-
Promise.resolve()
|
|
698
|
-
|
|
756
|
+
Promise.resolve()
|
|
757
|
+
.then(async () => {
|
|
758
|
+
if (this.pendingReconnect) {
|
|
759
|
+
// still valid?
|
|
699
760
|
await this.reconnect("write", // connectionMode
|
|
700
761
|
"Switch to write");
|
|
701
762
|
}
|
|
702
|
-
})
|
|
763
|
+
})
|
|
764
|
+
.catch(() => { });
|
|
703
765
|
}
|
|
704
766
|
return;
|
|
705
767
|
}
|
|
@@ -709,7 +771,8 @@ export class ConnectionManager {
|
|
|
709
771
|
beforeProcessingIncomingOp(message) {
|
|
710
772
|
// if we have connection, and message is local, then we better treat is as local!
|
|
711
773
|
assert(this.clientId !== message.clientId || this.lastSubmittedClientId === message.clientId, 0x0ee /* "Not accounting local messages correctly" */);
|
|
712
|
-
if (this.lastSubmittedClientId !== undefined &&
|
|
774
|
+
if (this.lastSubmittedClientId !== undefined &&
|
|
775
|
+
this.lastSubmittedClientId === message.clientId) {
|
|
713
776
|
const clientSequenceNumber = message.clientSequenceNumber;
|
|
714
777
|
assert(this.clientSequenceNumberObserved < clientSequenceNumber, 0x0ef /* "client seq# not growing" */);
|
|
715
778
|
assert(clientSequenceNumber <= this.clientSequenceNumber, 0x0f0 /* "Incoming local client seq# > generated by this client" */);
|