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