@fluidframework/container-loader 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +45 -4
- package/closeAndGetPendingLocalState.md +51 -0
- package/dist/connectionManager.d.ts +2 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +59 -17
- 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 +160 -105
- 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 +4 -5
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +7 -10
- 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 +2 -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 +25 -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/protocol.d.ts +1 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +4 -2
- 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/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts +2 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +60 -18
- 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 +164 -109
- 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 +4 -5
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +7 -10
- 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 +24 -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/protocol.d.ts +1 -0
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +3 -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/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 +65 -24
- package/src/connectionStateHandler.ts +17 -5
- package/src/container.ts +239 -137
- package/src/containerContext.ts +74 -11
- package/src/containerStorageAdapter.ts +113 -9
- package/src/contracts.ts +2 -2
- package/src/deltaManager.ts +12 -14
- package/src/deltaManagerProxy.ts +18 -73
- package/src/index.ts +3 -3
- package/src/loader.ts +31 -26
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +4 -1
- package/src/protocolTreeDocumentStorageService.ts +6 -3
- package/src/utils.ts +7 -4
package/src/container.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
// eslint-disable-next-line import/no-internal-modules
|
|
7
7
|
import merge from "lodash/merge";
|
|
8
|
+
// eslint-disable-next-line import/no-internal-modules
|
|
9
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
10
|
+
|
|
8
11
|
import { v4 as uuid } from "uuid";
|
|
9
12
|
import {
|
|
10
13
|
ITelemetryLogger,
|
|
@@ -12,10 +15,10 @@ import {
|
|
|
12
15
|
TelemetryEventCategory,
|
|
13
16
|
} from "@fluidframework/common-definitions";
|
|
14
17
|
import { assert, performance, unreachableCase } from "@fluidframework/common-utils";
|
|
15
|
-
import { IRequest, IResponse, IFluidRouter } from "@fluidframework/core-interfaces";
|
|
18
|
+
import { IRequest, IResponse, IFluidRouter, FluidObject } from "@fluidframework/core-interfaces";
|
|
16
19
|
import {
|
|
17
20
|
IAudience,
|
|
18
|
-
|
|
21
|
+
IConnectionDetailsInternal,
|
|
19
22
|
IContainer,
|
|
20
23
|
IContainerEvents,
|
|
21
24
|
IDeltaManager,
|
|
@@ -44,6 +47,7 @@ import {
|
|
|
44
47
|
combineAppAndProtocolSummary,
|
|
45
48
|
runWithRetry,
|
|
46
49
|
isFluidResolvedUrl,
|
|
50
|
+
isCombinedAppAndProtocolSummary,
|
|
47
51
|
} from "@fluidframework/driver-utils";
|
|
48
52
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
49
53
|
import {
|
|
@@ -53,7 +57,6 @@ import {
|
|
|
53
57
|
ICommittedProposal,
|
|
54
58
|
IDocumentAttributes,
|
|
55
59
|
IDocumentMessage,
|
|
56
|
-
IProtocolState,
|
|
57
60
|
IQuorumClients,
|
|
58
61
|
IQuorumProposals,
|
|
59
62
|
ISequencedClient,
|
|
@@ -74,7 +77,6 @@ import {
|
|
|
74
77
|
raiseConnectedEvent,
|
|
75
78
|
TelemetryLogger,
|
|
76
79
|
connectedEventName,
|
|
77
|
-
disconnectedEventName,
|
|
78
80
|
normalizeError,
|
|
79
81
|
MonitoringContext,
|
|
80
82
|
loggerToMonitoringContext,
|
|
@@ -87,7 +89,12 @@ import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
|
87
89
|
import { DeltaManagerProxy } from "./deltaManagerProxy";
|
|
88
90
|
import { ILoaderOptions, Loader, RelativeLoader } from "./loader";
|
|
89
91
|
import { pkgVersion } from "./packageVersion";
|
|
90
|
-
import {
|
|
92
|
+
import {
|
|
93
|
+
ContainerStorageAdapter,
|
|
94
|
+
getBlobContentsFromTree,
|
|
95
|
+
getBlobContentsFromTreeWithBlobContents,
|
|
96
|
+
ISerializableBlobContents,
|
|
97
|
+
} from "./containerStorageAdapter";
|
|
91
98
|
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
|
|
92
99
|
import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
|
|
93
100
|
import {
|
|
@@ -98,13 +105,22 @@ import {
|
|
|
98
105
|
import { CollabWindowTracker } from "./collabWindowTracker";
|
|
99
106
|
import { ConnectionManager } from "./connectionManager";
|
|
100
107
|
import { ConnectionState } from "./connectionState";
|
|
101
|
-
import {
|
|
108
|
+
import {
|
|
109
|
+
OnlyValidTermValue,
|
|
110
|
+
IProtocolHandler,
|
|
111
|
+
ProtocolHandler,
|
|
112
|
+
ProtocolHandlerBuilder,
|
|
113
|
+
} from "./protocol";
|
|
102
114
|
|
|
103
115
|
const detachedContainerRefSeqNumber = 0;
|
|
104
116
|
|
|
105
117
|
const dirtyContainerEvent = "dirty";
|
|
106
118
|
const savedContainerEvent = "saved";
|
|
107
119
|
|
|
120
|
+
/**
|
|
121
|
+
* @deprecated this is an internal interface and will not longer be exported in future versions
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
108
124
|
export interface IContainerLoadOptions {
|
|
109
125
|
/**
|
|
110
126
|
* Disables the Container from reconnecting if false, allows reconnect otherwise.
|
|
@@ -125,6 +141,10 @@ export interface IContainerLoadOptions {
|
|
|
125
141
|
loadMode?: IContainerLoadMode;
|
|
126
142
|
}
|
|
127
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @deprecated this is an internal interface and will not longer be exported in future versions
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
128
148
|
export interface IContainerConfig {
|
|
129
149
|
resolvedUrl?: IFluidResolvedUrl;
|
|
130
150
|
canReconnect?: boolean;
|
|
@@ -255,17 +275,36 @@ export async function ReportIfTooLong(
|
|
|
255
275
|
/**
|
|
256
276
|
* State saved by a container at close time, to be used to load a new instance
|
|
257
277
|
* of the container to the same state
|
|
278
|
+
* @deprecated this is an internal interface and will not longer be exported in future versions
|
|
279
|
+
* @internal
|
|
258
280
|
*/
|
|
259
281
|
export interface IPendingContainerState {
|
|
260
282
|
pendingRuntimeState: unknown;
|
|
283
|
+
/**
|
|
284
|
+
* Snapshot from which container initially loaded.
|
|
285
|
+
*/
|
|
286
|
+
baseSnapshot: ISnapshotTree;
|
|
287
|
+
/**
|
|
288
|
+
* Serializable blobs from the base snapshot. Used to load offline since
|
|
289
|
+
* storage is not available.
|
|
290
|
+
*/
|
|
291
|
+
snapshotBlobs: ISerializableBlobContents;
|
|
292
|
+
/**
|
|
293
|
+
* All ops since base snapshot sequence number up to the latest op
|
|
294
|
+
* seen when the container was closed. Used to apply stashed (saved pending)
|
|
295
|
+
* ops at the same sequence number at which they were made.
|
|
296
|
+
*/
|
|
297
|
+
savedOps: ISequencedDocumentMessage[];
|
|
261
298
|
url: string;
|
|
262
|
-
protocol: IProtocolState;
|
|
263
299
|
term: number;
|
|
264
300
|
clientId?: string;
|
|
265
301
|
}
|
|
266
302
|
|
|
267
303
|
const summarizerClientType = "summarizer";
|
|
268
304
|
|
|
305
|
+
/**
|
|
306
|
+
* @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
|
|
307
|
+
*/
|
|
269
308
|
export class Container
|
|
270
309
|
extends EventEmitterWithErrorHandling<IContainerEvents>
|
|
271
310
|
implements IContainer
|
|
@@ -274,6 +313,7 @@ export class Container
|
|
|
274
313
|
|
|
275
314
|
/**
|
|
276
315
|
* Load an existing container.
|
|
316
|
+
* @internal
|
|
277
317
|
*/
|
|
278
318
|
public static async load(
|
|
279
319
|
loader: Loader,
|
|
@@ -434,9 +474,9 @@ export class Container
|
|
|
434
474
|
|
|
435
475
|
private _attachState = AttachState.Detached;
|
|
436
476
|
|
|
437
|
-
private readonly
|
|
477
|
+
private readonly storageAdapter: ContainerStorageAdapter;
|
|
438
478
|
public get storage(): IDocumentStorageService {
|
|
439
|
-
return this.
|
|
479
|
+
return this.storageAdapter;
|
|
440
480
|
}
|
|
441
481
|
|
|
442
482
|
private readonly clientDetailsOverride: IClientDetails | undefined;
|
|
@@ -467,6 +507,9 @@ export class Container
|
|
|
467
507
|
private _resolvedUrl: IFluidResolvedUrl | undefined;
|
|
468
508
|
private attachStarted = false;
|
|
469
509
|
private _dirtyContainer = false;
|
|
510
|
+
private readonly savedOps: ISequencedDocumentMessage[] = [];
|
|
511
|
+
private baseSnapshot?: ISnapshotTree;
|
|
512
|
+
private baseSnapshotBlobs?: ISerializableBlobContents;
|
|
470
513
|
|
|
471
514
|
private lastVisible: number | undefined;
|
|
472
515
|
private readonly visibilityEventHandler: (() => void) | undefined;
|
|
@@ -549,6 +592,14 @@ export class Container
|
|
|
549
592
|
return this._deltaManager.clientDetails;
|
|
550
593
|
}
|
|
551
594
|
|
|
595
|
+
private get offlineLoadEnabled(): boolean {
|
|
596
|
+
// summarizer will not have any pending state we want to save
|
|
597
|
+
return (
|
|
598
|
+
(this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false) &&
|
|
599
|
+
this.clientDetails.capabilities.interactive
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
552
603
|
/**
|
|
553
604
|
* Get the code details that are currently specified for the container.
|
|
554
605
|
* @returns The current code details if any are specified, undefined if none are specified.
|
|
@@ -596,6 +647,44 @@ export class Container
|
|
|
596
647
|
return this.loader.services.codeLoader;
|
|
597
648
|
}
|
|
598
649
|
|
|
650
|
+
/**
|
|
651
|
+
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
652
|
+
*/
|
|
653
|
+
public async getEntryPoint?(): Promise<FluidObject | undefined> {
|
|
654
|
+
// Only the disposing/disposed lifecycle states should prevent access to the entryPoint; closing/closed should still
|
|
655
|
+
// allow it since they mean a kind of read-only state for the Container.
|
|
656
|
+
// Note that all 4 are lifecycle states but only 'closed' and 'disposed' are emitted as events.
|
|
657
|
+
if (this._lifecycleState === "disposing" || this._lifecycleState === "disposed") {
|
|
658
|
+
throw new UsageError("The container is disposing or disposed");
|
|
659
|
+
}
|
|
660
|
+
while (this._context === undefined) {
|
|
661
|
+
await new Promise<void>((resolve, reject) => {
|
|
662
|
+
const contextChangedHandler = () => {
|
|
663
|
+
resolve();
|
|
664
|
+
this.off("disposed", disposedHandler);
|
|
665
|
+
};
|
|
666
|
+
const disposedHandler = (error) => {
|
|
667
|
+
reject(error ?? "The Container is disposed");
|
|
668
|
+
this.off("contextChanged", contextChangedHandler);
|
|
669
|
+
};
|
|
670
|
+
this.once("contextChanged", contextChangedHandler);
|
|
671
|
+
this.once("disposed", disposedHandler);
|
|
672
|
+
});
|
|
673
|
+
// The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
|
|
674
|
+
// should have set this._context; making sure.
|
|
675
|
+
assert(
|
|
676
|
+
this._context !== undefined,
|
|
677
|
+
0x5a2 /* Context still not defined after contextChanged event */,
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
// Disable lint rule for the sake of more complete stack traces
|
|
681
|
+
// eslint-disable-next-line no-return-await
|
|
682
|
+
return await this._context.getEntryPoint?.();
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* @internal
|
|
687
|
+
*/
|
|
599
688
|
constructor(
|
|
600
689
|
private readonly loader: Loader,
|
|
601
690
|
config: IContainerConfig,
|
|
@@ -650,6 +739,7 @@ export class Container
|
|
|
650
739
|
dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
|
|
651
740
|
dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
|
|
652
741
|
dmLastMsqSeqClientId: () => this.deltaManager?.lastMessage?.clientId,
|
|
742
|
+
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
653
743
|
connectionStateDuration: () =>
|
|
654
744
|
performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
655
745
|
},
|
|
@@ -658,18 +748,10 @@ export class Container
|
|
|
658
748
|
// Prefix all events in this file with container-loader
|
|
659
749
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
|
|
660
750
|
|
|
661
|
-
|
|
662
|
-
this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
663
|
-
this.loader.services.options.summarizeProtocolTree;
|
|
664
|
-
|
|
665
|
-
this.options = {
|
|
666
|
-
...this.loader.services.options,
|
|
667
|
-
summarizeProtocolTree,
|
|
668
|
-
};
|
|
751
|
+
this.options = cloneDeep(this.loader.services.options);
|
|
669
752
|
|
|
670
753
|
this._deltaManager = this.createDeltaManager();
|
|
671
754
|
|
|
672
|
-
this._clientId = config.serializedContainerState?.clientId;
|
|
673
755
|
this.connectionStateHandler = createConnectionStateHandler(
|
|
674
756
|
{
|
|
675
757
|
logger: this.mc.logger,
|
|
@@ -725,19 +807,33 @@ export class Container
|
|
|
725
807
|
},
|
|
726
808
|
},
|
|
727
809
|
this.deltaManager,
|
|
728
|
-
|
|
810
|
+
config.serializedContainerState?.clientId,
|
|
729
811
|
);
|
|
730
812
|
|
|
731
813
|
this.on(savedContainerEvent, () => {
|
|
732
814
|
this.connectionStateHandler.containerSaved();
|
|
733
815
|
});
|
|
734
816
|
|
|
735
|
-
|
|
817
|
+
// We expose our storage publicly, so it's possible others may call uploadSummaryWithContext() with a
|
|
818
|
+
// non-combined summary tree (in particular, ContainerRuntime.submitSummary). We'll intercept those calls
|
|
819
|
+
// using this callback and fix them up.
|
|
820
|
+
const addProtocolSummaryIfMissing = (summaryTree: ISummaryTree) =>
|
|
821
|
+
isCombinedAppAndProtocolSummary(summaryTree) === true
|
|
822
|
+
? summaryTree
|
|
823
|
+
: combineAppAndProtocolSummary(summaryTree, this.captureProtocolSummary());
|
|
824
|
+
|
|
825
|
+
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
826
|
+
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
827
|
+
const forceEnableSummarizeProtocolTree =
|
|
828
|
+
this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
829
|
+
this.loader.services.options.summarizeProtocolTree;
|
|
830
|
+
|
|
831
|
+
this.storageAdapter = new ContainerStorageAdapter(
|
|
736
832
|
this.loader.services.detachedBlobStorage,
|
|
737
833
|
this.mc.logger,
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
834
|
+
config.serializedContainerState?.snapshotBlobs,
|
|
835
|
+
addProtocolSummaryIfMissing,
|
|
836
|
+
forceEnableSummarizeProtocolTree,
|
|
741
837
|
);
|
|
742
838
|
|
|
743
839
|
const isDomAvailable =
|
|
@@ -760,43 +856,6 @@ export class Container
|
|
|
760
856
|
};
|
|
761
857
|
document.addEventListener("visibilitychange", this.visibilityEventHandler);
|
|
762
858
|
}
|
|
763
|
-
|
|
764
|
-
// We observed that most users of platform do not check Container.connected event on load, causing bugs.
|
|
765
|
-
// As such, we are raising events when new listener pops up.
|
|
766
|
-
// Note that we can raise both "disconnected" & "connect" events at the same time,
|
|
767
|
-
// if we are in connecting stage.
|
|
768
|
-
this.on("newListener", (event: string, listener: (...args: any[]) => void) => {
|
|
769
|
-
// Fire events on the end of JS turn, giving a chance for caller to be in consistent state.
|
|
770
|
-
Promise.resolve()
|
|
771
|
-
.then(() => {
|
|
772
|
-
switch (event) {
|
|
773
|
-
case dirtyContainerEvent:
|
|
774
|
-
if (this._dirtyContainer) {
|
|
775
|
-
listener();
|
|
776
|
-
}
|
|
777
|
-
break;
|
|
778
|
-
case savedContainerEvent:
|
|
779
|
-
if (!this._dirtyContainer) {
|
|
780
|
-
listener();
|
|
781
|
-
}
|
|
782
|
-
break;
|
|
783
|
-
case connectedEventName:
|
|
784
|
-
if (this.connected) {
|
|
785
|
-
listener(this.clientId);
|
|
786
|
-
}
|
|
787
|
-
break;
|
|
788
|
-
case disconnectedEventName:
|
|
789
|
-
if (!this.connected) {
|
|
790
|
-
listener();
|
|
791
|
-
}
|
|
792
|
-
break;
|
|
793
|
-
default:
|
|
794
|
-
}
|
|
795
|
-
})
|
|
796
|
-
.catch((error) => {
|
|
797
|
-
this.mc.logger.sendErrorEvent({ eventName: "RaiseConnectedEventError" }, error);
|
|
798
|
-
});
|
|
799
|
-
});
|
|
800
859
|
}
|
|
801
860
|
|
|
802
861
|
/**
|
|
@@ -840,10 +899,15 @@ export class Container
|
|
|
840
899
|
try {
|
|
841
900
|
// Raise event first, to ensure we capture _lifecycleState before transition.
|
|
842
901
|
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
902
|
+
// Log generic events instead of error events if container is in loading state, as most errors are not really FF errors
|
|
903
|
+
// which can pollute telemetry for real bugs
|
|
843
904
|
this.mc.logger.sendTelemetryEvent(
|
|
844
905
|
{
|
|
845
906
|
eventName: "ContainerClose",
|
|
846
|
-
category:
|
|
907
|
+
category:
|
|
908
|
+
this._lifecycleState !== "loading" && error !== undefined
|
|
909
|
+
? "error"
|
|
910
|
+
: "generic",
|
|
847
911
|
},
|
|
848
912
|
error,
|
|
849
913
|
);
|
|
@@ -856,7 +920,7 @@ export class Container
|
|
|
856
920
|
|
|
857
921
|
this._context?.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
858
922
|
|
|
859
|
-
this.
|
|
923
|
+
this.storageAdapter.dispose();
|
|
860
924
|
|
|
861
925
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
862
926
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
@@ -889,7 +953,7 @@ export class Container
|
|
|
889
953
|
this.mc.logger.sendTelemetryEvent(
|
|
890
954
|
{
|
|
891
955
|
eventName: "ContainerDispose",
|
|
892
|
-
category:
|
|
956
|
+
category: "generic",
|
|
893
957
|
},
|
|
894
958
|
error,
|
|
895
959
|
);
|
|
@@ -905,7 +969,7 @@ export class Container
|
|
|
905
969
|
|
|
906
970
|
this._context?.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
907
971
|
|
|
908
|
-
this.
|
|
972
|
+
this.storageAdapter.dispose();
|
|
909
973
|
|
|
910
974
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
911
975
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
@@ -933,6 +997,9 @@ export class Container
|
|
|
933
997
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
934
998
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
935
999
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
1000
|
+
if (!this.offlineLoadEnabled) {
|
|
1001
|
+
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
1002
|
+
}
|
|
936
1003
|
assert(
|
|
937
1004
|
this.attachState === AttachState.Attached,
|
|
938
1005
|
0x0d1 /* "Container should be attached before close" */,
|
|
@@ -942,15 +1009,15 @@ export class Container
|
|
|
942
1009
|
0x0d2 /* "resolved url should be valid Fluid url" */,
|
|
943
1010
|
);
|
|
944
1011
|
assert(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
|
|
945
|
-
assert(
|
|
946
|
-
|
|
947
|
-
0x37e /* Must have a valid protocol handler instance */,
|
|
948
|
-
);
|
|
1012
|
+
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
1013
|
+
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
949
1014
|
const pendingState: IPendingContainerState = {
|
|
950
1015
|
pendingRuntimeState: this.context.getPendingLocalState(),
|
|
1016
|
+
baseSnapshot: this.baseSnapshot,
|
|
1017
|
+
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1018
|
+
savedOps: this.savedOps,
|
|
951
1019
|
url: this.resolvedUrl.url,
|
|
952
|
-
|
|
953
|
-
term: this._protocolHandler.attributes.term,
|
|
1020
|
+
term: OnlyValidTermValue,
|
|
954
1021
|
clientId: this.clientId,
|
|
955
1022
|
};
|
|
956
1023
|
|
|
@@ -1031,9 +1098,13 @@ export class Container
|
|
|
1031
1098
|
// starting to attach the container to storage.
|
|
1032
1099
|
// Also, this should only be fired in detached container.
|
|
1033
1100
|
this._attachState = AttachState.Attaching;
|
|
1034
|
-
this.
|
|
1035
|
-
|
|
1036
|
-
|
|
1101
|
+
this.emit("attaching");
|
|
1102
|
+
if (this.offlineLoadEnabled) {
|
|
1103
|
+
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
1104
|
+
this.baseSnapshot = snapshot;
|
|
1105
|
+
this.baseSnapshotBlobs =
|
|
1106
|
+
getBlobContentsFromTreeWithBlobContents(snapshot);
|
|
1107
|
+
}
|
|
1037
1108
|
}
|
|
1038
1109
|
|
|
1039
1110
|
// Actually go and create the resolved document
|
|
@@ -1062,7 +1133,7 @@ export class Container
|
|
|
1062
1133
|
const resolvedUrl = this.service.resolvedUrl;
|
|
1063
1134
|
ensureFluidResolvedUrl(resolvedUrl);
|
|
1064
1135
|
this._resolvedUrl = resolvedUrl;
|
|
1065
|
-
await this.
|
|
1136
|
+
await this.storageAdapter.connectToService(this.service);
|
|
1066
1137
|
|
|
1067
1138
|
if (hasAttachmentBlobs) {
|
|
1068
1139
|
// upload blobs to storage
|
|
@@ -1082,7 +1153,7 @@ export class Container
|
|
|
1082
1153
|
for (const id of newIds) {
|
|
1083
1154
|
const blob =
|
|
1084
1155
|
await this.loader.services.detachedBlobStorage.readBlob(id);
|
|
1085
|
-
const response = await this.
|
|
1156
|
+
const response = await this.storageAdapter.createBlob(blob);
|
|
1086
1157
|
redirectTable.set(id, response.id);
|
|
1087
1158
|
}
|
|
1088
1159
|
}
|
|
@@ -1093,11 +1164,15 @@ export class Container
|
|
|
1093
1164
|
summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1094
1165
|
|
|
1095
1166
|
this._attachState = AttachState.Attaching;
|
|
1096
|
-
this.
|
|
1097
|
-
|
|
1098
|
-
|
|
1167
|
+
this.emit("attaching");
|
|
1168
|
+
if (this.offlineLoadEnabled) {
|
|
1169
|
+
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
1170
|
+
this.baseSnapshot = snapshot;
|
|
1171
|
+
this.baseSnapshotBlobs =
|
|
1172
|
+
getBlobContentsFromTreeWithBlobContents(snapshot);
|
|
1173
|
+
}
|
|
1099
1174
|
|
|
1100
|
-
await this.
|
|
1175
|
+
await this.storageAdapter.uploadSummaryWithContext(summary, {
|
|
1101
1176
|
referenceSequenceNumber: 0,
|
|
1102
1177
|
ackHandle: undefined,
|
|
1103
1178
|
proposalHandle: undefined,
|
|
@@ -1272,7 +1347,7 @@ export class Container
|
|
|
1272
1347
|
}
|
|
1273
1348
|
|
|
1274
1349
|
private async getVersion(version: string | null): Promise<IVersion | undefined> {
|
|
1275
|
-
const versions = await this.
|
|
1350
|
+
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
1276
1351
|
return versions[0];
|
|
1277
1352
|
}
|
|
1278
1353
|
|
|
@@ -1329,15 +1404,15 @@ export class Container
|
|
|
1329
1404
|
|
|
1330
1405
|
// Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
|
|
1331
1406
|
// DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
|
|
1332
|
-
if (loadMode.deltaConnection === undefined) {
|
|
1407
|
+
if (loadMode.deltaConnection === undefined && !pendingLocalState) {
|
|
1333
1408
|
this.connectToDeltaStream(connectionArgs);
|
|
1334
1409
|
}
|
|
1335
1410
|
|
|
1336
1411
|
if (!pendingLocalState) {
|
|
1337
|
-
await this.
|
|
1412
|
+
await this.storageAdapter.connectToService(this.service);
|
|
1338
1413
|
} else {
|
|
1339
1414
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
1340
|
-
this.
|
|
1415
|
+
this.storageAdapter.connectToService(this.service).catch((error) => {
|
|
1341
1416
|
this.close(error);
|
|
1342
1417
|
this.dispose?.(error);
|
|
1343
1418
|
});
|
|
@@ -1349,20 +1424,30 @@ export class Container
|
|
|
1349
1424
|
const { snapshot, versionId } =
|
|
1350
1425
|
pendingLocalState === undefined
|
|
1351
1426
|
? await this.fetchSnapshotTree(specifiedVersion)
|
|
1352
|
-
: { snapshot:
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1427
|
+
: { snapshot: pendingLocalState.baseSnapshot, versionId: undefined };
|
|
1428
|
+
|
|
1429
|
+
if (pendingLocalState) {
|
|
1430
|
+
this.baseSnapshot = pendingLocalState.baseSnapshot;
|
|
1431
|
+
this.baseSnapshotBlobs = pendingLocalState.snapshotBlobs;
|
|
1432
|
+
} else {
|
|
1433
|
+
assert(snapshot !== undefined, 0x237 /* "Snapshot should exist" */);
|
|
1434
|
+
if (this.offlineLoadEnabled) {
|
|
1435
|
+
this.baseSnapshot = snapshot;
|
|
1436
|
+
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
1437
|
+
this.baseSnapshotBlobs = await getBlobContentsFromTree(snapshot, this.storage);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
const attributes: IDocumentAttributes = await this.getDocumentAttributes(
|
|
1442
|
+
this.storageAdapter,
|
|
1443
|
+
snapshot,
|
|
1356
1444
|
);
|
|
1357
1445
|
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
minimumSequenceNumber: pendingLocalState.protocol.minimumSequenceNumber,
|
|
1364
|
-
term: pendingLocalState.term,
|
|
1365
|
-
};
|
|
1446
|
+
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
1447
|
+
const sequenceNumber =
|
|
1448
|
+
pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber;
|
|
1449
|
+
const dmAttributes =
|
|
1450
|
+
sequenceNumber !== undefined ? { ...attributes, sequenceNumber } : attributes;
|
|
1366
1451
|
|
|
1367
1452
|
let opsBeforeReturnP: Promise<void> | undefined;
|
|
1368
1453
|
|
|
@@ -1373,15 +1458,15 @@ export class Container
|
|
|
1373
1458
|
// Start prefetch, but not set opsBeforeReturnP - boot is not blocked by it!
|
|
1374
1459
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1375
1460
|
this.attachDeltaManagerOpHandler(
|
|
1376
|
-
|
|
1461
|
+
dmAttributes,
|
|
1377
1462
|
loadMode.deltaConnection !== "none" ? "all" : "none",
|
|
1378
1463
|
);
|
|
1379
1464
|
break;
|
|
1380
1465
|
case "cached":
|
|
1381
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(
|
|
1466
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
1382
1467
|
break;
|
|
1383
1468
|
case "all":
|
|
1384
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(
|
|
1469
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "all");
|
|
1385
1470
|
break;
|
|
1386
1471
|
default:
|
|
1387
1472
|
unreachableCase(loadMode.opsBeforeReturn);
|
|
@@ -1389,22 +1474,7 @@ export class Container
|
|
|
1389
1474
|
|
|
1390
1475
|
// ...load in the existing quorum
|
|
1391
1476
|
// Initialize the protocol handler
|
|
1392
|
-
|
|
1393
|
-
await this.initializeProtocolStateFromSnapshot(
|
|
1394
|
-
attributes,
|
|
1395
|
-
this.storageService,
|
|
1396
|
-
snapshot,
|
|
1397
|
-
);
|
|
1398
|
-
} else {
|
|
1399
|
-
this.initializeProtocolState(
|
|
1400
|
-
attributes,
|
|
1401
|
-
{
|
|
1402
|
-
members: pendingLocalState.protocol.members,
|
|
1403
|
-
proposals: pendingLocalState.protocol.proposals,
|
|
1404
|
-
values: pendingLocalState.protocol.values,
|
|
1405
|
-
}, // pending IQuorumSnapshot
|
|
1406
|
-
);
|
|
1407
|
-
}
|
|
1477
|
+
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
1408
1478
|
|
|
1409
1479
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1410
1480
|
await this.instantiateContext(
|
|
@@ -1414,6 +1484,24 @@ export class Container
|
|
|
1414
1484
|
pendingLocalState?.pendingRuntimeState,
|
|
1415
1485
|
);
|
|
1416
1486
|
|
|
1487
|
+
// replay saved ops
|
|
1488
|
+
if (pendingLocalState) {
|
|
1489
|
+
for (const message of pendingLocalState.savedOps) {
|
|
1490
|
+
this.processRemoteMessage(message);
|
|
1491
|
+
|
|
1492
|
+
// allow runtime to apply stashed ops at this op's sequence number
|
|
1493
|
+
await this.context.notifyOpReplay(message);
|
|
1494
|
+
}
|
|
1495
|
+
pendingLocalState.savedOps = [];
|
|
1496
|
+
|
|
1497
|
+
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
1498
|
+
assert(
|
|
1499
|
+
this.clientId === undefined,
|
|
1500
|
+
0x5d6 /* Unexpected clientId when setting stashed clientId */,
|
|
1501
|
+
);
|
|
1502
|
+
this._clientId = pendingLocalState?.clientId;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1417
1505
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
1418
1506
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
1419
1507
|
if (!this.closed) {
|
|
@@ -1437,6 +1525,11 @@ export class Container
|
|
|
1437
1525
|
|
|
1438
1526
|
switch (loadMode.deltaConnection) {
|
|
1439
1527
|
case undefined:
|
|
1528
|
+
if (pendingLocalState) {
|
|
1529
|
+
// connect to delta stream now since we did not before
|
|
1530
|
+
this.connectToDeltaStream(connectionArgs);
|
|
1531
|
+
}
|
|
1532
|
+
// intentional fallthrough
|
|
1440
1533
|
case "delayed":
|
|
1441
1534
|
assert(
|
|
1442
1535
|
this.inboundQueuePausedFromInit,
|
|
@@ -1476,7 +1569,7 @@ export class Container
|
|
|
1476
1569
|
private async createDetached(source: IFluidCodeDetails) {
|
|
1477
1570
|
const attributes: IDocumentAttributes = {
|
|
1478
1571
|
sequenceNumber: detachedContainerRefSeqNumber,
|
|
1479
|
-
term:
|
|
1572
|
+
term: OnlyValidTermValue,
|
|
1480
1573
|
minimumSequenceNumber: 0,
|
|
1481
1574
|
};
|
|
1482
1575
|
|
|
@@ -1512,15 +1605,15 @@ export class Container
|
|
|
1512
1605
|
}
|
|
1513
1606
|
|
|
1514
1607
|
const snapshotTree = getSnapshotTreeFromSerializedContainer(detachedContainerSnapshot);
|
|
1515
|
-
this.
|
|
1516
|
-
const attributes = await this.getDocumentAttributes(this.
|
|
1608
|
+
this.storageAdapter.loadSnapshotForRehydratingContainer(snapshotTree);
|
|
1609
|
+
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshotTree);
|
|
1517
1610
|
|
|
1518
1611
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1519
1612
|
|
|
1520
1613
|
// Initialize the protocol handler
|
|
1521
1614
|
const baseTree = getProtocolSnapshotTree(snapshotTree);
|
|
1522
1615
|
const qValues = await readAndParse<[string, ICommittedProposal][]>(
|
|
1523
|
-
this.
|
|
1616
|
+
this.storageAdapter,
|
|
1524
1617
|
baseTree.blobs.quorumValues,
|
|
1525
1618
|
);
|
|
1526
1619
|
const codeDetails = getCodeDetailsFromQuorumValues(qValues);
|
|
@@ -1550,7 +1643,7 @@ export class Container
|
|
|
1550
1643
|
return {
|
|
1551
1644
|
minimumSequenceNumber: 0,
|
|
1552
1645
|
sequenceNumber: 0,
|
|
1553
|
-
term:
|
|
1646
|
+
term: OnlyValidTermValue,
|
|
1554
1647
|
};
|
|
1555
1648
|
}
|
|
1556
1649
|
|
|
@@ -1562,11 +1655,6 @@ export class Container
|
|
|
1562
1655
|
|
|
1563
1656
|
const attributes = await readAndParse<IDocumentAttributes>(storage, attributesHash);
|
|
1564
1657
|
|
|
1565
|
-
// Backward compatibility for older summaries with no term
|
|
1566
|
-
if (attributes.term === undefined) {
|
|
1567
|
-
attributes.term = 1;
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
1658
|
return attributes;
|
|
1571
1659
|
}
|
|
1572
1660
|
|
|
@@ -1731,6 +1819,7 @@ export class Container
|
|
|
1731
1819
|
(props: IConnectionManagerFactoryArgs) =>
|
|
1732
1820
|
new ConnectionManager(
|
|
1733
1821
|
serviceProvider,
|
|
1822
|
+
() => this.isDirty,
|
|
1734
1823
|
this.client,
|
|
1735
1824
|
this._canReconnect,
|
|
1736
1825
|
ChildLogger.create(this.subLogger, "ConnectionManager"),
|
|
@@ -1744,7 +1833,7 @@ export class Container
|
|
|
1744
1833
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1745
1834
|
deltaManager.inboundSignal.pause();
|
|
1746
1835
|
|
|
1747
|
-
deltaManager.on("connect", (details:
|
|
1836
|
+
deltaManager.on("connect", (details: IConnectionDetailsInternal, _opsBehind?: number) => {
|
|
1748
1837
|
assert(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
|
|
1749
1838
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1750
1839
|
});
|
|
@@ -1792,7 +1881,6 @@ export class Container
|
|
|
1792
1881
|
return this._deltaManager.attachOpHandler(
|
|
1793
1882
|
attributes.minimumSequenceNumber,
|
|
1794
1883
|
attributes.sequenceNumber,
|
|
1795
|
-
attributes.term ?? 1,
|
|
1796
1884
|
{
|
|
1797
1885
|
process: (message) => this.processRemoteMessage(message),
|
|
1798
1886
|
processSignal: (message) => {
|
|
@@ -1882,10 +1970,7 @@ export class Container
|
|
|
1882
1970
|
|
|
1883
1971
|
// Both protocol and context should not be undefined if we got so far.
|
|
1884
1972
|
|
|
1885
|
-
this.setContextConnectedState(
|
|
1886
|
-
state,
|
|
1887
|
-
this._deltaManager.connectionManager.readOnlyInfo.readonly ?? false,
|
|
1888
|
-
);
|
|
1973
|
+
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
1889
1974
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1890
1975
|
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
1891
1976
|
|
|
@@ -1923,7 +2008,7 @@ export class Container
|
|
|
1923
2008
|
}
|
|
1924
2009
|
|
|
1925
2010
|
/** @returns clientSequenceNumber of last message in a batch */
|
|
1926
|
-
private submitBatch(batch: IBatchMessage[]): number {
|
|
2011
|
+
private submitBatch(batch: IBatchMessage[], referenceSequenceNumber?: number): number {
|
|
1927
2012
|
let clientSequenceNumber = -1;
|
|
1928
2013
|
for (const message of batch) {
|
|
1929
2014
|
clientSequenceNumber = this.submitMessage(
|
|
@@ -1932,13 +2017,14 @@ export class Container
|
|
|
1932
2017
|
true, // batch
|
|
1933
2018
|
message.metadata,
|
|
1934
2019
|
message.compression,
|
|
2020
|
+
referenceSequenceNumber,
|
|
1935
2021
|
);
|
|
1936
2022
|
}
|
|
1937
2023
|
this._deltaManager.flush();
|
|
1938
2024
|
return clientSequenceNumber;
|
|
1939
2025
|
}
|
|
1940
2026
|
|
|
1941
|
-
private submitSummaryMessage(summary: ISummaryContent) {
|
|
2027
|
+
private submitSummaryMessage(summary: ISummaryContent, referenceSequenceNumber?: number) {
|
|
1942
2028
|
// github #6451: this is only needed for staging so the server
|
|
1943
2029
|
// know when the protocol tree is included
|
|
1944
2030
|
// this can be removed once all clients send
|
|
@@ -1946,11 +2032,14 @@ export class Container
|
|
|
1946
2032
|
if (summary.details === undefined) {
|
|
1947
2033
|
summary.details = {};
|
|
1948
2034
|
}
|
|
1949
|
-
summary.details.includesProtocolTree = this.
|
|
2035
|
+
summary.details.includesProtocolTree = this.storageAdapter.summarizeProtocolTree;
|
|
1950
2036
|
return this.submitMessage(
|
|
1951
2037
|
MessageType.Summarize,
|
|
1952
2038
|
JSON.stringify(summary),
|
|
1953
2039
|
false /* batch */,
|
|
2040
|
+
undefined /* metadata */,
|
|
2041
|
+
undefined /* compression */,
|
|
2042
|
+
referenceSequenceNumber,
|
|
1954
2043
|
);
|
|
1955
2044
|
}
|
|
1956
2045
|
|
|
@@ -1960,6 +2049,7 @@ export class Container
|
|
|
1960
2049
|
batch?: boolean,
|
|
1961
2050
|
metadata?: any,
|
|
1962
2051
|
compression?: string,
|
|
2052
|
+
referenceSequenceNumber?: number,
|
|
1963
2053
|
): number {
|
|
1964
2054
|
if (this.connectionState !== ConnectionState.Connected) {
|
|
1965
2055
|
this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
|
|
@@ -1968,10 +2058,20 @@ export class Container
|
|
|
1968
2058
|
|
|
1969
2059
|
this.messageCountAfterDisconnection += 1;
|
|
1970
2060
|
this.collabWindowTracker?.stopSequenceNumberUpdate();
|
|
1971
|
-
return this._deltaManager.submit(
|
|
2061
|
+
return this._deltaManager.submit(
|
|
2062
|
+
type,
|
|
2063
|
+
contents,
|
|
2064
|
+
batch,
|
|
2065
|
+
metadata,
|
|
2066
|
+
compression,
|
|
2067
|
+
referenceSequenceNumber,
|
|
2068
|
+
);
|
|
1972
2069
|
}
|
|
1973
2070
|
|
|
1974
2071
|
private processRemoteMessage(message: ISequencedDocumentMessage) {
|
|
2072
|
+
if (this.offlineLoadEnabled) {
|
|
2073
|
+
this.savedOps.push(message);
|
|
2074
|
+
}
|
|
1975
2075
|
const local = this.clientId === message.clientId;
|
|
1976
2076
|
|
|
1977
2077
|
// Allow the protocol handler to process the message
|
|
@@ -2044,7 +2144,7 @@ export class Container
|
|
|
2044
2144
|
});
|
|
2045
2145
|
}
|
|
2046
2146
|
this._loadedFromVersion = version;
|
|
2047
|
-
const snapshot = (await this.
|
|
2147
|
+
const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
|
|
2048
2148
|
|
|
2049
2149
|
if (snapshot === undefined && version !== undefined) {
|
|
2050
2150
|
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
@@ -2083,8 +2183,10 @@ export class Container
|
|
|
2083
2183
|
loader,
|
|
2084
2184
|
(type, contents, batch, metadata) =>
|
|
2085
2185
|
this.submitContainerMessage(type, contents, batch, metadata),
|
|
2086
|
-
(summaryOp: ISummaryContent) =>
|
|
2087
|
-
|
|
2186
|
+
(summaryOp: ISummaryContent, referenceSequenceNumber?: number) =>
|
|
2187
|
+
this.submitSummaryMessage(summaryOp, referenceSequenceNumber),
|
|
2188
|
+
(batch: IBatchMessage[], referenceSequenceNumber?: number) =>
|
|
2189
|
+
this.submitBatch(batch, referenceSequenceNumber),
|
|
2088
2190
|
(message) => this.submitSignal(message),
|
|
2089
2191
|
(error?: ICriticalContainerError) => this.dispose?.(error),
|
|
2090
2192
|
(error?: ICriticalContainerError) => this.close(error),
|