@fluidframework/container-loader 2.0.0-dev.3.1.0.125672 → 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/README.md +7 -4
- package/closeAndGetPendingLocalState.md +51 -0
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +43 -11
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +4 -4
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +7 -0
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +44 -4
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +152 -93
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +18 -8
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +47 -4
- 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 +87 -11
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +2 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +3 -2
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +4 -2
- 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/index.d.ts +3 -2
- 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 +10 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +22 -16
- 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/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/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +44 -12
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +4 -4
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +7 -0
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +44 -4
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +155 -96
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +18 -8
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +48 -5
- 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 +85 -11
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +2 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +3 -2
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +4 -2
- 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/index.d.ts +3 -2
- 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 +10 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +21 -16
- 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/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/utils.d.ts.map +1 -1
- package/lib/utils.js +2 -1
- package/lib/utils.js.map +1 -1
- package/package.json +64 -56
- package/src/connectionManager.ts +48 -17
- package/src/connectionStateHandler.ts +17 -5
- package/src/container.ts +224 -116
- package/src/containerContext.ts +74 -11
- package/src/containerStorageAdapter.ts +113 -9
- package/src/contracts.ts +2 -2
- package/src/deltaManager.ts +9 -4
- package/src/deltaManagerProxy.ts +18 -73
- package/src/index.ts +2 -3
- package/src/loader.ts +28 -26
- package/src/packageVersion.ts +1 -1
- package/src/protocolTreeDocumentStorageService.ts +6 -3
- package/src/utils.ts +7 -4
package/dist/container.js
CHANGED
|
@@ -127,9 +127,15 @@ async function ReportIfTooLong(logger, eventName, action) {
|
|
|
127
127
|
}
|
|
128
128
|
exports.ReportIfTooLong = ReportIfTooLong;
|
|
129
129
|
const summarizerClientType = "summarizer";
|
|
130
|
+
/**
|
|
131
|
+
* @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
|
|
132
|
+
*/
|
|
130
133
|
class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
134
|
+
/**
|
|
135
|
+
* @internal
|
|
136
|
+
*/
|
|
131
137
|
constructor(loader, config, protocolHandlerBuilder) {
|
|
132
|
-
var _a, _b;
|
|
138
|
+
var _a, _b, _c;
|
|
133
139
|
super((name, error) => {
|
|
134
140
|
this.mc.logger.sendErrorEvent({
|
|
135
141
|
eventName: "ContainerEventHandlerException",
|
|
@@ -165,6 +171,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
165
171
|
this.messageCountAfterDisconnection = 0;
|
|
166
172
|
this.attachStarted = false;
|
|
167
173
|
this._dirtyContainer = false;
|
|
174
|
+
this.savedOps = [];
|
|
168
175
|
this.setAutoReconnectTime = common_utils_1.performance.now();
|
|
169
176
|
this._disposed = false;
|
|
170
177
|
this.clientDetailsOverride = config.clientDetailsOverride;
|
|
@@ -203,15 +210,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
203
210
|
dmLastMsqSeqNumber: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber; },
|
|
204
211
|
dmLastMsqSeqTimestamp: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.timestamp; },
|
|
205
212
|
dmLastMsqSeqClientId: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId; },
|
|
213
|
+
dmLastMsgClientSeq: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientSequenceNumber; },
|
|
206
214
|
connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
207
215
|
},
|
|
208
216
|
});
|
|
209
217
|
// Prefix all events in this file with container-loader
|
|
210
218
|
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.subLogger, "Container"));
|
|
211
|
-
|
|
212
|
-
this.options = Object.assign(Object.assign({}, this.loader.services.options), { summarizeProtocolTree });
|
|
219
|
+
this.options = Object.assign({}, this.loader.services.options);
|
|
213
220
|
this._deltaManager = this.createDeltaManager();
|
|
214
|
-
this._clientId = (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId;
|
|
215
221
|
this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
|
|
216
222
|
logger: this.mc.logger,
|
|
217
223
|
connectionStateChanged: (value, oldState, reason) => {
|
|
@@ -250,13 +256,20 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
250
256
|
this.connect();
|
|
251
257
|
}
|
|
252
258
|
},
|
|
253
|
-
}, this.deltaManager,
|
|
259
|
+
}, this.deltaManager, (_a = config.serializedContainerState) === null || _a === void 0 ? void 0 : _a.clientId);
|
|
254
260
|
this.on(savedContainerEvent, () => {
|
|
255
261
|
this.connectionStateHandler.containerSaved();
|
|
256
262
|
});
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
263
|
+
// We expose our storage publicly, so it's possible others may call uploadSummaryWithContext() with a
|
|
264
|
+
// non-combined summary tree (in particular, ContainerRuntime.submitSummary). We'll intercept those calls
|
|
265
|
+
// using this callback and fix them up.
|
|
266
|
+
const addProtocolSummaryIfMissing = (summaryTree) => (0, driver_utils_1.isCombinedAppAndProtocolSummary)(summaryTree) === true
|
|
267
|
+
? summaryTree
|
|
268
|
+
: (0, driver_utils_1.combineAppAndProtocolSummary)(summaryTree, this.captureProtocolSummary());
|
|
269
|
+
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
270
|
+
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
271
|
+
const forceEnableSummarizeProtocolTree = (_b = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _b !== void 0 ? _b : this.loader.services.options.summarizeProtocolTree;
|
|
272
|
+
this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, (_c = config.serializedContainerState) === null || _c === void 0 ? void 0 : _c.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
260
273
|
const isDomAvailable = typeof document === "object" &&
|
|
261
274
|
document !== null &&
|
|
262
275
|
typeof document.addEventListener === "function" &&
|
|
@@ -277,45 +290,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
277
290
|
};
|
|
278
291
|
document.addEventListener("visibilitychange", this.visibilityEventHandler);
|
|
279
292
|
}
|
|
280
|
-
// We observed that most users of platform do not check Container.connected event on load, causing bugs.
|
|
281
|
-
// As such, we are raising events when new listener pops up.
|
|
282
|
-
// Note that we can raise both "disconnected" & "connect" events at the same time,
|
|
283
|
-
// if we are in connecting stage.
|
|
284
|
-
this.on("newListener", (event, listener) => {
|
|
285
|
-
// Fire events on the end of JS turn, giving a chance for caller to be in consistent state.
|
|
286
|
-
Promise.resolve()
|
|
287
|
-
.then(() => {
|
|
288
|
-
switch (event) {
|
|
289
|
-
case dirtyContainerEvent:
|
|
290
|
-
if (this._dirtyContainer) {
|
|
291
|
-
listener();
|
|
292
|
-
}
|
|
293
|
-
break;
|
|
294
|
-
case savedContainerEvent:
|
|
295
|
-
if (!this._dirtyContainer) {
|
|
296
|
-
listener();
|
|
297
|
-
}
|
|
298
|
-
break;
|
|
299
|
-
case telemetry_utils_1.connectedEventName:
|
|
300
|
-
if (this.connected) {
|
|
301
|
-
listener(this.clientId);
|
|
302
|
-
}
|
|
303
|
-
break;
|
|
304
|
-
case telemetry_utils_1.disconnectedEventName:
|
|
305
|
-
if (!this.connected) {
|
|
306
|
-
listener();
|
|
307
|
-
}
|
|
308
|
-
break;
|
|
309
|
-
default:
|
|
310
|
-
}
|
|
311
|
-
})
|
|
312
|
-
.catch((error) => {
|
|
313
|
-
this.mc.logger.sendErrorEvent({ eventName: "RaiseConnectedEventError" }, error);
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
293
|
}
|
|
317
294
|
/**
|
|
318
295
|
* Load an existing container.
|
|
296
|
+
* @internal
|
|
319
297
|
*/
|
|
320
298
|
static async load(loader, loadOptions, pendingLocalState, protocolHandlerBuilder) {
|
|
321
299
|
const container = new Container(loader, {
|
|
@@ -393,7 +371,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
393
371
|
this._lifecycleState === "disposed");
|
|
394
372
|
}
|
|
395
373
|
get storage() {
|
|
396
|
-
return this.
|
|
374
|
+
return this.storageAdapter;
|
|
397
375
|
}
|
|
398
376
|
get context() {
|
|
399
377
|
if (this._context === undefined) {
|
|
@@ -464,6 +442,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
464
442
|
get clientDetails() {
|
|
465
443
|
return this._deltaManager.clientDetails;
|
|
466
444
|
}
|
|
445
|
+
get offlineLoadEnabled() {
|
|
446
|
+
var _a;
|
|
447
|
+
// summarizer will not have any pending state we want to save
|
|
448
|
+
return (((_a = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) !== null && _a !== void 0 ? _a : false) &&
|
|
449
|
+
this.clientDetails.capabilities.interactive);
|
|
450
|
+
}
|
|
467
451
|
/**
|
|
468
452
|
* Get the code details that are currently specified for the container.
|
|
469
453
|
* @returns The current code details if any are specified, undefined if none are specified.
|
|
@@ -506,6 +490,38 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
506
490
|
get codeLoader() {
|
|
507
491
|
return this.loader.services.codeLoader;
|
|
508
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
495
|
+
*/
|
|
496
|
+
async getEntryPoint() {
|
|
497
|
+
var _a, _b;
|
|
498
|
+
// Only the disposing/disposed lifecycle states should prevent access to the entryPoint; closing/closed should still
|
|
499
|
+
// allow it since they mean a kind of read-only state for the Container.
|
|
500
|
+
// Note that all 4 are lifecycle states but only 'closed' and 'disposed' are emitted as events.
|
|
501
|
+
if (this._lifecycleState === "disposing" || this._lifecycleState === "disposed") {
|
|
502
|
+
throw new container_utils_1.UsageError("The container is disposing or disposed");
|
|
503
|
+
}
|
|
504
|
+
while (this._context === undefined) {
|
|
505
|
+
await new Promise((resolve, reject) => {
|
|
506
|
+
const contextChangedHandler = () => {
|
|
507
|
+
resolve();
|
|
508
|
+
this.off("disposed", disposedHandler);
|
|
509
|
+
};
|
|
510
|
+
const disposedHandler = (error) => {
|
|
511
|
+
reject(error !== null && error !== void 0 ? error : "The Container is disposed");
|
|
512
|
+
this.off("contextChanged", contextChangedHandler);
|
|
513
|
+
};
|
|
514
|
+
this.once("contextChanged", contextChangedHandler);
|
|
515
|
+
this.once("disposed", disposedHandler);
|
|
516
|
+
});
|
|
517
|
+
// The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
|
|
518
|
+
// should have set this._context; making sure.
|
|
519
|
+
(0, common_utils_1.assert)(this._context !== undefined, 0x5a2 /* Context still not defined after contextChanged event */);
|
|
520
|
+
}
|
|
521
|
+
// Disable lint rule for the sake of more complete stack traces
|
|
522
|
+
// eslint-disable-next-line no-return-await
|
|
523
|
+
return await ((_b = (_a = this._context).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
524
|
+
}
|
|
509
525
|
/**
|
|
510
526
|
* Retrieves the quorum associated with the document
|
|
511
527
|
*/
|
|
@@ -536,15 +552,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
536
552
|
try {
|
|
537
553
|
// Raise event first, to ensure we capture _lifecycleState before transition.
|
|
538
554
|
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
555
|
+
// Log generic events instead of error events if container is in loading state, as most errors are not really FF errors
|
|
556
|
+
// which can pollute telemetry for real bugs
|
|
539
557
|
this.mc.logger.sendTelemetryEvent({
|
|
540
558
|
eventName: "ContainerClose",
|
|
541
|
-
category:
|
|
559
|
+
category: this._lifecycleState !== "loading" && error !== undefined
|
|
560
|
+
? "error"
|
|
561
|
+
: "generic",
|
|
542
562
|
}, error);
|
|
543
563
|
this._lifecycleState = "closing";
|
|
544
564
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
545
565
|
this.connectionStateHandler.dispose();
|
|
546
566
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
547
|
-
this.
|
|
567
|
+
this.storageAdapter.dispose();
|
|
548
568
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
549
569
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
550
570
|
// Driver need to ensure all caches are cleared on critical errors
|
|
@@ -573,7 +593,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
573
593
|
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
574
594
|
this.mc.logger.sendTelemetryEvent({
|
|
575
595
|
eventName: "ContainerDispose",
|
|
576
|
-
category:
|
|
596
|
+
category: "generic",
|
|
577
597
|
}, error);
|
|
578
598
|
// ! Progressing from "closed" to "disposing" is not allowed
|
|
579
599
|
if (this._lifecycleState !== "closed") {
|
|
@@ -582,7 +602,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
582
602
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
583
603
|
this.connectionStateHandler.dispose();
|
|
584
604
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
585
|
-
this.
|
|
605
|
+
this.storageAdapter.dispose();
|
|
586
606
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
587
607
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
588
608
|
// Driver need to ensure all caches are cleared on critical errors
|
|
@@ -605,14 +625,21 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
605
625
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
606
626
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
607
627
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
628
|
+
if (!this.offlineLoadEnabled) {
|
|
629
|
+
throw new container_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
|
|
630
|
+
}
|
|
608
631
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
609
632
|
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
610
633
|
(0, common_utils_1.assert)(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
|
|
611
634
|
(0, common_utils_1.assert)(this._protocolHandler.attributes.term !== undefined, 0x37e /* Must have a valid protocol handler instance */);
|
|
635
|
+
(0, common_utils_1.assert)(!!this.baseSnapshot, "no base snapshot");
|
|
636
|
+
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, "no snapshot blobs");
|
|
612
637
|
const pendingState = {
|
|
613
638
|
pendingRuntimeState: this.context.getPendingLocalState(),
|
|
639
|
+
baseSnapshot: this.baseSnapshot,
|
|
640
|
+
snapshotBlobs: this.baseSnapshotBlobs,
|
|
641
|
+
savedOps: this.savedOps,
|
|
614
642
|
url: this.resolvedUrl.url,
|
|
615
|
-
protocol: this.protocolHandler.getProtocolState(),
|
|
616
643
|
term: this._protocolHandler.attributes.term,
|
|
617
644
|
clientId: this.clientId,
|
|
618
645
|
};
|
|
@@ -665,7 +692,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
665
692
|
// starting to attach the container to storage.
|
|
666
693
|
// Also, this should only be fired in detached container.
|
|
667
694
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
668
|
-
this.
|
|
695
|
+
this.emit("attaching");
|
|
696
|
+
if (this.offlineLoadEnabled) {
|
|
697
|
+
const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
|
|
698
|
+
this.baseSnapshot = snapshot;
|
|
699
|
+
this.baseSnapshotBlobs =
|
|
700
|
+
(0, containerStorageAdapter_1.getBlobContentsFromTreeWithBlobContents)(snapshot);
|
|
701
|
+
}
|
|
669
702
|
}
|
|
670
703
|
// Actually go and create the resolved document
|
|
671
704
|
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
@@ -679,7 +712,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
679
712
|
const resolvedUrl = this.service.resolvedUrl;
|
|
680
713
|
(0, driver_utils_1.ensureFluidResolvedUrl)(resolvedUrl);
|
|
681
714
|
this._resolvedUrl = resolvedUrl;
|
|
682
|
-
await this.
|
|
715
|
+
await this.storageAdapter.connectToService(this.service);
|
|
683
716
|
if (hasAttachmentBlobs) {
|
|
684
717
|
// upload blobs to storage
|
|
685
718
|
(0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
@@ -693,7 +726,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
693
726
|
.filter((id) => !redirectTable.has(id));
|
|
694
727
|
for (const id of newIds) {
|
|
695
728
|
const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
|
|
696
|
-
const response = await this.
|
|
729
|
+
const response = await this.storageAdapter.createBlob(blob);
|
|
697
730
|
redirectTable.set(id, response.id);
|
|
698
731
|
}
|
|
699
732
|
}
|
|
@@ -702,8 +735,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
702
735
|
const protocolSummary = this.captureProtocolSummary();
|
|
703
736
|
summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
704
737
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
705
|
-
this.
|
|
706
|
-
|
|
738
|
+
this.emit("attaching");
|
|
739
|
+
if (this.offlineLoadEnabled) {
|
|
740
|
+
const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
|
|
741
|
+
this.baseSnapshot = snapshot;
|
|
742
|
+
this.baseSnapshotBlobs =
|
|
743
|
+
(0, containerStorageAdapter_1.getBlobContentsFromTreeWithBlobContents)(snapshot);
|
|
744
|
+
}
|
|
745
|
+
await this.storageAdapter.uploadSummaryWithContext(summary, {
|
|
707
746
|
referenceSequenceNumber: 0,
|
|
708
747
|
ackHandle: undefined,
|
|
709
748
|
proposalHandle: undefined,
|
|
@@ -838,7 +877,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
838
877
|
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
839
878
|
}
|
|
840
879
|
async getVersion(version) {
|
|
841
|
-
const versions = await this.
|
|
880
|
+
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
842
881
|
return versions[0];
|
|
843
882
|
}
|
|
844
883
|
recordConnectStartTime() {
|
|
@@ -860,6 +899,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
860
899
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
861
900
|
*/
|
|
862
901
|
async load(specifiedVersion, loadMode, pendingLocalState) {
|
|
902
|
+
var _a;
|
|
863
903
|
if (this._resolvedUrl === undefined) {
|
|
864
904
|
throw new Error("Attempting to load without a resolved url");
|
|
865
905
|
}
|
|
@@ -880,15 +920,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
880
920
|
};
|
|
881
921
|
// Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
|
|
882
922
|
// DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
|
|
883
|
-
if (loadMode.deltaConnection === undefined) {
|
|
923
|
+
if (loadMode.deltaConnection === undefined && !pendingLocalState) {
|
|
884
924
|
this.connectToDeltaStream(connectionArgs);
|
|
885
925
|
}
|
|
886
926
|
if (!pendingLocalState) {
|
|
887
|
-
await this.
|
|
927
|
+
await this.storageAdapter.connectToService(this.service);
|
|
888
928
|
}
|
|
889
929
|
else {
|
|
890
930
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
891
|
-
this.
|
|
931
|
+
this.storageAdapter.connectToService(this.service).catch((error) => {
|
|
892
932
|
var _a;
|
|
893
933
|
this.close(error);
|
|
894
934
|
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
@@ -898,15 +938,23 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
898
938
|
// Fetch specified snapshot.
|
|
899
939
|
const { snapshot, versionId } = pendingLocalState === undefined
|
|
900
940
|
? await this.fetchSnapshotTree(specifiedVersion)
|
|
901
|
-
: { snapshot:
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
941
|
+
: { snapshot: pendingLocalState.baseSnapshot, versionId: undefined };
|
|
942
|
+
if (pendingLocalState) {
|
|
943
|
+
this.baseSnapshot = pendingLocalState.baseSnapshot;
|
|
944
|
+
this.baseSnapshotBlobs = pendingLocalState.snapshotBlobs;
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
(0, common_utils_1.assert)(snapshot !== undefined, 0x237 /* "Snapshot should exist" */);
|
|
948
|
+
if (this.offlineLoadEnabled) {
|
|
949
|
+
this.baseSnapshot = snapshot;
|
|
950
|
+
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
951
|
+
this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storage);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
955
|
+
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
956
|
+
const sequenceNumber = (_a = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.savedOps[pendingLocalState.savedOps.length - 1]) === null || _a === void 0 ? void 0 : _a.sequenceNumber;
|
|
957
|
+
const dmAttributes = sequenceNumber !== undefined ? Object.assign(Object.assign({}, attributes), { sequenceNumber }) : attributes;
|
|
910
958
|
let opsBeforeReturnP;
|
|
911
959
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
912
960
|
// Kick off any ops fetching if required.
|
|
@@ -914,32 +962,35 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
914
962
|
case undefined:
|
|
915
963
|
// Start prefetch, but not set opsBeforeReturnP - boot is not blocked by it!
|
|
916
964
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
917
|
-
this.attachDeltaManagerOpHandler(
|
|
965
|
+
this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
|
|
918
966
|
break;
|
|
919
967
|
case "cached":
|
|
920
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(
|
|
968
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
921
969
|
break;
|
|
922
970
|
case "all":
|
|
923
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(
|
|
971
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "all");
|
|
924
972
|
break;
|
|
925
973
|
default:
|
|
926
974
|
(0, common_utils_1.unreachableCase)(loadMode.opsBeforeReturn);
|
|
927
975
|
}
|
|
928
976
|
// ...load in the existing quorum
|
|
929
977
|
// Initialize the protocol handler
|
|
930
|
-
|
|
931
|
-
await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot);
|
|
932
|
-
}
|
|
933
|
-
else {
|
|
934
|
-
this.initializeProtocolState(attributes, {
|
|
935
|
-
members: pendingLocalState.protocol.members,
|
|
936
|
-
proposals: pendingLocalState.protocol.proposals,
|
|
937
|
-
values: pendingLocalState.protocol.values,
|
|
938
|
-
});
|
|
939
|
-
}
|
|
978
|
+
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
940
979
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
941
980
|
await this.instantiateContext(true, // existing
|
|
942
981
|
codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
|
|
982
|
+
// replay saved ops
|
|
983
|
+
if (pendingLocalState) {
|
|
984
|
+
for (const message of pendingLocalState.savedOps) {
|
|
985
|
+
this.processRemoteMessage(message);
|
|
986
|
+
// allow runtime to apply stashed ops at this op's sequence number
|
|
987
|
+
await this.context.notifyOpReplay(message);
|
|
988
|
+
}
|
|
989
|
+
pendingLocalState.savedOps = [];
|
|
990
|
+
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
991
|
+
(0, common_utils_1.assert)(this.clientId === undefined, "Unexpected clientId when setting stashed clientId");
|
|
992
|
+
this._clientId = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId;
|
|
993
|
+
}
|
|
943
994
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
944
995
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
945
996
|
if (!this.closed) {
|
|
@@ -952,6 +1003,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
952
1003
|
}
|
|
953
1004
|
switch (loadMode.deltaConnection) {
|
|
954
1005
|
case undefined:
|
|
1006
|
+
if (pendingLocalState) {
|
|
1007
|
+
// connect to delta stream now since we did not before
|
|
1008
|
+
this.connectToDeltaStream(connectionArgs);
|
|
1009
|
+
}
|
|
1010
|
+
// intentional fallthrough
|
|
955
1011
|
case "delayed":
|
|
956
1012
|
(0, common_utils_1.assert)(this.inboundQueuePausedFromInit, 0x346 /* inboundQueuePausedFromInit should be true */);
|
|
957
1013
|
this.inboundQueuePausedFromInit = false;
|
|
@@ -1006,12 +1062,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1006
1062
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
1007
1063
|
}
|
|
1008
1064
|
const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
|
|
1009
|
-
this.
|
|
1010
|
-
const attributes = await this.getDocumentAttributes(this.
|
|
1065
|
+
this.storageAdapter.loadSnapshotForRehydratingContainer(snapshotTree);
|
|
1066
|
+
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshotTree);
|
|
1011
1067
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1012
1068
|
// Initialize the protocol handler
|
|
1013
1069
|
const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshotTree);
|
|
1014
|
-
const qValues = await (0, driver_utils_1.readAndParse)(this.
|
|
1070
|
+
const qValues = await (0, driver_utils_1.readAndParse)(this.storageAdapter, baseTree.blobs.quorumValues);
|
|
1015
1071
|
const codeDetails = (0, quorum_1.getCodeDetailsFromQuorumValues)(qValues);
|
|
1016
1072
|
this.initializeProtocolState(attributes, {
|
|
1017
1073
|
members: [],
|
|
@@ -1288,16 +1344,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1288
1344
|
}
|
|
1289
1345
|
}
|
|
1290
1346
|
/** @returns clientSequenceNumber of last message in a batch */
|
|
1291
|
-
submitBatch(batch) {
|
|
1347
|
+
submitBatch(batch, referenceSequenceNumber) {
|
|
1292
1348
|
let clientSequenceNumber = -1;
|
|
1293
1349
|
for (const message of batch) {
|
|
1294
1350
|
clientSequenceNumber = this.submitMessage(protocol_definitions_1.MessageType.Operation, message.contents, true, // batch
|
|
1295
|
-
message.metadata, message.compression);
|
|
1351
|
+
message.metadata, message.compression, referenceSequenceNumber);
|
|
1296
1352
|
}
|
|
1297
1353
|
this._deltaManager.flush();
|
|
1298
1354
|
return clientSequenceNumber;
|
|
1299
1355
|
}
|
|
1300
|
-
submitSummaryMessage(summary) {
|
|
1356
|
+
submitSummaryMessage(summary, referenceSequenceNumber) {
|
|
1301
1357
|
// github #6451: this is only needed for staging so the server
|
|
1302
1358
|
// know when the protocol tree is included
|
|
1303
1359
|
// this can be removed once all clients send
|
|
@@ -1305,10 +1361,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1305
1361
|
if (summary.details === undefined) {
|
|
1306
1362
|
summary.details = {};
|
|
1307
1363
|
}
|
|
1308
|
-
summary.details.includesProtocolTree = this.
|
|
1309
|
-
return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch
|
|
1364
|
+
summary.details.includesProtocolTree = this.storageAdapter.summarizeProtocolTree;
|
|
1365
|
+
return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch */, undefined /* metadata */, undefined /* compression */, referenceSequenceNumber);
|
|
1310
1366
|
}
|
|
1311
|
-
submitMessage(type, contents, batch, metadata, compression) {
|
|
1367
|
+
submitMessage(type, contents, batch, metadata, compression, referenceSequenceNumber) {
|
|
1312
1368
|
var _a;
|
|
1313
1369
|
if (this.connectionState !== connectionState_1.ConnectionState.Connected) {
|
|
1314
1370
|
this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
|
|
@@ -1316,9 +1372,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1316
1372
|
}
|
|
1317
1373
|
this.messageCountAfterDisconnection += 1;
|
|
1318
1374
|
(_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
|
|
1319
|
-
return this._deltaManager.submit(type, contents, batch, metadata, compression);
|
|
1375
|
+
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1320
1376
|
}
|
|
1321
1377
|
processRemoteMessage(message) {
|
|
1378
|
+
if (this.offlineLoadEnabled) {
|
|
1379
|
+
this.savedOps.push(message);
|
|
1380
|
+
}
|
|
1322
1381
|
const local = this.clientId === message.clientId;
|
|
1323
1382
|
// Allow the protocol handler to process the message
|
|
1324
1383
|
const result = this.protocolHandler.processMessage(message, local);
|
|
@@ -1370,7 +1429,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1370
1429
|
});
|
|
1371
1430
|
}
|
|
1372
1431
|
this._loadedFromVersion = version;
|
|
1373
|
-
const snapshot = (_a = (await this.
|
|
1432
|
+
const snapshot = (_a = (await this.storageAdapter.getSnapshotTree(version))) !== null && _a !== void 0 ? _a : undefined;
|
|
1374
1433
|
if (snapshot === undefined && version !== undefined) {
|
|
1375
1434
|
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
1376
1435
|
}
|
|
@@ -1389,7 +1448,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1389
1448
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1390
1449
|
// are set. Global requests will still go directly to the loader
|
|
1391
1450
|
const loader = new loader_1.RelativeLoader(this, this.loader);
|
|
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);
|
|
1451
|
+
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, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (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);
|
|
1393
1452
|
this.emit("contextChanged", codeDetails);
|
|
1394
1453
|
}
|
|
1395
1454
|
updateDirtyContainerState(dirty) {
|