@fluidframework/container-loader 2.0.0-rc.1.0.4 → 2.0.0-rc.2.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/{.eslintrc.js → .eslintrc.cjs} +5 -6
- package/{.mocharc.js → .mocharc.cjs} +1 -1
- package/CHANGELOG.md +48 -0
- package/README.md +3 -3
- package/{api-extractor-esm.json → api-extractor-cjs.json} +5 -1
- package/api-extractor-lint.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/container-loader.api.md +2 -2
- package/dist/attachment.d.ts +115 -0
- package/dist/attachment.d.ts.map +1 -0
- package/dist/attachment.js +83 -0
- package/dist/attachment.js.map +1 -0
- package/dist/audience.d.ts +9 -4
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +10 -4
- package/dist/audience.js.map +1 -1
- package/dist/connectionManager.d.ts +3 -3
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +17 -18
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.d.ts +1 -0
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionState.js +1 -0
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +7 -7
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +32 -32
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container-loader-alpha.d.ts +2 -1
- package/dist/container-loader-beta.d.ts +3 -0
- package/dist/container-loader-public.d.ts +3 -0
- package/dist/container-loader-untrimmed.d.ts +5 -5
- package/dist/container.d.ts +29 -27
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +219 -284
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +3 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +2 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +5 -6
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +14 -21
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +3 -3
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +5 -5
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +6 -6
- package/dist/deltaManager.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +3 -3
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +13 -17
- package/dist/loader.js.map +1 -1
- package/dist/location-redirection-utilities/index.d.ts +1 -1
- package/dist/location-redirection-utilities/index.d.ts.map +1 -1
- package/dist/location-redirection-utilities/index.js +3 -3
- package/dist/location-redirection-utilities/index.js.map +1 -1
- package/dist/package.json +3 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +1 -3
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +2 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +8 -6
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/serializedStateManager.d.ts +44 -0
- package/dist/serializedStateManager.d.ts.map +1 -0
- package/dist/serializedStateManager.js +149 -0
- package/dist/serializedStateManager.js.map +1 -0
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/utils.d.ts +16 -11
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +107 -32
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.ts +115 -0
- package/lib/attachment.d.ts.map +1 -0
- package/lib/attachment.js +79 -0
- package/lib/attachment.js.map +1 -0
- package/lib/{audience.d.mts → audience.d.ts} +14 -5
- package/lib/audience.d.ts.map +1 -0
- package/lib/{audience.mjs → audience.js} +14 -4
- package/lib/audience.js.map +1 -0
- package/lib/{catchUpMonitor.d.mts → catchUpMonitor.d.ts} +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -0
- package/lib/{catchUpMonitor.mjs → catchUpMonitor.js} +1 -1
- package/lib/catchUpMonitor.js.map +1 -0
- package/lib/{connectionManager.d.mts → connectionManager.d.ts} +4 -4
- package/lib/connectionManager.d.ts.map +1 -0
- package/lib/{connectionManager.mjs → connectionManager.js} +7 -10
- package/lib/connectionManager.js.map +1 -0
- package/lib/{connectionState.d.mts → connectionState.d.ts} +2 -1
- package/lib/connectionState.d.ts.map +1 -0
- package/lib/{connectionState.mjs → connectionState.js} +2 -1
- package/lib/connectionState.js.map +1 -0
- package/lib/{connectionStateHandler.d.mts → connectionStateHandler.d.ts} +8 -8
- package/lib/connectionStateHandler.d.ts.map +1 -0
- package/lib/{connectionStateHandler.mjs → connectionStateHandler.js} +3 -3
- package/lib/connectionStateHandler.js.map +1 -0
- package/lib/{container-loader-alpha.d.mts → container-loader-alpha.d.ts} +2 -1
- package/lib/{container-loader-beta.d.mts → container-loader-beta.d.ts} +3 -0
- package/lib/{container-loader-public.d.mts → container-loader-public.d.ts} +3 -0
- package/lib/{container-loader-untrimmed.d.mts → container-loader-untrimmed.d.ts} +5 -5
- package/lib/{container.d.mts → container.d.ts} +30 -28
- package/lib/container.d.ts.map +1 -0
- package/lib/{container.mjs → container.js} +179 -247
- package/lib/container.js.map +1 -0
- package/lib/{containerContext.d.mts → containerContext.d.ts} +4 -3
- package/lib/containerContext.d.ts.map +1 -0
- package/lib/{containerContext.mjs → containerContext.js} +3 -2
- package/lib/containerContext.js.map +1 -0
- package/lib/{containerStorageAdapter.d.mts → containerStorageAdapter.d.ts} +6 -7
- package/lib/containerStorageAdapter.d.ts.map +1 -0
- package/lib/{containerStorageAdapter.mjs → containerStorageAdapter.js} +13 -20
- package/lib/containerStorageAdapter.js.map +1 -0
- package/lib/{contracts.d.mts → contracts.d.ts} +4 -4
- package/lib/contracts.d.ts.map +1 -0
- package/lib/{contracts.mjs → contracts.js} +1 -1
- package/lib/contracts.js.map +1 -0
- package/lib/{debugLogger.d.mts → debugLogger.d.ts} +1 -1
- package/lib/debugLogger.d.ts.map +1 -0
- package/lib/{debugLogger.mjs → debugLogger.js} +2 -1
- package/lib/debugLogger.js.map +1 -0
- package/lib/{deltaManager.d.mts → deltaManager.d.ts} +6 -6
- package/lib/deltaManager.d.ts.map +1 -0
- package/lib/{deltaManager.mjs → deltaManager.js} +4 -4
- package/lib/deltaManager.js.map +1 -0
- package/lib/{deltaQueue.d.mts → deltaQueue.d.ts} +1 -1
- package/lib/deltaQueue.d.ts.map +1 -0
- package/lib/{deltaQueue.mjs → deltaQueue.js} +1 -1
- package/lib/deltaQueue.js.map +1 -0
- package/lib/{disposal.d.mts → disposal.d.ts} +1 -1
- package/lib/disposal.d.ts.map +1 -0
- package/lib/{disposal.mjs → disposal.js} +1 -1
- package/lib/disposal.js.map +1 -0
- package/lib/{error.d.mts → error.d.ts} +1 -1
- package/lib/error.d.ts.map +1 -0
- package/lib/{error.mjs → error.js} +1 -1
- package/lib/error.js.map +1 -0
- package/lib/{index.d.mts → index.d.ts} +7 -7
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +10 -0
- package/lib/index.js.map +1 -0
- package/lib/{loader.d.mts → loader.d.ts} +4 -4
- package/lib/loader.d.ts.map +1 -0
- package/lib/{loader.mjs → loader.js} +7 -11
- package/lib/loader.js.map +1 -0
- package/lib/location-redirection-utilities/{index.mjs → index.d.ts} +2 -2
- package/lib/location-redirection-utilities/index.d.ts.map +1 -0
- package/lib/location-redirection-utilities/{index.d.mts → index.js} +2 -2
- package/lib/location-redirection-utilities/index.js.map +1 -0
- package/lib/location-redirection-utilities/{resolveWithLocationRedirection.d.mts → resolveWithLocationRedirection.d.ts} +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
- package/lib/location-redirection-utilities/{resolveWithLocationRedirection.mjs → resolveWithLocationRedirection.js} +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -0
- package/lib/{noopHeuristic.d.mts → noopHeuristic.d.ts} +1 -1
- package/lib/noopHeuristic.d.ts.map +1 -0
- package/lib/{noopHeuristic.mjs → noopHeuristic.js} +1 -1
- package/lib/noopHeuristic.js.map +1 -0
- package/lib/{packageVersion.d.mts → packageVersion.d.ts} +2 -2
- package/lib/packageVersion.d.ts.map +1 -0
- package/lib/{packageVersion.mjs → packageVersion.js} +2 -2
- package/lib/packageVersion.js.map +1 -0
- package/lib/{protocol.d.mts → protocol.d.ts} +1 -1
- package/lib/protocol.d.ts.map +1 -0
- package/lib/{protocol.mjs → protocol.js} +1 -1
- package/lib/protocol.js.map +1 -0
- package/lib/{protocolTreeDocumentStorageService.d.mts → protocolTreeDocumentStorageService.d.ts} +2 -2
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -0
- package/lib/{protocolTreeDocumentStorageService.mjs → protocolTreeDocumentStorageService.js} +2 -4
- package/lib/protocolTreeDocumentStorageService.js.map +1 -0
- package/lib/{quorum.d.mts → quorum.d.ts} +5 -1
- package/lib/quorum.d.ts.map +1 -0
- package/lib/{quorum.mjs → quorum.js} +1 -1
- package/lib/quorum.js.map +1 -0
- package/lib/{retriableDocumentStorageService.d.mts → retriableDocumentStorageService.d.ts} +3 -3
- package/lib/retriableDocumentStorageService.d.ts.map +1 -0
- package/lib/{retriableDocumentStorageService.mjs → retriableDocumentStorageService.js} +10 -8
- package/lib/retriableDocumentStorageService.js.map +1 -0
- package/lib/serializedStateManager.d.ts +44 -0
- package/lib/serializedStateManager.d.ts.map +1 -0
- package/lib/serializedStateManager.js +145 -0
- package/lib/serializedStateManager.js.map +1 -0
- package/lib/test/attachment.spec.js +380 -0
- package/lib/test/attachment.spec.js.map +1 -0
- package/lib/test/catchUpMonitor.spec.js +88 -0
- package/lib/test/catchUpMonitor.spec.js.map +1 -0
- package/lib/test/connectionManager.spec.js +201 -0
- package/lib/test/connectionManager.spec.js.map +1 -0
- package/lib/test/connectionStateHandler.spec.js +555 -0
- package/lib/test/connectionStateHandler.spec.js.map +1 -0
- package/lib/test/container.spec.js +64 -0
- package/lib/test/container.spec.js.map +1 -0
- package/lib/test/deltaManager.spec.js +405 -0
- package/lib/test/deltaManager.spec.js.map +1 -0
- package/lib/test/loader.spec.js +212 -0
- package/lib/test/loader.spec.js.map +1 -0
- package/lib/test/locationRedirectionTests.spec.js +44 -0
- package/lib/test/locationRedirectionTests.spec.js.map +1 -0
- package/lib/test/serializedStateManager.spec.js +148 -0
- package/lib/test/serializedStateManager.spec.js.map +1 -0
- package/lib/test/snapshotConversionTest.spec.js +79 -0
- package/lib/test/snapshotConversionTest.spec.js.map +1 -0
- package/lib/test/types/validateContainerLoaderPrevious.generated.js +38 -0
- package/lib/test/types/validateContainerLoaderPrevious.generated.js.map +1 -0
- package/lib/test/utils.spec.js +31 -0
- package/lib/test/utils.spec.js.map +1 -0
- package/lib/{utils.d.mts → utils.d.ts} +17 -12
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +206 -0
- package/lib/utils.js.map +1 -0
- package/package.json +63 -63
- package/src/attachment.ts +222 -0
- package/src/audience.ts +9 -3
- package/src/connectionManager.ts +9 -11
- package/src/connectionState.ts +1 -0
- package/src/connectionStateHandler.ts +8 -7
- package/src/container.ts +297 -323
- package/src/containerContext.ts +2 -1
- package/src/containerStorageAdapter.ts +21 -26
- package/src/contracts.ts +3 -3
- package/src/debugLogger.ts +2 -2
- package/src/deltaManager.ts +8 -8
- package/src/error.ts +2 -2
- package/src/index.ts +6 -6
- package/src/loader.ts +9 -13
- package/src/location-redirection-utilities/index.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/protocolTreeDocumentStorageService.ts +1 -3
- package/src/retriableDocumentStorageService.ts +18 -8
- package/src/serializedStateManager.ts +217 -0
- package/src/utils.ts +140 -43
- package/tsconfig.cjs.json +7 -0
- package/tsconfig.json +2 -5
- package/lib/audience.d.mts.map +0 -1
- package/lib/audience.mjs.map +0 -1
- package/lib/catchUpMonitor.d.mts.map +0 -1
- package/lib/catchUpMonitor.mjs.map +0 -1
- package/lib/connectionManager.d.mts.map +0 -1
- package/lib/connectionManager.mjs.map +0 -1
- package/lib/connectionState.d.mts.map +0 -1
- package/lib/connectionState.mjs.map +0 -1
- package/lib/connectionStateHandler.d.mts.map +0 -1
- package/lib/connectionStateHandler.mjs.map +0 -1
- package/lib/container.d.mts.map +0 -1
- package/lib/container.mjs.map +0 -1
- package/lib/containerContext.d.mts.map +0 -1
- package/lib/containerContext.mjs.map +0 -1
- package/lib/containerStorageAdapter.d.mts.map +0 -1
- package/lib/containerStorageAdapter.mjs.map +0 -1
- package/lib/contracts.d.mts.map +0 -1
- package/lib/contracts.mjs.map +0 -1
- package/lib/debugLogger.d.mts.map +0 -1
- package/lib/debugLogger.mjs.map +0 -1
- package/lib/deltaManager.d.mts.map +0 -1
- package/lib/deltaManager.mjs.map +0 -1
- package/lib/deltaQueue.d.mts.map +0 -1
- package/lib/deltaQueue.mjs.map +0 -1
- package/lib/disposal.d.mts.map +0 -1
- package/lib/disposal.mjs.map +0 -1
- package/lib/error.d.mts.map +0 -1
- package/lib/error.mjs.map +0 -1
- package/lib/index.d.mts.map +0 -1
- package/lib/index.mjs +0 -10
- package/lib/index.mjs.map +0 -1
- package/lib/loader.d.mts.map +0 -1
- package/lib/loader.mjs.map +0 -1
- package/lib/location-redirection-utilities/index.d.mts.map +0 -1
- package/lib/location-redirection-utilities/index.mjs.map +0 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.mts.map +0 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.mjs.map +0 -1
- package/lib/noopHeuristic.d.mts.map +0 -1
- package/lib/noopHeuristic.mjs.map +0 -1
- package/lib/packageVersion.d.mts.map +0 -1
- package/lib/packageVersion.mjs.map +0 -1
- package/lib/protocol.d.mts.map +0 -1
- package/lib/protocol.mjs.map +0 -1
- package/lib/protocolTreeDocumentStorageService.d.mts.map +0 -1
- package/lib/protocolTreeDocumentStorageService.mjs.map +0 -1
- package/lib/quorum.d.mts.map +0 -1
- package/lib/quorum.mjs.map +0 -1
- package/lib/retriableDocumentStorageService.d.mts.map +0 -1
- package/lib/retriableDocumentStorageService.mjs.map +0 -1
- package/lib/utils.d.mts.map +0 -1
- package/lib/utils.mjs +0 -133
- package/lib/utils.mjs.map +0 -1
package/src/container.ts
CHANGED
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { v4 as uuid } from "uuid";
|
|
7
|
-
import { assert, unreachableCase } from "@fluidframework/core-utils";
|
|
7
|
+
import { assert, unreachableCase, isPromiseLike } from "@fluidframework/core-utils";
|
|
8
8
|
import { TypedEventEmitter, performance } from "@fluid-internal/client-utils";
|
|
9
9
|
import {
|
|
10
10
|
IEvent,
|
|
11
|
-
|
|
12
|
-
TelemetryEventCategory,
|
|
13
|
-
IRequest,
|
|
11
|
+
ITelemetryBaseProperties,
|
|
14
12
|
FluidObject,
|
|
15
13
|
LogLevel,
|
|
14
|
+
IRequest,
|
|
16
15
|
} from "@fluidframework/core-interfaces";
|
|
17
16
|
import {
|
|
18
17
|
AttachState,
|
|
@@ -41,6 +40,7 @@ import {
|
|
|
41
40
|
IDocumentServiceFactory,
|
|
42
41
|
IDocumentStorageService,
|
|
43
42
|
IResolvedUrl,
|
|
43
|
+
ISnapshot,
|
|
44
44
|
IThrottlingWarning,
|
|
45
45
|
IUrlResolver,
|
|
46
46
|
} from "@fluidframework/driver-definitions";
|
|
@@ -48,9 +48,10 @@ import {
|
|
|
48
48
|
readAndParse,
|
|
49
49
|
OnlineStatus,
|
|
50
50
|
isOnline,
|
|
51
|
-
runWithRetry,
|
|
52
51
|
isCombinedAppAndProtocolSummary,
|
|
53
52
|
MessageType2,
|
|
53
|
+
isInstanceOfISnapshot,
|
|
54
|
+
runWithRetry,
|
|
54
55
|
} from "@fluidframework/driver-utils";
|
|
55
56
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
56
57
|
import {
|
|
@@ -86,41 +87,45 @@ import {
|
|
|
86
87
|
formatTick,
|
|
87
88
|
GenericError,
|
|
88
89
|
UsageError,
|
|
90
|
+
IFluidErrorBase,
|
|
91
|
+
type TelemetryEventCategory,
|
|
89
92
|
} from "@fluidframework/telemetry-utils";
|
|
90
|
-
import
|
|
91
|
-
import {
|
|
93
|
+
import structuredClone from "@ungap/structured-clone";
|
|
94
|
+
import { Audience } from "./audience.js";
|
|
95
|
+
import { ContainerContext } from "./containerContext.js";
|
|
92
96
|
import {
|
|
93
97
|
ReconnectMode,
|
|
94
98
|
IConnectionManagerFactoryArgs,
|
|
95
99
|
getPackageName,
|
|
96
100
|
IConnectionDetailsInternal,
|
|
97
101
|
IConnectionStateChangeReason,
|
|
98
|
-
} from "./contracts";
|
|
99
|
-
import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
100
|
-
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
|
|
101
|
-
import { pkgVersion } from "./packageVersion";
|
|
102
|
-
import {
|
|
103
|
-
|
|
104
|
-
getBlobContentsFromTree,
|
|
105
|
-
getBlobContentsFromTreeWithBlobContents,
|
|
106
|
-
ISerializableBlobContents,
|
|
107
|
-
} from "./containerStorageAdapter";
|
|
108
|
-
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
|
|
102
|
+
} from "./contracts.js";
|
|
103
|
+
import { DeltaManager, IConnectionArgs } from "./deltaManager.js";
|
|
104
|
+
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader.js";
|
|
105
|
+
import { pkgVersion } from "./packageVersion.js";
|
|
106
|
+
import { ContainerStorageAdapter, ISerializableBlobContents } from "./containerStorageAdapter.js";
|
|
107
|
+
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler.js";
|
|
109
108
|
import {
|
|
109
|
+
ISnapshotTreeWithBlobContents,
|
|
110
110
|
combineAppAndProtocolSummary,
|
|
111
111
|
getProtocolSnapshotTree,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
import {
|
|
112
|
+
getSnapshotTreeAndBlobsFromSerializedContainer,
|
|
113
|
+
combineSnapshotTreeAndSnapshotBlobs,
|
|
114
|
+
getDetachedContainerStateFromSerializedContainer,
|
|
115
|
+
runSingle,
|
|
116
|
+
} from "./utils.js";
|
|
117
|
+
import { initQuorumValuesFromCodeDetails } from "./quorum.js";
|
|
118
|
+
import { NoopHeuristic } from "./noopHeuristic.js";
|
|
119
|
+
import { ConnectionManager } from "./connectionManager.js";
|
|
120
|
+
import { ConnectionState } from "./connectionState.js";
|
|
118
121
|
import {
|
|
119
122
|
IProtocolHandler,
|
|
120
123
|
ProtocolHandler,
|
|
121
124
|
ProtocolHandlerBuilder,
|
|
122
125
|
protocolHandlerShouldProcessSignal,
|
|
123
|
-
} from "./protocol";
|
|
126
|
+
} from "./protocol.js";
|
|
127
|
+
import { AttachProcessProps, AttachmentData, runRetriableAttachProcess } from "./attachment.js";
|
|
128
|
+
import { SerializedStateManager } from "./serializedStateManager.js";
|
|
124
129
|
|
|
125
130
|
const detachedContainerRefSeqNumber = 0;
|
|
126
131
|
|
|
@@ -129,8 +134,6 @@ const savedContainerEvent = "saved";
|
|
|
129
134
|
|
|
130
135
|
const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
131
136
|
|
|
132
|
-
const hasBlobsSummaryTree = ".hasAttachmentBlobs";
|
|
133
|
-
|
|
134
137
|
/**
|
|
135
138
|
* @internal
|
|
136
139
|
*/
|
|
@@ -312,9 +315,7 @@ export async function waitContainerToCatchUp(container: IContainer) {
|
|
|
312
315
|
});
|
|
313
316
|
}
|
|
314
317
|
|
|
315
|
-
const getCodeProposal =
|
|
316
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
317
|
-
(quorum: IQuorumProposals) => quorum.get("code") ?? quorum.get("code2");
|
|
318
|
+
const getCodeProposal = (quorum: IQuorumProposals) => quorum.get("code") ?? quorum.get("code2");
|
|
318
319
|
|
|
319
320
|
/**
|
|
320
321
|
* Helper function to report to telemetry cases where operation takes longer than expected (200ms)
|
|
@@ -325,7 +326,7 @@ const getCodeProposal =
|
|
|
325
326
|
export async function ReportIfTooLong(
|
|
326
327
|
logger: ITelemetryLoggerExt,
|
|
327
328
|
eventName: string,
|
|
328
|
-
action: () => Promise<
|
|
329
|
+
action: () => Promise<ITelemetryBaseProperties>,
|
|
329
330
|
) {
|
|
330
331
|
const event = PerformanceEvent.start(logger, { eventName });
|
|
331
332
|
const props = await action();
|
|
@@ -340,6 +341,7 @@ export async function ReportIfTooLong(
|
|
|
340
341
|
* @internal
|
|
341
342
|
*/
|
|
342
343
|
export interface IPendingContainerState {
|
|
344
|
+
attached: true;
|
|
343
345
|
pendingRuntimeState: unknown;
|
|
344
346
|
/**
|
|
345
347
|
* Snapshot from which container initially loaded.
|
|
@@ -360,6 +362,19 @@ export interface IPendingContainerState {
|
|
|
360
362
|
clientId?: string;
|
|
361
363
|
}
|
|
362
364
|
|
|
365
|
+
/**
|
|
366
|
+
* State saved by a container in detached state, to be used to load a new instance
|
|
367
|
+
* of the container to the same state (rehydrate)
|
|
368
|
+
* @internal
|
|
369
|
+
*/
|
|
370
|
+
export interface IPendingDetachedContainerState {
|
|
371
|
+
attached: false;
|
|
372
|
+
baseSnapshot: ISnapshotTree;
|
|
373
|
+
snapshotBlobs: ISerializableBlobContents;
|
|
374
|
+
hasAttachmentBlobs: boolean;
|
|
375
|
+
pendingRuntimeState?: unknown;
|
|
376
|
+
}
|
|
377
|
+
|
|
363
378
|
const summarizerClientType = "summarizer";
|
|
364
379
|
|
|
365
380
|
interface IContainerLifecycleEvents extends IEvent {
|
|
@@ -373,7 +388,6 @@ export class Container
|
|
|
373
388
|
{
|
|
374
389
|
/**
|
|
375
390
|
* Load an existing container.
|
|
376
|
-
* @internal
|
|
377
391
|
*/
|
|
378
392
|
public static async load(
|
|
379
393
|
loadProps: IContainerLoadProps,
|
|
@@ -471,12 +485,9 @@ export class Container
|
|
|
471
485
|
container.mc.logger,
|
|
472
486
|
{ eventName: "RehydrateDetachedFromSnapshot" },
|
|
473
487
|
async (_event) => {
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
488
|
+
const detachedContainerState: IPendingDetachedContainerState =
|
|
489
|
+
getDetachedContainerStateFromSerializedContainer(snapshot);
|
|
490
|
+
await container.rehydrateDetachedFromSnapshot(detachedContainerState);
|
|
480
491
|
return container;
|
|
481
492
|
},
|
|
482
493
|
{ start: true, end: true, cancel: "generic" },
|
|
@@ -501,7 +512,6 @@ export class Container
|
|
|
501
512
|
|
|
502
513
|
/**
|
|
503
514
|
* Used by the RelativeLoader to spawn a new Container for the same document. Used to create the summarizing client.
|
|
504
|
-
* @internal
|
|
505
515
|
*/
|
|
506
516
|
public readonly clone: (
|
|
507
517
|
loadProps: IContainerLoadProps,
|
|
@@ -551,8 +561,6 @@ export class Container
|
|
|
551
561
|
return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
|
|
552
562
|
}
|
|
553
563
|
|
|
554
|
-
private _attachState = AttachState.Detached;
|
|
555
|
-
|
|
556
564
|
private readonly storageAdapter: ContainerStorageAdapter;
|
|
557
565
|
|
|
558
566
|
private readonly _deltaManager: DeltaManager<ConnectionManager>;
|
|
@@ -578,17 +586,16 @@ export class Container
|
|
|
578
586
|
private firstConnection = true;
|
|
579
587
|
private readonly connectionTransitionTimes: number[] = [];
|
|
580
588
|
private _loadedFromVersion: IVersion | undefined;
|
|
581
|
-
private attachStarted = false;
|
|
582
589
|
private _dirtyContainer = false;
|
|
583
|
-
private
|
|
584
|
-
private
|
|
585
|
-
private baseSnapshotBlobs?: ISerializableBlobContents;
|
|
590
|
+
private attachmentData: AttachmentData = { state: AttachState.Detached };
|
|
591
|
+
private readonly serializedStateManager: SerializedStateManager;
|
|
586
592
|
private readonly _containerId: string;
|
|
587
593
|
|
|
588
594
|
private lastVisible: number | undefined;
|
|
589
595
|
private readonly visibilityEventHandler: (() => void) | undefined;
|
|
590
596
|
private readonly connectionStateHandler: IConnectionStateHandler;
|
|
591
597
|
private readonly clientsWhoShouldHaveLeft = new Set<string>();
|
|
598
|
+
private _containerMetadata: Readonly<Record<string, string>> = {};
|
|
592
599
|
|
|
593
600
|
private setAutoReconnectTime = performance.now();
|
|
594
601
|
|
|
@@ -617,6 +624,10 @@ export class Container
|
|
|
617
624
|
return this._deltaManager.readOnlyInfo;
|
|
618
625
|
}
|
|
619
626
|
|
|
627
|
+
public get containerMetadata(): Record<string, string> {
|
|
628
|
+
return this._containerMetadata;
|
|
629
|
+
}
|
|
630
|
+
|
|
620
631
|
/**
|
|
621
632
|
* Sends signal to runtime (and data stores) to be read-only.
|
|
622
633
|
* Hosts may have read only views, indicating to data stores that no edits are allowed.
|
|
@@ -660,12 +671,8 @@ export class Container
|
|
|
660
671
|
return this._clientId;
|
|
661
672
|
}
|
|
662
673
|
|
|
663
|
-
private get
|
|
664
|
-
|
|
665
|
-
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ??
|
|
666
|
-
this.options?.enableOfflineLoad === true;
|
|
667
|
-
// summarizer will not have any pending state we want to save
|
|
668
|
-
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
674
|
+
private get isInteractiveClient(): boolean {
|
|
675
|
+
return this.deltaManager.clientDetails.capabilities.interactive;
|
|
669
676
|
}
|
|
670
677
|
|
|
671
678
|
/**
|
|
@@ -734,9 +741,6 @@ export class Container
|
|
|
734
741
|
|
|
735
742
|
private readonly _lifecycleEvents = new TypedEventEmitter<IContainerLifecycleEvents>();
|
|
736
743
|
|
|
737
|
-
/**
|
|
738
|
-
* @internal
|
|
739
|
-
*/
|
|
740
744
|
constructor(
|
|
741
745
|
createProps: IContainerCreateProps,
|
|
742
746
|
loadProps?: Pick<IContainerLoadProps, "pendingLocalState">,
|
|
@@ -809,7 +813,7 @@ export class Container
|
|
|
809
813
|
|
|
810
814
|
this.client = Container.setupClient(
|
|
811
815
|
this._containerId,
|
|
812
|
-
|
|
816
|
+
options.client,
|
|
813
817
|
this.clientDetailsOverride,
|
|
814
818
|
);
|
|
815
819
|
|
|
@@ -829,7 +833,7 @@ export class Container
|
|
|
829
833
|
clientType, // Differentiating summarizer container from main container
|
|
830
834
|
containerId: this._containerId,
|
|
831
835
|
docId: () => this.resolvedUrl?.id,
|
|
832
|
-
containerAttachState: () => this.
|
|
836
|
+
containerAttachState: () => this.attachState,
|
|
833
837
|
containerLifecycleState: () => this._lifecycleState,
|
|
834
838
|
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
835
839
|
serializedContainer: pendingLocalState !== undefined,
|
|
@@ -886,7 +890,7 @@ export class Container
|
|
|
886
890
|
logConnectionIssue: (
|
|
887
891
|
eventName: string,
|
|
888
892
|
category: TelemetryEventCategory,
|
|
889
|
-
details?:
|
|
893
|
+
details?: ITelemetryBaseProperties,
|
|
890
894
|
) => {
|
|
891
895
|
const mode = this.connectionMode;
|
|
892
896
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
@@ -952,6 +956,17 @@ export class Container
|
|
|
952
956
|
forceEnableSummarizeProtocolTree,
|
|
953
957
|
);
|
|
954
958
|
|
|
959
|
+
const offlineLoadEnabled =
|
|
960
|
+
(this.isInteractiveClient &&
|
|
961
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) ??
|
|
962
|
+
options.enableOfflineLoad === true;
|
|
963
|
+
this.serializedStateManager = new SerializedStateManager(
|
|
964
|
+
pendingLocalState,
|
|
965
|
+
this.subLogger,
|
|
966
|
+
this.storageAdapter,
|
|
967
|
+
offlineLoadEnabled,
|
|
968
|
+
);
|
|
969
|
+
|
|
955
970
|
const isDomAvailable =
|
|
956
971
|
typeof document === "object" &&
|
|
957
972
|
document !== null &&
|
|
@@ -1030,6 +1045,11 @@ export class Container
|
|
|
1030
1045
|
|
|
1031
1046
|
this._lifecycleState = "closing";
|
|
1032
1047
|
|
|
1048
|
+
// Back-compat for Old driver
|
|
1049
|
+
if (this.service?.off !== undefined) {
|
|
1050
|
+
this.service?.off("metadataUpdate", this.metadataUpdateHandler);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1033
1053
|
this._protocolHandler?.close();
|
|
1034
1054
|
|
|
1035
1055
|
this.connectionStateHandler.dispose();
|
|
@@ -1127,202 +1147,175 @@ export class Container
|
|
|
1127
1147
|
}
|
|
1128
1148
|
|
|
1129
1149
|
private async getPendingLocalStateCore(props: IGetPendingLocalStateProps) {
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
}
|
|
1149
|
-
assert(
|
|
1150
|
-
this.attachState === AttachState.Attached,
|
|
1151
|
-
0x0d1 /* "Container should be attached before close" */,
|
|
1152
|
-
);
|
|
1153
|
-
assert(
|
|
1154
|
-
this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid",
|
|
1155
|
-
0x0d2 /* "resolved url should be valid Fluid url" */,
|
|
1156
|
-
);
|
|
1157
|
-
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
1158
|
-
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
1159
|
-
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
1160
|
-
const pendingState: IPendingContainerState = {
|
|
1161
|
-
pendingRuntimeState,
|
|
1162
|
-
baseSnapshot: this.baseSnapshot,
|
|
1163
|
-
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1164
|
-
savedOps: this.savedOps,
|
|
1165
|
-
url: this.resolvedUrl.url,
|
|
1166
|
-
// no need to save this if there is no pending runtime state
|
|
1167
|
-
clientId: pendingRuntimeState !== undefined ? this.clientId : undefined,
|
|
1168
|
-
};
|
|
1169
|
-
|
|
1170
|
-
return JSON.stringify(pendingState);
|
|
1171
|
-
},
|
|
1150
|
+
if (this.closed || this._disposed) {
|
|
1151
|
+
throw new UsageError(
|
|
1152
|
+
"Pending state cannot be retried if the container is closed or disposed",
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
assert(
|
|
1156
|
+
this.attachmentData.state === AttachState.Attached,
|
|
1157
|
+
0x0d1 /* "Container should be attached before close" */,
|
|
1158
|
+
);
|
|
1159
|
+
assert(
|
|
1160
|
+
this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid",
|
|
1161
|
+
0x0d2 /* "resolved url should be valid Fluid url" */,
|
|
1162
|
+
);
|
|
1163
|
+
const pendingState = await this.serializedStateManager.getPendingLocalStateCore(
|
|
1164
|
+
props,
|
|
1165
|
+
this.clientId,
|
|
1166
|
+
this.runtime,
|
|
1167
|
+
this.resolvedUrl,
|
|
1172
1168
|
);
|
|
1169
|
+
return pendingState;
|
|
1173
1170
|
}
|
|
1174
1171
|
|
|
1175
1172
|
public get attachState(): AttachState {
|
|
1176
|
-
return this.
|
|
1173
|
+
return this.attachmentData.state;
|
|
1177
1174
|
}
|
|
1178
1175
|
|
|
1179
1176
|
public serialize(): string {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
0x0d3 /* "Should only be called in detached container" */,
|
|
1183
|
-
);
|
|
1184
|
-
|
|
1185
|
-
const appSummary: ISummaryTree = this.runtime.createSummary();
|
|
1186
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
1187
|
-
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1188
|
-
|
|
1189
|
-
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
1190
|
-
combinedSummary.tree[hasBlobsSummaryTree] = {
|
|
1191
|
-
type: SummaryType.Blob,
|
|
1192
|
-
content: "true",
|
|
1193
|
-
};
|
|
1177
|
+
if (this.attachmentData.state === AttachState.Attached || this.closed) {
|
|
1178
|
+
throw new UsageError("Container must not be attached or closed.");
|
|
1194
1179
|
}
|
|
1195
|
-
return JSON.stringify(combinedSummary);
|
|
1196
|
-
}
|
|
1197
1180
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
attachProps?: { deltaConnection?: "none" | "delayed" },
|
|
1201
|
-
): Promise<void> {
|
|
1202
|
-
await PerformanceEvent.timedExecAsync(
|
|
1203
|
-
this.mc.logger,
|
|
1204
|
-
{ eventName: "Attach" },
|
|
1205
|
-
async () => {
|
|
1206
|
-
if (this._lifecycleState !== "loaded") {
|
|
1207
|
-
// pre-0.58 error message: containerNotValidForAttach
|
|
1208
|
-
throw new UsageError(
|
|
1209
|
-
`The Container is not in a valid state for attach [${this._lifecycleState}]`,
|
|
1210
|
-
);
|
|
1211
|
-
}
|
|
1181
|
+
const attachingData =
|
|
1182
|
+
this.attachmentData.state === AttachState.Attaching ? this.attachmentData : undefined;
|
|
1212
1183
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
)
|
|
1218
|
-
|
|
1184
|
+
const combinedSummary =
|
|
1185
|
+
attachingData?.summary ??
|
|
1186
|
+
combineAppAndProtocolSummary(
|
|
1187
|
+
this.runtime.createSummary(),
|
|
1188
|
+
this.captureProtocolSummary(),
|
|
1189
|
+
);
|
|
1219
1190
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
1191
|
+
const { tree: snapshot, blobs } =
|
|
1192
|
+
getSnapshotTreeAndBlobsFromSerializedContainer(combinedSummary);
|
|
1223
1193
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
0x0d6 /* "Inbound queue should be empty when attaching" */,
|
|
1228
|
-
);
|
|
1194
|
+
const pendingRuntimeState =
|
|
1195
|
+
attachingData !== undefined ? this.runtime.getPendingLocalState() : undefined;
|
|
1196
|
+
assert(!isPromiseLike(pendingRuntimeState), 0x8e3 /* should not be a promise */);
|
|
1229
1197
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
assert(
|
|
1257
|
-
this.client.details.type !== summarizerClientType &&
|
|
1258
|
-
createNewResolvedUrl !== undefined,
|
|
1259
|
-
0x2c4 /* "client should not be summarizer before container is created" */,
|
|
1260
|
-
);
|
|
1261
|
-
this.service = await runWithRetry(
|
|
1262
|
-
async () =>
|
|
1263
|
-
this.serviceFactory.createContainer(
|
|
1264
|
-
summary,
|
|
1265
|
-
createNewResolvedUrl,
|
|
1266
|
-
this.subLogger,
|
|
1267
|
-
false, // clientIsSummarizer
|
|
1268
|
-
),
|
|
1269
|
-
"containerAttach",
|
|
1270
|
-
this.mc.logger,
|
|
1271
|
-
{
|
|
1272
|
-
cancel: this._deltaManager.closeAbortController.signal,
|
|
1273
|
-
}, // progress
|
|
1198
|
+
const detachedContainerState: IPendingDetachedContainerState = {
|
|
1199
|
+
attached: false,
|
|
1200
|
+
baseSnapshot: snapshot,
|
|
1201
|
+
snapshotBlobs: blobs,
|
|
1202
|
+
pendingRuntimeState,
|
|
1203
|
+
hasAttachmentBlobs: !!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
|
|
1204
|
+
};
|
|
1205
|
+
return JSON.stringify(detachedContainerState);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
public readonly attach = runSingle(
|
|
1209
|
+
async (
|
|
1210
|
+
request: IRequest,
|
|
1211
|
+
attachProps?: { deltaConnection?: "none" | "delayed" },
|
|
1212
|
+
): Promise<void> => {
|
|
1213
|
+
await PerformanceEvent.timedExecAsync(
|
|
1214
|
+
this.mc.logger,
|
|
1215
|
+
{ eventName: "Attach" },
|
|
1216
|
+
async () => {
|
|
1217
|
+
if (
|
|
1218
|
+
this._lifecycleState !== "loaded" ||
|
|
1219
|
+
this.attachmentData.state === AttachState.Attached
|
|
1220
|
+
) {
|
|
1221
|
+
// pre-0.58 error message: containerNotValidForAttach
|
|
1222
|
+
throw new UsageError(
|
|
1223
|
+
`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`,
|
|
1274
1224
|
);
|
|
1275
1225
|
}
|
|
1276
|
-
this.storageAdapter.connectToService(this.service);
|
|
1277
1226
|
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1227
|
+
const normalizeErrorAndClose = (error: unknown): IFluidErrorBase => {
|
|
1228
|
+
const newError = normalizeError(error);
|
|
1229
|
+
this.close(newError);
|
|
1230
|
+
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
1231
|
+
newError.addTelemetryProperties({
|
|
1232
|
+
resolvedUrl: this.service?.resolvedUrl?.url,
|
|
1233
|
+
});
|
|
1234
|
+
return newError;
|
|
1235
|
+
};
|
|
1284
1236
|
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1237
|
+
const setAttachmentData: AttachProcessProps["setAttachmentData"] = (
|
|
1238
|
+
attachmentData,
|
|
1239
|
+
) => {
|
|
1240
|
+
const previousState = this.attachmentData.state;
|
|
1241
|
+
this.attachmentData = attachmentData;
|
|
1242
|
+
const state = this.attachmentData.state;
|
|
1243
|
+
if (state !== previousState && state !== AttachState.Detached) {
|
|
1244
|
+
try {
|
|
1245
|
+
this.runtime.setAttachState(state);
|
|
1246
|
+
this.emit(state.toLocaleLowerCase());
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
throw normalizeErrorAndClose(error);
|
|
1297
1249
|
}
|
|
1298
1250
|
}
|
|
1251
|
+
};
|
|
1299
1252
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1253
|
+
const createAttachmentSummary: AttachProcessProps["createAttachmentSummary"] = (
|
|
1254
|
+
redirectTable?: Map<string, string>,
|
|
1255
|
+
) => {
|
|
1256
|
+
try {
|
|
1257
|
+
assert(
|
|
1258
|
+
this.deltaManager.inbound.length === 0,
|
|
1259
|
+
0x0d6 /* "Inbound queue should be empty when attaching" */,
|
|
1260
|
+
);
|
|
1261
|
+
return combineAppAndProtocolSummary(
|
|
1262
|
+
this.runtime.createSummary(redirectTable),
|
|
1263
|
+
this.captureProtocolSummary(),
|
|
1264
|
+
);
|
|
1265
|
+
} catch (error) {
|
|
1266
|
+
throw normalizeErrorAndClose(error);
|
|
1313
1267
|
}
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
const createOrGetStorageService: AttachProcessProps["createOrGetStorageService"] =
|
|
1271
|
+
async (summary) => {
|
|
1272
|
+
// Actually go and create the resolved document
|
|
1273
|
+
if (this.service === undefined) {
|
|
1274
|
+
const createNewResolvedUrl =
|
|
1275
|
+
await this.urlResolver.resolve(request);
|
|
1276
|
+
assert(
|
|
1277
|
+
this.client.details.type !== summarizerClientType &&
|
|
1278
|
+
createNewResolvedUrl !== undefined,
|
|
1279
|
+
0x2c4 /* "client should not be summarizer before container is created" */,
|
|
1280
|
+
);
|
|
1281
|
+
this.service = await runWithRetry(
|
|
1282
|
+
async () =>
|
|
1283
|
+
this.serviceFactory.createContainer(
|
|
1284
|
+
summary,
|
|
1285
|
+
createNewResolvedUrl,
|
|
1286
|
+
this.subLogger,
|
|
1287
|
+
false, // clientIsSummarizer
|
|
1288
|
+
),
|
|
1289
|
+
"containerAttach",
|
|
1290
|
+
this.mc.logger,
|
|
1291
|
+
{
|
|
1292
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
1293
|
+
}, // progress
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
this.storageAdapter.connectToService(this.service);
|
|
1297
|
+
return this.storageAdapter;
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1300
|
+
let attachP = runRetriableAttachProcess({
|
|
1301
|
+
initialAttachmentData: this.attachmentData,
|
|
1302
|
+
offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
|
|
1303
|
+
detachedBlobStorage: this.detachedBlobStorage,
|
|
1304
|
+
setAttachmentData,
|
|
1305
|
+
createAttachmentSummary,
|
|
1306
|
+
createOrGetStorageService,
|
|
1307
|
+
});
|
|
1314
1308
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1309
|
+
// only enable the new behavior if the config is set
|
|
1310
|
+
if (
|
|
1311
|
+
this.mc.config.getBoolean("Fluid.Container.RetryOnAttachFailure") !== true
|
|
1312
|
+
) {
|
|
1313
|
+
attachP = attachP.catch((error) => {
|
|
1314
|
+
throw normalizeErrorAndClose(error);
|
|
1319
1315
|
});
|
|
1320
1316
|
}
|
|
1321
1317
|
|
|
1322
|
-
this.
|
|
1323
|
-
this.runtime.setAttachState(AttachState.Attached);
|
|
1324
|
-
this.emit("attached");
|
|
1325
|
-
|
|
1318
|
+
this.serializedStateManager.setSnapshot(await attachP);
|
|
1326
1319
|
if (!this.closed) {
|
|
1327
1320
|
this.handleDeltaConnectionArg(
|
|
1328
1321
|
{
|
|
@@ -1332,17 +1325,11 @@ export class Container
|
|
|
1332
1325
|
attachProps?.deltaConnection,
|
|
1333
1326
|
);
|
|
1334
1327
|
}
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
throw newError;
|
|
1341
|
-
}
|
|
1342
|
-
},
|
|
1343
|
-
{ start: true, end: true, cancel: "generic" },
|
|
1344
|
-
);
|
|
1345
|
-
}
|
|
1328
|
+
},
|
|
1329
|
+
{ start: true, end: true, cancel: "generic" },
|
|
1330
|
+
);
|
|
1331
|
+
},
|
|
1332
|
+
);
|
|
1346
1333
|
|
|
1347
1334
|
private setAutoReconnectInternal(mode: ReconnectMode, reason: IConnectionStateChangeReason) {
|
|
1348
1335
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
@@ -1369,7 +1356,7 @@ export class Container
|
|
|
1369
1356
|
public connect() {
|
|
1370
1357
|
if (this.closed) {
|
|
1371
1358
|
throw new UsageError(`The Container is closed and cannot be connected`);
|
|
1372
|
-
} else if (this.
|
|
1359
|
+
} else if (this.attachState !== AttachState.Attached) {
|
|
1373
1360
|
throw new UsageError(`The Container is not attached and cannot be connected`);
|
|
1374
1361
|
} else if (!this.connected) {
|
|
1375
1362
|
// Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
|
|
@@ -1385,7 +1372,7 @@ export class Container
|
|
|
1385
1372
|
private connectInternal(args: IConnectionArgs) {
|
|
1386
1373
|
assert(!this.closed, 0x2c5 /* "Attempting to connect() a closed Container" */);
|
|
1387
1374
|
assert(
|
|
1388
|
-
this.
|
|
1375
|
+
this.attachState === AttachState.Attached,
|
|
1389
1376
|
0x2c6 /* "Attempting to connect() a container that is not attached" */,
|
|
1390
1377
|
);
|
|
1391
1378
|
|
|
@@ -1520,11 +1507,6 @@ export class Container
|
|
|
1520
1507
|
return true;
|
|
1521
1508
|
}
|
|
1522
1509
|
|
|
1523
|
-
private async getVersion(version: string | null): Promise<IVersion | undefined> {
|
|
1524
|
-
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
1525
|
-
return versions[0];
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
1510
|
private connectToDeltaStream(args: IConnectionArgs) {
|
|
1529
1511
|
// All agents need "write" access, including summarizer.
|
|
1530
1512
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
@@ -1534,6 +1516,22 @@ export class Container
|
|
|
1534
1516
|
this._deltaManager.connect(args);
|
|
1535
1517
|
}
|
|
1536
1518
|
|
|
1519
|
+
private readonly metadataUpdateHandler = (metadata: Record<string, string>) => {
|
|
1520
|
+
this._containerMetadata = { ...this._containerMetadata, ...metadata };
|
|
1521
|
+
this.emit("metadataUpdate", metadata);
|
|
1522
|
+
};
|
|
1523
|
+
|
|
1524
|
+
private async createDocumentService(
|
|
1525
|
+
serviceProvider: () => Promise<IDocumentService>,
|
|
1526
|
+
): Promise<IDocumentService> {
|
|
1527
|
+
const service = await serviceProvider();
|
|
1528
|
+
// Back-compat for Old driver
|
|
1529
|
+
if (service.on !== undefined) {
|
|
1530
|
+
service.on("metadataUpdate", this.metadataUpdateHandler);
|
|
1531
|
+
}
|
|
1532
|
+
return service;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1537
1535
|
/**
|
|
1538
1536
|
* Load container.
|
|
1539
1537
|
*
|
|
@@ -1547,10 +1545,12 @@ export class Container
|
|
|
1547
1545
|
loadToSequenceNumber: number | undefined,
|
|
1548
1546
|
) {
|
|
1549
1547
|
const timings: Record<string, number> = { phase1: performance.now() };
|
|
1550
|
-
this.service = await this.
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1548
|
+
this.service = await this.createDocumentService(async () =>
|
|
1549
|
+
this.serviceFactory.createDocumentService(
|
|
1550
|
+
resolvedUrl,
|
|
1551
|
+
this.subLogger,
|
|
1552
|
+
this.client.details.type === summarizerClientType,
|
|
1553
|
+
),
|
|
1554
1554
|
);
|
|
1555
1555
|
|
|
1556
1556
|
// Except in cases where it has stashed ops or requested by feature gate, the container will connect in "read" mode
|
|
@@ -1573,33 +1573,20 @@ export class Container
|
|
|
1573
1573
|
|
|
1574
1574
|
this.storageAdapter.connectToService(this.service);
|
|
1575
1575
|
|
|
1576
|
-
this.
|
|
1576
|
+
this.attachmentData = {
|
|
1577
|
+
state: AttachState.Attached,
|
|
1578
|
+
};
|
|
1577
1579
|
|
|
1578
1580
|
timings.phase2 = performance.now();
|
|
1579
1581
|
// Fetch specified snapshot.
|
|
1580
|
-
const {
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
if (pendingLocalState) {
|
|
1586
|
-
this.baseSnapshot = pendingLocalState.baseSnapshot;
|
|
1587
|
-
this.baseSnapshotBlobs = pendingLocalState.snapshotBlobs;
|
|
1588
|
-
} else {
|
|
1589
|
-
assert(snapshot !== undefined, 0x237 /* "Snapshot should exist" */);
|
|
1590
|
-
if (this.offlineLoadEnabled) {
|
|
1591
|
-
this.baseSnapshot = snapshot;
|
|
1592
|
-
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
1593
|
-
this.baseSnapshotBlobs = await getBlobContentsFromTree(
|
|
1594
|
-
snapshot,
|
|
1595
|
-
this.storageAdapter,
|
|
1596
|
-
);
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1582
|
+
const { snapshotTree, version } = await this.serializedStateManager.fetchSnapshot(
|
|
1583
|
+
specifiedVersion,
|
|
1584
|
+
this.service?.policies?.supportGetSnapshotApi,
|
|
1585
|
+
);
|
|
1586
|
+
this._loadedFromVersion = version;
|
|
1600
1587
|
const attributes: IDocumentAttributes = await this.getDocumentAttributes(
|
|
1601
1588
|
this.storageAdapter,
|
|
1602
|
-
|
|
1589
|
+
snapshotTree,
|
|
1603
1590
|
);
|
|
1604
1591
|
|
|
1605
1592
|
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
@@ -1686,15 +1673,20 @@ export class Container
|
|
|
1686
1673
|
|
|
1687
1674
|
// ...load in the existing quorum
|
|
1688
1675
|
// Initialize the protocol handler
|
|
1689
|
-
await this.initializeProtocolStateFromSnapshot(
|
|
1676
|
+
await this.initializeProtocolStateFromSnapshot(
|
|
1677
|
+
attributes,
|
|
1678
|
+
this.storageAdapter,
|
|
1679
|
+
snapshotTree,
|
|
1680
|
+
);
|
|
1690
1681
|
|
|
1691
1682
|
timings.phase3 = performance.now();
|
|
1692
1683
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1693
1684
|
await this.instantiateRuntime(
|
|
1694
1685
|
codeDetails,
|
|
1695
|
-
|
|
1686
|
+
snapshotTree,
|
|
1696
1687
|
// give runtime a dummy value so it knows we're loading from a stash blob
|
|
1697
1688
|
pendingLocalState ? pendingLocalState?.pendingRuntimeState ?? {} : undefined,
|
|
1689
|
+
isInstanceOfISnapshot(snapshotTree) ? snapshotTree : undefined,
|
|
1698
1690
|
);
|
|
1699
1691
|
|
|
1700
1692
|
// replay saved ops
|
|
@@ -1777,7 +1769,7 @@ export class Container
|
|
|
1777
1769
|
);
|
|
1778
1770
|
return {
|
|
1779
1771
|
sequenceNumber: attributes.sequenceNumber,
|
|
1780
|
-
version:
|
|
1772
|
+
version: version?.id,
|
|
1781
1773
|
dmLastProcessedSeqNumber: this._deltaManager.lastSequenceNumber,
|
|
1782
1774
|
dmLastKnownSeqNumber: this._deltaManager.lastKnownSeqNumber,
|
|
1783
1775
|
};
|
|
@@ -1807,24 +1799,30 @@ export class Container
|
|
|
1807
1799
|
this.setLoaded();
|
|
1808
1800
|
}
|
|
1809
1801
|
|
|
1810
|
-
private async rehydrateDetachedFromSnapshot(
|
|
1811
|
-
|
|
1802
|
+
private async rehydrateDetachedFromSnapshot({
|
|
1803
|
+
baseSnapshot,
|
|
1804
|
+
snapshotBlobs,
|
|
1805
|
+
hasAttachmentBlobs,
|
|
1806
|
+
pendingRuntimeState,
|
|
1807
|
+
}: IPendingDetachedContainerState) {
|
|
1808
|
+
if (hasAttachmentBlobs) {
|
|
1812
1809
|
assert(
|
|
1813
1810
|
!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
|
|
1814
1811
|
0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */,
|
|
1815
1812
|
);
|
|
1816
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
1817
|
-
delete detachedContainerSnapshot.tree[hasBlobsSummaryTree];
|
|
1818
1813
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
this.storageAdapter.
|
|
1822
|
-
const attributes = await this.getDocumentAttributes(
|
|
1814
|
+
const snapshotTreeWithBlobContents: ISnapshotTreeWithBlobContents =
|
|
1815
|
+
combineSnapshotTreeAndSnapshotBlobs(baseSnapshot, snapshotBlobs);
|
|
1816
|
+
this.storageAdapter.loadSnapshotFromSnapshotBlobs(snapshotBlobs);
|
|
1817
|
+
const attributes = await this.getDocumentAttributes(
|
|
1818
|
+
this.storageAdapter,
|
|
1819
|
+
snapshotTreeWithBlobContents,
|
|
1820
|
+
);
|
|
1823
1821
|
|
|
1824
1822
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1825
1823
|
|
|
1826
1824
|
// Initialize the protocol handler
|
|
1827
|
-
const baseTree = getProtocolSnapshotTree(
|
|
1825
|
+
const baseTree = getProtocolSnapshotTree(snapshotTreeWithBlobContents);
|
|
1828
1826
|
const qValues = await readAndParse<[string, ICommittedProposal][]>(
|
|
1829
1827
|
this.storageAdapter,
|
|
1830
1828
|
baseTree.blobs.quorumValues,
|
|
@@ -1839,7 +1837,11 @@ export class Container
|
|
|
1839
1837
|
);
|
|
1840
1838
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1841
1839
|
|
|
1842
|
-
await this.instantiateRuntime(
|
|
1840
|
+
await this.instantiateRuntime(
|
|
1841
|
+
codeDetails,
|
|
1842
|
+
snapshotTreeWithBlobContents,
|
|
1843
|
+
pendingRuntimeState,
|
|
1844
|
+
);
|
|
1843
1845
|
|
|
1844
1846
|
this.setLoaded();
|
|
1845
1847
|
}
|
|
@@ -1983,13 +1985,12 @@ export class Container
|
|
|
1983
1985
|
|
|
1984
1986
|
private static setupClient(
|
|
1985
1987
|
containerId: string,
|
|
1986
|
-
|
|
1988
|
+
loaderOptionsClient?: IClient,
|
|
1987
1989
|
clientDetailsOverride?: IClientDetails,
|
|
1988
1990
|
): IClient {
|
|
1989
|
-
const loaderOptionsClient = structuredClone(options?.client);
|
|
1990
1991
|
const client: IClient =
|
|
1991
1992
|
loaderOptionsClient !== undefined
|
|
1992
|
-
? (loaderOptionsClient
|
|
1993
|
+
? structuredClone(loaderOptionsClient)
|
|
1993
1994
|
: {
|
|
1994
1995
|
details: {
|
|
1995
1996
|
capabilities: { interactive: true },
|
|
@@ -2067,10 +2068,10 @@ export class Container
|
|
|
2067
2068
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
2068
2069
|
});
|
|
2069
2070
|
|
|
2070
|
-
deltaManager.on("disconnect", (
|
|
2071
|
+
deltaManager.on("disconnect", (text, error) => {
|
|
2071
2072
|
this.noopHeuristic?.notifyDisconnect();
|
|
2072
2073
|
if (!this.closed) {
|
|
2073
|
-
this.connectionStateHandler.receivedDisconnectEvent(
|
|
2074
|
+
this.connectionStateHandler.receivedDisconnectEvent({ text, error });
|
|
2074
2075
|
}
|
|
2075
2076
|
});
|
|
2076
2077
|
|
|
@@ -2294,9 +2295,6 @@ export class Container
|
|
|
2294
2295
|
}
|
|
2295
2296
|
|
|
2296
2297
|
private processRemoteMessage(message: ISequencedDocumentMessage) {
|
|
2297
|
-
if (this.offlineLoadEnabled) {
|
|
2298
|
-
this.savedOps.push(message);
|
|
2299
|
-
}
|
|
2300
2298
|
const local = this.clientId === message.clientId;
|
|
2301
2299
|
|
|
2302
2300
|
// Allow the protocol handler to process the message
|
|
@@ -2304,7 +2302,7 @@ export class Container
|
|
|
2304
2302
|
|
|
2305
2303
|
// Forward messages to the loaded runtime for processing
|
|
2306
2304
|
this.runtime.process(message, local);
|
|
2307
|
-
|
|
2305
|
+
this.serializedStateManager.addProcessedOp(message);
|
|
2308
2306
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
2309
2307
|
if (this.activeConnection()) {
|
|
2310
2308
|
if (this.noopHeuristic === undefined) {
|
|
@@ -2357,36 +2355,11 @@ export class Container
|
|
|
2357
2355
|
}
|
|
2358
2356
|
}
|
|
2359
2357
|
|
|
2360
|
-
/**
|
|
2361
|
-
* Get the most recent snapshot, or a specific version.
|
|
2362
|
-
* @param specifiedVersion - The specific version of the snapshot to retrieve
|
|
2363
|
-
* @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
|
|
2364
|
-
*/
|
|
2365
|
-
private async fetchSnapshotTree(
|
|
2366
|
-
specifiedVersion: string | undefined,
|
|
2367
|
-
): Promise<{ snapshot?: ISnapshotTree; versionId?: string }> {
|
|
2368
|
-
const version = await this.getVersion(specifiedVersion ?? null);
|
|
2369
|
-
|
|
2370
|
-
if (version === undefined && specifiedVersion !== undefined) {
|
|
2371
|
-
// We should have a defined version to load from if specified version requested
|
|
2372
|
-
this.mc.logger.sendErrorEvent({
|
|
2373
|
-
eventName: "NoVersionFoundWhenSpecified",
|
|
2374
|
-
id: specifiedVersion,
|
|
2375
|
-
});
|
|
2376
|
-
}
|
|
2377
|
-
this._loadedFromVersion = version;
|
|
2378
|
-
const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
|
|
2379
|
-
|
|
2380
|
-
if (snapshot === undefined && version !== undefined) {
|
|
2381
|
-
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
2382
|
-
}
|
|
2383
|
-
return { snapshot, versionId: version?.id };
|
|
2384
|
-
}
|
|
2385
|
-
|
|
2386
2358
|
private async instantiateRuntime(
|
|
2387
2359
|
codeDetails: IFluidCodeDetails,
|
|
2388
|
-
|
|
2360
|
+
snapshotTree: ISnapshotTree | undefined,
|
|
2389
2361
|
pendingLocalState?: unknown,
|
|
2362
|
+
snapshot?: ISnapshot,
|
|
2390
2363
|
) {
|
|
2391
2364
|
assert(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
2392
2365
|
|
|
@@ -2420,12 +2393,12 @@ export class Container
|
|
|
2420
2393
|
(this.protocolHandler.quorum.get("code") ??
|
|
2421
2394
|
this.protocolHandler.quorum.get("code2")) as IFluidCodeDetails | undefined;
|
|
2422
2395
|
|
|
2423
|
-
const existing =
|
|
2396
|
+
const existing = snapshotTree !== undefined;
|
|
2424
2397
|
|
|
2425
2398
|
const context = new ContainerContext(
|
|
2426
2399
|
this.options,
|
|
2427
2400
|
this.scope,
|
|
2428
|
-
|
|
2401
|
+
snapshotTree,
|
|
2429
2402
|
this._loadedFromVersion,
|
|
2430
2403
|
this._deltaManager,
|
|
2431
2404
|
this.storageAdapter,
|
|
@@ -2452,6 +2425,7 @@ export class Container
|
|
|
2452
2425
|
existing,
|
|
2453
2426
|
this.subLogger,
|
|
2454
2427
|
pendingLocalState,
|
|
2428
|
+
snapshot,
|
|
2455
2429
|
);
|
|
2456
2430
|
|
|
2457
2431
|
this._runtime = await PerformanceEvent.timedExecAsync(
|