@fluidframework/container-loader 2.0.0-dev-rc.4.0.0.261659 → 2.0.0-dev-rc.5.0.0.265721
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/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 +2 -1
- package/dist/audience.d.ts.map +1 -1
- 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 +2 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +28 -28
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +2 -2
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +7 -3
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +16 -3
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +28 -13
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +2 -2
- 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.js.map +1 -1
- package/dist/deltaManager.d.ts +3 -2
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +30 -30
- 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/disposal.js.map +1 -1
- package/dist/loadPaused.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +1 -1
- 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/noopHeuristic.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 +2 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/serializedStateManager.d.ts +70 -8
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +98 -28
- 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 +2 -1
- package/lib/audience.d.ts.map +1 -1
- 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 +2 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +5 -5
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +2 -2
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +7 -3
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +16 -3
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +29 -14
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -2
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +2 -2
- 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.js.map +1 -1
- package/lib/deltaManager.d.ts +3 -2
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +8 -8
- 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/disposal.js.map +1 -1
- package/lib/loadPaused.js.map +1 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +2 -2
- 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/noopHeuristic.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 +2 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js.map +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/serializedStateManager.d.ts +70 -8
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +99 -29
- 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 +19 -27
- package/src/attachment.ts +12 -13
- package/src/audience.ts +2 -5
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +4 -6
- package/src/connectionStateHandler.ts +10 -6
- package/src/container.ts +38 -14
- package/src/containerContext.ts +1 -1
- package/src/containerStorageAdapter.ts +5 -2
- package/src/contracts.ts +5 -4
- package/src/deltaManager.ts +3 -3
- package/src/deltaQueue.ts +1 -1
- package/src/loader.ts +5 -2
- package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/serializedStateManager.ts +137 -36
- package/src/utils.ts +19 -1
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
IGetPendingLocalStateProps,
|
|
8
8
|
IRuntime,
|
|
9
9
|
} from "@fluidframework/container-definitions/internal";
|
|
10
|
+
import { stringToBuffer } from "@fluid-internal/client-utils";
|
|
10
11
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
11
12
|
import {
|
|
12
13
|
FetchSource,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
IResolvedUrl,
|
|
15
16
|
ISnapshot,
|
|
16
17
|
} from "@fluidframework/driver-definitions/internal";
|
|
17
|
-
import {
|
|
18
|
+
import { getSnapshotTree } from "@fluidframework/driver-utils/internal";
|
|
18
19
|
import {
|
|
19
20
|
type IDocumentAttributes,
|
|
20
21
|
ISequencedDocumentMessage,
|
|
@@ -27,11 +28,15 @@ import {
|
|
|
27
28
|
UsageError,
|
|
28
29
|
createChildMonitoringContext,
|
|
29
30
|
} from "@fluidframework/telemetry-utils/internal";
|
|
30
|
-
import type { IEventProvider, IEvent } from "@fluidframework/core-interfaces";
|
|
31
|
-
import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
31
|
+
import type { ITelemetryBaseLogger, IEventProvider, IEvent } from "@fluidframework/core-interfaces";
|
|
32
32
|
import { ISerializableBlobContents, getBlobContentsFromTree } from "./containerStorageAdapter.js";
|
|
33
33
|
import { convertSnapshotToSnapshotInfo, getDocumentAttributes } from "./utils.js";
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
|
|
37
|
+
* that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because
|
|
38
|
+
* they are already converted to string.
|
|
39
|
+
*/
|
|
35
40
|
export interface SnapshotWithBlobs {
|
|
36
41
|
/**
|
|
37
42
|
* Snapshot from which container initially loaded.
|
|
@@ -43,19 +48,28 @@ export interface SnapshotWithBlobs {
|
|
|
43
48
|
*/
|
|
44
49
|
snapshotBlobs: ISerializableBlobContents;
|
|
45
50
|
}
|
|
51
|
+
|
|
46
52
|
/**
|
|
47
53
|
* State saved by a container at close time, to be used to load a new instance
|
|
48
54
|
* of the container to the same state
|
|
49
55
|
*
|
|
50
56
|
* This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
|
|
51
|
-
* that the blobs of ISnapshot of
|
|
57
|
+
* that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because
|
|
52
58
|
* they are already converted to string.
|
|
53
59
|
*
|
|
54
60
|
* @internal
|
|
55
61
|
*/
|
|
56
62
|
export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
63
|
+
/** This container was attached (as opposed to IPendingDetachedContainerState.attached which is false) */
|
|
57
64
|
attached: true;
|
|
65
|
+
/**
|
|
66
|
+
* Runtime-specific state that will be needed to properly rehydrate
|
|
67
|
+
* (it's included in ContainerContext passed to instantiateRuntime)
|
|
68
|
+
*/
|
|
58
69
|
pendingRuntimeState: unknown;
|
|
70
|
+
/**
|
|
71
|
+
* Any group snapshots (aka delay-loaded) we've downloaded from the service for this container
|
|
72
|
+
*/
|
|
59
73
|
loadedGroupIdSnapshots?: Record<string, ISnapshotInfo>;
|
|
60
74
|
/**
|
|
61
75
|
* All ops since base snapshot sequence number up to the latest op
|
|
@@ -63,7 +77,9 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
|
63
77
|
* ops at the same sequence number at which they were made.
|
|
64
78
|
*/
|
|
65
79
|
savedOps: ISequencedDocumentMessage[];
|
|
80
|
+
/** The Container's URL in the service, needed to hook up the driver during rehydration */
|
|
66
81
|
url: string;
|
|
82
|
+
/** If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected. */
|
|
67
83
|
clientId?: string;
|
|
68
84
|
}
|
|
69
85
|
|
|
@@ -73,8 +89,14 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
|
73
89
|
* @internal
|
|
74
90
|
*/
|
|
75
91
|
export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
|
|
92
|
+
/** This container was not attached (as opposed to IPendingContainerState.attached which is true) */
|
|
76
93
|
attached: false;
|
|
94
|
+
/** Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage */
|
|
77
95
|
hasAttachmentBlobs: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Runtime-specific state that will be needed to properly rehydrate
|
|
98
|
+
* (it's included in ContainerContext passed to instantiateRuntime)
|
|
99
|
+
*/
|
|
78
100
|
pendingRuntimeState?: unknown;
|
|
79
101
|
}
|
|
80
102
|
|
|
@@ -94,14 +116,29 @@ interface ISerializerEvent extends IEvent {
|
|
|
94
116
|
(event: "saved", listener: (dirty: boolean) => void): void;
|
|
95
117
|
}
|
|
96
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Helper class to manage the state of the container needed for proper serialization.
|
|
121
|
+
*
|
|
122
|
+
* It holds the pendingLocalState the container was rehydrated from (if any),
|
|
123
|
+
* as well as the snapshot to be used for serialization.
|
|
124
|
+
* It also keeps track of container dirty state and which local ops have been processed
|
|
125
|
+
*/
|
|
97
126
|
export class SerializedStateManager {
|
|
98
127
|
private readonly processedOps: ISequencedDocumentMessage[] = [];
|
|
99
128
|
private readonly mc: MonitoringContext;
|
|
100
129
|
private snapshot: ISnapshotInfo | undefined;
|
|
101
130
|
private latestSnapshot: ISnapshotInfo | undefined;
|
|
102
|
-
private
|
|
131
|
+
private refreshSnapshotP: Promise<void> | undefined;
|
|
103
132
|
private readonly lastSavedOpSequenceNumber: number = 0;
|
|
104
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
|
|
136
|
+
* @param subLogger - Container's logger to use as parent for our logger
|
|
137
|
+
* @param storageAdapter - Storage adapter for fetching snapshots
|
|
138
|
+
* @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?
|
|
139
|
+
* @param containerEvent - Source of the "saved" event when the container has all its pending state uploaded
|
|
140
|
+
* @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.
|
|
141
|
+
*/
|
|
105
142
|
constructor(
|
|
106
143
|
private readonly pendingLocalState: IPendingContainerState | undefined,
|
|
107
144
|
subLogger: ITelemetryBaseLogger,
|
|
@@ -127,10 +164,16 @@ export class SerializedStateManager {
|
|
|
127
164
|
return this._offlineLoadEnabled;
|
|
128
165
|
}
|
|
129
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage
|
|
169
|
+
*/
|
|
130
170
|
public get waitForInitialRefresh(): Promise<void> | undefined {
|
|
131
|
-
return this.
|
|
171
|
+
return this.refreshSnapshotP;
|
|
132
172
|
}
|
|
133
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Called whenever an incoming op is processed by the Container
|
|
176
|
+
*/
|
|
134
177
|
public addProcessedOp(message: ISequencedDocumentMessage) {
|
|
135
178
|
if (this.offlineLoadEnabled) {
|
|
136
179
|
this.processedOps.push(message);
|
|
@@ -138,26 +181,40 @@ export class SerializedStateManager {
|
|
|
138
181
|
}
|
|
139
182
|
}
|
|
140
183
|
|
|
184
|
+
/**
|
|
185
|
+
* This wraps the basic functionality of fetching the snapshot for this container during Container load.
|
|
186
|
+
*
|
|
187
|
+
* If we have pendingLocalState, we get the snapshot from there.
|
|
188
|
+
* Otherwise, fetch it from storage (according to specifiedVersion if provided)
|
|
189
|
+
*
|
|
190
|
+
* @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage
|
|
191
|
+
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
|
|
192
|
+
* @returns The snapshot to boot the container from
|
|
193
|
+
*/
|
|
141
194
|
public async fetchSnapshot(
|
|
142
195
|
specifiedVersion: string | undefined,
|
|
143
196
|
supportGetSnapshotApi: boolean,
|
|
144
197
|
) {
|
|
145
198
|
if (this.pendingLocalState === undefined) {
|
|
146
|
-
const { baseSnapshot, version } = await
|
|
199
|
+
const { baseSnapshot, version } = await getSnapshot(
|
|
147
200
|
this.mc,
|
|
148
201
|
this.storageAdapter,
|
|
149
202
|
supportGetSnapshotApi,
|
|
150
203
|
specifiedVersion,
|
|
151
204
|
);
|
|
205
|
+
const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);
|
|
152
206
|
// non-interactive clients will not have any pending state we want to save
|
|
153
207
|
if (this.offlineLoadEnabled) {
|
|
154
208
|
const snapshotBlobs = await getBlobContentsFromTree(
|
|
155
|
-
|
|
209
|
+
baseSnapshotTree,
|
|
210
|
+
this.storageAdapter,
|
|
211
|
+
);
|
|
212
|
+
const attributes = await getDocumentAttributes(
|
|
156
213
|
this.storageAdapter,
|
|
214
|
+
baseSnapshotTree,
|
|
157
215
|
);
|
|
158
|
-
const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);
|
|
159
216
|
this.snapshot = {
|
|
160
|
-
baseSnapshot,
|
|
217
|
+
baseSnapshot: baseSnapshotTree,
|
|
161
218
|
snapshotBlobs,
|
|
162
219
|
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
163
220
|
};
|
|
@@ -172,16 +229,43 @@ export class SerializedStateManager {
|
|
|
172
229
|
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
173
230
|
};
|
|
174
231
|
|
|
175
|
-
if (
|
|
176
|
-
this.
|
|
177
|
-
|
|
178
|
-
|
|
232
|
+
if (
|
|
233
|
+
this.refreshSnapshotP === undefined &&
|
|
234
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true
|
|
235
|
+
) {
|
|
236
|
+
// Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
|
|
237
|
+
this.refreshSnapshotP = this.refreshLatestSnapshot(supportGetSnapshotApi);
|
|
238
|
+
this.refreshSnapshotP.catch((e) => {
|
|
239
|
+
this.mc.logger.sendErrorEvent({
|
|
240
|
+
eventName: "RefreshLatestSnapshotFailed",
|
|
241
|
+
error: e,
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
}
|
|
179
245
|
|
|
180
|
-
|
|
246
|
+
const blobContents = new Map<string, ArrayBuffer>();
|
|
247
|
+
for (const [id, value] of Object.entries(snapshotBlobs)) {
|
|
248
|
+
blobContents.set(id, stringToBuffer(value, "utf8"));
|
|
249
|
+
}
|
|
250
|
+
const iSnapshot: ISnapshot = {
|
|
251
|
+
sequenceNumber: this.snapshot.snapshotSequenceNumber,
|
|
252
|
+
snapshotTree: baseSnapshot,
|
|
253
|
+
blobContents,
|
|
254
|
+
latestSequenceNumber: undefined,
|
|
255
|
+
ops: [],
|
|
256
|
+
snapshotFormatV: 1,
|
|
257
|
+
};
|
|
258
|
+
return { baseSnapshot: iSnapshot, version: undefined };
|
|
181
259
|
}
|
|
182
260
|
}
|
|
183
261
|
|
|
184
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.
|
|
264
|
+
* Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)
|
|
265
|
+
*
|
|
266
|
+
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)
|
|
267
|
+
*/
|
|
268
|
+
private async refreshLatestSnapshot(supportGetSnapshotApi: boolean): Promise<void> {
|
|
185
269
|
this.latestSnapshot = await getLatestSnapshotInfo(
|
|
186
270
|
this.mc,
|
|
187
271
|
this.storageAdapter,
|
|
@@ -189,10 +273,14 @@ export class SerializedStateManager {
|
|
|
189
273
|
);
|
|
190
274
|
|
|
191
275
|
// These are loading groupIds that the containerRuntime has requested over its lifetime.
|
|
276
|
+
// We will fetch the latest snapshot for the groupIds, which will update storageAdapter.loadedGroupIdSnapshots's cache
|
|
192
277
|
const downloadedGroupIds = Object.keys(this.storageAdapter.loadedGroupIdSnapshots);
|
|
193
|
-
// We are making two network calls because it requires work for storage to add a special base groupId.
|
|
194
278
|
if (supportGetSnapshotApi && downloadedGroupIds.length > 0) {
|
|
195
|
-
assert(
|
|
279
|
+
assert(
|
|
280
|
+
this.storageAdapter.getSnapshot !== undefined,
|
|
281
|
+
0x972 /* getSnapshot should exist */,
|
|
282
|
+
);
|
|
283
|
+
// (This is a separate network call from above because it requires work for storage to add a special base groupId)
|
|
196
284
|
const snapshot = await this.storageAdapter.getSnapshot({
|
|
197
285
|
versionId: undefined,
|
|
198
286
|
scenarioName: "getLatestSnapshotInfo",
|
|
@@ -200,9 +288,9 @@ export class SerializedStateManager {
|
|
|
200
288
|
loadingGroupIds: downloadedGroupIds,
|
|
201
289
|
fetchSource: FetchSource.noCache,
|
|
202
290
|
});
|
|
203
|
-
assert(snapshot !== undefined,
|
|
204
|
-
return convertSnapshotToSnapshotInfo(snapshot);
|
|
291
|
+
assert(snapshot !== undefined, 0x973 /* Snapshot should exist */);
|
|
205
292
|
}
|
|
293
|
+
|
|
206
294
|
this.updateSnapshotAndProcessedOpsMaybe();
|
|
207
295
|
}
|
|
208
296
|
|
|
@@ -259,9 +347,10 @@ export class SerializedStateManager {
|
|
|
259
347
|
}
|
|
260
348
|
|
|
261
349
|
/**
|
|
350
|
+
* When the Container attaches, we need to stash the initial snapshot (a form of the attach summary).
|
|
262
351
|
* This method is only meant to be used by Container.attach() to set the initial
|
|
263
352
|
* base snapshot when attaching.
|
|
264
|
-
* @param snapshot - snapshot and blobs collected while attaching
|
|
353
|
+
* @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
|
|
265
354
|
*/
|
|
266
355
|
public setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined) {
|
|
267
356
|
if (this.offlineLoadEnabled) {
|
|
@@ -284,18 +373,26 @@ export class SerializedStateManager {
|
|
|
284
373
|
}
|
|
285
374
|
}
|
|
286
375
|
|
|
287
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Assembles and serializes the {@link IPendingContainerState} for the container,
|
|
378
|
+
* to be stored and used to rehydrate the container at a later time.
|
|
379
|
+
*/
|
|
380
|
+
public async getPendingLocalState(
|
|
288
381
|
props: IGetPendingLocalStateProps,
|
|
289
382
|
clientId: string | undefined,
|
|
290
383
|
runtime: Pick<IRuntime, "getPendingLocalState">,
|
|
291
384
|
resolvedUrl: IResolvedUrl,
|
|
292
|
-
) {
|
|
385
|
+
): Promise<string> {
|
|
293
386
|
return PerformanceEvent.timedExecAsync(
|
|
294
387
|
this.mc.logger,
|
|
295
388
|
{
|
|
296
389
|
eventName: "getPendingLocalState",
|
|
297
|
-
|
|
298
|
-
|
|
390
|
+
details: {
|
|
391
|
+
notifyImminentClosure: props.notifyImminentClosure,
|
|
392
|
+
sessionExpiryTimerStarted: props.sessionExpiryTimerStarted,
|
|
393
|
+
snapshotSequenceNumber: props.snapshotSequenceNumber,
|
|
394
|
+
processedOpsSize: this.processedOps.length,
|
|
395
|
+
},
|
|
299
396
|
clientId,
|
|
300
397
|
},
|
|
301
398
|
async () => {
|
|
@@ -356,20 +453,27 @@ export async function getLatestSnapshotInfo(
|
|
|
356
453
|
mc.logger,
|
|
357
454
|
{ eventName: "GetLatestSnapshotInfo" },
|
|
358
455
|
async () => {
|
|
359
|
-
const { baseSnapshot } = await
|
|
456
|
+
const { baseSnapshot } = await getSnapshot(
|
|
360
457
|
mc,
|
|
361
458
|
storageAdapter,
|
|
362
459
|
supportGetSnapshotApi,
|
|
363
460
|
undefined,
|
|
364
461
|
);
|
|
462
|
+
|
|
463
|
+
const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);
|
|
365
464
|
const snapshotFetchedTime = Date.now();
|
|
366
|
-
const snapshotBlobs = await getBlobContentsFromTree(
|
|
465
|
+
const snapshotBlobs = await getBlobContentsFromTree(baseSnapshotTree, storageAdapter);
|
|
367
466
|
const attributes: IDocumentAttributes = await getDocumentAttributes(
|
|
368
467
|
storageAdapter,
|
|
369
|
-
|
|
468
|
+
baseSnapshotTree,
|
|
370
469
|
);
|
|
371
470
|
const snapshotSequenceNumber = attributes.sequenceNumber;
|
|
372
|
-
return {
|
|
471
|
+
return {
|
|
472
|
+
baseSnapshot: baseSnapshotTree,
|
|
473
|
+
snapshotBlobs,
|
|
474
|
+
snapshotSequenceNumber,
|
|
475
|
+
snapshotFetchedTime,
|
|
476
|
+
};
|
|
373
477
|
},
|
|
374
478
|
).catch(() => undefined);
|
|
375
479
|
}
|
|
@@ -383,7 +487,7 @@ export async function getLatestSnapshotInfo(
|
|
|
383
487
|
* @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.
|
|
384
488
|
* @returns - An ISnapshotTree and its version.
|
|
385
489
|
*/
|
|
386
|
-
async function
|
|
490
|
+
async function getSnapshot(
|
|
387
491
|
mc: MonitoringContext,
|
|
388
492
|
storageAdapter: Pick<
|
|
389
493
|
IDocumentStorageService,
|
|
@@ -391,15 +495,12 @@ async function getSnapshotTree(
|
|
|
391
495
|
>,
|
|
392
496
|
supportGetSnapshotApi: boolean,
|
|
393
497
|
specifiedVersion: string | undefined,
|
|
394
|
-
): Promise<{ baseSnapshot: ISnapshotTree; version?: IVersion }> {
|
|
498
|
+
): Promise<{ baseSnapshot: ISnapshot | ISnapshotTree; version?: IVersion }> {
|
|
395
499
|
const { snapshot, version } = supportGetSnapshotApi
|
|
396
500
|
? await fetchISnapshot(mc, storageAdapter, specifiedVersion)
|
|
397
501
|
: await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
: snapshot;
|
|
401
|
-
assert(baseSnapshot !== undefined, 0x8e4 /* Snapshot should exist */);
|
|
402
|
-
return { baseSnapshot, version };
|
|
502
|
+
assert(snapshot !== undefined, 0x8e4 /* Snapshot should exist */);
|
|
503
|
+
return { baseSnapshot: snapshot, version };
|
|
403
504
|
}
|
|
404
505
|
|
|
405
506
|
/**
|
package/src/utils.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { Uint8ArrayToString, bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
|
|
7
7
|
import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
8
|
-
import { DriverErrorTypes } from "@fluidframework/driver-definitions";
|
|
8
|
+
import { DriverErrorTypes } from "@fluidframework/driver-definitions/internal";
|
|
9
9
|
import {
|
|
10
10
|
IDocumentStorageService,
|
|
11
11
|
type ISnapshot,
|
|
@@ -27,6 +27,7 @@ import { v4 as uuid } from "uuid";
|
|
|
27
27
|
|
|
28
28
|
import { ISerializableBlobContents } from "./containerStorageAdapter.js";
|
|
29
29
|
import type {
|
|
30
|
+
IPendingContainerState,
|
|
30
31
|
IPendingDetachedContainerState,
|
|
31
32
|
ISnapshotInfo,
|
|
32
33
|
SnapshotWithBlobs,
|
|
@@ -313,6 +314,11 @@ function isPendingDetachedContainerState(
|
|
|
313
314
|
return true;
|
|
314
315
|
}
|
|
315
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Parses the given string into {@link IPendingDetachedContainerState} format,
|
|
319
|
+
* with validation (if invalid, throws a UsageError).
|
|
320
|
+
* This is the inverse of the JSON.stringify call in {@link Container.serialize}
|
|
321
|
+
*/
|
|
316
322
|
export function getDetachedContainerStateFromSerializedContainer(
|
|
317
323
|
serializedContainer: string,
|
|
318
324
|
): IPendingDetachedContainerState {
|
|
@@ -335,6 +341,18 @@ export function getDetachedContainerStateFromSerializedContainer(
|
|
|
335
341
|
}
|
|
336
342
|
}
|
|
337
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Blindly parses the given string into {@link IPendingContainerState} format.
|
|
346
|
+
* This is the inverse of the JSON.stringify call in {@link SerializedStateManager.getPendingLocalState}
|
|
347
|
+
*/
|
|
348
|
+
export function getAttachedContainerStateFromSerializedContainer(
|
|
349
|
+
serializedContainer: string | undefined,
|
|
350
|
+
): IPendingContainerState | undefined {
|
|
351
|
+
return serializedContainer !== undefined
|
|
352
|
+
? (JSON.parse(serializedContainer) as IPendingContainerState)
|
|
353
|
+
: undefined;
|
|
354
|
+
}
|
|
355
|
+
|
|
338
356
|
/**
|
|
339
357
|
* Ensures only a single instance of the provided async function is running.
|
|
340
358
|
* If there are multiple calls they will all get the same promise to wait on.
|