@fluidframework/container-loader 2.0.0-rc.3.0.2 → 2.0.0-rc.4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/api-report/container-loader.api.md +5 -1
- package/dist/attachment.d.ts +3 -2
- package/dist/attachment.d.ts.map +1 -1
- package/dist/attachment.js +5 -5
- package/dist/attachment.js.map +1 -1
- package/dist/audience.d.ts +6 -4
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +18 -3
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +7 -3
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +57 -38
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +31 -10
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +49 -36
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +22 -13
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +145 -117
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +3 -3
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +12 -3
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +42 -4
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +2 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.d.ts +1 -2
- package/dist/debugLogger.d.ts.map +1 -1
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +5 -6
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +29 -24
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js.map +1 -1
- package/dist/error.d.ts +1 -2
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +2 -2
- package/dist/loadPaused.d.ts +35 -0
- package/dist/loadPaused.d.ts.map +1 -0
- package/dist/loadPaused.js +115 -0
- package/dist/loadPaused.js.map +1 -0
- package/dist/loader.d.ts +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +1 -14
- package/dist/loader.js.map +1 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +4 -4
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +3 -0
- package/dist/protocol.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/retriableDocumentStorageService.d.ts +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/serializedStateManager.d.ts +89 -9
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +150 -34
- package/dist/serializedStateManager.js.map +1 -1
- package/dist/utils.d.ts +11 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +29 -14
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.ts +3 -2
- package/lib/attachment.d.ts.map +1 -1
- package/lib/attachment.js +5 -5
- package/lib/attachment.js.map +1 -1
- package/lib/audience.d.ts +6 -4
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +19 -4
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +7 -3
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +36 -17
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +31 -10
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +49 -36
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +22 -13
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +146 -118
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +3 -3
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +12 -3
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +42 -4
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +2 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.d.ts +1 -2
- package/lib/debugLogger.d.ts.map +1 -1
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +5 -6
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +10 -5
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js.map +1 -1
- package/lib/error.d.ts +1 -2
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +2 -2
- package/lib/loadPaused.d.ts +35 -0
- package/lib/loadPaused.d.ts.map +1 -0
- package/lib/loadPaused.js +111 -0
- package/lib/loadPaused.js.map +1 -0
- package/lib/loader.d.ts +1 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +3 -16
- package/lib/loader.js.map +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +3 -0
- package/lib/protocol.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/retriableDocumentStorageService.d.ts +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/serializedStateManager.d.ts +89 -9
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +146 -30
- package/lib/serializedStateManager.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/utils.d.ts +11 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +15 -1
- package/lib/utils.js.map +1 -1
- package/package.json +24 -21
- package/src/attachment.ts +12 -13
- package/src/audience.ts +30 -9
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +45 -22
- package/src/connectionStateHandler.ts +78 -45
- package/src/container.ts +181 -160
- package/src/containerContext.ts +2 -2
- package/src/containerStorageAdapter.ts +61 -6
- package/src/contracts.ts +5 -4
- package/src/debugLogger.ts +1 -1
- package/src/deltaManager.ts +15 -8
- package/src/deltaQueue.ts +1 -1
- package/src/error.ts +1 -1
- package/src/index.ts +1 -0
- package/src/loadPaused.ts +140 -0
- package/src/loader.ts +6 -23
- package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +4 -0
- package/src/retriableDocumentStorageService.ts +5 -2
- package/src/serializedStateManager.ts +215 -48
- package/src/utils.ts +19 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retriableDocumentStorageService.js","sourceRoot":"","sources":["../src/retriableDocumentStorageService.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,kEAA6D;AAS7D,oEAAqE;
|
|
1
|
+
{"version":3,"file":"retriableDocumentStorageService.js","sourceRoot":"","sources":["../src/retriableDocumentStorageService.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tFetchSource,\n\tIDocumentStorageService,\n\tIDocumentStorageServicePolicies,\n\tISnapshot,\n\tISnapshotFetchOptions,\n\tISummaryContext,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tICreateBlobResponse,\n\tISnapshotTree,\n\tISummaryHandle,\n\tISummaryTree,\n\tIVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport {\n\tITelemetryLoggerExt,\n\tGenericError,\n\tUsageError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n\tprivate _disposed = false;\n\tprivate internalStorageService: IDocumentStorageService | undefined;\n\tconstructor(\n\t\tprivate readonly internalStorageServiceP: Promise<IDocumentStorageService>,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tthis.internalStorageServiceP.then((s) => (this.internalStorageService = s)).catch(() => {});\n\t}\n\n\tpublic get policies(): IDocumentStorageServicePolicies | undefined {\n\t\tif (this.internalStorageService) {\n\t\t\treturn this.internalStorageService.policies;\n\t\t}\n\t\tthrow new Error(\"storage service not yet instantiated\");\n\t}\n\tpublic get disposed() {\n\t\treturn this._disposed;\n\t}\n\tpublic dispose() {\n\t\tthis._disposed = true;\n\t}\n\n\tpublic async getSnapshotTree(\n\t\tversion?: IVersion,\n\t\tscenarioName?: string,\n\t): Promise<ISnapshotTree | null> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getSnapshotTree(version, scenarioName),\n\t\t\t\t),\n\t\t\t\"storage_getSnapshotTree\",\n\t\t);\n\t}\n\n\tpublic async getSnapshot(snapshotFetchOptions?: ISnapshotFetchOptions): Promise<ISnapshot> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) => {\n\t\t\t\t\tif (s.getSnapshot !== undefined) {\n\t\t\t\t\t\treturn s.getSnapshot(snapshotFetchOptions);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\t\"getSnapshot api should exist on internal storage in RetriableDocStorageService class\",\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t\"storage_getSnapshot\",\n\t\t);\n\t}\n\n\tpublic async readBlob(id: string): Promise<ArrayBufferLike> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.readBlob(id)),\n\t\t\t\"storage_readBlob\",\n\t\t);\n\t}\n\n\tpublic async getVersions(\n\t\tversionId: string | null,\n\t\tcount: number,\n\t\tscenarioName?: string,\n\t\tfetchSource?: FetchSource,\n\t): Promise<IVersion[]> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getVersions(versionId, count, scenarioName, fetchSource),\n\t\t\t\t),\n\t\t\t\"storage_getVersions\",\n\t\t);\n\t}\n\n\tpublic async uploadSummaryWithContext(\n\t\tsummary: ISummaryTree,\n\t\tcontext: ISummaryContext,\n\t): Promise<string> {\n\t\t// Not using retry loop here. Couple reasons:\n\t\t// 1. If client lost connectivity, then retry loop will result in uploading stale summary\n\t\t// by stale summarizer after connectivity comes back. It will cause failures for this client and for\n\t\t// real (new) summarizer. This problem in particular should be solved in future by supplying abort handle\n\t\t// on all APIs and caller (ContainerRuntime.submitSummary) aborting call on loss of connectivity\n\t\t// 2. Similar, if we get 429 with retryAfter = 10 minutes, it's likely not the right call to retry summary\n\t\t// upload in 10 minutes - it's better to keep processing ops and retry later. Though caller needs to take\n\t\t// retryAfter into account!\n\t\t// But retry loop is required for creation flow (Container.attach)\n\t\tassert(\n\t\t\t(context.referenceSequenceNumber === 0) === (context.ackHandle === undefined),\n\t\t\t0x251 /* \"creation summary has to have seq=0 && handle === undefined\" */,\n\t\t);\n\t\tif (context.referenceSequenceNumber !== 0) {\n\t\t\treturn this.internalStorageServiceP.then(async (s) =>\n\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t);\n\t\t}\n\n\t\t// Creation flow with attachment blobs - need to do retries!\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t\t),\n\t\t\t\"storage_uploadSummaryWithContext\",\n\t\t);\n\t}\n\n\tpublic async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.downloadSummary(handle)),\n\t\t\t\"storage_downloadSummary\",\n\t\t);\n\t}\n\n\tpublic async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.createBlob(file)),\n\t\t\t\"storage_createBlob\",\n\t\t);\n\t}\n\n\tprivate checkStorageDisposed(callName: string, error: unknown) {\n\t\tif (this._disposed) {\n\t\t\tthis.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: `${callName}_abortedStorageDisposed`,\n\t\t\t\t\tfetchCallName: callName, // fetchCallName matches logs in runWithRetry.ts\n\t\t\t\t},\n\t\t\t\terror,\n\t\t\t);\n\t\t\t// pre-0.58 error message: storageServiceDisposedCannotRetry\n\t\t\tthrow new GenericError(\"Storage Service is disposed. Cannot retry\", {\n\t\t\t\tcanRetry: false,\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\n\tprivate async runWithRetry<T>(api: () => Promise<T>, callName: string): Promise<T> {\n\t\treturn runWithRetry(api, callName, this.logger, {\n\t\t\tonRetry: (_delayInMs: number, error: unknown) =>\n\t\t\t\tthis.checkStorageDisposed(callName, error),\n\t\t});\n\t}\n}\n"]}
|
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
import { IGetPendingLocalStateProps, IRuntime } from "@fluidframework/container-definitions/internal";
|
|
6
6
|
import { IDocumentStorageService, IResolvedUrl, ISnapshot } from "@fluidframework/driver-definitions/internal";
|
|
7
7
|
import { ISequencedDocumentMessage, ISnapshotTree, IVersion } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
9
8
|
import { MonitoringContext } from "@fluidframework/telemetry-utils/internal";
|
|
9
|
+
import type { ITelemetryBaseLogger, IEventProvider, IEvent } from "@fluidframework/core-interfaces";
|
|
10
10
|
import { ISerializableBlobContents } from "./containerStorageAdapter.js";
|
|
11
|
+
/**
|
|
12
|
+
* This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
|
|
13
|
+
* that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because
|
|
14
|
+
* they are already converted to string.
|
|
15
|
+
*/
|
|
11
16
|
export interface SnapshotWithBlobs {
|
|
12
17
|
/**
|
|
13
18
|
* Snapshot from which container initially loaded.
|
|
@@ -22,18 +27,34 @@ export interface SnapshotWithBlobs {
|
|
|
22
27
|
/**
|
|
23
28
|
* State saved by a container at close time, to be used to load a new instance
|
|
24
29
|
* of the container to the same state
|
|
30
|
+
*
|
|
31
|
+
* This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
|
|
32
|
+
* that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because
|
|
33
|
+
* they are already converted to string.
|
|
34
|
+
*
|
|
25
35
|
* @internal
|
|
26
36
|
*/
|
|
27
37
|
export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
38
|
+
/** This container was attached (as opposed to IPendingDetachedContainerState.attached which is false) */
|
|
28
39
|
attached: true;
|
|
40
|
+
/**
|
|
41
|
+
* Runtime-specific state that will be needed to properly rehydrate
|
|
42
|
+
* (it's included in ContainerContext passed to instantiateRuntime)
|
|
43
|
+
*/
|
|
29
44
|
pendingRuntimeState: unknown;
|
|
45
|
+
/**
|
|
46
|
+
* Any group snapshots (aka delay-loaded) we've downloaded from the service for this container
|
|
47
|
+
*/
|
|
48
|
+
loadedGroupIdSnapshots?: Record<string, ISnapshotInfo>;
|
|
30
49
|
/**
|
|
31
50
|
* All ops since base snapshot sequence number up to the latest op
|
|
32
51
|
* seen when the container was closed. Used to apply stashed (saved pending)
|
|
33
52
|
* ops at the same sequence number at which they were made.
|
|
34
53
|
*/
|
|
35
54
|
savedOps: ISequencedDocumentMessage[];
|
|
55
|
+
/** The Container's URL in the service, needed to hook up the driver during rehydration */
|
|
36
56
|
url: string;
|
|
57
|
+
/** If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected. */
|
|
37
58
|
clientId?: string;
|
|
38
59
|
}
|
|
39
60
|
/**
|
|
@@ -42,41 +63,99 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
|
42
63
|
* @internal
|
|
43
64
|
*/
|
|
44
65
|
export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
|
|
66
|
+
/** This container was not attached (as opposed to IPendingContainerState.attached which is true) */
|
|
45
67
|
attached: false;
|
|
68
|
+
/** Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage */
|
|
46
69
|
hasAttachmentBlobs: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Runtime-specific state that will be needed to properly rehydrate
|
|
72
|
+
* (it's included in ContainerContext passed to instantiateRuntime)
|
|
73
|
+
*/
|
|
47
74
|
pendingRuntimeState?: unknown;
|
|
48
75
|
}
|
|
49
76
|
export interface ISnapshotInfo extends SnapshotWithBlobs {
|
|
50
77
|
snapshotSequenceNumber: number;
|
|
78
|
+
snapshotFetchedTime?: number | undefined;
|
|
79
|
+
}
|
|
80
|
+
export type ISerializedStateManagerDocumentStorageService = Pick<IDocumentStorageService, "getSnapshot" | "getSnapshotTree" | "getVersions" | "readBlob"> & {
|
|
81
|
+
loadedGroupIdSnapshots: Record<string, ISnapshot>;
|
|
82
|
+
};
|
|
83
|
+
interface ISerializerEvent extends IEvent {
|
|
84
|
+
(event: "saved", listener: (dirty: boolean) => void): void;
|
|
51
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Helper class to manage the state of the container needed for proper serialization.
|
|
88
|
+
*
|
|
89
|
+
* It holds the pendingLocalState the container was rehydrated from (if any),
|
|
90
|
+
* as well as the snapshot to be used for serialization.
|
|
91
|
+
* It also keeps track of container dirty state and which local ops have been processed
|
|
92
|
+
*/
|
|
52
93
|
export declare class SerializedStateManager {
|
|
53
94
|
private readonly pendingLocalState;
|
|
54
95
|
private readonly storageAdapter;
|
|
55
96
|
private readonly _offlineLoadEnabled;
|
|
56
|
-
private readonly
|
|
97
|
+
private readonly containerDirty;
|
|
57
98
|
private readonly processedOps;
|
|
58
|
-
private snapshot;
|
|
59
99
|
private readonly mc;
|
|
100
|
+
private snapshot;
|
|
60
101
|
private latestSnapshot;
|
|
61
|
-
private
|
|
62
|
-
|
|
102
|
+
private refreshSnapshotP;
|
|
103
|
+
private readonly lastSavedOpSequenceNumber;
|
|
104
|
+
/**
|
|
105
|
+
* @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
|
|
106
|
+
* @param subLogger - Container's logger to use as parent for our logger
|
|
107
|
+
* @param storageAdapter - Storage adapter for fetching snapshots
|
|
108
|
+
* @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?
|
|
109
|
+
* @param containerEvent - Source of the "saved" event when the container has all its pending state uploaded
|
|
110
|
+
* @param containerDirty - Is the container "dirty"? That's the opposite of "saved" - there is pending state that may not have been received yet by the service.
|
|
111
|
+
*/
|
|
112
|
+
constructor(pendingLocalState: IPendingContainerState | undefined, subLogger: ITelemetryBaseLogger, storageAdapter: ISerializedStateManagerDocumentStorageService, _offlineLoadEnabled: boolean, containerEvent: IEventProvider<ISerializerEvent>, containerDirty: () => boolean);
|
|
63
113
|
get offlineLoadEnabled(): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage
|
|
116
|
+
*/
|
|
117
|
+
get waitForInitialRefresh(): Promise<void> | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Called whenever an incoming op is processed by the Container
|
|
120
|
+
*/
|
|
64
121
|
addProcessedOp(message: ISequencedDocumentMessage): void;
|
|
122
|
+
/**
|
|
123
|
+
* This wraps the basic functionality of fetching the snapshot for this container during Container load.
|
|
124
|
+
*
|
|
125
|
+
* If we have pendingLocalState, we get the snapshot from there.
|
|
126
|
+
* Otherwise, fetch it from storage (according to specifiedVersion if provided)
|
|
127
|
+
*
|
|
128
|
+
* @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage
|
|
129
|
+
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
|
|
130
|
+
* @returns The snapshot to boot the container from
|
|
131
|
+
*/
|
|
65
132
|
fetchSnapshot(specifiedVersion: string | undefined, supportGetSnapshotApi: boolean): Promise<{
|
|
66
|
-
baseSnapshot: ISnapshotTree;
|
|
133
|
+
baseSnapshot: ISnapshotTree | ISnapshot;
|
|
67
134
|
version: IVersion | undefined;
|
|
68
135
|
}>;
|
|
136
|
+
/**
|
|
137
|
+
* Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.
|
|
138
|
+
* Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)
|
|
139
|
+
*
|
|
140
|
+
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)
|
|
141
|
+
*/
|
|
142
|
+
private refreshLatestSnapshot;
|
|
69
143
|
/**
|
|
70
144
|
* Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.
|
|
71
145
|
*/
|
|
72
146
|
private updateSnapshotAndProcessedOpsMaybe;
|
|
73
147
|
/**
|
|
148
|
+
* When the Container attaches, we need to stash the initial snapshot (a form of the attach summary).
|
|
74
149
|
* This method is only meant to be used by Container.attach() to set the initial
|
|
75
150
|
* base snapshot when attaching.
|
|
76
|
-
* @param snapshot - snapshot and blobs collected while attaching
|
|
151
|
+
* @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
|
|
77
152
|
*/
|
|
78
153
|
setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined): void;
|
|
79
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Assembles and serializes the {@link IPendingContainerState} for the container,
|
|
156
|
+
* to be stored and used to rehydrate the container at a later time.
|
|
157
|
+
*/
|
|
158
|
+
getPendingLocalState(props: IGetPendingLocalStateProps, clientId: string | undefined, runtime: Pick<IRuntime, "getPendingLocalState">, resolvedUrl: IResolvedUrl): Promise<string>;
|
|
80
159
|
}
|
|
81
160
|
/**
|
|
82
161
|
* Retrieves the most recent snapshot and returns its info.
|
|
@@ -86,7 +165,7 @@ export declare class SerializedStateManager {
|
|
|
86
165
|
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
|
|
87
166
|
* @returns a SnapshotInfo object containing the snapshot tree, snapshot blobs and its sequence number.
|
|
88
167
|
*/
|
|
89
|
-
export declare function getLatestSnapshotInfo(mc: MonitoringContext, storageAdapter:
|
|
168
|
+
export declare function getLatestSnapshotInfo(mc: MonitoringContext, storageAdapter: ISerializedStateManagerDocumentStorageService, supportGetSnapshotApi: boolean): Promise<ISnapshotInfo | undefined>;
|
|
90
169
|
/**
|
|
91
170
|
* Fetches an ISnapshot from a storage adapter based on the specified version.
|
|
92
171
|
*
|
|
@@ -111,4 +190,5 @@ export declare function fetchISnapshotTree(mc: MonitoringContext, storageAdapter
|
|
|
111
190
|
snapshot?: ISnapshotTree;
|
|
112
191
|
version?: IVersion | undefined;
|
|
113
192
|
}>;
|
|
193
|
+
export {};
|
|
114
194
|
//# sourceMappingURL=serializedStateManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializedStateManager.d.ts","sourceRoot":"","sources":["../src/serializedStateManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,0BAA0B,EAC1B,QAAQ,EACR,MAAM,gDAAgD,CAAC;
|
|
1
|
+
{"version":3,"file":"serializedStateManager.d.ts","sourceRoot":"","sources":["../src/serializedStateManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,41 +5,85 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.fetchISnapshotTree = exports.fetchISnapshot = exports.getLatestSnapshotInfo = exports.SerializedStateManager = void 0;
|
|
8
|
+
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
8
9
|
const internal_1 = require("@fluidframework/core-utils/internal");
|
|
9
|
-
const internal_2 = require("@fluidframework/driver-
|
|
10
|
-
const internal_3 = require("@fluidframework/
|
|
10
|
+
const internal_2 = require("@fluidframework/driver-definitions/internal");
|
|
11
|
+
const internal_3 = require("@fluidframework/driver-utils/internal");
|
|
12
|
+
const internal_4 = require("@fluidframework/telemetry-utils/internal");
|
|
11
13
|
const containerStorageAdapter_js_1 = require("./containerStorageAdapter.js");
|
|
12
14
|
const utils_js_1 = require("./utils.js");
|
|
15
|
+
/**
|
|
16
|
+
* Helper class to manage the state of the container needed for proper serialization.
|
|
17
|
+
*
|
|
18
|
+
* It holds the pendingLocalState the container was rehydrated from (if any),
|
|
19
|
+
* as well as the snapshot to be used for serialization.
|
|
20
|
+
* It also keeps track of container dirty state and which local ops have been processed
|
|
21
|
+
*/
|
|
13
22
|
class SerializedStateManager {
|
|
14
|
-
|
|
23
|
+
/**
|
|
24
|
+
* @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
|
|
25
|
+
* @param subLogger - Container's logger to use as parent for our logger
|
|
26
|
+
* @param storageAdapter - Storage adapter for fetching snapshots
|
|
27
|
+
* @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?
|
|
28
|
+
* @param containerEvent - Source of the "saved" event when the container has all its pending state uploaded
|
|
29
|
+
* @param containerDirty - Is the container "dirty"? That's the opposite of "saved" - there is pending state that may not have been received yet by the service.
|
|
30
|
+
*/
|
|
31
|
+
constructor(pendingLocalState, subLogger, storageAdapter, _offlineLoadEnabled, containerEvent, containerDirty) {
|
|
15
32
|
this.pendingLocalState = pendingLocalState;
|
|
16
33
|
this.storageAdapter = storageAdapter;
|
|
17
34
|
this._offlineLoadEnabled = _offlineLoadEnabled;
|
|
18
|
-
this.
|
|
35
|
+
this.containerDirty = containerDirty;
|
|
19
36
|
this.processedOps = [];
|
|
20
|
-
this.
|
|
37
|
+
this.lastSavedOpSequenceNumber = 0;
|
|
38
|
+
this.mc = (0, internal_4.createChildMonitoringContext)({
|
|
21
39
|
logger: subLogger,
|
|
22
40
|
namespace: "serializedStateManager",
|
|
23
41
|
});
|
|
42
|
+
if (pendingLocalState && pendingLocalState.savedOps.length > 0) {
|
|
43
|
+
const savedOpsSize = pendingLocalState.savedOps.length;
|
|
44
|
+
this.lastSavedOpSequenceNumber =
|
|
45
|
+
pendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;
|
|
46
|
+
}
|
|
47
|
+
containerEvent.once("saved", () => this.updateSnapshotAndProcessedOpsMaybe());
|
|
24
48
|
}
|
|
25
49
|
get offlineLoadEnabled() {
|
|
26
50
|
return this._offlineLoadEnabled;
|
|
27
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage
|
|
54
|
+
*/
|
|
55
|
+
get waitForInitialRefresh() {
|
|
56
|
+
return this.refreshSnapshotP;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Called whenever an incoming op is processed by the Container
|
|
60
|
+
*/
|
|
28
61
|
addProcessedOp(message) {
|
|
29
62
|
if (this.offlineLoadEnabled) {
|
|
30
63
|
this.processedOps.push(message);
|
|
31
64
|
this.updateSnapshotAndProcessedOpsMaybe();
|
|
32
65
|
}
|
|
33
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* This wraps the basic functionality of fetching the snapshot for this container during Container load.
|
|
69
|
+
*
|
|
70
|
+
* If we have pendingLocalState, we get the snapshot from there.
|
|
71
|
+
* Otherwise, fetch it from storage (according to specifiedVersion if provided)
|
|
72
|
+
*
|
|
73
|
+
* @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage
|
|
74
|
+
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
|
|
75
|
+
* @returns The snapshot to boot the container from
|
|
76
|
+
*/
|
|
34
77
|
async fetchSnapshot(specifiedVersion, supportGetSnapshotApi) {
|
|
35
78
|
if (this.pendingLocalState === undefined) {
|
|
36
|
-
const { baseSnapshot, version } = await
|
|
79
|
+
const { baseSnapshot, version } = await getSnapshot(this.mc, this.storageAdapter, supportGetSnapshotApi, specifiedVersion);
|
|
80
|
+
const baseSnapshotTree = (0, internal_3.getSnapshotTree)(baseSnapshot);
|
|
37
81
|
// non-interactive clients will not have any pending state we want to save
|
|
38
82
|
if (this.offlineLoadEnabled) {
|
|
39
|
-
const snapshotBlobs = await (0, containerStorageAdapter_js_1.getBlobContentsFromTree)(
|
|
40
|
-
const attributes = await (0, utils_js_1.getDocumentAttributes)(this.storageAdapter,
|
|
83
|
+
const snapshotBlobs = await (0, containerStorageAdapter_js_1.getBlobContentsFromTree)(baseSnapshotTree, this.storageAdapter);
|
|
84
|
+
const attributes = await (0, utils_js_1.getDocumentAttributes)(this.storageAdapter, baseSnapshotTree);
|
|
41
85
|
this.snapshot = {
|
|
42
|
-
baseSnapshot,
|
|
86
|
+
baseSnapshot: baseSnapshotTree,
|
|
43
87
|
snapshotBlobs,
|
|
44
88
|
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
45
89
|
};
|
|
@@ -54,19 +98,66 @@ class SerializedStateManager {
|
|
|
54
98
|
snapshotBlobs,
|
|
55
99
|
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
56
100
|
};
|
|
57
|
-
|
|
58
|
-
this.
|
|
59
|
-
this
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
|
|
101
|
+
if (this.refreshSnapshotP === undefined &&
|
|
102
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true) {
|
|
103
|
+
// Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
|
|
104
|
+
this.refreshSnapshotP = this.refreshLatestSnapshot(supportGetSnapshotApi);
|
|
105
|
+
this.refreshSnapshotP.catch((e) => {
|
|
106
|
+
this.mc.logger.sendErrorEvent({
|
|
107
|
+
eventName: "RefreshLatestSnapshotFailed",
|
|
108
|
+
error: e,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const blobContents = new Map();
|
|
113
|
+
for (const [id, value] of Object.entries(snapshotBlobs)) {
|
|
114
|
+
blobContents.set(id, (0, client_utils_1.stringToBuffer)(value, "utf8"));
|
|
115
|
+
}
|
|
116
|
+
const iSnapshot = {
|
|
117
|
+
sequenceNumber: this.snapshot.snapshotSequenceNumber,
|
|
118
|
+
snapshotTree: baseSnapshot,
|
|
119
|
+
blobContents,
|
|
120
|
+
latestSequenceNumber: undefined,
|
|
121
|
+
ops: [],
|
|
122
|
+
snapshotFormatV: 1,
|
|
123
|
+
};
|
|
124
|
+
return { baseSnapshot: iSnapshot, version: undefined };
|
|
63
125
|
}
|
|
64
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.
|
|
129
|
+
* Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)
|
|
130
|
+
*
|
|
131
|
+
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)
|
|
132
|
+
*/
|
|
133
|
+
async refreshLatestSnapshot(supportGetSnapshotApi) {
|
|
134
|
+
this.latestSnapshot = await getLatestSnapshotInfo(this.mc, this.storageAdapter, supportGetSnapshotApi);
|
|
135
|
+
// These are loading groupIds that the containerRuntime has requested over its lifetime.
|
|
136
|
+
// We will fetch the latest snapshot for the groupIds, which will update storageAdapter.loadedGroupIdSnapshots's cache
|
|
137
|
+
const downloadedGroupIds = Object.keys(this.storageAdapter.loadedGroupIdSnapshots);
|
|
138
|
+
if (supportGetSnapshotApi && downloadedGroupIds.length > 0) {
|
|
139
|
+
(0, internal_1.assert)(this.storageAdapter.getSnapshot !== undefined, 0x972 /* getSnapshot should exist */);
|
|
140
|
+
// (This is a separate network call from above because it requires work for storage to add a special base groupId)
|
|
141
|
+
const snapshot = await this.storageAdapter.getSnapshot({
|
|
142
|
+
versionId: undefined,
|
|
143
|
+
scenarioName: "getLatestSnapshotInfo",
|
|
144
|
+
cacheSnapshot: false,
|
|
145
|
+
loadingGroupIds: downloadedGroupIds,
|
|
146
|
+
fetchSource: internal_2.FetchSource.noCache,
|
|
147
|
+
});
|
|
148
|
+
(0, internal_1.assert)(snapshot !== undefined, 0x973 /* Snapshot should exist */);
|
|
149
|
+
}
|
|
150
|
+
this.updateSnapshotAndProcessedOpsMaybe();
|
|
151
|
+
}
|
|
65
152
|
/**
|
|
66
153
|
* Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.
|
|
67
154
|
*/
|
|
68
155
|
updateSnapshotAndProcessedOpsMaybe() {
|
|
69
|
-
if (this.latestSnapshot === undefined ||
|
|
156
|
+
if (this.latestSnapshot === undefined ||
|
|
157
|
+
this.processedOps.length === 0 ||
|
|
158
|
+
this.processedOps[this.processedOps.length - 1].sequenceNumber <
|
|
159
|
+
this.lastSavedOpSequenceNumber ||
|
|
160
|
+
this.containerDirty()) {
|
|
70
161
|
// can't refresh latest snapshot until we have processed the ops up to it.
|
|
71
162
|
// Pending state would be behind the latest snapshot.
|
|
72
163
|
return;
|
|
@@ -103,9 +194,10 @@ class SerializedStateManager {
|
|
|
103
194
|
}
|
|
104
195
|
}
|
|
105
196
|
/**
|
|
197
|
+
* When the Container attaches, we need to stash the initial snapshot (a form of the attach summary).
|
|
106
198
|
* This method is only meant to be used by Container.attach() to set the initial
|
|
107
199
|
* base snapshot when attaching.
|
|
108
|
-
* @param snapshot - snapshot and blobs collected while attaching
|
|
200
|
+
* @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
|
|
109
201
|
*/
|
|
110
202
|
setInitialSnapshot(snapshot) {
|
|
111
203
|
if (this.offlineLoadEnabled) {
|
|
@@ -120,27 +212,47 @@ class SerializedStateManager {
|
|
|
120
212
|
this.snapshot = { ...snapshot, snapshotSequenceNumber: attributes.sequenceNumber };
|
|
121
213
|
}
|
|
122
214
|
}
|
|
123
|
-
|
|
124
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Assembles and serializes the {@link IPendingContainerState} for the container,
|
|
217
|
+
* to be stored and used to rehydrate the container at a later time.
|
|
218
|
+
*/
|
|
219
|
+
async getPendingLocalState(props, clientId, runtime, resolvedUrl) {
|
|
220
|
+
return internal_4.PerformanceEvent.timedExecAsync(this.mc.logger, {
|
|
125
221
|
eventName: "getPendingLocalState",
|
|
126
222
|
notifyImminentClosure: props.notifyImminentClosure,
|
|
127
223
|
processedOpsSize: this.processedOps.length,
|
|
128
224
|
clientId,
|
|
129
225
|
}, async () => {
|
|
130
226
|
if (!this.offlineLoadEnabled) {
|
|
131
|
-
throw new
|
|
227
|
+
throw new internal_4.UsageError("Can't get pending local state unless offline load is enabled");
|
|
132
228
|
}
|
|
133
229
|
(0, internal_1.assert)(this.snapshot !== undefined, 0x8e5 /* no base data */);
|
|
134
|
-
const pendingRuntimeState = await runtime.getPendingLocalState(
|
|
230
|
+
const pendingRuntimeState = await runtime.getPendingLocalState({
|
|
231
|
+
...props,
|
|
232
|
+
snapshotSequenceNumber: this.snapshot.snapshotSequenceNumber,
|
|
233
|
+
sessionExpiryTimerStarted: this.snapshot.snapshotFetchedTime,
|
|
234
|
+
});
|
|
235
|
+
// This conversion is required because ArrayBufferLike doesn't survive JSON.stringify
|
|
236
|
+
const loadedGroupIdSnapshots = {};
|
|
237
|
+
let hasGroupIdSnapshots = false;
|
|
238
|
+
const groupIdSnapshots = Object.entries(this.storageAdapter.loadedGroupIdSnapshots);
|
|
239
|
+
if (groupIdSnapshots.length > 0) {
|
|
240
|
+
for (const [groupId, snapshot] of groupIdSnapshots) {
|
|
241
|
+
hasGroupIdSnapshots = true;
|
|
242
|
+
loadedGroupIdSnapshots[groupId] = (0, utils_js_1.convertSnapshotToSnapshotInfo)(snapshot);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
135
245
|
const pendingState = {
|
|
136
246
|
attached: true,
|
|
137
247
|
pendingRuntimeState,
|
|
138
248
|
baseSnapshot: this.snapshot.baseSnapshot,
|
|
139
249
|
snapshotBlobs: this.snapshot.snapshotBlobs,
|
|
250
|
+
loadedGroupIdSnapshots: hasGroupIdSnapshots
|
|
251
|
+
? loadedGroupIdSnapshots
|
|
252
|
+
: undefined,
|
|
140
253
|
savedOps: this.processedOps,
|
|
141
254
|
url: resolvedUrl.url,
|
|
142
|
-
|
|
143
|
-
clientId: pendingRuntimeState !== undefined ? clientId : undefined,
|
|
255
|
+
clientId,
|
|
144
256
|
};
|
|
145
257
|
return JSON.stringify(pendingState);
|
|
146
258
|
});
|
|
@@ -156,12 +268,19 @@ exports.SerializedStateManager = SerializedStateManager;
|
|
|
156
268
|
* @returns a SnapshotInfo object containing the snapshot tree, snapshot blobs and its sequence number.
|
|
157
269
|
*/
|
|
158
270
|
async function getLatestSnapshotInfo(mc, storageAdapter, supportGetSnapshotApi) {
|
|
159
|
-
return
|
|
160
|
-
const { baseSnapshot } = await
|
|
161
|
-
const
|
|
162
|
-
const
|
|
271
|
+
return internal_4.PerformanceEvent.timedExecAsync(mc.logger, { eventName: "GetLatestSnapshotInfo" }, async () => {
|
|
272
|
+
const { baseSnapshot } = await getSnapshot(mc, storageAdapter, supportGetSnapshotApi, undefined);
|
|
273
|
+
const baseSnapshotTree = (0, internal_3.getSnapshotTree)(baseSnapshot);
|
|
274
|
+
const snapshotFetchedTime = Date.now();
|
|
275
|
+
const snapshotBlobs = await (0, containerStorageAdapter_js_1.getBlobContentsFromTree)(baseSnapshotTree, storageAdapter);
|
|
276
|
+
const attributes = await (0, utils_js_1.getDocumentAttributes)(storageAdapter, baseSnapshotTree);
|
|
163
277
|
const snapshotSequenceNumber = attributes.sequenceNumber;
|
|
164
|
-
return {
|
|
278
|
+
return {
|
|
279
|
+
baseSnapshot: baseSnapshotTree,
|
|
280
|
+
snapshotBlobs,
|
|
281
|
+
snapshotSequenceNumber,
|
|
282
|
+
snapshotFetchedTime,
|
|
283
|
+
};
|
|
165
284
|
}).catch(() => undefined);
|
|
166
285
|
}
|
|
167
286
|
exports.getLatestSnapshotInfo = getLatestSnapshotInfo;
|
|
@@ -174,15 +293,12 @@ exports.getLatestSnapshotInfo = getLatestSnapshotInfo;
|
|
|
174
293
|
* @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.
|
|
175
294
|
* @returns - An ISnapshotTree and its version.
|
|
176
295
|
*/
|
|
177
|
-
async function
|
|
296
|
+
async function getSnapshot(mc, storageAdapter, supportGetSnapshotApi, specifiedVersion) {
|
|
178
297
|
const { snapshot, version } = supportGetSnapshotApi
|
|
179
298
|
? await fetchISnapshot(mc, storageAdapter, specifiedVersion)
|
|
180
299
|
: await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
: snapshot;
|
|
184
|
-
(0, internal_1.assert)(baseSnapshot !== undefined, 0x8e4 /* Snapshot should exist */);
|
|
185
|
-
return { baseSnapshot, version };
|
|
300
|
+
(0, internal_1.assert)(snapshot !== undefined, 0x8e4 /* Snapshot should exist */);
|
|
301
|
+
return { baseSnapshot: snapshot, version };
|
|
186
302
|
}
|
|
187
303
|
/**
|
|
188
304
|
* Fetches an ISnapshot from a storage adapter based on the specified version.
|