@fluidframework/container-loader 2.63.0-359461 → 2.63.0-359962
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/api-report/container-loader.legacy.alpha.api.md +3 -0
- package/dist/attachment.d.ts +2 -7
- package/dist/attachment.d.ts.map +1 -1
- package/dist/attachment.js +2 -4
- package/dist/attachment.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +7 -7
- package/dist/container.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +2 -2
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +1 -1
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/createAndLoadContainerUtils.js +1 -1
- package/dist/createAndLoadContainerUtils.js.map +1 -1
- package/dist/frozenServices.d.ts +10 -1
- package/dist/frozenServices.d.ts.map +1 -1
- package/dist/frozenServices.js +24 -4
- package/dist/frozenServices.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/legacyAlpha.d.ts +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/serializedStateManager.d.ts +19 -12
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +112 -98
- package/dist/serializedStateManager.js.map +1 -1
- package/dist/utils.d.ts +10 -4
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +43 -25
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.ts +2 -7
- package/lib/attachment.d.ts.map +1 -1
- package/lib/attachment.js +3 -5
- package/lib/attachment.js.map +1 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +8 -8
- package/lib/container.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +2 -2
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +1 -1
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/createAndLoadContainerUtils.js +2 -2
- package/lib/createAndLoadContainerUtils.js.map +1 -1
- package/lib/frozenServices.d.ts +10 -1
- package/lib/frozenServices.d.ts.map +1 -1
- package/lib/frozenServices.js +20 -1
- package/lib/frozenServices.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/legacyAlpha.d.ts +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/serializedStateManager.d.ts +19 -12
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +114 -100
- package/lib/serializedStateManager.js.map +1 -1
- package/lib/utils.d.ts +10 -4
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +41 -24
- package/lib/utils.js.map +1 -1
- package/package.json +11 -11
- package/src/attachment.ts +7 -13
- package/src/container.ts +12 -10
- package/src/containerStorageAdapter.ts +5 -6
- package/src/createAndLoadContainerUtils.ts +2 -2
- package/src/frozenServices.ts +28 -2
- package/src/index.ts +1 -0
- package/src/packageVersion.ts +1 -1
- package/src/serializedStateManager.ts +166 -132
- package/src/utils.ts +53 -31
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
IEvent,
|
|
11
11
|
ITelemetryBaseLogger,
|
|
12
12
|
} from "@fluidframework/core-interfaces";
|
|
13
|
+
import type { IDisposable } from "@fluidframework/core-interfaces/internal";
|
|
13
14
|
import { Timer, assert } from "@fluidframework/core-utils/internal";
|
|
14
15
|
import {
|
|
15
16
|
FetchSource,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
type IVersion,
|
|
22
23
|
type ISequencedDocumentMessage,
|
|
23
24
|
} from "@fluidframework/driver-definitions/internal";
|
|
24
|
-
import { getSnapshotTree } from "@fluidframework/driver-utils/internal";
|
|
25
|
+
import { getSnapshotTree, isInstanceOfISnapshot } from "@fluidframework/driver-utils/internal";
|
|
25
26
|
import {
|
|
26
27
|
type MonitoringContext,
|
|
27
28
|
PerformanceEvent,
|
|
@@ -30,10 +31,14 @@ import {
|
|
|
30
31
|
} from "@fluidframework/telemetry-utils/internal";
|
|
31
32
|
|
|
32
33
|
import {
|
|
33
|
-
type ISerializableBlobContents,
|
|
34
34
|
getBlobContentsFromTree,
|
|
35
|
+
type ISerializableBlobContents,
|
|
35
36
|
} from "./containerStorageAdapter.js";
|
|
36
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
convertISnapshotToSnapshotWithBlobs,
|
|
39
|
+
convertSnapshotToSnapshotInfo,
|
|
40
|
+
getDocumentAttributes,
|
|
41
|
+
} from "./utils.js";
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
44
|
* This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
|
|
@@ -75,7 +80,7 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
|
75
80
|
/**
|
|
76
81
|
* Any group snapshots (aka delay-loaded) we've downloaded from the service for this container
|
|
77
82
|
*/
|
|
78
|
-
loadedGroupIdSnapshots?: Record<string,
|
|
83
|
+
loadedGroupIdSnapshots?: Record<string, SerializedSnapshotInfo>;
|
|
79
84
|
/**
|
|
80
85
|
* All ops since base snapshot sequence number up to the latest op
|
|
81
86
|
* seen when the container was closed. Used to apply stashed (saved pending)
|
|
@@ -117,9 +122,14 @@ export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
|
|
|
117
122
|
pendingRuntimeState?: unknown;
|
|
118
123
|
}
|
|
119
124
|
|
|
120
|
-
export interface
|
|
125
|
+
export interface SerializedSnapshotInfo extends SnapshotWithBlobs {
|
|
126
|
+
snapshotSequenceNumber: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface ISnapshotInfo {
|
|
121
130
|
snapshotSequenceNumber: number;
|
|
122
131
|
snapshotFetchedTime?: number | undefined;
|
|
132
|
+
snapshot: ISnapshot | ISnapshotTree;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
export type ISerializedStateManagerDocumentStorageService = Pick<
|
|
@@ -161,10 +171,10 @@ class RefreshPromiseTracker {
|
|
|
161
171
|
* as well as the snapshot to be used for serialization.
|
|
162
172
|
* It also keeps track of container dirty state and which local ops have been processed
|
|
163
173
|
*/
|
|
164
|
-
export class SerializedStateManager {
|
|
174
|
+
export class SerializedStateManager implements IDisposable {
|
|
165
175
|
private readonly processedOps: ISequencedDocumentMessage[] = [];
|
|
166
176
|
private readonly mc: MonitoringContext;
|
|
167
|
-
private
|
|
177
|
+
private snapshotInfo: ISnapshotInfo | undefined;
|
|
168
178
|
private latestSnapshot: ISnapshotInfo | undefined;
|
|
169
179
|
private readonly refreshTracker = new RefreshPromiseTracker(
|
|
170
180
|
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
@@ -176,12 +186,13 @@ export class SerializedStateManager {
|
|
|
176
186
|
error,
|
|
177
187
|
),
|
|
178
188
|
);
|
|
179
|
-
private
|
|
180
|
-
private readonly refreshTimer: Timer;
|
|
189
|
+
private lastSavedOpSequenceNumber: number = 0;
|
|
190
|
+
private readonly refreshTimer: Timer | undefined;
|
|
181
191
|
private readonly snapshotRefreshTimeoutMs: number = 60 * 60 * 24 * 1000;
|
|
192
|
+
readonly #snapshotRefreshEnabled: boolean;
|
|
193
|
+
#disposed: boolean = false;
|
|
182
194
|
|
|
183
195
|
/**
|
|
184
|
-
* @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
|
|
185
196
|
* @param subLogger - Container's logger to use as parent for our logger
|
|
186
197
|
* @param storageAdapter - Storage adapter for fetching snapshots
|
|
187
198
|
* @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?
|
|
@@ -189,7 +200,6 @@ export class SerializedStateManager {
|
|
|
189
200
|
* @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.
|
|
190
201
|
*/
|
|
191
202
|
constructor(
|
|
192
|
-
private readonly pendingLocalState: IPendingContainerState | undefined,
|
|
193
203
|
subLogger: ITelemetryBaseLogger,
|
|
194
204
|
private readonly storageAdapter: ISerializedStateManagerDocumentStorageService,
|
|
195
205
|
private readonly _offlineLoadEnabled: boolean,
|
|
@@ -204,19 +214,30 @@ export class SerializedStateManager {
|
|
|
204
214
|
});
|
|
205
215
|
|
|
206
216
|
this.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
pendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;
|
|
217
|
-
}
|
|
217
|
+
|
|
218
|
+
this.#snapshotRefreshEnabled =
|
|
219
|
+
_offlineLoadEnabled &&
|
|
220
|
+
(this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") ??
|
|
221
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineFull")) === true;
|
|
222
|
+
|
|
223
|
+
this.refreshTimer = this.#snapshotRefreshEnabled
|
|
224
|
+
? new Timer(this.snapshotRefreshTimeoutMs, () => this.tryRefreshSnapshot())
|
|
225
|
+
: undefined;
|
|
218
226
|
containerEvent.on("saved", () => this.updateSnapshotAndProcessedOpsMaybe());
|
|
219
227
|
}
|
|
228
|
+
public get disposed(): boolean {
|
|
229
|
+
return this.#disposed;
|
|
230
|
+
}
|
|
231
|
+
dispose(): void {
|
|
232
|
+
this.#disposed = true;
|
|
233
|
+
this.refreshTimer?.clear();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private verifyNotDisposed(): void {
|
|
237
|
+
if (this.#disposed) {
|
|
238
|
+
throw new Error("SerializedStateManager used after dispose.");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
220
241
|
|
|
221
242
|
public get offlineLoadEnabled(): boolean {
|
|
222
243
|
return this._offlineLoadEnabled;
|
|
@@ -248,69 +269,74 @@ export class SerializedStateManager {
|
|
|
248
269
|
* Otherwise, fetch it from storage (according to specifiedVersion if provided).
|
|
249
270
|
*
|
|
250
271
|
* @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage.
|
|
251
|
-
* @param
|
|
272
|
+
* @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
|
|
252
273
|
* @returns The snapshot to boot the container from
|
|
253
274
|
*/
|
|
254
|
-
public async fetchSnapshot(
|
|
255
|
-
|
|
275
|
+
public async fetchSnapshot(
|
|
276
|
+
specifiedVersion: string | undefined,
|
|
277
|
+
pendingLocalState: IPendingContainerState | undefined,
|
|
278
|
+
): Promise<{
|
|
279
|
+
snapshot: ISnapshot | ISnapshotTree;
|
|
256
280
|
version: IVersion | undefined;
|
|
257
281
|
attributes: IDocumentAttributes;
|
|
258
282
|
}> {
|
|
259
|
-
|
|
260
|
-
|
|
283
|
+
this.verifyNotDisposed();
|
|
284
|
+
if (pendingLocalState === undefined) {
|
|
285
|
+
const { snapshot, version } = await getSnapshot(
|
|
261
286
|
this.mc,
|
|
262
287
|
this.storageAdapter,
|
|
263
288
|
this.supportGetSnapshotApi(),
|
|
264
289
|
specifiedVersion,
|
|
265
290
|
);
|
|
266
|
-
const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(
|
|
291
|
+
const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(snapshot);
|
|
267
292
|
const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshotTree);
|
|
268
|
-
// non-interactive clients will not have any pending state we want to save
|
|
269
293
|
if (this.offlineLoadEnabled) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
baseSnapshot: baseSnapshotTree,
|
|
276
|
-
snapshotBlobs,
|
|
277
|
-
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
278
|
-
};
|
|
279
|
-
this.refreshTimer.start();
|
|
280
|
-
return attributes.sequenceNumber;
|
|
281
|
-
}),
|
|
282
|
-
);
|
|
294
|
+
this.refreshTimer?.start();
|
|
295
|
+
this.snapshotInfo = {
|
|
296
|
+
snapshot,
|
|
297
|
+
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
298
|
+
};
|
|
283
299
|
}
|
|
284
|
-
return {
|
|
300
|
+
return { snapshot, version, attributes };
|
|
285
301
|
} else {
|
|
286
|
-
const { baseSnapshot, snapshotBlobs } =
|
|
302
|
+
const { baseSnapshot, snapshotBlobs, savedOps } = pendingLocalState;
|
|
287
303
|
const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);
|
|
288
|
-
this.snapshot = {
|
|
289
|
-
baseSnapshot,
|
|
290
|
-
snapshotBlobs,
|
|
291
|
-
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
292
|
-
};
|
|
293
|
-
this.tryRefreshSnapshot();
|
|
294
304
|
const blobContents = new Map<string, ArrayBuffer>();
|
|
295
305
|
for (const [id, value] of Object.entries(snapshotBlobs)) {
|
|
296
306
|
blobContents.set(id, stringToBuffer(value, "utf8"));
|
|
297
307
|
}
|
|
298
|
-
const
|
|
299
|
-
sequenceNumber:
|
|
308
|
+
const snapshot: ISnapshot = {
|
|
309
|
+
sequenceNumber: attributes.sequenceNumber,
|
|
300
310
|
snapshotTree: baseSnapshot,
|
|
301
311
|
blobContents,
|
|
302
312
|
latestSequenceNumber: undefined,
|
|
303
313
|
ops: [],
|
|
304
314
|
snapshotFormatV: 1,
|
|
305
315
|
};
|
|
306
|
-
|
|
316
|
+
|
|
317
|
+
if (this.offlineLoadEnabled) {
|
|
318
|
+
// special case handle. Obtaining the last saved op seq num to avoid
|
|
319
|
+
// refreshing the snapshot before we have processed it. It could cause
|
|
320
|
+
// a subsequent stashing to have a newer snapshot than allowed.
|
|
321
|
+
if (savedOps.length > 0) {
|
|
322
|
+
const savedOpsSize = savedOps.length;
|
|
323
|
+
this.lastSavedOpSequenceNumber = savedOps[savedOpsSize - 1].sequenceNumber;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.snapshotInfo = {
|
|
327
|
+
snapshot,
|
|
328
|
+
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
329
|
+
};
|
|
330
|
+
this.tryRefreshSnapshot();
|
|
331
|
+
}
|
|
332
|
+
return { snapshot, version: undefined, attributes };
|
|
307
333
|
}
|
|
308
334
|
}
|
|
309
335
|
|
|
310
336
|
private tryRefreshSnapshot(): void {
|
|
311
337
|
if (
|
|
312
|
-
|
|
313
|
-
|
|
338
|
+
this.#snapshotRefreshEnabled &&
|
|
339
|
+
!this.#disposed &&
|
|
314
340
|
!this.refreshTracker.hasPromise &&
|
|
315
341
|
this.latestSnapshot === undefined
|
|
316
342
|
) {
|
|
@@ -332,6 +358,10 @@ export class SerializedStateManager {
|
|
|
332
358
|
supportGetSnapshotApi,
|
|
333
359
|
);
|
|
334
360
|
|
|
361
|
+
if (this.#disposed) {
|
|
362
|
+
return -1;
|
|
363
|
+
}
|
|
364
|
+
|
|
335
365
|
// These are loading groupIds that the containerRuntime has requested over its lifetime.
|
|
336
366
|
// We will fetch the latest snapshot for the groupIds, which will update storageAdapter.loadedGroupIdSnapshots's cache
|
|
337
367
|
const downloadedGroupIds = Object.keys(this.storageAdapter.loadedGroupIdSnapshots);
|
|
@@ -360,6 +390,7 @@ export class SerializedStateManager {
|
|
|
360
390
|
private updateSnapshotAndProcessedOpsMaybe(): number {
|
|
361
391
|
const snapshotSequenceNumber = this.latestSnapshot?.snapshotSequenceNumber;
|
|
362
392
|
if (
|
|
393
|
+
this.#disposed ||
|
|
363
394
|
snapshotSequenceNumber === undefined ||
|
|
364
395
|
this.processedOps.length === 0 ||
|
|
365
396
|
this.processedOps[this.processedOps.length - 1].sequenceNumber <
|
|
@@ -382,17 +413,17 @@ export class SerializedStateManager {
|
|
|
382
413
|
snapshotSequenceNumber,
|
|
383
414
|
firstProcessedOpSequenceNumber,
|
|
384
415
|
lastProcessedOpSequenceNumber,
|
|
385
|
-
stashedSnapshotSequenceNumber: this.
|
|
416
|
+
stashedSnapshotSequenceNumber: this.snapshotInfo?.snapshotSequenceNumber,
|
|
386
417
|
});
|
|
387
418
|
this.latestSnapshot = undefined;
|
|
388
|
-
this.refreshTimer
|
|
419
|
+
this.refreshTimer?.restart();
|
|
389
420
|
} else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {
|
|
390
421
|
// Snapshot seq num is between the first and last processed op.
|
|
391
422
|
// Remove the ops that are already part of the snapshot
|
|
392
423
|
this.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);
|
|
393
|
-
this.
|
|
424
|
+
this.snapshotInfo = this.latestSnapshot;
|
|
394
425
|
this.latestSnapshot = undefined;
|
|
395
|
-
this.refreshTimer
|
|
426
|
+
this.refreshTimer?.restart();
|
|
396
427
|
this.mc.logger.sendTelemetryEvent({
|
|
397
428
|
eventName: "SnapshotRefreshed",
|
|
398
429
|
snapshotSequenceNumber,
|
|
@@ -410,31 +441,15 @@ export class SerializedStateManager {
|
|
|
410
441
|
* base snapshot when attaching.
|
|
411
442
|
* @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
|
|
412
443
|
*/
|
|
413
|
-
public setInitialSnapshot(snapshot:
|
|
444
|
+
public setInitialSnapshot(snapshot: ISnapshot): void {
|
|
445
|
+
this.verifyNotDisposed();
|
|
414
446
|
if (this.offlineLoadEnabled) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
assert(snapshot !== undefined, 0x938 /* attachment snapshot should be defined */);
|
|
420
|
-
const { baseSnapshot, snapshotBlobs } = snapshot;
|
|
421
|
-
const attributesHash =
|
|
422
|
-
".protocol" in baseSnapshot.trees
|
|
423
|
-
? baseSnapshot.trees[".protocol"].blobs.attributes
|
|
424
|
-
: baseSnapshot.blobs[".attributes"];
|
|
425
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
426
|
-
const attributes = JSON.parse(snapshotBlobs[attributesHash]);
|
|
427
|
-
assert(
|
|
428
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
429
|
-
attributes.sequenceNumber === 0,
|
|
430
|
-
0x939 /* trying to set a non attachment snapshot */,
|
|
431
|
-
);
|
|
432
|
-
this.snapshot = {
|
|
433
|
-
...snapshot,
|
|
434
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
435
|
-
snapshotSequenceNumber: attributes.sequenceNumber as number,
|
|
447
|
+
this.snapshotInfo = {
|
|
448
|
+
snapshot,
|
|
449
|
+
snapshotSequenceNumber: snapshot.sequenceNumber ?? 0,
|
|
450
|
+
snapshotFetchedTime: Date.now(),
|
|
436
451
|
};
|
|
437
|
-
this.refreshTimer
|
|
452
|
+
this.refreshTimer?.start();
|
|
438
453
|
}
|
|
439
454
|
}
|
|
440
455
|
|
|
@@ -447,6 +462,11 @@ export class SerializedStateManager {
|
|
|
447
462
|
runtime: Pick<IRuntime, "getPendingLocalState">,
|
|
448
463
|
resolvedUrl: IResolvedUrl,
|
|
449
464
|
): Promise<string> {
|
|
465
|
+
this.verifyNotDisposed();
|
|
466
|
+
if (!this.offlineLoadEnabled) {
|
|
467
|
+
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
468
|
+
}
|
|
469
|
+
|
|
450
470
|
return PerformanceEvent.timedExecAsync(
|
|
451
471
|
this.mc.logger,
|
|
452
472
|
{
|
|
@@ -460,25 +480,14 @@ export class SerializedStateManager {
|
|
|
460
480
|
clientId,
|
|
461
481
|
},
|
|
462
482
|
async () => {
|
|
463
|
-
|
|
464
|
-
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
465
|
-
}
|
|
466
|
-
if (this.snapshot === undefined && this.refreshTracker.hasPromise) {
|
|
467
|
-
// we deferred the initial download of the snapshot to not block
|
|
468
|
-
// the container load flow, so if it is not resolved
|
|
469
|
-
// and we don't have a snapshot, we will wait for the download
|
|
470
|
-
// to finish.
|
|
471
|
-
await this.refreshTracker.Promise;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
assert(this.snapshot !== undefined, 0x8e5 /* no base data */);
|
|
483
|
+
assert(this.snapshotInfo !== undefined, 0x8e5 /* no base data */);
|
|
475
484
|
const pendingRuntimeState = await runtime.getPendingLocalState({
|
|
476
485
|
notifyImminentClosure: false,
|
|
477
|
-
snapshotSequenceNumber: this.
|
|
478
|
-
sessionExpiryTimerStarted: this.
|
|
486
|
+
snapshotSequenceNumber: this.snapshotInfo.snapshotSequenceNumber,
|
|
487
|
+
sessionExpiryTimerStarted: this.snapshotInfo.snapshotFetchedTime,
|
|
479
488
|
});
|
|
480
489
|
// This conversion is required because ArrayBufferLike doesn't survive JSON.stringify
|
|
481
|
-
const loadedGroupIdSnapshots = {};
|
|
490
|
+
const loadedGroupIdSnapshots: Record<string, SerializedSnapshotInfo> = {};
|
|
482
491
|
let hasGroupIdSnapshots = false;
|
|
483
492
|
const groupIdSnapshots = Object.entries(this.storageAdapter.loadedGroupIdSnapshots);
|
|
484
493
|
if (groupIdSnapshots.length > 0) {
|
|
@@ -487,11 +496,20 @@ export class SerializedStateManager {
|
|
|
487
496
|
loadedGroupIdSnapshots[groupId] = convertSnapshotToSnapshotInfo(snapshot);
|
|
488
497
|
}
|
|
489
498
|
}
|
|
499
|
+
|
|
500
|
+
const snapshotWithBlobs: SnapshotWithBlobs = isInstanceOfISnapshot(
|
|
501
|
+
this.snapshotInfo.snapshot,
|
|
502
|
+
)
|
|
503
|
+
? convertISnapshotToSnapshotWithBlobs(this.snapshotInfo.snapshot)
|
|
504
|
+
: await convertSnapshotTreeToSnapshotWithBlobs(
|
|
505
|
+
this.snapshotInfo.snapshot,
|
|
506
|
+
this.storageAdapter,
|
|
507
|
+
);
|
|
508
|
+
|
|
490
509
|
const pendingState: IPendingContainerState = {
|
|
491
510
|
attached: true,
|
|
492
511
|
pendingRuntimeState,
|
|
493
|
-
|
|
494
|
-
snapshotBlobs: this.snapshot.snapshotBlobs,
|
|
512
|
+
...snapshotWithBlobs,
|
|
495
513
|
loadedGroupIdSnapshots: hasGroupIdSnapshots ? loadedGroupIdSnapshots : undefined,
|
|
496
514
|
savedOps: this.processedOps,
|
|
497
515
|
url: resolvedUrl.url,
|
|
@@ -504,6 +522,17 @@ export class SerializedStateManager {
|
|
|
504
522
|
}
|
|
505
523
|
}
|
|
506
524
|
|
|
525
|
+
async function convertSnapshotTreeToSnapshotWithBlobs(
|
|
526
|
+
snapshot: ISnapshotTree,
|
|
527
|
+
storageAdapter: ISerializedStateManagerDocumentStorageService,
|
|
528
|
+
): Promise<SnapshotWithBlobs> {
|
|
529
|
+
const snapshotBlobs = await getBlobContentsFromTree(snapshot, storageAdapter);
|
|
530
|
+
return {
|
|
531
|
+
baseSnapshot: snapshot,
|
|
532
|
+
snapshotBlobs,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
507
536
|
/**
|
|
508
537
|
* Retrieves the most recent snapshot and returns its info.
|
|
509
538
|
*
|
|
@@ -517,41 +546,46 @@ export async function getLatestSnapshotInfo(
|
|
|
517
546
|
storageAdapter: ISerializedStateManagerDocumentStorageService,
|
|
518
547
|
supportGetSnapshotApi: boolean,
|
|
519
548
|
): Promise<ISnapshotInfo | undefined> {
|
|
520
|
-
return PerformanceEvent.timedExecAsync(
|
|
549
|
+
return PerformanceEvent.timedExecAsync<ISnapshotInfo | undefined>(
|
|
521
550
|
mc.logger,
|
|
522
551
|
{ eventName: "GetLatestSnapshotInfo" },
|
|
523
|
-
async () => {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
552
|
+
async (event) => {
|
|
553
|
+
try {
|
|
554
|
+
// get the latest non cached snapshot version
|
|
555
|
+
const specifiedVersion: IVersion[] = await storageAdapter.getVersions(
|
|
556
|
+
// eslint-disable-next-line unicorn/no-null
|
|
557
|
+
null,
|
|
558
|
+
1,
|
|
559
|
+
"getLatestSnapshotInfo",
|
|
560
|
+
FetchSource.noCache,
|
|
561
|
+
);
|
|
562
|
+
const { snapshot: baseSnapshot } = await getSnapshot(
|
|
563
|
+
mc,
|
|
564
|
+
storageAdapter,
|
|
565
|
+
supportGetSnapshotApi,
|
|
566
|
+
specifiedVersion[0]?.id,
|
|
567
|
+
);
|
|
538
568
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
569
|
+
const { sequenceNumber, snapshotTree } = isInstanceOfISnapshot(baseSnapshot)
|
|
570
|
+
? baseSnapshot
|
|
571
|
+
: { snapshotTree: baseSnapshot, sequenceNumber: undefined };
|
|
572
|
+
|
|
573
|
+
const snapshotSequenceNumber: number =
|
|
574
|
+
sequenceNumber ??
|
|
575
|
+
(await getDocumentAttributes(storageAdapter, snapshotTree).then(
|
|
576
|
+
(a) => a.sequenceNumber,
|
|
577
|
+
));
|
|
578
|
+
return {
|
|
579
|
+
snapshot: baseSnapshot,
|
|
580
|
+
snapshotSequenceNumber,
|
|
581
|
+
snapshotFetchedTime: Date.now(),
|
|
582
|
+
};
|
|
583
|
+
} catch (error) {
|
|
584
|
+
event.cancel(undefined, error);
|
|
585
|
+
}
|
|
586
|
+
return undefined;
|
|
553
587
|
},
|
|
554
|
-
)
|
|
588
|
+
);
|
|
555
589
|
}
|
|
556
590
|
|
|
557
591
|
/**
|
|
@@ -571,12 +605,12 @@ async function getSnapshot(
|
|
|
571
605
|
>,
|
|
572
606
|
supportGetSnapshotApi: boolean,
|
|
573
607
|
specifiedVersion: string | undefined,
|
|
574
|
-
): Promise<{
|
|
608
|
+
): Promise<{ snapshot: ISnapshot | ISnapshotTree; version?: IVersion }> {
|
|
575
609
|
const { snapshot, version } = supportGetSnapshotApi
|
|
576
610
|
? await fetchISnapshot(mc, storageAdapter, specifiedVersion)
|
|
577
611
|
: await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);
|
|
578
612
|
assert(snapshot !== undefined, 0x8e4 /* Snapshot should exist */);
|
|
579
|
-
return {
|
|
613
|
+
return { snapshot, version };
|
|
580
614
|
}
|
|
581
615
|
|
|
582
616
|
/**
|