@fluidframework/container-loader 2.0.0-internal.5.4.0 → 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 +81 -0
- package/dist/connectionManager.d.ts +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +24 -25
- 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 +10 -5
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +160 -99
- 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.js +2 -3
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +16 -3
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +62 -24
- 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 +57 -42
- 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/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/connectionManager.d.ts +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +25 -26
- 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 +10 -5
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +159 -98
- 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.js +2 -3
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +16 -3
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +62 -24
- 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 +57 -42
- 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/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 +11 -11
- package/src/connectionManager.ts +7 -3
- package/src/connectionStateHandler.ts +3 -2
- package/src/container.ts +113 -28
- package/src/containerContext.ts +0 -24
- package/src/contracts.ts +16 -5
- package/src/deltaManager.ts +22 -5
- package/src/loader.ts +37 -23
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +0 -1
- package/src/utils.ts +29 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-loader",
|
|
3
|
-
"version": "2.0.0-internal.
|
|
3
|
+
"version": "2.0.0-internal.6.0.0",
|
|
4
4
|
"description": "Fluid container loader",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
39
39
|
"@fluidframework/common-utils": "^1.1.1",
|
|
40
|
-
"@fluidframework/container-definitions": ">=2.0.0-internal.
|
|
41
|
-
"@fluidframework/container-utils": ">=2.0.0-internal.
|
|
42
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.
|
|
43
|
-
"@fluidframework/driver-definitions": ">=2.0.0-internal.
|
|
44
|
-
"@fluidframework/driver-utils": ">=2.0.0-internal.
|
|
45
|
-
"@fluidframework/protocol-base": "^0.
|
|
40
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
41
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
42
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
43
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
44
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
45
|
+
"@fluidframework/protocol-base": "^1.0.0",
|
|
46
46
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
47
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-internal.
|
|
47
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
48
48
|
"debug": "^4.1.1",
|
|
49
49
|
"double-ended-queue": "^2.1.0-0",
|
|
50
50
|
"events": "^3.1.0",
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"uuid": "^8.3.1"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@fluid-internal/test-loader-utils": ">=2.0.0-internal.
|
|
56
|
+
"@fluid-internal/test-loader-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
57
57
|
"@fluid-tools/build-cli": "^0.21.0",
|
|
58
|
-
"@fluidframework/build-common": "^
|
|
58
|
+
"@fluidframework/build-common": "^2.0.0",
|
|
59
59
|
"@fluidframework/build-tools": "^0.21.0",
|
|
60
60
|
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.5.2.0",
|
|
61
61
|
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
62
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.
|
|
62
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
|
|
63
63
|
"@microsoft/api-extractor": "^7.34.4",
|
|
64
64
|
"@types/double-ended-queue": "^2.1.0",
|
|
65
65
|
"@types/events": "^3.0.0",
|
package/src/connectionManager.ts
CHANGED
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
8
8
|
import {
|
|
9
|
+
ICriticalContainerError,
|
|
9
10
|
IDeltaQueue,
|
|
10
11
|
ReadOnlyInfo,
|
|
11
|
-
IConnectionDetailsInternal,
|
|
12
|
-
ICriticalContainerError,
|
|
13
12
|
} from "@fluidframework/container-definitions";
|
|
14
13
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
15
14
|
import {
|
|
@@ -44,7 +43,12 @@ import {
|
|
|
44
43
|
ISequencedDocumentSystemMessage,
|
|
45
44
|
} from "@fluidframework/protocol-definitions";
|
|
46
45
|
import { ITelemetryLoggerExt, formatTick, normalizeError } from "@fluidframework/telemetry-utils";
|
|
47
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
ReconnectMode,
|
|
48
|
+
IConnectionManager,
|
|
49
|
+
IConnectionManagerFactoryArgs,
|
|
50
|
+
IConnectionDetailsInternal,
|
|
51
|
+
} from "./contracts";
|
|
48
52
|
import { DeltaQueue } from "./deltaQueue";
|
|
49
53
|
import { SignalType } from "./protocol";
|
|
50
54
|
import { isDeltaStreamConnectionForbiddenError } from "./utils";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { assert, Timer } from "@fluidframework/common-utils";
|
|
8
|
-
import {
|
|
8
|
+
import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
9
9
|
import { IAnyDriverError } from "@fluidframework/driver-definitions";
|
|
10
10
|
import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
|
|
11
11
|
import {
|
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
PerformanceEvent,
|
|
14
14
|
loggerToMonitoringContext,
|
|
15
15
|
} from "@fluidframework/telemetry-utils";
|
|
16
|
-
import { ConnectionState } from "./connectionState";
|
|
17
16
|
import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor";
|
|
17
|
+
import { ConnectionState } from "./connectionState";
|
|
18
|
+
import { IConnectionDetailsInternal } from "./contracts";
|
|
18
19
|
import { IProtocolHandler } from "./protocol";
|
|
19
20
|
|
|
20
21
|
// Based on recent data, it looks like majority of cases where we get stuck are due to really slow or
|
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,
|
|
@@ -99,7 +97,12 @@ import {
|
|
|
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,7 +113,11 @@ import {
|
|
|
110
113
|
ISerializableBlobContents,
|
|
111
114
|
} from "./containerStorageAdapter";
|
|
112
115
|
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
|
|
113
|
-
import {
|
|
116
|
+
import {
|
|
117
|
+
combineAppAndProtocolSummary,
|
|
118
|
+
getProtocolSnapshotTree,
|
|
119
|
+
getSnapshotTreeFromSerializedContainer,
|
|
120
|
+
} from "./utils";
|
|
114
121
|
import { initQuorumValuesFromCodeDetails } from "./quorum";
|
|
115
122
|
import { NoopHeuristic } from "./noopHeuristic";
|
|
116
123
|
import { ConnectionManager } from "./connectionManager";
|
|
@@ -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
|
})
|
|
@@ -1054,16 +1067,21 @@ export class Container
|
|
|
1054
1067
|
}
|
|
1055
1068
|
}
|
|
1056
1069
|
|
|
1057
|
-
public closeAndGetPendingLocalState(): string {
|
|
1070
|
+
public async closeAndGetPendingLocalState(): Promise<string> {
|
|
1058
1071
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
1059
1072
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
1060
1073
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
1061
|
-
|
|
1074
|
+
this.disconnect(); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
1075
|
+
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
1062
1076
|
this.close();
|
|
1063
1077
|
return pendingState;
|
|
1064
1078
|
}
|
|
1065
1079
|
|
|
1066
|
-
public getPendingLocalState(): string {
|
|
1080
|
+
public async getPendingLocalState(): Promise<string> {
|
|
1081
|
+
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
private async getPendingLocalStateCore(props: { notifyImminentClosure: boolean }) {
|
|
1067
1085
|
if (!this.offlineLoadEnabled) {
|
|
1068
1086
|
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
1069
1087
|
}
|
|
@@ -1082,8 +1100,9 @@ export class Container
|
|
|
1082
1100
|
);
|
|
1083
1101
|
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
1084
1102
|
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
1103
|
+
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
1085
1104
|
const pendingState: IPendingContainerState = {
|
|
1086
|
-
pendingRuntimeState
|
|
1105
|
+
pendingRuntimeState,
|
|
1087
1106
|
baseSnapshot: this.baseSnapshot,
|
|
1088
1107
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1089
1108
|
savedOps: this.savedOps,
|
|
@@ -1468,7 +1487,8 @@ export class Container
|
|
|
1468
1487
|
specifiedVersion: string | undefined,
|
|
1469
1488
|
loadMode: IContainerLoadMode,
|
|
1470
1489
|
resolvedUrl: IResolvedUrl,
|
|
1471
|
-
pendingLocalState
|
|
1490
|
+
pendingLocalState: IPendingContainerState | undefined,
|
|
1491
|
+
loadToSequenceNumber: number | undefined,
|
|
1472
1492
|
) {
|
|
1473
1493
|
this.service = await this.serviceFactory.createDocumentService(
|
|
1474
1494
|
resolvedUrl,
|
|
@@ -1542,6 +1562,57 @@ export class Container
|
|
|
1542
1562
|
|
|
1543
1563
|
let opsBeforeReturnP: Promise<void> | undefined;
|
|
1544
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
|
+
|
|
1545
1616
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
1546
1617
|
// Kick off any ops fetching if required.
|
|
1547
1618
|
switch (loadMode.opsBeforeReturn) {
|
|
@@ -1553,6 +1624,9 @@ export class Container
|
|
|
1553
1624
|
loadMode.deltaConnection !== "none" ? "all" : "none",
|
|
1554
1625
|
);
|
|
1555
1626
|
break;
|
|
1627
|
+
case "sequenceNumber":
|
|
1628
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
|
|
1629
|
+
break;
|
|
1556
1630
|
case "cached":
|
|
1557
1631
|
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
1558
1632
|
break;
|
|
@@ -1636,6 +1710,22 @@ export class Container
|
|
|
1636
1710
|
}
|
|
1637
1711
|
}
|
|
1638
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
|
+
|
|
1639
1729
|
// Safety net: static version of Container.load() should have learned about it through "closed" handler.
|
|
1640
1730
|
// But if that did not happen for some reason, fail load for sure.
|
|
1641
1731
|
// Otherwise we can get into situations where container is closed and does not try to connect to ordering
|
|
@@ -1965,7 +2055,7 @@ export class Container
|
|
|
1965
2055
|
|
|
1966
2056
|
private async attachDeltaManagerOpHandler(
|
|
1967
2057
|
attributes: IDocumentAttributes,
|
|
1968
|
-
prefetchType?: "cached" | "all" | "none",
|
|
2058
|
+
prefetchType?: "sequenceNumber" | "cached" | "all" | "none",
|
|
1969
2059
|
) {
|
|
1970
2060
|
return this._deltaManager.attachOpHandler(
|
|
1971
2061
|
attributes.minimumSequenceNumber,
|
|
@@ -2323,9 +2413,7 @@ export class Container
|
|
|
2323
2413
|
(error?: ICriticalContainerError) => this.close(error),
|
|
2324
2414
|
this.updateDirtyContainerState,
|
|
2325
2415
|
this.getAbsoluteUrl,
|
|
2326
|
-
() => this.resolvedUrl?.id,
|
|
2327
2416
|
() => this.clientId,
|
|
2328
|
-
() => this._deltaManager.serviceConfiguration,
|
|
2329
2417
|
() => this.attachState,
|
|
2330
2418
|
() => this.connected,
|
|
2331
2419
|
getSpecifiedCodeDetails,
|
|
@@ -2334,9 +2422,6 @@ export class Container
|
|
|
2334
2422
|
this.subLogger,
|
|
2335
2423
|
pendingLocalState,
|
|
2336
2424
|
);
|
|
2337
|
-
this._lifecycleEvents.once("disposed", () => {
|
|
2338
|
-
context.dispose();
|
|
2339
|
-
});
|
|
2340
2425
|
|
|
2341
2426
|
this._runtime = await PerformanceEvent.timedExecAsync(
|
|
2342
2427
|
this.subLogger,
|
|
@@ -2388,7 +2473,7 @@ export interface IContainerExperimental extends IContainer {
|
|
|
2388
2473
|
* @experimental misuse of this API can result in duplicate op submission and potential document corruption
|
|
2389
2474
|
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2390
2475
|
*/
|
|
2391
|
-
getPendingLocalState?(): string
|
|
2476
|
+
getPendingLocalState?(): Promise<string>;
|
|
2392
2477
|
|
|
2393
2478
|
/**
|
|
2394
2479
|
* Closes the container and returns serialized local state intended to be
|
|
@@ -2396,5 +2481,5 @@ export interface IContainerExperimental extends IContainer {
|
|
|
2396
2481
|
* @experimental
|
|
2397
2482
|
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2398
2483
|
*/
|
|
2399
|
-
closeAndGetPendingLocalState(): string
|
|
2484
|
+
closeAndGetPendingLocalState?(): Promise<string>;
|
|
2400
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
|
}
|
package/src/contracts.ts
CHANGED
|
@@ -5,19 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryProperties } from "@fluidframework/core-interfaces";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
ReadOnlyInfo,
|
|
10
|
-
IConnectionDetailsInternal,
|
|
8
|
+
IConnectionDetails,
|
|
11
9
|
ICriticalContainerError,
|
|
10
|
+
IDeltaQueue,
|
|
12
11
|
IFluidCodeDetails,
|
|
13
12
|
isFluidPackage,
|
|
13
|
+
ReadOnlyInfo,
|
|
14
14
|
} from "@fluidframework/container-definitions";
|
|
15
15
|
import {
|
|
16
16
|
ConnectionMode,
|
|
17
|
-
IDocumentMessage,
|
|
18
|
-
ISequencedDocumentMessage,
|
|
19
17
|
IClientConfiguration,
|
|
20
18
|
IClientDetails,
|
|
19
|
+
IDocumentMessage,
|
|
20
|
+
ISequencedDocumentMessage,
|
|
21
|
+
ISignalClient,
|
|
21
22
|
ISignalMessage,
|
|
22
23
|
} from "@fluidframework/protocol-definitions";
|
|
23
24
|
import { IAnyDriverError, IContainerPackageInfo } from "@fluidframework/driver-definitions";
|
|
@@ -28,6 +29,16 @@ export enum ReconnectMode {
|
|
|
28
29
|
Enabled = "Enabled",
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Internal version of IConnectionDetails with props are only exposed internally
|
|
34
|
+
*/
|
|
35
|
+
export interface IConnectionDetailsInternal extends IConnectionDetails {
|
|
36
|
+
mode: ConnectionMode;
|
|
37
|
+
version: string;
|
|
38
|
+
initialClients: ISignalClient[];
|
|
39
|
+
reason: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
/**
|
|
32
43
|
* Connection manager (implements this interface) is responsible for maintaining connection
|
|
33
44
|
* to relay service.
|
package/src/deltaManager.ts
CHANGED
|
@@ -7,13 +7,11 @@ import { v4 as uuid } from "uuid";
|
|
|
7
7
|
import { IEventProvider } from "@fluidframework/common-definitions";
|
|
8
8
|
import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
ICriticalContainerError,
|
|
11
11
|
IDeltaManager,
|
|
12
12
|
IDeltaManagerEvents,
|
|
13
13
|
IDeltaQueue,
|
|
14
|
-
ICriticalContainerError,
|
|
15
14
|
IThrottlingWarning,
|
|
16
|
-
IConnectionDetailsInternal,
|
|
17
15
|
} from "@fluidframework/container-definitions";
|
|
18
16
|
import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
19
17
|
import {
|
|
@@ -44,7 +42,11 @@ import {
|
|
|
44
42
|
DataProcessingError,
|
|
45
43
|
UsageError,
|
|
46
44
|
} from "@fluidframework/container-utils";
|
|
47
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
IConnectionDetailsInternal,
|
|
47
|
+
IConnectionManager,
|
|
48
|
+
IConnectionManagerFactoryArgs,
|
|
49
|
+
} from "./contracts";
|
|
48
50
|
import { DeltaQueue } from "./deltaQueue";
|
|
49
51
|
import { OnlyValidTermValue } from "./protocol";
|
|
50
52
|
|
|
@@ -73,6 +75,21 @@ interface IBatchMetadata {
|
|
|
73
75
|
batch?: boolean;
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Interface used to define a strategy for handling incoming delta messages
|
|
80
|
+
*/
|
|
81
|
+
export interface IDeltaHandlerStrategy {
|
|
82
|
+
/**
|
|
83
|
+
* Processes the message.
|
|
84
|
+
*/
|
|
85
|
+
process: (message: ISequencedDocumentMessage) => void;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Processes the signal.
|
|
89
|
+
*/
|
|
90
|
+
processSignal: (message: ISignalMessage) => void;
|
|
91
|
+
}
|
|
92
|
+
|
|
76
93
|
/**
|
|
77
94
|
* Determines if message was sent by client, not service
|
|
78
95
|
*/
|
|
@@ -495,7 +512,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
495
512
|
minSequenceNumber: number,
|
|
496
513
|
sequenceNumber: number,
|
|
497
514
|
handler: IDeltaHandlerStrategy,
|
|
498
|
-
prefetchType: "cached" | "all" | "none" = "none",
|
|
515
|
+
prefetchType: "sequenceNumber" | "cached" | "all" | "none" = "none",
|
|
499
516
|
) {
|
|
500
517
|
this.initSequenceNumber = sequenceNumber;
|
|
501
518
|
this.lastProcessedSequenceNumber = sequenceNumber;
|
package/src/loader.ts
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
IResolvedUrl,
|
|
38
38
|
IUrlResolver,
|
|
39
39
|
} from "@fluidframework/driver-definitions";
|
|
40
|
-
import {
|
|
40
|
+
import { UsageError } from "@fluidframework/container-utils";
|
|
41
41
|
import { Container, IPendingContainerState } from "./container";
|
|
42
42
|
import { IParsedUrl, parseUrl } from "./utils";
|
|
43
43
|
import { pkgVersion } from "./packageVersion";
|
|
@@ -45,11 +45,7 @@ import { ProtocolHandlerBuilder } from "./protocol";
|
|
|
45
45
|
import { DebugLogger } from "./debugLogger";
|
|
46
46
|
|
|
47
47
|
function canUseCache(request: IRequest): boolean {
|
|
48
|
-
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return request.headers[LoaderHeader.cache] !== false;
|
|
48
|
+
return request.headers?.[LoaderHeader.cache] === true;
|
|
53
49
|
}
|
|
54
50
|
|
|
55
51
|
function ensureResolvedUrlDefined(
|
|
@@ -68,6 +64,9 @@ export class RelativeLoader implements ILoader {
|
|
|
68
64
|
private readonly loader: ILoader | undefined,
|
|
69
65
|
) {}
|
|
70
66
|
|
|
67
|
+
/**
|
|
68
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
|
|
69
|
+
*/
|
|
71
70
|
public get IFluidRouter(): IFluidRouter {
|
|
72
71
|
return this;
|
|
73
72
|
}
|
|
@@ -99,6 +98,9 @@ export class RelativeLoader implements ILoader {
|
|
|
99
98
|
return this.loader.resolve(request);
|
|
100
99
|
}
|
|
101
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
|
|
103
|
+
*/
|
|
102
104
|
public async request(request: IRequest): Promise<IResponse> {
|
|
103
105
|
if (request.url.startsWith("/")) {
|
|
104
106
|
const container = await this.resolve(request);
|
|
@@ -348,6 +350,9 @@ export class Loader implements IHostLoader {
|
|
|
348
350
|
});
|
|
349
351
|
}
|
|
350
352
|
|
|
353
|
+
/**
|
|
354
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
|
|
355
|
+
*/
|
|
351
356
|
public get IFluidRouter(): IFluidRouter {
|
|
352
357
|
return this;
|
|
353
358
|
}
|
|
@@ -383,6 +388,9 @@ export class Loader implements IHostLoader {
|
|
|
383
388
|
});
|
|
384
389
|
}
|
|
385
390
|
|
|
391
|
+
/**
|
|
392
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
|
|
393
|
+
*/
|
|
386
394
|
public async request(request: IRequest): Promise<IResponse> {
|
|
387
395
|
return PerformanceEvent.timedExecAsync(
|
|
388
396
|
this.mc.logger,
|
|
@@ -456,11 +464,29 @@ export class Loader implements IHostLoader {
|
|
|
456
464
|
// If set in both query string and headers, use query string. Also write the value from the query string into the header either way.
|
|
457
465
|
request.headers[LoaderHeader.version] =
|
|
458
466
|
parsed.version ?? request.headers[LoaderHeader.version];
|
|
467
|
+
const cacheHeader = request.headers[LoaderHeader.cache];
|
|
459
468
|
const canCache =
|
|
460
|
-
|
|
461
|
-
|
|
469
|
+
// Take header value if present, else use ILoaderOptions.cache value
|
|
470
|
+
(cacheHeader !== undefined ? cacheHeader === true : this.cachingEnabled) &&
|
|
462
471
|
pendingLocalState === undefined;
|
|
463
|
-
const fromSequenceNumber = request.headers[LoaderHeader.sequenceNumber]
|
|
472
|
+
const fromSequenceNumber = request.headers[LoaderHeader.sequenceNumber] as
|
|
473
|
+
| number
|
|
474
|
+
| undefined;
|
|
475
|
+
const opsBeforeReturn = request.headers[LoaderHeader.loadMode]?.opsBeforeReturn as
|
|
476
|
+
| string
|
|
477
|
+
| undefined;
|
|
478
|
+
|
|
479
|
+
if (
|
|
480
|
+
opsBeforeReturn === "sequenceNumber" &&
|
|
481
|
+
(fromSequenceNumber === undefined || fromSequenceNumber < 0)
|
|
482
|
+
) {
|
|
483
|
+
// If opsBeforeReturn is set to "sequenceNumber", then fromSequenceNumber should be set to a non-negative integer.
|
|
484
|
+
throw new UsageError("sequenceNumber must be set to a non-negative integer");
|
|
485
|
+
} else if (opsBeforeReturn !== "sequenceNumber" && fromSequenceNumber !== undefined) {
|
|
486
|
+
// If opsBeforeReturn is not set to "sequenceNumber", then fromSequenceNumber should be undefined (default value).
|
|
487
|
+
// In this case, we should throw an error since opsBeforeReturn is not explicitly set to "sequenceNumber".
|
|
488
|
+
throw new UsageError('opsBeforeReturn must be set to "sequenceNumber"');
|
|
489
|
+
}
|
|
464
490
|
|
|
465
491
|
let container: Container;
|
|
466
492
|
if (canCache) {
|
|
@@ -477,24 +503,11 @@ export class Loader implements IHostLoader {
|
|
|
477
503
|
container = await this.loadContainer(request, resolvedAsFluid, pendingLocalState);
|
|
478
504
|
}
|
|
479
505
|
|
|
480
|
-
if (container.deltaManager.lastSequenceNumber <= fromSequenceNumber) {
|
|
481
|
-
await new Promise<void>((resolve, reject) => {
|
|
482
|
-
function opHandler(message: ISequencedDocumentMessage) {
|
|
483
|
-
if (message.sequenceNumber > fromSequenceNumber) {
|
|
484
|
-
resolve();
|
|
485
|
-
container.removeListener("op", opHandler);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
container.on("op", opHandler);
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
506
|
return { container, parsed };
|
|
494
507
|
}
|
|
495
508
|
|
|
496
509
|
private get cachingEnabled() {
|
|
497
|
-
return this.services.options.cache
|
|
510
|
+
return this.services.options.cache === true;
|
|
498
511
|
}
|
|
499
512
|
|
|
500
513
|
private async loadContainer(
|
|
@@ -508,6 +521,7 @@ export class Loader implements IHostLoader {
|
|
|
508
521
|
version: request.headers?.[LoaderHeader.version] ?? undefined,
|
|
509
522
|
loadMode: request.headers?.[LoaderHeader.loadMode],
|
|
510
523
|
pendingLocalState,
|
|
524
|
+
loadToSequenceNumber: request.headers?.[LoaderHeader.sequenceNumber],
|
|
511
525
|
},
|
|
512
526
|
{
|
|
513
527
|
canReconnect: request.headers?.[LoaderHeader.reconnect],
|
package/src/packageVersion.ts
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -49,7 +49,6 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
|
|
|
49
49
|
super(
|
|
50
50
|
attributes.minimumSequenceNumber,
|
|
51
51
|
attributes.sequenceNumber,
|
|
52
|
-
OnlyValidTermValue,
|
|
53
52
|
quorumSnapshot.members,
|
|
54
53
|
quorumSnapshot.proposals,
|
|
55
54
|
quorumSnapshot.values,
|