@fluidframework/container-loader 2.0.0-internal.5.3.2 → 2.0.0-internal.6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/README.md +6 -3
- package/dist/audience.d.ts +1 -0
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +3 -1
- package/dist/audience.js.map +1 -1
- package/dist/connectionManager.d.ts +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +30 -36
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +2 -1
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +9 -16
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +12 -8
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +199 -156
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -12
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +1 -20
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.js +3 -5
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +11 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +3 -3
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.d.ts +30 -0
- package/dist/debugLogger.d.ts.map +1 -0
- package/dist/debugLogger.js +95 -0
- package/dist/debugLogger.js.map +1 -0
- package/dist/deltaManager.d.ts +16 -4
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +79 -33
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +1 -2
- package/dist/deltaQueue.js.map +1 -1
- package/dist/loader.d.ts +12 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +73 -47
- 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 -3
- package/dist/protocol.js.map +1 -1
- package/dist/quorum.d.ts +4 -1
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js +1 -13
- package/dist/quorum.js.map +1 -1
- package/dist/utils.d.ts +8 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -6
- package/dist/utils.js.map +1 -1
- package/lib/audience.d.ts +1 -0
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +3 -1
- package/lib/audience.js.map +1 -1
- package/lib/connectionManager.d.ts +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +32 -35
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +2 -1
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +9 -16
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +12 -8
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +200 -157
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -12
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +1 -20
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.js +3 -5
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +11 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +3 -3
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.d.ts +30 -0
- package/lib/debugLogger.d.ts.map +1 -0
- package/lib/debugLogger.js +91 -0
- package/lib/debugLogger.js.map +1 -0
- package/lib/deltaManager.d.ts +16 -4
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +77 -28
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +1 -2
- package/lib/deltaQueue.js.map +1 -1
- package/lib/loader.d.ts +12 -0
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +73 -47
- 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 -3
- package/lib/protocol.js.map +1 -1
- package/lib/quorum.d.ts +4 -1
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js +0 -11
- package/lib/quorum.js.map +1 -1
- package/lib/utils.d.ts +8 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +22 -5
- package/lib/utils.js.map +1 -1
- package/package.json +14 -14
- package/src/audience.ts +6 -0
- package/src/connectionManager.ts +13 -14
- package/src/connectionStateHandler.ts +3 -2
- package/src/container.ts +178 -120
- package/src/containerContext.ts +0 -24
- package/src/contracts.ts +16 -5
- package/src/debugLogger.ts +113 -0
- package/src/deltaManager.ts +50 -9
- package/src/loader.ts +53 -30
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +0 -1
- package/src/quorum.ts +0 -10
- package/src/utils.ts +29 -0
package/src/container.ts
CHANGED
|
@@ -23,27 +23,26 @@ import {
|
|
|
23
23
|
FluidObject,
|
|
24
24
|
} from "@fluidframework/core-interfaces";
|
|
25
25
|
import {
|
|
26
|
+
AttachState,
|
|
27
|
+
ContainerWarning,
|
|
26
28
|
IAudience,
|
|
27
|
-
|
|
29
|
+
IBatchMessage,
|
|
30
|
+
ICodeDetailsLoader,
|
|
28
31
|
IContainer,
|
|
29
32
|
IContainerEvents,
|
|
30
|
-
IDeltaManager,
|
|
31
|
-
ICriticalContainerError,
|
|
32
|
-
ContainerWarning,
|
|
33
|
-
AttachState,
|
|
34
|
-
IThrottlingWarning,
|
|
35
|
-
ReadOnlyInfo,
|
|
36
33
|
IContainerLoadMode,
|
|
34
|
+
ICriticalContainerError,
|
|
35
|
+
IDeltaManager,
|
|
37
36
|
IFluidCodeDetails,
|
|
38
|
-
isFluidCodeDetails,
|
|
39
|
-
IBatchMessage,
|
|
40
|
-
ICodeDetailsLoader,
|
|
41
37
|
IHostLoader,
|
|
42
38
|
IFluidModuleWithDetails,
|
|
43
39
|
IProvideRuntimeFactory,
|
|
44
40
|
IProvideFluidCodeDetailsComparer,
|
|
45
41
|
IFluidCodeDetailsComparer,
|
|
46
42
|
IRuntime,
|
|
43
|
+
isFluidCodeDetails,
|
|
44
|
+
IThrottlingWarning,
|
|
45
|
+
ReadOnlyInfo,
|
|
47
46
|
} from "@fluidframework/container-definitions";
|
|
48
47
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
49
48
|
import {
|
|
@@ -58,7 +57,6 @@ import {
|
|
|
58
57
|
readAndParse,
|
|
59
58
|
OnlineStatus,
|
|
60
59
|
isOnline,
|
|
61
|
-
combineAppAndProtocolSummary,
|
|
62
60
|
runWithRetry,
|
|
63
61
|
isCombinedAppAndProtocolSummary,
|
|
64
62
|
MessageType2,
|
|
@@ -85,21 +83,26 @@ import {
|
|
|
85
83
|
SummaryType,
|
|
86
84
|
} from "@fluidframework/protocol-definitions";
|
|
87
85
|
import {
|
|
88
|
-
|
|
86
|
+
createChildLogger,
|
|
89
87
|
EventEmitterWithErrorHandling,
|
|
90
88
|
PerformanceEvent,
|
|
91
89
|
raiseConnectedEvent,
|
|
92
|
-
TelemetryLogger,
|
|
93
90
|
connectedEventName,
|
|
94
91
|
normalizeError,
|
|
95
92
|
MonitoringContext,
|
|
96
|
-
|
|
93
|
+
createChildMonitoringContext,
|
|
97
94
|
wrapError,
|
|
98
95
|
ITelemetryLoggerExt,
|
|
96
|
+
formatTick,
|
|
99
97
|
} from "@fluidframework/telemetry-utils";
|
|
100
98
|
import { Audience } from "./audience";
|
|
101
99
|
import { ContainerContext } from "./containerContext";
|
|
102
|
-
import {
|
|
100
|
+
import {
|
|
101
|
+
ReconnectMode,
|
|
102
|
+
IConnectionManagerFactoryArgs,
|
|
103
|
+
getPackageName,
|
|
104
|
+
IConnectionDetailsInternal,
|
|
105
|
+
} from "./contracts";
|
|
103
106
|
import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
104
107
|
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
|
|
105
108
|
import { pkgVersion } from "./packageVersion";
|
|
@@ -110,8 +113,12 @@ import {
|
|
|
110
113
|
ISerializableBlobContents,
|
|
111
114
|
} from "./containerStorageAdapter";
|
|
112
115
|
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
|
|
113
|
-
import {
|
|
114
|
-
|
|
116
|
+
import {
|
|
117
|
+
combineAppAndProtocolSummary,
|
|
118
|
+
getProtocolSnapshotTree,
|
|
119
|
+
getSnapshotTreeFromSerializedContainer,
|
|
120
|
+
} from "./utils";
|
|
121
|
+
import { initQuorumValuesFromCodeDetails } from "./quorum";
|
|
115
122
|
import { NoopHeuristic } from "./noopHeuristic";
|
|
116
123
|
import { ConnectionManager } from "./connectionManager";
|
|
117
124
|
import { ConnectionState } from "./connectionState";
|
|
@@ -151,6 +158,11 @@ export interface IContainerLoadProps {
|
|
|
151
158
|
* The pending state serialized from a pervious container instance
|
|
152
159
|
*/
|
|
153
160
|
readonly pendingLocalState?: IPendingContainerState;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Load the container to at least this sequence number.
|
|
164
|
+
*/
|
|
165
|
+
readonly loadToSequenceNumber?: number;
|
|
154
166
|
}
|
|
155
167
|
|
|
156
168
|
/**
|
|
@@ -369,7 +381,8 @@ export class Container
|
|
|
369
381
|
loadProps: IContainerLoadProps,
|
|
370
382
|
createProps: IContainerCreateProps,
|
|
371
383
|
): Promise<Container> {
|
|
372
|
-
const { version, pendingLocalState, loadMode, resolvedUrl } =
|
|
384
|
+
const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } =
|
|
385
|
+
loadProps;
|
|
373
386
|
|
|
374
387
|
const container = new Container(createProps, loadProps);
|
|
375
388
|
|
|
@@ -398,7 +411,7 @@ export class Container
|
|
|
398
411
|
container.on("closed", onClosed);
|
|
399
412
|
|
|
400
413
|
container
|
|
401
|
-
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
414
|
+
.load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
|
|
402
415
|
.finally(() => {
|
|
403
416
|
container.removeListener("closed", onClosed);
|
|
404
417
|
})
|
|
@@ -473,7 +486,7 @@ export class Container
|
|
|
473
486
|
private readonly codeLoader: ICodeDetailsLoader;
|
|
474
487
|
private readonly options: ILoaderOptions;
|
|
475
488
|
private readonly scope: FluidObject;
|
|
476
|
-
private readonly subLogger:
|
|
489
|
+
private readonly subLogger: ITelemetryLoggerExt;
|
|
477
490
|
private readonly detachedBlobStorage: IDetachedBlobStorage | undefined;
|
|
478
491
|
private readonly protocolHandlerBuilder: ProtocolHandlerBuilder;
|
|
479
492
|
|
|
@@ -523,13 +536,14 @@ export class Container
|
|
|
523
536
|
|
|
524
537
|
public get closed(): boolean {
|
|
525
538
|
return (
|
|
526
|
-
this._lifecycleState === "closing" ||
|
|
527
|
-
this._lifecycleState === "closed" ||
|
|
528
|
-
this._lifecycleState === "disposing" ||
|
|
529
|
-
this._lifecycleState === "disposed"
|
|
539
|
+
this._lifecycleState === "closing" || this._lifecycleState === "closed" || this.disposed
|
|
530
540
|
);
|
|
531
541
|
}
|
|
532
542
|
|
|
543
|
+
public get disposed(): boolean {
|
|
544
|
+
return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
|
|
545
|
+
}
|
|
546
|
+
|
|
533
547
|
private _attachState = AttachState.Detached;
|
|
534
548
|
|
|
535
549
|
private readonly storageAdapter: ContainerStorageAdapter;
|
|
@@ -556,7 +570,6 @@ export class Container
|
|
|
556
570
|
private inboundQueuePausedFromInit = true;
|
|
557
571
|
private firstConnection = true;
|
|
558
572
|
private readonly connectionTransitionTimes: number[] = [];
|
|
559
|
-
private messageCountAfterDisconnection: number = 0;
|
|
560
573
|
private _loadedFromVersion: IVersion | undefined;
|
|
561
574
|
private attachStarted = false;
|
|
562
575
|
private _dirtyContainer = false;
|
|
@@ -769,42 +782,45 @@ export class Container
|
|
|
769
782
|
}`;
|
|
770
783
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
771
784
|
// We assign the id later so property getter is used.
|
|
772
|
-
this.subLogger =
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
//
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
this.deltaManager?.lastMessage?.
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
785
|
+
this.subLogger = createChildLogger({
|
|
786
|
+
logger: subLogger,
|
|
787
|
+
properties: {
|
|
788
|
+
all: {
|
|
789
|
+
clientType, // Differentiating summarizer container from main container
|
|
790
|
+
containerId: uuid(),
|
|
791
|
+
docId: () => this.resolvedUrl?.id,
|
|
792
|
+
containerAttachState: () => this._attachState,
|
|
793
|
+
containerLifecycleState: () => this._lifecycleState,
|
|
794
|
+
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
795
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
796
|
+
},
|
|
797
|
+
// we need to be judicious with our logging here to avoid generating too much data
|
|
798
|
+
// all data logged here should be broadly applicable, and not specific to a
|
|
799
|
+
// specific error or class of errors
|
|
800
|
+
error: {
|
|
801
|
+
// load information to associate errors with the specific load point
|
|
802
|
+
dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
|
|
803
|
+
dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
|
|
804
|
+
dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
|
|
805
|
+
containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
|
|
806
|
+
containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
|
|
807
|
+
// message information to associate errors with the specific execution state
|
|
808
|
+
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
809
|
+
dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
|
|
810
|
+
dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
|
|
811
|
+
dmLastMsqSeqClientId: () =>
|
|
812
|
+
this.deltaManager?.lastMessage?.clientId === null
|
|
813
|
+
? "null"
|
|
814
|
+
: this.deltaManager?.lastMessage?.clientId,
|
|
815
|
+
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
816
|
+
connectionStateDuration: () =>
|
|
817
|
+
performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
818
|
+
},
|
|
803
819
|
},
|
|
804
820
|
});
|
|
805
821
|
|
|
806
822
|
// Prefix all events in this file with container-loader
|
|
807
|
-
this.mc =
|
|
823
|
+
this.mc = createChildMonitoringContext({ logger: this.subLogger, namespace: "Container" });
|
|
808
824
|
|
|
809
825
|
this._deltaManager = this.createDeltaManager();
|
|
810
826
|
|
|
@@ -900,8 +916,8 @@ export class Container
|
|
|
900
916
|
document !== null &&
|
|
901
917
|
typeof document.addEventListener === "function" &&
|
|
902
918
|
document.addEventListener !== null;
|
|
903
|
-
// keep track of last time page was visible for telemetry
|
|
904
|
-
if (isDomAvailable) {
|
|
919
|
+
// keep track of last time page was visible for telemetry (on interactive clients only)
|
|
920
|
+
if (isDomAvailable && interactive) {
|
|
905
921
|
this.lastVisible = document.hidden ? performance.now() : undefined;
|
|
906
922
|
this.visibilityEventHandler = () => {
|
|
907
923
|
if (document.hidden) {
|
|
@@ -1051,16 +1067,21 @@ export class Container
|
|
|
1051
1067
|
}
|
|
1052
1068
|
}
|
|
1053
1069
|
|
|
1054
|
-
public closeAndGetPendingLocalState(): string {
|
|
1070
|
+
public async closeAndGetPendingLocalState(): Promise<string> {
|
|
1055
1071
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
1056
1072
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
1057
1073
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
1058
|
-
|
|
1074
|
+
this.disconnect(); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
1075
|
+
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
1059
1076
|
this.close();
|
|
1060
1077
|
return pendingState;
|
|
1061
1078
|
}
|
|
1062
1079
|
|
|
1063
|
-
public getPendingLocalState(): string {
|
|
1080
|
+
public async getPendingLocalState(): Promise<string> {
|
|
1081
|
+
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
private async getPendingLocalStateCore(props: { notifyImminentClosure: boolean }) {
|
|
1064
1085
|
if (!this.offlineLoadEnabled) {
|
|
1065
1086
|
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
1066
1087
|
}
|
|
@@ -1079,8 +1100,9 @@ export class Container
|
|
|
1079
1100
|
);
|
|
1080
1101
|
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
1081
1102
|
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
1103
|
+
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
1082
1104
|
const pendingState: IPendingContainerState = {
|
|
1083
|
-
pendingRuntimeState
|
|
1105
|
+
pendingRuntimeState,
|
|
1084
1106
|
baseSnapshot: this.baseSnapshot,
|
|
1085
1107
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1086
1108
|
savedOps: this.savedOps,
|
|
@@ -1465,7 +1487,8 @@ export class Container
|
|
|
1465
1487
|
specifiedVersion: string | undefined,
|
|
1466
1488
|
loadMode: IContainerLoadMode,
|
|
1467
1489
|
resolvedUrl: IResolvedUrl,
|
|
1468
|
-
pendingLocalState
|
|
1490
|
+
pendingLocalState: IPendingContainerState | undefined,
|
|
1491
|
+
loadToSequenceNumber: number | undefined,
|
|
1469
1492
|
) {
|
|
1470
1493
|
this.service = await this.serviceFactory.createDocumentService(
|
|
1471
1494
|
resolvedUrl,
|
|
@@ -1539,6 +1562,57 @@ export class Container
|
|
|
1539
1562
|
|
|
1540
1563
|
let opsBeforeReturnP: Promise<void> | undefined;
|
|
1541
1564
|
|
|
1565
|
+
if (loadMode.pauseAfterLoad === true) {
|
|
1566
|
+
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
1567
|
+
if (loadMode.opsBeforeReturn === "sequenceNumber") {
|
|
1568
|
+
assert(
|
|
1569
|
+
loadToSequenceNumber !== undefined,
|
|
1570
|
+
0x727 /* sequenceNumber should be defined */,
|
|
1571
|
+
);
|
|
1572
|
+
// Note: It is possible that we think the latest snapshot is newer than the specified sequence number
|
|
1573
|
+
// due to saved ops that may be replayed after the snapshot.
|
|
1574
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
|
|
1575
|
+
if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
|
|
1576
|
+
throw new Error(
|
|
1577
|
+
"Cannot satisfy request to pause the container at the specified sequence number. Most recent snapshot is newer than the specified sequence number.",
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
// Force readonly mode - this will ensure we don't receive an error for the lack of join op
|
|
1583
|
+
this.forceReadonly(true);
|
|
1584
|
+
|
|
1585
|
+
// We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
|
|
1586
|
+
const opHandler = () => {
|
|
1587
|
+
if (loadToSequenceNumber === undefined) {
|
|
1588
|
+
// If there is no specified sequence number, pause after the inbound queue is empty.
|
|
1589
|
+
if (this.deltaManager.inbound.length !== 0) {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
} else {
|
|
1593
|
+
// If there is a specified sequence number, keep processing until we reach it.
|
|
1594
|
+
if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// Pause op processing once we have processed the desired number of ops.
|
|
1600
|
+
void this.deltaManager.inbound.pause();
|
|
1601
|
+
void this.deltaManager.outbound.pause();
|
|
1602
|
+
this.off("op", opHandler);
|
|
1603
|
+
};
|
|
1604
|
+
if (
|
|
1605
|
+
(loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
|
|
1606
|
+
this.deltaManager.lastSequenceNumber === loadToSequenceNumber
|
|
1607
|
+
) {
|
|
1608
|
+
// If we have already reached the desired sequence number, call opHandler() to pause immediately.
|
|
1609
|
+
opHandler();
|
|
1610
|
+
} else {
|
|
1611
|
+
// If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
|
|
1612
|
+
this.on("op", opHandler);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1542
1616
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
1543
1617
|
// Kick off any ops fetching if required.
|
|
1544
1618
|
switch (loadMode.opsBeforeReturn) {
|
|
@@ -1550,6 +1624,9 @@ export class Container
|
|
|
1550
1624
|
loadMode.deltaConnection !== "none" ? "all" : "none",
|
|
1551
1625
|
);
|
|
1552
1626
|
break;
|
|
1627
|
+
case "sequenceNumber":
|
|
1628
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
|
|
1629
|
+
break;
|
|
1553
1630
|
case "cached":
|
|
1554
1631
|
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
1555
1632
|
break;
|
|
@@ -1565,8 +1642,7 @@ export class Container
|
|
|
1565
1642
|
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
1566
1643
|
|
|
1567
1644
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1568
|
-
await this.
|
|
1569
|
-
true, // existing
|
|
1645
|
+
await this.instantiateRuntime(
|
|
1570
1646
|
codeDetails,
|
|
1571
1647
|
snapshot,
|
|
1572
1648
|
pendingLocalState?.pendingRuntimeState,
|
|
@@ -1634,6 +1710,22 @@ export class Container
|
|
|
1634
1710
|
}
|
|
1635
1711
|
}
|
|
1636
1712
|
|
|
1713
|
+
// If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
|
|
1714
|
+
if (
|
|
1715
|
+
loadToSequenceNumber !== undefined &&
|
|
1716
|
+
this.deltaManager.lastSequenceNumber < loadToSequenceNumber
|
|
1717
|
+
) {
|
|
1718
|
+
await new Promise<void>((resolve, reject) => {
|
|
1719
|
+
const opHandler = (message: ISequencedDocumentMessage) => {
|
|
1720
|
+
if (message.sequenceNumber >= loadToSequenceNumber) {
|
|
1721
|
+
resolve();
|
|
1722
|
+
this.off("op", opHandler);
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
this.on("op", opHandler);
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1637
1729
|
// Safety net: static version of Container.load() should have learned about it through "closed" handler.
|
|
1638
1730
|
// But if that did not happen for some reason, fail load for sure.
|
|
1639
1731
|
// Otherwise we can get into situations where container is closed and does not try to connect to ordering
|
|
@@ -1654,7 +1746,7 @@ export class Container
|
|
|
1654
1746
|
};
|
|
1655
1747
|
}
|
|
1656
1748
|
|
|
1657
|
-
private async createDetached(
|
|
1749
|
+
private async createDetached(codeDetails: IFluidCodeDetails) {
|
|
1658
1750
|
const attributes: IDocumentAttributes = {
|
|
1659
1751
|
sequenceNumber: detachedContainerRefSeqNumber,
|
|
1660
1752
|
term: OnlyValidTermValue,
|
|
@@ -1664,7 +1756,7 @@ export class Container
|
|
|
1664
1756
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1665
1757
|
|
|
1666
1758
|
// Need to just seed the source data in the code quorum. Quorum itself is empty
|
|
1667
|
-
const qValues = initQuorumValuesFromCodeDetails(
|
|
1759
|
+
const qValues = initQuorumValuesFromCodeDetails(codeDetails);
|
|
1668
1760
|
this.initializeProtocolState(
|
|
1669
1761
|
attributes,
|
|
1670
1762
|
{
|
|
@@ -1674,10 +1766,7 @@ export class Container
|
|
|
1674
1766
|
}, // IQuorumSnapShot
|
|
1675
1767
|
);
|
|
1676
1768
|
|
|
1677
|
-
|
|
1678
|
-
await this.instantiateContextDetached(
|
|
1679
|
-
false, // existing
|
|
1680
|
-
);
|
|
1769
|
+
await this.instantiateRuntime(codeDetails, undefined);
|
|
1681
1770
|
|
|
1682
1771
|
this.setLoaded();
|
|
1683
1772
|
}
|
|
@@ -1703,21 +1792,17 @@ export class Container
|
|
|
1703
1792
|
this.storageAdapter,
|
|
1704
1793
|
baseTree.blobs.quorumValues,
|
|
1705
1794
|
);
|
|
1706
|
-
const codeDetails = getCodeDetailsFromQuorumValues(qValues);
|
|
1707
1795
|
this.initializeProtocolState(
|
|
1708
1796
|
attributes,
|
|
1709
1797
|
{
|
|
1710
1798
|
members: [],
|
|
1711
1799
|
proposals: [],
|
|
1712
|
-
values:
|
|
1713
|
-
codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : [],
|
|
1800
|
+
values: qValues,
|
|
1714
1801
|
}, // IQuorumSnapShot
|
|
1715
1802
|
);
|
|
1803
|
+
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1716
1804
|
|
|
1717
|
-
await this.
|
|
1718
|
-
true, // existing
|
|
1719
|
-
snapshotTree,
|
|
1720
|
-
);
|
|
1805
|
+
await this.instantiateRuntime(codeDetails, snapshotTree);
|
|
1721
1806
|
|
|
1722
1807
|
this.setLoaded();
|
|
1723
1808
|
}
|
|
@@ -1786,7 +1871,10 @@ export class Container
|
|
|
1786
1871
|
this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })),
|
|
1787
1872
|
);
|
|
1788
1873
|
|
|
1789
|
-
const protocolLogger =
|
|
1874
|
+
const protocolLogger = createChildLogger({
|
|
1875
|
+
logger: this.subLogger,
|
|
1876
|
+
namespace: "ProtocolHandler",
|
|
1877
|
+
});
|
|
1790
1878
|
|
|
1791
1879
|
protocol.quorum.on("error", (error) => {
|
|
1792
1880
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -1897,7 +1985,7 @@ export class Container
|
|
|
1897
1985
|
const serviceProvider = () => this.service;
|
|
1898
1986
|
const deltaManager = new DeltaManager<ConnectionManager>(
|
|
1899
1987
|
serviceProvider,
|
|
1900
|
-
|
|
1988
|
+
createChildLogger({ logger: this.subLogger, namespace: "DeltaManager" }),
|
|
1901
1989
|
() => this.activeConnection(),
|
|
1902
1990
|
(props: IConnectionManagerFactoryArgs) =>
|
|
1903
1991
|
new ConnectionManager(
|
|
@@ -1905,7 +1993,7 @@ export class Container
|
|
|
1905
1993
|
() => this.isDirty,
|
|
1906
1994
|
this.client,
|
|
1907
1995
|
this._canReconnect,
|
|
1908
|
-
|
|
1996
|
+
createChildLogger({ logger: this.subLogger, namespace: "ConnectionManager" }),
|
|
1909
1997
|
props,
|
|
1910
1998
|
),
|
|
1911
1999
|
);
|
|
@@ -1967,7 +2055,7 @@ export class Container
|
|
|
1967
2055
|
|
|
1968
2056
|
private async attachDeltaManagerOpHandler(
|
|
1969
2057
|
attributes: IDocumentAttributes,
|
|
1970
|
-
prefetchType?: "cached" | "all" | "none",
|
|
2058
|
+
prefetchType?: "sequenceNumber" | "cached" | "all" | "none",
|
|
1971
2059
|
) {
|
|
1972
2060
|
return this._deltaManager.attachOpHandler(
|
|
1973
2061
|
attributes.minimumSequenceNumber,
|
|
@@ -2004,7 +2092,7 @@ export class Container
|
|
|
2004
2092
|
if (value === ConnectionState.Connected) {
|
|
2005
2093
|
durationFromDisconnected =
|
|
2006
2094
|
time - this.connectionTransitionTimes[ConnectionState.Disconnected];
|
|
2007
|
-
durationFromDisconnected =
|
|
2095
|
+
durationFromDisconnected = formatTick(durationFromDisconnected);
|
|
2008
2096
|
} else if (value === ConnectionState.CatchingUp) {
|
|
2009
2097
|
// This info is of most interesting while Catching Up.
|
|
2010
2098
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
@@ -2038,6 +2126,7 @@ export class Container
|
|
|
2038
2126
|
: undefined,
|
|
2039
2127
|
checkpointSequenceNumber,
|
|
2040
2128
|
quorumSize: this._protocolHandler?.quorum.getMembers().size,
|
|
2129
|
+
isDirty: this.isDirty,
|
|
2041
2130
|
...this._deltaManager.connectionProps,
|
|
2042
2131
|
},
|
|
2043
2132
|
error,
|
|
@@ -2061,26 +2150,11 @@ export class Container
|
|
|
2061
2150
|
}
|
|
2062
2151
|
const state = this.connectionState === ConnectionState.Connected;
|
|
2063
2152
|
|
|
2064
|
-
const logOpsOnReconnect: boolean =
|
|
2065
|
-
this.connectionState === ConnectionState.Connected &&
|
|
2066
|
-
!this.firstConnection &&
|
|
2067
|
-
this.connectionMode === "write";
|
|
2068
|
-
if (logOpsOnReconnect) {
|
|
2069
|
-
this.messageCountAfterDisconnection = 0;
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
2153
|
// Both protocol and context should not be undefined if we got so far.
|
|
2073
2154
|
|
|
2074
2155
|
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
2075
2156
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
2076
2157
|
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
2077
|
-
|
|
2078
|
-
if (logOpsOnReconnect) {
|
|
2079
|
-
this.mc.logger.sendTelemetryEvent({
|
|
2080
|
-
eventName: "OpsSentOnReconnect",
|
|
2081
|
-
count: this.messageCountAfterDisconnection,
|
|
2082
|
-
});
|
|
2083
|
-
}
|
|
2084
2158
|
}
|
|
2085
2159
|
|
|
2086
2160
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
@@ -2156,7 +2230,6 @@ export class Container
|
|
|
2156
2230
|
return -1;
|
|
2157
2231
|
}
|
|
2158
2232
|
|
|
2159
|
-
this.messageCountAfterDisconnection += 1;
|
|
2160
2233
|
this.noopHeuristic?.notifyMessageSent();
|
|
2161
2234
|
return this._deltaManager.submit(
|
|
2162
2235
|
type,
|
|
@@ -2280,17 +2353,7 @@ export class Container
|
|
|
2280
2353
|
return { snapshot, versionId: version?.id };
|
|
2281
2354
|
}
|
|
2282
2355
|
|
|
2283
|
-
private async
|
|
2284
|
-
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
2285
|
-
if (codeDetails === undefined) {
|
|
2286
|
-
throw new Error("pkg should be provided in create flow!!");
|
|
2287
|
-
}
|
|
2288
|
-
|
|
2289
|
-
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
private async instantiateContext(
|
|
2293
|
-
existing: boolean,
|
|
2356
|
+
private async instantiateRuntime(
|
|
2294
2357
|
codeDetails: IFluidCodeDetails,
|
|
2295
2358
|
snapshot: ISnapshotTree | undefined,
|
|
2296
2359
|
pendingLocalState?: unknown,
|
|
@@ -2327,6 +2390,8 @@ export class Container
|
|
|
2327
2390
|
(this.protocolHandler.quorum.get("code") ??
|
|
2328
2391
|
this.protocolHandler.quorum.get("code2")) as IFluidCodeDetails | undefined;
|
|
2329
2392
|
|
|
2393
|
+
const existing = snapshot !== undefined;
|
|
2394
|
+
|
|
2330
2395
|
const context = new ContainerContext(
|
|
2331
2396
|
this.options,
|
|
2332
2397
|
this.scope,
|
|
@@ -2348,9 +2413,7 @@ export class Container
|
|
|
2348
2413
|
(error?: ICriticalContainerError) => this.close(error),
|
|
2349
2414
|
this.updateDirtyContainerState,
|
|
2350
2415
|
this.getAbsoluteUrl,
|
|
2351
|
-
() => this.resolvedUrl?.id,
|
|
2352
2416
|
() => this.clientId,
|
|
2353
|
-
() => this._deltaManager.serviceConfiguration,
|
|
2354
2417
|
() => this.attachState,
|
|
2355
2418
|
() => this.connected,
|
|
2356
2419
|
getSpecifiedCodeDetails,
|
|
@@ -2359,9 +2422,6 @@ export class Container
|
|
|
2359
2422
|
this.subLogger,
|
|
2360
2423
|
pendingLocalState,
|
|
2361
2424
|
);
|
|
2362
|
-
this._lifecycleEvents.once("disposed", () => {
|
|
2363
|
-
context.dispose();
|
|
2364
|
-
});
|
|
2365
2425
|
|
|
2366
2426
|
this._runtime = await PerformanceEvent.timedExecAsync(
|
|
2367
2427
|
this.subLogger,
|
|
@@ -2371,8 +2431,6 @@ export class Container
|
|
|
2371
2431
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
2372
2432
|
|
|
2373
2433
|
this._loadedCodeDetails = codeDetails;
|
|
2374
|
-
|
|
2375
|
-
this.emit("contextChanged", codeDetails);
|
|
2376
2434
|
}
|
|
2377
2435
|
|
|
2378
2436
|
private readonly updateDirtyContainerState = (dirty: boolean) => {
|
|
@@ -2415,7 +2473,7 @@ export interface IContainerExperimental extends IContainer {
|
|
|
2415
2473
|
* @experimental misuse of this API can result in duplicate op submission and potential document corruption
|
|
2416
2474
|
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2417
2475
|
*/
|
|
2418
|
-
getPendingLocalState?(): string
|
|
2476
|
+
getPendingLocalState?(): Promise<string>;
|
|
2419
2477
|
|
|
2420
2478
|
/**
|
|
2421
2479
|
* Closes the container and returns serialized local state intended to be
|
|
@@ -2423,5 +2481,5 @@ export interface IContainerExperimental extends IContainer {
|
|
|
2423
2481
|
* @experimental
|
|
2424
2482
|
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2425
2483
|
*/
|
|
2426
|
-
closeAndGetPendingLocalState(): string
|
|
2484
|
+
closeAndGetPendingLocalState?(): Promise<string>;
|
|
2427
2485
|
}
|
package/src/containerContext.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
import { FluidObject } from "@fluidframework/core-interfaces";
|
|
19
19
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
20
20
|
import {
|
|
21
|
-
IClientConfiguration,
|
|
22
21
|
IClientDetails,
|
|
23
22
|
IDocumentMessage,
|
|
24
23
|
IQuorumClients,
|
|
@@ -46,13 +45,6 @@ export class ContainerContext implements IContainerContext {
|
|
|
46
45
|
return this._getClientId();
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
/**
|
|
50
|
-
* DISCLAIMER: this id is only for telemetry purposes. Not suitable for any other usages.
|
|
51
|
-
*/
|
|
52
|
-
public get id(): string {
|
|
53
|
-
return this._getContainerDiagnosticId() ?? "";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
48
|
/**
|
|
57
49
|
* When true, ops are free to flow
|
|
58
50
|
* When false, ops should be kept as pending or rejected
|
|
@@ -61,16 +53,6 @@ export class ContainerContext implements IContainerContext {
|
|
|
61
53
|
return this._getConnected();
|
|
62
54
|
}
|
|
63
55
|
|
|
64
|
-
public get serviceConfiguration(): IClientConfiguration | undefined {
|
|
65
|
-
return this._getServiceConfiguration();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private _disposed = false;
|
|
69
|
-
|
|
70
|
-
public get disposed() {
|
|
71
|
-
return this._disposed;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
56
|
constructor(
|
|
75
57
|
public readonly options: ILoaderOptions,
|
|
76
58
|
public readonly scope: FluidObject,
|
|
@@ -101,9 +83,7 @@ export class ContainerContext implements IContainerContext {
|
|
|
101
83
|
public readonly closeFn: (error?: ICriticalContainerError) => void,
|
|
102
84
|
public readonly updateDirtyContainerState: (dirty: boolean) => void,
|
|
103
85
|
public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>,
|
|
104
|
-
private readonly _getContainerDiagnosticId: () => string | undefined,
|
|
105
86
|
private readonly _getClientId: () => string | undefined,
|
|
106
|
-
private readonly _getServiceConfiguration: () => IClientConfiguration | undefined,
|
|
107
87
|
private readonly _getAttachState: () => AttachState,
|
|
108
88
|
private readonly _getConnected: () => boolean,
|
|
109
89
|
public readonly getSpecifiedCodeDetails: () => IFluidCodeDetails | undefined,
|
|
@@ -113,10 +93,6 @@ export class ContainerContext implements IContainerContext {
|
|
|
113
93
|
public readonly pendingLocalState?: unknown,
|
|
114
94
|
) {}
|
|
115
95
|
|
|
116
|
-
public dispose(error?: Error): void {
|
|
117
|
-
this._disposed = true;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
96
|
public getLoadedFromVersion(): IVersion | undefined {
|
|
121
97
|
return this._version;
|
|
122
98
|
}
|