@fluidframework/container-loader 2.0.0-dev-rc.1.0.0.228517 → 2.0.0-dev-rc.2.0.0.245554
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 +21 -20
- 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} +11 -12
- 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 +15 -13
- 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
|
@@ -3,32 +3,34 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { v4 as uuid } from "uuid";
|
|
6
|
-
import { assert, unreachableCase } from "@fluidframework/core-utils";
|
|
6
|
+
import { assert, unreachableCase, isPromiseLike } from "@fluidframework/core-utils";
|
|
7
7
|
import { TypedEventEmitter, performance } from "@fluid-internal/client-utils";
|
|
8
8
|
import { LogLevel, } from "@fluidframework/core-interfaces";
|
|
9
9
|
import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
|
|
10
|
-
import { readAndParse, OnlineStatus, isOnline,
|
|
10
|
+
import { readAndParse, OnlineStatus, isOnline, isCombinedAppAndProtocolSummary, MessageType2, isInstanceOfISnapshot, runWithRetry, } from "@fluidframework/driver-utils";
|
|
11
11
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
12
12
|
import { createChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, connectedEventName, normalizeError, createChildMonitoringContext, wrapError, formatTick, GenericError, UsageError, } from "@fluidframework/telemetry-utils";
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
13
|
+
import structuredClone from "@ungap/structured-clone";
|
|
14
|
+
import { Audience } from "./audience.js";
|
|
15
|
+
import { ContainerContext } from "./containerContext.js";
|
|
16
|
+
import { ReconnectMode, getPackageName, } from "./contracts.js";
|
|
17
|
+
import { DeltaManager } from "./deltaManager.js";
|
|
18
|
+
import { RelativeLoader } from "./loader.js";
|
|
19
|
+
import { pkgVersion } from "./packageVersion.js";
|
|
20
|
+
import { ContainerStorageAdapter } from "./containerStorageAdapter.js";
|
|
21
|
+
import { createConnectionStateHandler } from "./connectionStateHandler.js";
|
|
22
|
+
import { combineAppAndProtocolSummary, getProtocolSnapshotTree, getSnapshotTreeAndBlobsFromSerializedContainer, combineSnapshotTreeAndSnapshotBlobs, getDetachedContainerStateFromSerializedContainer, runSingle, } from "./utils.js";
|
|
23
|
+
import { initQuorumValuesFromCodeDetails } from "./quorum.js";
|
|
24
|
+
import { NoopHeuristic } from "./noopHeuristic.js";
|
|
25
|
+
import { ConnectionManager } from "./connectionManager.js";
|
|
26
|
+
import { ConnectionState } from "./connectionState.js";
|
|
27
|
+
import { ProtocolHandler, protocolHandlerShouldProcessSignal, } from "./protocol.js";
|
|
28
|
+
import { runRetriableAttachProcess } from "./attachment.js";
|
|
29
|
+
import { SerializedStateManager } from "./serializedStateManager.js";
|
|
27
30
|
const detachedContainerRefSeqNumber = 0;
|
|
28
31
|
const dirtyContainerEvent = "dirty";
|
|
29
32
|
const savedContainerEvent = "saved";
|
|
30
33
|
const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
31
|
-
const hasBlobsSummaryTree = ".hasAttachmentBlobs";
|
|
32
34
|
/**
|
|
33
35
|
* Waits until container connects to delta storage and gets up-to-date.
|
|
34
36
|
*
|
|
@@ -103,9 +105,7 @@ export async function waitContainerToCatchUp(container) {
|
|
|
103
105
|
}
|
|
104
106
|
});
|
|
105
107
|
}
|
|
106
|
-
const getCodeProposal =
|
|
107
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
108
|
-
(quorum) => quorum.get("code") ?? quorum.get("code2");
|
|
108
|
+
const getCodeProposal = (quorum) => quorum.get("code") ?? quorum.get("code2");
|
|
109
109
|
/**
|
|
110
110
|
* Helper function to report to telemetry cases where operation takes longer than expected (200ms)
|
|
111
111
|
* @param logger - logger to use
|
|
@@ -123,7 +123,6 @@ const summarizerClientType = "summarizer";
|
|
|
123
123
|
export class Container extends EventEmitterWithErrorHandling {
|
|
124
124
|
/**
|
|
125
125
|
* Load an existing container.
|
|
126
|
-
* @internal
|
|
127
126
|
*/
|
|
128
127
|
static async load(loadProps, createProps) {
|
|
129
128
|
const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } = loadProps;
|
|
@@ -180,11 +179,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
180
179
|
static async rehydrateDetachedFromSnapshot(createProps, snapshot) {
|
|
181
180
|
const container = new Container(createProps);
|
|
182
181
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
throw new UsageError("Cannot rehydrate detached container. Incorrect format");
|
|
186
|
-
}
|
|
187
|
-
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
182
|
+
const detachedContainerState = getDetachedContainerStateFromSerializedContainer(snapshot);
|
|
183
|
+
await container.rehydrateDetachedFromSnapshot(detachedContainerState);
|
|
188
184
|
return container;
|
|
189
185
|
}, { start: true, end: true, cancel: "generic" });
|
|
190
186
|
}
|
|
@@ -235,6 +231,9 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
235
231
|
get readOnlyInfo() {
|
|
236
232
|
return this._deltaManager.readOnlyInfo;
|
|
237
233
|
}
|
|
234
|
+
get containerMetadata() {
|
|
235
|
+
return this._containerMetadata;
|
|
236
|
+
}
|
|
238
237
|
/**
|
|
239
238
|
* Sends signal to runtime (and data stores) to be read-only.
|
|
240
239
|
* Hosts may have read only views, indicating to data stores that no edits are allowed.
|
|
@@ -271,11 +270,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
271
270
|
get clientId() {
|
|
272
271
|
return this._clientId;
|
|
273
272
|
}
|
|
274
|
-
get
|
|
275
|
-
|
|
276
|
-
this.options?.enableOfflineLoad === true;
|
|
277
|
-
// summarizer will not have any pending state we want to save
|
|
278
|
-
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
273
|
+
get isInteractiveClient() {
|
|
274
|
+
return this.deltaManager.clientDetails.capabilities.interactive;
|
|
279
275
|
}
|
|
280
276
|
/**
|
|
281
277
|
* Get the code details that are currently specified for the container.
|
|
@@ -330,9 +326,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
330
326
|
this._lifecycleEvents.once("disposed", disposedHandler);
|
|
331
327
|
});
|
|
332
328
|
}
|
|
333
|
-
/**
|
|
334
|
-
* @internal
|
|
335
|
-
*/
|
|
336
329
|
constructor(createProps, loadProps) {
|
|
337
330
|
super((name, error) => {
|
|
338
331
|
this.mc.logger.sendErrorEvent({
|
|
@@ -356,24 +349,102 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
356
349
|
* disposed: Container has been disposed
|
|
357
350
|
*/
|
|
358
351
|
this._lifecycleState = "loading";
|
|
359
|
-
this._attachState = AttachState.Detached;
|
|
360
352
|
/** During initialization we pause the inbound queues. We track this state to ensure we only call resume once */
|
|
361
353
|
this.inboundQueuePausedFromInit = true;
|
|
362
354
|
this.firstConnection = true;
|
|
363
355
|
this.connectionTransitionTimes = [];
|
|
364
|
-
this.attachStarted = false;
|
|
365
356
|
this._dirtyContainer = false;
|
|
366
|
-
this.
|
|
357
|
+
this.attachmentData = { state: AttachState.Detached };
|
|
367
358
|
this.clientsWhoShouldHaveLeft = new Set();
|
|
359
|
+
this._containerMetadata = {};
|
|
368
360
|
this.setAutoReconnectTime = performance.now();
|
|
369
361
|
this._lifecycleEvents = new TypedEventEmitter();
|
|
370
362
|
this._disposed = false;
|
|
363
|
+
this.attach = runSingle(async (request, attachProps) => {
|
|
364
|
+
await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
365
|
+
if (this._lifecycleState !== "loaded" ||
|
|
366
|
+
this.attachmentData.state === AttachState.Attached) {
|
|
367
|
+
// pre-0.58 error message: containerNotValidForAttach
|
|
368
|
+
throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
|
|
369
|
+
}
|
|
370
|
+
const normalizeErrorAndClose = (error) => {
|
|
371
|
+
const newError = normalizeError(error);
|
|
372
|
+
this.close(newError);
|
|
373
|
+
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
374
|
+
newError.addTelemetryProperties({
|
|
375
|
+
resolvedUrl: this.service?.resolvedUrl?.url,
|
|
376
|
+
});
|
|
377
|
+
return newError;
|
|
378
|
+
};
|
|
379
|
+
const setAttachmentData = (attachmentData) => {
|
|
380
|
+
const previousState = this.attachmentData.state;
|
|
381
|
+
this.attachmentData = attachmentData;
|
|
382
|
+
const state = this.attachmentData.state;
|
|
383
|
+
if (state !== previousState && state !== AttachState.Detached) {
|
|
384
|
+
try {
|
|
385
|
+
this.runtime.setAttachState(state);
|
|
386
|
+
this.emit(state.toLocaleLowerCase());
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
throw normalizeErrorAndClose(error);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
const createAttachmentSummary = (redirectTable) => {
|
|
394
|
+
try {
|
|
395
|
+
assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
396
|
+
return combineAppAndProtocolSummary(this.runtime.createSummary(redirectTable), this.captureProtocolSummary());
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
throw normalizeErrorAndClose(error);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
const createOrGetStorageService = async (summary) => {
|
|
403
|
+
// Actually go and create the resolved document
|
|
404
|
+
if (this.service === undefined) {
|
|
405
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
406
|
+
assert(this.client.details.type !== summarizerClientType &&
|
|
407
|
+
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
408
|
+
this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
409
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
this.storageAdapter.connectToService(this.service);
|
|
413
|
+
return this.storageAdapter;
|
|
414
|
+
};
|
|
415
|
+
let attachP = runRetriableAttachProcess({
|
|
416
|
+
initialAttachmentData: this.attachmentData,
|
|
417
|
+
offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
|
|
418
|
+
detachedBlobStorage: this.detachedBlobStorage,
|
|
419
|
+
setAttachmentData,
|
|
420
|
+
createAttachmentSummary,
|
|
421
|
+
createOrGetStorageService,
|
|
422
|
+
});
|
|
423
|
+
// only enable the new behavior if the config is set
|
|
424
|
+
if (this.mc.config.getBoolean("Fluid.Container.RetryOnAttachFailure") !== true) {
|
|
425
|
+
attachP = attachP.catch((error) => {
|
|
426
|
+
throw normalizeErrorAndClose(error);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
this.serializedStateManager.setSnapshot(await attachP);
|
|
430
|
+
if (!this.closed) {
|
|
431
|
+
this.handleDeltaConnectionArg({
|
|
432
|
+
fetchOpsFromStorage: false,
|
|
433
|
+
reason: { text: "createDetached" },
|
|
434
|
+
}, attachProps?.deltaConnection);
|
|
435
|
+
}
|
|
436
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
437
|
+
});
|
|
371
438
|
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
372
439
|
if (this.resolvedUrl === undefined) {
|
|
373
440
|
return undefined;
|
|
374
441
|
}
|
|
375
442
|
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, getPackageName(this._loadedCodeDetails));
|
|
376
443
|
};
|
|
444
|
+
this.metadataUpdateHandler = (metadata) => {
|
|
445
|
+
this._containerMetadata = { ...this._containerMetadata, ...metadata };
|
|
446
|
+
this.emit("metadataUpdate", metadata);
|
|
447
|
+
};
|
|
377
448
|
this.updateDirtyContainerState = (dirty) => {
|
|
378
449
|
if (this._dirtyContainer === dirty) {
|
|
379
450
|
return;
|
|
@@ -407,7 +478,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
407
478
|
});
|
|
408
479
|
};
|
|
409
480
|
this._containerId = uuid();
|
|
410
|
-
this.client = Container.setupClient(this._containerId,
|
|
481
|
+
this.client = Container.setupClient(this._containerId, options.client, this.clientDetailsOverride);
|
|
411
482
|
// Create logger for data stores to use
|
|
412
483
|
const type = this.client.details.type;
|
|
413
484
|
const interactive = this.client.details.capabilities.interactive;
|
|
@@ -421,7 +492,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
421
492
|
clientType,
|
|
422
493
|
containerId: this._containerId,
|
|
423
494
|
docId: () => this.resolvedUrl?.id,
|
|
424
|
-
containerAttachState: () => this.
|
|
495
|
+
containerAttachState: () => this.attachState,
|
|
425
496
|
containerLifecycleState: () => this._lifecycleState,
|
|
426
497
|
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
427
498
|
serializedContainer: pendingLocalState !== undefined,
|
|
@@ -513,6 +584,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
513
584
|
const forceEnableSummarizeProtocolTree = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
514
585
|
options.summarizeProtocolTree;
|
|
515
586
|
this.storageAdapter = new ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
587
|
+
const offlineLoadEnabled = (this.isInteractiveClient &&
|
|
588
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) ??
|
|
589
|
+
options.enableOfflineLoad === true;
|
|
590
|
+
this.serializedStateManager = new SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled);
|
|
516
591
|
const isDomAvailable = typeof document === "object" &&
|
|
517
592
|
document !== null &&
|
|
518
593
|
typeof document.addEventListener === "function" &&
|
|
@@ -572,6 +647,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
572
647
|
: "generic",
|
|
573
648
|
}, error);
|
|
574
649
|
this._lifecycleState = "closing";
|
|
650
|
+
// Back-compat for Old driver
|
|
651
|
+
if (this.service?.off !== undefined) {
|
|
652
|
+
this.service?.off("metadataUpdate", this.metadataUpdateHandler);
|
|
653
|
+
}
|
|
575
654
|
this._protocolHandler?.close();
|
|
576
655
|
this.connectionStateHandler.dispose();
|
|
577
656
|
}
|
|
@@ -647,149 +726,35 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
647
726
|
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
648
727
|
}
|
|
649
728
|
async getPendingLocalStateCore(props) {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
658
|
-
}
|
|
659
|
-
if (this.closed || this._disposed) {
|
|
660
|
-
throw new UsageError("Pending state cannot be retried if the container is closed or disposed");
|
|
661
|
-
}
|
|
662
|
-
assert(this.attachState === AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
663
|
-
assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
664
|
-
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
665
|
-
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
666
|
-
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
667
|
-
const pendingState = {
|
|
668
|
-
pendingRuntimeState,
|
|
669
|
-
baseSnapshot: this.baseSnapshot,
|
|
670
|
-
snapshotBlobs: this.baseSnapshotBlobs,
|
|
671
|
-
savedOps: this.savedOps,
|
|
672
|
-
url: this.resolvedUrl.url,
|
|
673
|
-
// no need to save this if there is no pending runtime state
|
|
674
|
-
clientId: pendingRuntimeState !== undefined ? this.clientId : undefined,
|
|
675
|
-
};
|
|
676
|
-
return JSON.stringify(pendingState);
|
|
677
|
-
});
|
|
729
|
+
if (this.closed || this._disposed) {
|
|
730
|
+
throw new UsageError("Pending state cannot be retried if the container is closed or disposed");
|
|
731
|
+
}
|
|
732
|
+
assert(this.attachmentData.state === AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
733
|
+
assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
734
|
+
const pendingState = await this.serializedStateManager.getPendingLocalStateCore(props, this.clientId, this.runtime, this.resolvedUrl);
|
|
735
|
+
return pendingState;
|
|
678
736
|
}
|
|
679
737
|
get attachState() {
|
|
680
|
-
return this.
|
|
738
|
+
return this.attachmentData.state;
|
|
681
739
|
}
|
|
682
740
|
serialize() {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
686
|
-
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
687
|
-
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
688
|
-
combinedSummary.tree[hasBlobsSummaryTree] = {
|
|
689
|
-
type: SummaryType.Blob,
|
|
690
|
-
content: "true",
|
|
691
|
-
};
|
|
741
|
+
if (this.attachmentData.state === AttachState.Attached || this.closed) {
|
|
742
|
+
throw new UsageError("Container must not be attached or closed.");
|
|
692
743
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
708
|
-
let summary;
|
|
709
|
-
if (!hasAttachmentBlobs) {
|
|
710
|
-
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
711
|
-
// semantics around what the attach means as far as async code goes.
|
|
712
|
-
const appSummary = this.runtime.createSummary();
|
|
713
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
714
|
-
summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
715
|
-
// Set the state as attaching as we are starting the process of attaching container.
|
|
716
|
-
// This should be fired after taking the summary because it is the place where we are
|
|
717
|
-
// starting to attach the container to storage.
|
|
718
|
-
// Also, this should only be fired in detached container.
|
|
719
|
-
this._attachState = AttachState.Attaching;
|
|
720
|
-
this.runtime.setAttachState(AttachState.Attaching);
|
|
721
|
-
this.emit("attaching");
|
|
722
|
-
if (this.offlineLoadEnabled) {
|
|
723
|
-
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
724
|
-
this.baseSnapshot = snapshot;
|
|
725
|
-
this.baseSnapshotBlobs =
|
|
726
|
-
getBlobContentsFromTreeWithBlobContents(snapshot);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
// Actually go and create the resolved document
|
|
730
|
-
if (this.service === undefined) {
|
|
731
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
732
|
-
assert(this.client.details.type !== summarizerClientType &&
|
|
733
|
-
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
734
|
-
this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
735
|
-
cancel: this._deltaManager.closeAbortController.signal,
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
this.storageAdapter.connectToService(this.service);
|
|
739
|
-
if (hasAttachmentBlobs) {
|
|
740
|
-
// upload blobs to storage
|
|
741
|
-
assert(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
742
|
-
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
743
|
-
// support blob handles that only know about the local IDs
|
|
744
|
-
const redirectTable = new Map();
|
|
745
|
-
// if new blobs are added while uploading, upload them too
|
|
746
|
-
while (redirectTable.size < this.detachedBlobStorage.size) {
|
|
747
|
-
const newIds = this.detachedBlobStorage
|
|
748
|
-
.getBlobIds()
|
|
749
|
-
.filter((id) => !redirectTable.has(id));
|
|
750
|
-
for (const id of newIds) {
|
|
751
|
-
const blob = await this.detachedBlobStorage.readBlob(id);
|
|
752
|
-
const response = await this.storageAdapter.createBlob(blob);
|
|
753
|
-
redirectTable.set(id, response.id);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
// take summary and upload
|
|
757
|
-
const appSummary = this.runtime.createSummary(redirectTable);
|
|
758
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
759
|
-
summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
760
|
-
this._attachState = AttachState.Attaching;
|
|
761
|
-
this.runtime.setAttachState(AttachState.Attaching);
|
|
762
|
-
this.emit("attaching");
|
|
763
|
-
if (this.offlineLoadEnabled) {
|
|
764
|
-
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
765
|
-
this.baseSnapshot = snapshot;
|
|
766
|
-
this.baseSnapshotBlobs =
|
|
767
|
-
getBlobContentsFromTreeWithBlobContents(snapshot);
|
|
768
|
-
}
|
|
769
|
-
await this.storageAdapter.uploadSummaryWithContext(summary, {
|
|
770
|
-
referenceSequenceNumber: 0,
|
|
771
|
-
ackHandle: undefined,
|
|
772
|
-
proposalHandle: undefined,
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
this._attachState = AttachState.Attached;
|
|
776
|
-
this.runtime.setAttachState(AttachState.Attached);
|
|
777
|
-
this.emit("attached");
|
|
778
|
-
if (!this.closed) {
|
|
779
|
-
this.handleDeltaConnectionArg({
|
|
780
|
-
fetchOpsFromStorage: false,
|
|
781
|
-
reason: { text: "createDetached" },
|
|
782
|
-
}, attachProps?.deltaConnection);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
catch (error) {
|
|
786
|
-
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
787
|
-
const newError = normalizeError(error);
|
|
788
|
-
newError.addTelemetryProperties({ resolvedUrl: this.resolvedUrl?.url });
|
|
789
|
-
this.close(newError);
|
|
790
|
-
throw newError;
|
|
791
|
-
}
|
|
792
|
-
}, { start: true, end: true, cancel: "generic" });
|
|
744
|
+
const attachingData = this.attachmentData.state === AttachState.Attaching ? this.attachmentData : undefined;
|
|
745
|
+
const combinedSummary = attachingData?.summary ??
|
|
746
|
+
combineAppAndProtocolSummary(this.runtime.createSummary(), this.captureProtocolSummary());
|
|
747
|
+
const { tree: snapshot, blobs } = getSnapshotTreeAndBlobsFromSerializedContainer(combinedSummary);
|
|
748
|
+
const pendingRuntimeState = attachingData !== undefined ? this.runtime.getPendingLocalState() : undefined;
|
|
749
|
+
assert(!isPromiseLike(pendingRuntimeState), 0x8e3 /* should not be a promise */);
|
|
750
|
+
const detachedContainerState = {
|
|
751
|
+
attached: false,
|
|
752
|
+
baseSnapshot: snapshot,
|
|
753
|
+
snapshotBlobs: blobs,
|
|
754
|
+
pendingRuntimeState,
|
|
755
|
+
hasAttachmentBlobs: !!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
|
|
756
|
+
};
|
|
757
|
+
return JSON.stringify(detachedContainerState);
|
|
793
758
|
}
|
|
794
759
|
setAutoReconnectInternal(mode, reason) {
|
|
795
760
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
@@ -811,7 +776,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
811
776
|
if (this.closed) {
|
|
812
777
|
throw new UsageError(`The Container is closed and cannot be connected`);
|
|
813
778
|
}
|
|
814
|
-
else if (this.
|
|
779
|
+
else if (this.attachState !== AttachState.Attached) {
|
|
815
780
|
throw new UsageError(`The Container is not attached and cannot be connected`);
|
|
816
781
|
}
|
|
817
782
|
else if (!this.connected) {
|
|
@@ -826,7 +791,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
826
791
|
}
|
|
827
792
|
connectInternal(args) {
|
|
828
793
|
assert(!this.closed, 0x2c5 /* "Attempting to connect() a closed Container" */);
|
|
829
|
-
assert(this.
|
|
794
|
+
assert(this.attachState === AttachState.Attached, 0x2c6 /* "Attempting to connect() a container that is not attached" */);
|
|
830
795
|
// Resume processing ops and connect to delta stream
|
|
831
796
|
this.resumeInternal(args);
|
|
832
797
|
// Set Auto Reconnect Mode
|
|
@@ -919,10 +884,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
919
884
|
}
|
|
920
885
|
return true;
|
|
921
886
|
}
|
|
922
|
-
async getVersion(version) {
|
|
923
|
-
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
924
|
-
return versions[0];
|
|
925
|
-
}
|
|
926
887
|
connectToDeltaStream(args) {
|
|
927
888
|
// All agents need "write" access, including summarizer.
|
|
928
889
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
@@ -930,6 +891,14 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
930
891
|
}
|
|
931
892
|
this._deltaManager.connect(args);
|
|
932
893
|
}
|
|
894
|
+
async createDocumentService(serviceProvider) {
|
|
895
|
+
const service = await serviceProvider();
|
|
896
|
+
// Back-compat for Old driver
|
|
897
|
+
if (service.on !== undefined) {
|
|
898
|
+
service.on("metadataUpdate", this.metadataUpdateHandler);
|
|
899
|
+
}
|
|
900
|
+
return service;
|
|
901
|
+
}
|
|
933
902
|
/**
|
|
934
903
|
* Load container.
|
|
935
904
|
*
|
|
@@ -937,7 +906,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
937
906
|
*/
|
|
938
907
|
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState, loadToSequenceNumber) {
|
|
939
908
|
const timings = { phase1: performance.now() };
|
|
940
|
-
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
909
|
+
this.service = await this.createDocumentService(async () => this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType));
|
|
941
910
|
// Except in cases where it has stashed ops or requested by feature gate, the container will connect in "read" mode
|
|
942
911
|
const mode = this.mc.config.getBoolean("Fluid.Container.ForceWriteConnection") === true ||
|
|
943
912
|
(pendingLocalState?.savedOps.length ?? 0) > 0
|
|
@@ -954,25 +923,14 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
954
923
|
this.connectToDeltaStream(connectionArgs);
|
|
955
924
|
}
|
|
956
925
|
this.storageAdapter.connectToService(this.service);
|
|
957
|
-
this.
|
|
926
|
+
this.attachmentData = {
|
|
927
|
+
state: AttachState.Attached,
|
|
928
|
+
};
|
|
958
929
|
timings.phase2 = performance.now();
|
|
959
930
|
// Fetch specified snapshot.
|
|
960
|
-
const {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
if (pendingLocalState) {
|
|
964
|
-
this.baseSnapshot = pendingLocalState.baseSnapshot;
|
|
965
|
-
this.baseSnapshotBlobs = pendingLocalState.snapshotBlobs;
|
|
966
|
-
}
|
|
967
|
-
else {
|
|
968
|
-
assert(snapshot !== undefined, 0x237 /* "Snapshot should exist" */);
|
|
969
|
-
if (this.offlineLoadEnabled) {
|
|
970
|
-
this.baseSnapshot = snapshot;
|
|
971
|
-
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
972
|
-
this.baseSnapshotBlobs = await getBlobContentsFromTree(snapshot, this.storageAdapter);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
931
|
+
const { snapshotTree, version } = await this.serializedStateManager.fetchSnapshot(specifiedVersion, this.service?.policies?.supportGetSnapshotApi);
|
|
932
|
+
this._loadedFromVersion = version;
|
|
933
|
+
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshotTree);
|
|
976
934
|
// If we saved ops, we will replay them and don't need DeltaManager to fetch them
|
|
977
935
|
const sequenceNumber = pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber;
|
|
978
936
|
const dmAttributes = sequenceNumber !== undefined ? { ...attributes, sequenceNumber } : attributes;
|
|
@@ -1037,12 +995,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1037
995
|
}
|
|
1038
996
|
// ...load in the existing quorum
|
|
1039
997
|
// Initialize the protocol handler
|
|
1040
|
-
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter,
|
|
998
|
+
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshotTree);
|
|
1041
999
|
timings.phase3 = performance.now();
|
|
1042
1000
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1043
|
-
await this.instantiateRuntime(codeDetails,
|
|
1001
|
+
await this.instantiateRuntime(codeDetails, snapshotTree,
|
|
1044
1002
|
// give runtime a dummy value so it knows we're loading from a stash blob
|
|
1045
|
-
pendingLocalState ? pendingLocalState?.pendingRuntimeState ?? {} : undefined);
|
|
1003
|
+
pendingLocalState ? pendingLocalState?.pendingRuntimeState ?? {} : undefined, isInstanceOfISnapshot(snapshotTree) ? snapshotTree : undefined);
|
|
1046
1004
|
// replay saved ops
|
|
1047
1005
|
if (pendingLocalState) {
|
|
1048
1006
|
for (const message of pendingLocalState.savedOps) {
|
|
@@ -1097,7 +1055,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1097
1055
|
}, undefined, LogLevel.verbose);
|
|
1098
1056
|
return {
|
|
1099
1057
|
sequenceNumber: attributes.sequenceNumber,
|
|
1100
|
-
version:
|
|
1058
|
+
version: version?.id,
|
|
1101
1059
|
dmLastProcessedSeqNumber: this._deltaManager.lastSequenceNumber,
|
|
1102
1060
|
dmLastKnownSeqNumber: this._deltaManager.lastKnownSeqNumber,
|
|
1103
1061
|
};
|
|
@@ -1118,18 +1076,16 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1118
1076
|
await this.instantiateRuntime(codeDetails, undefined);
|
|
1119
1077
|
this.setLoaded();
|
|
1120
1078
|
}
|
|
1121
|
-
async rehydrateDetachedFromSnapshot(
|
|
1122
|
-
if (
|
|
1079
|
+
async rehydrateDetachedFromSnapshot({ baseSnapshot, snapshotBlobs, hasAttachmentBlobs, pendingRuntimeState, }) {
|
|
1080
|
+
if (hasAttachmentBlobs) {
|
|
1123
1081
|
assert(!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1124
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
1125
|
-
delete detachedContainerSnapshot.tree[hasBlobsSummaryTree];
|
|
1126
1082
|
}
|
|
1127
|
-
const
|
|
1128
|
-
this.storageAdapter.
|
|
1129
|
-
const attributes = await this.getDocumentAttributes(this.storageAdapter,
|
|
1083
|
+
const snapshotTreeWithBlobContents = combineSnapshotTreeAndSnapshotBlobs(baseSnapshot, snapshotBlobs);
|
|
1084
|
+
this.storageAdapter.loadSnapshotFromSnapshotBlobs(snapshotBlobs);
|
|
1085
|
+
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshotTreeWithBlobContents);
|
|
1130
1086
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1131
1087
|
// Initialize the protocol handler
|
|
1132
|
-
const baseTree = getProtocolSnapshotTree(
|
|
1088
|
+
const baseTree = getProtocolSnapshotTree(snapshotTreeWithBlobContents);
|
|
1133
1089
|
const qValues = await readAndParse(this.storageAdapter, baseTree.blobs.quorumValues);
|
|
1134
1090
|
this.initializeProtocolState(attributes, {
|
|
1135
1091
|
members: [],
|
|
@@ -1137,7 +1093,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1137
1093
|
values: qValues,
|
|
1138
1094
|
});
|
|
1139
1095
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1140
|
-
await this.instantiateRuntime(codeDetails,
|
|
1096
|
+
await this.instantiateRuntime(codeDetails, snapshotTreeWithBlobContents, pendingRuntimeState);
|
|
1141
1097
|
this.setLoaded();
|
|
1142
1098
|
}
|
|
1143
1099
|
async getDocumentAttributes(storage, tree) {
|
|
@@ -1237,10 +1193,9 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1237
1193
|
const pkg = getCodeProposal(quorum);
|
|
1238
1194
|
return pkg;
|
|
1239
1195
|
}
|
|
1240
|
-
static setupClient(containerId,
|
|
1241
|
-
const loaderOptionsClient = structuredClone(options?.client);
|
|
1196
|
+
static setupClient(containerId, loaderOptionsClient, clientDetailsOverride) {
|
|
1242
1197
|
const client = loaderOptionsClient !== undefined
|
|
1243
|
-
? loaderOptionsClient
|
|
1198
|
+
? structuredClone(loaderOptionsClient)
|
|
1244
1199
|
: {
|
|
1245
1200
|
details: {
|
|
1246
1201
|
capabilities: { interactive: true },
|
|
@@ -1294,10 +1249,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1294
1249
|
deltaManager.on("cancelEstablishingConnection", (reason) => {
|
|
1295
1250
|
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1296
1251
|
});
|
|
1297
|
-
deltaManager.on("disconnect", (
|
|
1252
|
+
deltaManager.on("disconnect", (text, error) => {
|
|
1298
1253
|
this.noopHeuristic?.notifyDisconnect();
|
|
1299
1254
|
if (!this.closed) {
|
|
1300
|
-
this.connectionStateHandler.receivedDisconnectEvent(
|
|
1255
|
+
this.connectionStateHandler.receivedDisconnectEvent({ text, error });
|
|
1301
1256
|
}
|
|
1302
1257
|
});
|
|
1303
1258
|
deltaManager.on("throttled", (warning) => {
|
|
@@ -1442,14 +1397,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1442
1397
|
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1443
1398
|
}
|
|
1444
1399
|
processRemoteMessage(message) {
|
|
1445
|
-
if (this.offlineLoadEnabled) {
|
|
1446
|
-
this.savedOps.push(message);
|
|
1447
|
-
}
|
|
1448
1400
|
const local = this.clientId === message.clientId;
|
|
1449
1401
|
// Allow the protocol handler to process the message
|
|
1450
1402
|
const result = this.protocolHandler.processMessage(message, local);
|
|
1451
1403
|
// Forward messages to the loaded runtime for processing
|
|
1452
1404
|
this.runtime.process(message, local);
|
|
1405
|
+
this.serializedStateManager.addProcessedOp(message);
|
|
1453
1406
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1454
1407
|
if (this.activeConnection()) {
|
|
1455
1408
|
if (this.noopHeuristic === undefined) {
|
|
@@ -1490,28 +1443,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1490
1443
|
this.runtime.processSignal(message, local);
|
|
1491
1444
|
}
|
|
1492
1445
|
}
|
|
1493
|
-
|
|
1494
|
-
* Get the most recent snapshot, or a specific version.
|
|
1495
|
-
* @param specifiedVersion - The specific version of the snapshot to retrieve
|
|
1496
|
-
* @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
|
|
1497
|
-
*/
|
|
1498
|
-
async fetchSnapshotTree(specifiedVersion) {
|
|
1499
|
-
const version = await this.getVersion(specifiedVersion ?? null);
|
|
1500
|
-
if (version === undefined && specifiedVersion !== undefined) {
|
|
1501
|
-
// We should have a defined version to load from if specified version requested
|
|
1502
|
-
this.mc.logger.sendErrorEvent({
|
|
1503
|
-
eventName: "NoVersionFoundWhenSpecified",
|
|
1504
|
-
id: specifiedVersion,
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
this._loadedFromVersion = version;
|
|
1508
|
-
const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
|
|
1509
|
-
if (snapshot === undefined && version !== undefined) {
|
|
1510
|
-
this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
|
|
1511
|
-
}
|
|
1512
|
-
return { snapshot, versionId: version?.id };
|
|
1513
|
-
}
|
|
1514
|
-
async instantiateRuntime(codeDetails, snapshot, pendingLocalState) {
|
|
1446
|
+
async instantiateRuntime(codeDetails, snapshotTree, pendingLocalState, snapshot) {
|
|
1515
1447
|
assert(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1516
1448
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1517
1449
|
// are set. Global requests will still go directly to the loader
|
|
@@ -1532,8 +1464,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1532
1464
|
}
|
|
1533
1465
|
const getSpecifiedCodeDetails = () => (this.protocolHandler.quorum.get("code") ??
|
|
1534
1466
|
this.protocolHandler.quorum.get("code2"));
|
|
1535
|
-
const existing =
|
|
1536
|
-
const context = new ContainerContext(this.options, this.scope,
|
|
1467
|
+
const existing = snapshotTree !== undefined;
|
|
1468
|
+
const context = new ContainerContext(this.options, this.scope, snapshotTree, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (content, targetClientId) => this.submitSignal(content, targetClientId), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => this.resolvedUrl?.id, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState, snapshot);
|
|
1537
1469
|
this._runtime = await PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1538
1470
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1539
1471
|
this._loadedCodeDetails = codeDetails;
|
|
@@ -1576,4 +1508,4 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1576
1508
|
}
|
|
1577
1509
|
}
|
|
1578
1510
|
}
|
|
1579
|
-
//# sourceMappingURL=container.
|
|
1511
|
+
//# sourceMappingURL=container.js.map
|