@fluidframework/container-loader 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.3.1.0.125672
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 +58 -40
- package/api-extractor.json +2 -2
- 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 +64 -33
- 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 +3 -3
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +43 -21
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +20 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +187 -54
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +3 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +11 -10
- 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 +1 -1
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +18 -6
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +108 -35
- 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 +16 -8
- 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 +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 +66 -35
- 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 +3 -3
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +43 -21
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +20 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +191 -58
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +3 -2
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +11 -10
- 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 +1 -1
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +18 -6
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +110 -37
- 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 +16 -8
- 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 +22 -19
- 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 +1009 -941
- package/src/connectionState.ts +19 -19
- package/src/connectionStateHandler.ts +544 -462
- package/src/container.ts +2039 -1784
- package/src/containerContext.ts +353 -344
- package/src/containerStorageAdapter.ts +163 -153
- package/src/contracts.ts +155 -153
- package/src/deltaManager.ts +1069 -945
- package/src/deltaManagerProxy.ts +143 -137
- package/src/deltaQueue.ts +155 -151
- package/src/index.ts +14 -17
- package/src/loader.ts +427 -422
- 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/dist/container.js
CHANGED
|
@@ -71,8 +71,8 @@ async function waitContainerToCatchUp(container) {
|
|
|
71
71
|
// Waiting for "connected" state in either case gets us at least to our own Join op
|
|
72
72
|
// which is a reasonable approximation of "caught up"
|
|
73
73
|
const waitForOps = () => {
|
|
74
|
-
(0, common_utils_1.assert)(container.connectionState === connectionState_1.ConnectionState.CatchingUp
|
|
75
|
-
|
|
74
|
+
(0, common_utils_1.assert)(container.connectionState === connectionState_1.ConnectionState.CatchingUp ||
|
|
75
|
+
container.connectionState === connectionState_1.ConnectionState.Connected, 0x0cd /* "Container disconnected while waiting for ops!" */);
|
|
76
76
|
const hasCheckpointSequenceNumber = deltaManager.hasCheckpointSequenceNumber;
|
|
77
77
|
const connectionOpSeqNumber = deltaManager.lastKnownSeqNumber;
|
|
78
78
|
(0, common_utils_1.assert)(deltaManager.lastSequenceNumber <= connectionOpSeqNumber, 0x266 /* "lastKnownSeqNumber should never be below last processed sequence number" */);
|
|
@@ -113,7 +113,7 @@ const getCodeProposal =
|
|
|
113
113
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
114
114
|
(quorum) => { var _a; return (_a = quorum.get("code")) !== null && _a !== void 0 ? _a : quorum.get("code2"); };
|
|
115
115
|
/**
|
|
116
|
-
* Helper function to report to telemetry cases where operation takes longer than expected (
|
|
116
|
+
* Helper function to report to telemetry cases where operation takes longer than expected (200ms)
|
|
117
117
|
* @param logger - logger to use
|
|
118
118
|
* @param eventName - event name
|
|
119
119
|
* @param action - functor to call and measure
|
|
@@ -141,6 +141,21 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
141
141
|
// Tells if container can reconnect on losing fist connection
|
|
142
142
|
// If false, container gets closed on loss of connection.
|
|
143
143
|
this._canReconnect = true;
|
|
144
|
+
/**
|
|
145
|
+
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
146
|
+
*
|
|
147
|
+
* States are allowed to progress to further states:
|
|
148
|
+
* "loading" - "loaded" - "closing" - "disposing" - "closed" - "disposed"
|
|
149
|
+
*
|
|
150
|
+
* For example, moving from "closed" to "disposing" is not allowed since it is an earlier state.
|
|
151
|
+
*
|
|
152
|
+
* loading: Container has been created, but is not yet in normal/loaded state
|
|
153
|
+
* loaded: Container is in normal/loaded state
|
|
154
|
+
* closing: Container has started closing process (for re-entrancy prevention)
|
|
155
|
+
* disposing: Container has started disposing process (for re-entrancy prevention)
|
|
156
|
+
* closed: Container has closed
|
|
157
|
+
* disposed: Container has been disposed
|
|
158
|
+
*/
|
|
144
159
|
this._lifecycleState = "loading";
|
|
145
160
|
this._attachState = container_definitions_1.AttachState.Detached;
|
|
146
161
|
/** During initialization we pause the inbound queues. We track this state to ensure we only call resume once */
|
|
@@ -151,6 +166,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
151
166
|
this.attachStarted = false;
|
|
152
167
|
this._dirtyContainer = false;
|
|
153
168
|
this.setAutoReconnectTime = common_utils_1.performance.now();
|
|
169
|
+
this._disposed = false;
|
|
154
170
|
this.clientDetailsOverride = config.clientDetailsOverride;
|
|
155
171
|
this._resolvedUrl = config.resolvedUrl;
|
|
156
172
|
if (config.canReconnect !== undefined) {
|
|
@@ -204,17 +220,23 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
204
220
|
}
|
|
205
221
|
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
206
222
|
if (this._lifecycleState === "loaded") {
|
|
207
|
-
this.propagateConnectionState(false /* initial transition */, value === connectionState_1.ConnectionState.Disconnected
|
|
223
|
+
this.propagateConnectionState(false /* initial transition */, value === connectionState_1.ConnectionState.Disconnected
|
|
224
|
+
? reason
|
|
225
|
+
: undefined /* disconnectedReason */);
|
|
208
226
|
}
|
|
209
227
|
},
|
|
210
228
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
211
229
|
maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
|
|
212
|
-
logConnectionIssue: (eventName, details) => {
|
|
230
|
+
logConnectionIssue: (eventName, category, details) => {
|
|
213
231
|
const mode = this.connectionMode;
|
|
214
232
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
215
|
-
// its own join op.
|
|
233
|
+
// its own join op.
|
|
234
|
+
// Report issues only if we already loaded container - op processing is paused while container is loading,
|
|
235
|
+
// so we always time-out processing of join op in cases where fetching snapshot takes a minute.
|
|
236
|
+
// It's not a problem with op processing itself - such issues should be tracked as part of boot perf monitoring instead.
|
|
216
237
|
this._deltaManager.logConnectionIssue(Object.assign({ eventName,
|
|
217
|
-
mode,
|
|
238
|
+
mode, category: this._lifecycleState === "loading" ? "generic" : category, duration: common_utils_1.performance.now() -
|
|
239
|
+
this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
|
|
218
240
|
// If this is "write" connection, it took too long to receive join op. But in most cases that's due
|
|
219
241
|
// to very slow op fetches and we will eventually get there.
|
|
220
242
|
// For "read" connections, we get here due to self join signal not arriving on time. We will need to
|
|
@@ -248,7 +270,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
248
270
|
}
|
|
249
271
|
else {
|
|
250
272
|
// settimeout so this will hopefully fire after disconnect event if being hidden caused it
|
|
251
|
-
setTimeout(() => {
|
|
273
|
+
setTimeout(() => {
|
|
274
|
+
this.lastVisible = undefined;
|
|
275
|
+
}, 0);
|
|
252
276
|
}
|
|
253
277
|
};
|
|
254
278
|
document.addEventListener("visibilitychange", this.visibilityEventHandler);
|
|
@@ -259,7 +283,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
259
283
|
// if we are in connecting stage.
|
|
260
284
|
this.on("newListener", (event, listener) => {
|
|
261
285
|
// Fire events on the end of JS turn, giving a chance for caller to be in consistent state.
|
|
262
|
-
Promise.resolve()
|
|
286
|
+
Promise.resolve()
|
|
287
|
+
.then(() => {
|
|
263
288
|
switch (event) {
|
|
264
289
|
case dirtyContainerEvent:
|
|
265
290
|
if (this._dirtyContainer) {
|
|
@@ -283,7 +308,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
283
308
|
break;
|
|
284
309
|
default:
|
|
285
310
|
}
|
|
286
|
-
})
|
|
311
|
+
})
|
|
312
|
+
.catch((error) => {
|
|
287
313
|
this.mc.logger.sendErrorEvent({ eventName: "RaiseConnectedEventError" }, error);
|
|
288
314
|
});
|
|
289
315
|
});
|
|
@@ -311,7 +337,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
311
337
|
reject(err !== null && err !== void 0 ? err : new container_utils_1.GenericError("Container closed without error during load"));
|
|
312
338
|
};
|
|
313
339
|
container.on("closed", onClosed);
|
|
314
|
-
container
|
|
340
|
+
container
|
|
341
|
+
.load(version, mode, pendingLocalState)
|
|
315
342
|
.finally(() => {
|
|
316
343
|
container.removeListener("closed", onClosed);
|
|
317
344
|
})
|
|
@@ -360,7 +387,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
360
387
|
}
|
|
361
388
|
}
|
|
362
389
|
get closed() {
|
|
363
|
-
return (this._lifecycleState === "closing" ||
|
|
390
|
+
return (this._lifecycleState === "closing" ||
|
|
391
|
+
this._lifecycleState === "closed" ||
|
|
392
|
+
this._lifecycleState === "disposing" ||
|
|
393
|
+
this._lifecycleState === "disposed");
|
|
364
394
|
}
|
|
365
395
|
get storage() {
|
|
366
396
|
return this.storageService;
|
|
@@ -377,8 +407,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
377
407
|
}
|
|
378
408
|
return this._protocolHandler;
|
|
379
409
|
}
|
|
380
|
-
get connectionMode() {
|
|
381
|
-
|
|
410
|
+
get connectionMode() {
|
|
411
|
+
return this._deltaManager.connectionManager.connectionMode;
|
|
412
|
+
}
|
|
413
|
+
get IFluidRouter() {
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
382
416
|
get resolvedUrl() {
|
|
383
417
|
return this._resolvedUrl;
|
|
384
418
|
}
|
|
@@ -460,24 +494,39 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
460
494
|
get isDirty() {
|
|
461
495
|
return this._dirtyContainer;
|
|
462
496
|
}
|
|
463
|
-
get serviceFactory() {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
get
|
|
497
|
+
get serviceFactory() {
|
|
498
|
+
return this.loader.services.documentServiceFactory;
|
|
499
|
+
}
|
|
500
|
+
get urlResolver() {
|
|
501
|
+
return this.loader.services.urlResolver;
|
|
502
|
+
}
|
|
503
|
+
get scope() {
|
|
504
|
+
return this.loader.services.scope;
|
|
505
|
+
}
|
|
506
|
+
get codeLoader() {
|
|
507
|
+
return this.loader.services.codeLoader;
|
|
508
|
+
}
|
|
467
509
|
/**
|
|
468
510
|
* Retrieves the quorum associated with the document
|
|
469
511
|
*/
|
|
470
512
|
getQuorum() {
|
|
471
513
|
return this.protocolHandler.quorum;
|
|
472
514
|
}
|
|
515
|
+
dispose(error) {
|
|
516
|
+
this._deltaManager.close(error, true /* doDispose */);
|
|
517
|
+
this.verifyClosed();
|
|
518
|
+
}
|
|
473
519
|
close(error) {
|
|
474
520
|
// 1. Ensure that close sequence is exactly the same no matter if it's initiated by host or by DeltaManager
|
|
475
521
|
// 2. We need to ensure that we deliver disconnect event to runtime properly. See connectionStateChanged
|
|
476
522
|
// handler. We only deliver events if container fully loaded. Transitioning from "loading" ->
|
|
477
523
|
// "closing" will lose that info (can also solve by tracking extra state).
|
|
478
524
|
this._deltaManager.close(error);
|
|
525
|
+
this.verifyClosed();
|
|
526
|
+
}
|
|
527
|
+
verifyClosed() {
|
|
479
528
|
(0, common_utils_1.assert)(this.connectionState === connectionState_1.ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
480
|
-
(0, common_utils_1.assert)(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
|
|
529
|
+
(0, common_utils_1.assert)(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
|
|
481
530
|
}
|
|
482
531
|
closeCore(error) {
|
|
483
532
|
var _a, _b, _c;
|
|
@@ -505,7 +554,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
505
554
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
506
555
|
}
|
|
507
556
|
this.emit("closed", error);
|
|
508
|
-
this.removeAllListeners();
|
|
509
557
|
if (this.visibilityEventHandler !== undefined) {
|
|
510
558
|
document.removeEventListener("visibilitychange", this.visibilityEventHandler);
|
|
511
559
|
}
|
|
@@ -514,6 +562,45 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
514
562
|
this._lifecycleState = "closed";
|
|
515
563
|
}
|
|
516
564
|
}
|
|
565
|
+
disposeCore(error) {
|
|
566
|
+
var _a, _b, _c;
|
|
567
|
+
(0, common_utils_1.assert)(!this._disposed, 0x54c /* Container already disposed */);
|
|
568
|
+
this._disposed = true;
|
|
569
|
+
try {
|
|
570
|
+
// Ensure that we raise all key events even if one of these throws
|
|
571
|
+
try {
|
|
572
|
+
// Raise event first, to ensure we capture _lifecycleState before transition.
|
|
573
|
+
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
574
|
+
this.mc.logger.sendTelemetryEvent({
|
|
575
|
+
eventName: "ContainerDispose",
|
|
576
|
+
category: error === undefined ? "generic" : "error",
|
|
577
|
+
}, error);
|
|
578
|
+
// ! Progressing from "closed" to "disposing" is not allowed
|
|
579
|
+
if (this._lifecycleState !== "closed") {
|
|
580
|
+
this._lifecycleState = "disposing";
|
|
581
|
+
}
|
|
582
|
+
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
583
|
+
this.connectionStateHandler.dispose();
|
|
584
|
+
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
585
|
+
this.storageService.dispose();
|
|
586
|
+
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
587
|
+
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
588
|
+
// Driver need to ensure all caches are cleared on critical errors
|
|
589
|
+
(_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
|
|
590
|
+
}
|
|
591
|
+
catch (exception) {
|
|
592
|
+
this.mc.logger.sendErrorEvent({ eventName: "ContainerDisposeException" }, exception);
|
|
593
|
+
}
|
|
594
|
+
this.emit("disposed", error);
|
|
595
|
+
this.removeAllListeners();
|
|
596
|
+
if (this.visibilityEventHandler !== undefined) {
|
|
597
|
+
document.removeEventListener("visibilitychange", this.visibilityEventHandler);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
finally {
|
|
601
|
+
this._lifecycleState = "disposed";
|
|
602
|
+
}
|
|
603
|
+
}
|
|
517
604
|
closeAndGetPendingLocalState() {
|
|
518
605
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
519
606
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
@@ -530,6 +617,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
530
617
|
clientId: this.clientId,
|
|
531
618
|
};
|
|
532
619
|
this.mc.logger.sendTelemetryEvent({ eventName: "CloseAndGetPendingLocalState" });
|
|
620
|
+
// Only close here as method name suggests
|
|
533
621
|
this.close();
|
|
534
622
|
return JSON.stringify(pendingState);
|
|
535
623
|
}
|
|
@@ -541,13 +629,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
541
629
|
const appSummary = this.context.createSummary();
|
|
542
630
|
const protocolSummary = this.captureProtocolSummary();
|
|
543
631
|
const combinedSummary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
544
|
-
if (this.loader.services.detachedBlobStorage &&
|
|
545
|
-
|
|
632
|
+
if (this.loader.services.detachedBlobStorage &&
|
|
633
|
+
this.loader.services.detachedBlobStorage.size > 0) {
|
|
634
|
+
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
635
|
+
type: protocol_definitions_1.SummaryType.Blob,
|
|
636
|
+
content: "true",
|
|
637
|
+
};
|
|
546
638
|
}
|
|
547
639
|
return JSON.stringify(combinedSummary);
|
|
548
640
|
}
|
|
549
641
|
async attach(request) {
|
|
550
642
|
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
643
|
+
var _a;
|
|
551
644
|
if (this._lifecycleState !== "loaded") {
|
|
552
645
|
// pre-0.58 error message: containerNotValidForAttach
|
|
553
646
|
throw new container_utils_1.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
|
|
@@ -556,8 +649,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
556
649
|
(0, common_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
557
650
|
this.attachStarted = true;
|
|
558
651
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
559
|
-
const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
|
|
560
|
-
|
|
652
|
+
const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined &&
|
|
653
|
+
this.loader.services.detachedBlobStorage.size > 0;
|
|
561
654
|
try {
|
|
562
655
|
(0, common_utils_1.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
563
656
|
let summary;
|
|
@@ -595,7 +688,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
595
688
|
const redirectTable = new Map();
|
|
596
689
|
// if new blobs are added while uploading, upload them too
|
|
597
690
|
while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
|
|
598
|
-
const newIds = this.loader.services.detachedBlobStorage
|
|
691
|
+
const newIds = this.loader.services.detachedBlobStorage
|
|
692
|
+
.getBlobIds()
|
|
693
|
+
.filter((id) => !redirectTable.has(id));
|
|
599
694
|
for (const id of newIds) {
|
|
600
695
|
const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
|
|
601
696
|
const response = await this.storageService.createBlob(blob);
|
|
@@ -617,7 +712,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
617
712
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
618
713
|
this.emit("attached");
|
|
619
714
|
if (!this.closed) {
|
|
620
|
-
this.resumeInternal({
|
|
715
|
+
this.resumeInternal({
|
|
716
|
+
fetchOpsFromStorage: false,
|
|
717
|
+
reason: "createDetached",
|
|
718
|
+
});
|
|
621
719
|
}
|
|
622
720
|
}
|
|
623
721
|
catch (error) {
|
|
@@ -628,6 +726,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
628
726
|
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
629
727
|
}
|
|
630
728
|
this.close(newError);
|
|
729
|
+
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
631
730
|
throw newError;
|
|
632
731
|
}
|
|
633
732
|
}, { start: true, end: true, cancel: "generic" });
|
|
@@ -716,23 +815,27 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
716
815
|
throw new Error("Proposed code details should be greater than the current");
|
|
717
816
|
}
|
|
718
817
|
}
|
|
719
|
-
return this.protocolHandler.quorum
|
|
818
|
+
return this.protocolHandler.quorum
|
|
819
|
+
.propose("code", codeDetails)
|
|
720
820
|
.then(() => true)
|
|
721
821
|
.catch(() => false);
|
|
722
822
|
}
|
|
723
823
|
async processCodeProposal() {
|
|
824
|
+
var _a;
|
|
724
825
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
725
826
|
await Promise.all([
|
|
726
827
|
this.deltaManager.inbound.pause(),
|
|
727
|
-
this.deltaManager.inboundSignal.pause()
|
|
828
|
+
this.deltaManager.inboundSignal.pause(),
|
|
728
829
|
]);
|
|
729
|
-
if ((await this.context.satisfies(codeDetails) === true)
|
|
830
|
+
if ((await this.context.satisfies(codeDetails)) === true) {
|
|
730
831
|
this.deltaManager.inbound.resume();
|
|
731
832
|
this.deltaManager.inboundSignal.resume();
|
|
732
833
|
return;
|
|
733
834
|
}
|
|
734
835
|
// pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
|
|
735
|
-
|
|
836
|
+
const error = new container_utils_1.GenericError("Existing context does not satisfy incoming proposal");
|
|
837
|
+
this.close(error);
|
|
838
|
+
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
736
839
|
}
|
|
737
840
|
async getVersion(version) {
|
|
738
841
|
const versions = await this.storageService.getVersions(version, 1);
|
|
@@ -770,7 +873,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
770
873
|
// connections to same file) in two ways:
|
|
771
874
|
// A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
|
|
772
875
|
// B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
|
|
773
|
-
const connectionArgs = {
|
|
876
|
+
const connectionArgs = {
|
|
877
|
+
reason: "DocumentOpen",
|
|
878
|
+
mode: "write",
|
|
879
|
+
fetchOpsFromStorage: false,
|
|
880
|
+
};
|
|
774
881
|
// Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
|
|
775
882
|
// DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
|
|
776
883
|
if (loadMode.deltaConnection === undefined) {
|
|
@@ -781,7 +888,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
781
888
|
}
|
|
782
889
|
else {
|
|
783
890
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
784
|
-
this.storageService.connectToService(this.service).catch((error) =>
|
|
891
|
+
this.storageService.connectToService(this.service).catch((error) => {
|
|
892
|
+
var _a;
|
|
893
|
+
this.close(error);
|
|
894
|
+
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
895
|
+
});
|
|
785
896
|
}
|
|
786
897
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
787
898
|
// Fetch specified snapshot.
|
|
@@ -834,8 +945,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
834
945
|
if (!this.closed) {
|
|
835
946
|
if (opsBeforeReturnP !== undefined) {
|
|
836
947
|
this._deltaManager.inbound.resume();
|
|
837
|
-
await
|
|
838
|
-
await
|
|
948
|
+
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "WaitOps" }, async () => opsBeforeReturnP);
|
|
949
|
+
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "WaitOpProcessing" }, async () => this._deltaManager.inbound.waitTillProcessingDone());
|
|
839
950
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
840
951
|
this._deltaManager.inbound.pause();
|
|
841
952
|
}
|
|
@@ -890,7 +1001,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
890
1001
|
}
|
|
891
1002
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
892
1003
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
893
|
-
(0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage &&
|
|
1004
|
+
(0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage &&
|
|
1005
|
+
this.loader.services.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
894
1006
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
895
1007
|
}
|
|
896
1008
|
const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
|
|
@@ -937,11 +1049,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
937
1049
|
};
|
|
938
1050
|
if (snapshot !== undefined) {
|
|
939
1051
|
const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshot);
|
|
940
|
-
[quorumSnapshot.members, quorumSnapshot.proposals, quorumSnapshot.values] =
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1052
|
+
[quorumSnapshot.members, quorumSnapshot.proposals, quorumSnapshot.values] =
|
|
1053
|
+
await Promise.all([
|
|
1054
|
+
(0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumMembers),
|
|
1055
|
+
(0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumProposals),
|
|
1056
|
+
(0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumValues),
|
|
1057
|
+
]);
|
|
945
1058
|
}
|
|
946
1059
|
this.initializeProtocolState(attributes, quorumSnapshot);
|
|
947
1060
|
}
|
|
@@ -968,7 +1081,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
968
1081
|
});
|
|
969
1082
|
}
|
|
970
1083
|
this.processCodeProposal().catch((error) => {
|
|
971
|
-
|
|
1084
|
+
var _a;
|
|
1085
|
+
const normalizedError = (0, telemetry_utils_1.normalizeError)(error);
|
|
1086
|
+
this.close(normalizedError);
|
|
1087
|
+
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, normalizedError);
|
|
972
1088
|
throw error;
|
|
973
1089
|
});
|
|
974
1090
|
}
|
|
@@ -1025,7 +1141,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1025
1141
|
if (this.clientDetailsOverride !== undefined) {
|
|
1026
1142
|
(0, merge_1.default)(client.details, this.clientDetailsOverride);
|
|
1027
1143
|
}
|
|
1028
|
-
client.details.environment = [
|
|
1144
|
+
client.details.environment = [
|
|
1145
|
+
client.details.environment,
|
|
1146
|
+
` loaderVersion:${packageVersion_1.pkgVersion}`,
|
|
1147
|
+
].join(";");
|
|
1029
1148
|
return client;
|
|
1030
1149
|
}
|
|
1031
1150
|
/**
|
|
@@ -1035,8 +1154,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1035
1154
|
* If it's not true, runtime is not in position to send ops.
|
|
1036
1155
|
*/
|
|
1037
1156
|
activeConnection() {
|
|
1038
|
-
return this.connectionState === connectionState_1.ConnectionState.Connected &&
|
|
1039
|
-
this.connectionMode === "write";
|
|
1157
|
+
return (this.connectionState === connectionState_1.ConnectionState.Connected && this.connectionMode === "write");
|
|
1040
1158
|
}
|
|
1041
1159
|
createDeltaManager() {
|
|
1042
1160
|
const serviceProvider = () => this.service;
|
|
@@ -1053,7 +1171,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1053
1171
|
deltaManager.on("disconnect", (reason) => {
|
|
1054
1172
|
var _a;
|
|
1055
1173
|
(_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
|
|
1056
|
-
this.
|
|
1174
|
+
if (!this.closed) {
|
|
1175
|
+
this.connectionStateHandler.receivedDisconnectEvent(reason);
|
|
1176
|
+
}
|
|
1057
1177
|
});
|
|
1058
1178
|
deltaManager.on("throttled", (warning) => {
|
|
1059
1179
|
const warn = warning;
|
|
@@ -1071,6 +1191,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1071
1191
|
deltaManager.on("closed", (error) => {
|
|
1072
1192
|
this.closeCore(error);
|
|
1073
1193
|
});
|
|
1194
|
+
deltaManager.on("disposed", (error) => {
|
|
1195
|
+
this.disposeCore(error);
|
|
1196
|
+
});
|
|
1074
1197
|
return deltaManager;
|
|
1075
1198
|
}
|
|
1076
1199
|
async attachDeltaManagerOpHandler(attributes, prefetchType) {
|
|
@@ -1098,7 +1221,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1098
1221
|
}
|
|
1099
1222
|
else {
|
|
1100
1223
|
if (value === connectionState_1.ConnectionState.Connected) {
|
|
1101
|
-
durationFromDisconnected =
|
|
1224
|
+
durationFromDisconnected =
|
|
1225
|
+
time - this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected];
|
|
1102
1226
|
durationFromDisconnected = telemetry_utils_1.TelemetryLogger.formatTick(durationFromDisconnected);
|
|
1103
1227
|
}
|
|
1104
1228
|
else {
|
|
@@ -1141,19 +1265,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1141
1265
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1142
1266
|
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
1143
1267
|
if (logOpsOnReconnect) {
|
|
1144
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1268
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1269
|
+
eventName: "OpsSentOnReconnect",
|
|
1270
|
+
count: this.messageCountAfterDisconnection,
|
|
1271
|
+
});
|
|
1145
1272
|
}
|
|
1146
1273
|
}
|
|
1147
1274
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1148
1275
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
1276
|
+
var _a;
|
|
1149
1277
|
switch (type) {
|
|
1150
1278
|
case protocol_definitions_1.MessageType.Operation:
|
|
1151
1279
|
return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
|
|
1152
1280
|
case protocol_definitions_1.MessageType.Summarize:
|
|
1153
1281
|
return this.submitSummaryMessage(contents);
|
|
1154
|
-
default:
|
|
1155
|
-
|
|
1282
|
+
default: {
|
|
1283
|
+
const newError = new container_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
|
|
1284
|
+
this.close(newError);
|
|
1285
|
+
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
1156
1286
|
return -1;
|
|
1287
|
+
}
|
|
1157
1288
|
}
|
|
1158
1289
|
}
|
|
1159
1290
|
/** @returns clientSequenceNumber of last message in a batch */
|
|
@@ -1174,8 +1305,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1174
1305
|
if (summary.details === undefined) {
|
|
1175
1306
|
summary.details = {};
|
|
1176
1307
|
}
|
|
1177
|
-
summary.details.includesProtocolTree =
|
|
1178
|
-
this.options.summarizeProtocolTree === true;
|
|
1308
|
+
summary.details.includesProtocolTree = this.options.summarizeProtocolTree === true;
|
|
1179
1309
|
return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch */);
|
|
1180
1310
|
}
|
|
1181
1311
|
submitMessage(type, contents, batch, metadata, compression) {
|
|
@@ -1234,10 +1364,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1234
1364
|
const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : null);
|
|
1235
1365
|
if (version === undefined && specifiedVersion !== undefined) {
|
|
1236
1366
|
// We should have a defined version to load from if specified version requested
|
|
1237
|
-
this.mc.logger.sendErrorEvent({
|
|
1367
|
+
this.mc.logger.sendErrorEvent({
|
|
1368
|
+
eventName: "NoVersionFoundWhenSpecified",
|
|
1369
|
+
id: specifiedVersion,
|
|
1370
|
+
});
|
|
1238
1371
|
}
|
|
1239
1372
|
this._loadedFromVersion = version;
|
|
1240
|
-
const snapshot = (_a = await this.storageService.getSnapshotTree(version)) !== null && _a !== void 0 ? _a : undefined;
|
|
1373
|
+
const snapshot = (_a = (await this.storageService.getSnapshotTree(version))) !== null && _a !== void 0 ? _a : undefined;
|
|
1241
1374
|
if (snapshot === undefined && version !== undefined) {
|
|
1242
1375
|
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
1243
1376
|
}
|
|
@@ -1256,7 +1389,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1256
1389
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1257
1390
|
// are set. Global requests will still go directly to the loader
|
|
1258
1391
|
const loader = new loader_1.RelativeLoader(this, this.loader);
|
|
1259
|
-
this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp) => this.submitSummaryMessage(summaryOp), (batch) => this.submitBatch(batch), (message) => this.submitSignal(message), (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
|
|
1392
|
+
this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp) => this.submitSummaryMessage(summaryOp), (batch) => this.submitBatch(batch), (message) => this.submitSignal(message), (error) => { var _a; return (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error); }, (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
|
|
1260
1393
|
this.emit("contextChanged", codeDetails);
|
|
1261
1394
|
}
|
|
1262
1395
|
updateDirtyContainerState(dirty) {
|