@fluidframework/container-loader 2.0.2 → 2.1.0-276326
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +2 -5
- package/api-extractor/api-extractor.legacy.json +4 -0
- package/api-report/container-loader.beta.api.md +0 -27
- package/api-report/{container-loader.alpha.api.md → container-loader.legacy.alpha.api.md} +0 -27
- package/api-report/container-loader.public.api.md +0 -27
- package/dist/attachment.d.ts +2 -1
- package/dist/attachment.d.ts.map +1 -1
- package/dist/attachment.js.map +1 -1
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +4 -4
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts +15 -4
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js +12 -3
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +24 -8
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +36 -23
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +30 -20
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +15 -11
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +7 -2
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +45 -28
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +8 -4
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +3 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +12 -6
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +17 -8
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +4 -2
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.js +3 -3
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +13 -9
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +32 -23
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +1 -4
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +2 -2
- package/dist/deltaQueue.js.map +1 -1
- package/dist/disposal.d.ts +1 -1
- package/dist/disposal.d.ts.map +1 -1
- package/dist/disposal.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/loadPaused.d.ts +2 -2
- package/dist/loadPaused.d.ts.map +1 -1
- package/dist/loadPaused.js +7 -3
- package/dist/loadPaused.js.map +1 -1
- package/dist/loader.d.ts +10 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +11 -1
- package/dist/loader.js.map +1 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +2 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +3 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
- package/dist/memoryBlobStorage.d.ts.map +1 -1
- package/dist/memoryBlobStorage.js +4 -2
- package/dist/memoryBlobStorage.js.map +1 -1
- package/dist/noopHeuristic.js +1 -1
- package/dist/noopHeuristic.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol/protocol.d.ts +4 -3
- package/dist/protocol/protocol.d.ts.map +1 -1
- package/dist/protocol/protocol.js +6 -5
- package/dist/protocol/protocol.js.map +1 -1
- package/dist/protocol/quorum.d.ts +11 -8
- package/dist/protocol/quorum.d.ts.map +1 -1
- package/dist/protocol/quorum.js +8 -8
- package/dist/protocol/quorum.js.map +1 -1
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +7 -2
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +2 -2
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/serializedStateManager.d.ts +29 -12
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +55 -24
- package/dist/serializedStateManager.js.map +1 -1
- package/dist/utils.d.ts +4 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +15 -6
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.ts +2 -1
- package/lib/attachment.d.ts.map +1 -1
- package/lib/attachment.js.map +1 -1
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +4 -4
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +15 -4
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js +12 -3
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +24 -8
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +36 -23
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +30 -20
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +14 -12
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +7 -2
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +45 -28
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +8 -4
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +3 -1
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +12 -6
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +17 -8
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +4 -2
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.js +3 -3
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +13 -9
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +32 -23
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +1 -4
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +2 -2
- package/lib/deltaQueue.js.map +1 -1
- package/lib/disposal.d.ts +1 -1
- package/lib/disposal.d.ts.map +1 -1
- package/lib/disposal.js.map +1 -1
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/loadPaused.d.ts +2 -2
- package/lib/loadPaused.d.ts.map +1 -1
- package/lib/loadPaused.js +8 -4
- package/lib/loadPaused.js.map +1 -1
- package/lib/loader.d.ts +10 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +11 -1
- package/lib/loader.js.map +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts +2 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +3 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
- package/lib/memoryBlobStorage.d.ts.map +1 -1
- package/lib/memoryBlobStorage.js +4 -2
- package/lib/memoryBlobStorage.js.map +1 -1
- package/lib/noopHeuristic.js +1 -1
- package/lib/noopHeuristic.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol/protocol.d.ts +4 -3
- package/lib/protocol/protocol.d.ts.map +1 -1
- package/lib/protocol/protocol.js +6 -5
- package/lib/protocol/protocol.js.map +1 -1
- package/lib/protocol/quorum.d.ts +11 -8
- package/lib/protocol/quorum.d.ts.map +1 -1
- package/lib/protocol/quorum.js +8 -8
- package/lib/protocol/quorum.js.map +1 -1
- package/lib/protocol.d.ts +2 -0
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +7 -2
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +2 -2
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +4 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/serializedStateManager.d.ts +29 -12
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +56 -25
- package/lib/serializedStateManager.js.map +1 -1
- package/lib/utils.d.ts +4 -2
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +16 -7
- package/lib/utils.js.map +1 -1
- package/package.json +21 -17
- package/src/attachment.ts +2 -1
- package/src/audience.ts +4 -4
- package/src/catchUpMonitor.ts +23 -8
- package/src/connectionManager.ts +85 -60
- package/src/connectionStateHandler.ts +85 -63
- package/src/container.ts +118 -84
- package/src/containerContext.ts +5 -3
- package/src/containerStorageAdapter.ts +20 -13
- package/src/contracts.ts +21 -9
- package/src/debugLogger.ts +4 -4
- package/src/deltaManager.ts +75 -56
- package/src/deltaQueue.ts +16 -10
- package/src/disposal.ts +3 -3
- package/src/error.ts +2 -1
- package/src/loadPaused.ts +16 -8
- package/src/loader.ts +20 -2
- package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +7 -3
- package/src/memoryBlobStorage.ts +5 -3
- package/src/noopHeuristic.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/protocol/protocol.ts +12 -11
- package/src/protocol/quorum.ts +49 -40
- package/src/protocol.ts +12 -4
- package/src/protocolTreeDocumentStorageService.ts +3 -2
- package/src/retriableDocumentStorageService.ts +6 -3
- package/src/serializedStateManager.ts +95 -39
- package/src/utils.ts +26 -10
|
@@ -12,8 +12,9 @@ import type {
|
|
|
12
12
|
IEventProvider,
|
|
13
13
|
IEvent,
|
|
14
14
|
ITelemetryBaseLogger,
|
|
15
|
+
Tagged,
|
|
15
16
|
} from "@fluidframework/core-interfaces";
|
|
16
|
-
import { assert } from "@fluidframework/core-utils/internal";
|
|
17
|
+
import { Timer, assert } from "@fluidframework/core-utils/internal";
|
|
17
18
|
import {
|
|
18
19
|
FetchSource,
|
|
19
20
|
IDocumentStorageService,
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
PerformanceEvent,
|
|
31
32
|
UsageError,
|
|
32
33
|
createChildMonitoringContext,
|
|
34
|
+
type TelemetryEventPropertyTypeExt,
|
|
33
35
|
} from "@fluidframework/telemetry-utils/internal";
|
|
34
36
|
|
|
35
37
|
import {
|
|
@@ -66,7 +68,9 @@ export interface SnapshotWithBlobs {
|
|
|
66
68
|
* @internal
|
|
67
69
|
*/
|
|
68
70
|
export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
69
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* This container was attached (as opposed to IPendingDetachedContainerState.attached which is false)
|
|
73
|
+
*/
|
|
70
74
|
attached: true;
|
|
71
75
|
/**
|
|
72
76
|
* Runtime-specific state that will be needed to properly rehydrate
|
|
@@ -83,9 +87,13 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
|
83
87
|
* ops at the same sequence number at which they were made.
|
|
84
88
|
*/
|
|
85
89
|
savedOps: ISequencedDocumentMessage[];
|
|
86
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* The Container's URL in the service, needed to hook up the driver during rehydration
|
|
92
|
+
*/
|
|
87
93
|
url: string;
|
|
88
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected.
|
|
96
|
+
*/
|
|
89
97
|
clientId?: string;
|
|
90
98
|
}
|
|
91
99
|
|
|
@@ -95,11 +103,17 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
|
|
|
95
103
|
* @internal
|
|
96
104
|
*/
|
|
97
105
|
export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
|
|
98
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* This container was not attached (as opposed to IPendingContainerState.attached which is true)
|
|
108
|
+
*/
|
|
99
109
|
attached: false;
|
|
100
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage
|
|
112
|
+
*/
|
|
101
113
|
hasAttachmentBlobs: boolean;
|
|
102
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* Used by the memory blob storage to persisted attachment blobs
|
|
116
|
+
*/
|
|
103
117
|
attachmentBlobs?: string;
|
|
104
118
|
/**
|
|
105
119
|
* Runtime-specific state that will be needed to properly rehydrate
|
|
@@ -136,8 +150,10 @@ export class SerializedStateManager {
|
|
|
136
150
|
private readonly mc: MonitoringContext;
|
|
137
151
|
private snapshot: ISnapshotInfo | undefined;
|
|
138
152
|
private latestSnapshot: ISnapshotInfo | undefined;
|
|
139
|
-
private
|
|
153
|
+
private _refreshSnapshotP: Promise<number> | undefined;
|
|
140
154
|
private readonly lastSavedOpSequenceNumber: number = 0;
|
|
155
|
+
private readonly refreshTimer: Timer;
|
|
156
|
+
private readonly snapshotRefreshTimeoutMs: number = 60 * 60 * 24 * 1000;
|
|
141
157
|
|
|
142
158
|
/**
|
|
143
159
|
* @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
|
|
@@ -155,12 +171,17 @@ export class SerializedStateManager {
|
|
|
155
171
|
containerEvent: IEventProvider<ISerializerEvent>,
|
|
156
172
|
private readonly containerDirty: () => boolean,
|
|
157
173
|
private readonly supportGetSnapshotApi: () => boolean,
|
|
174
|
+
snapshotRefreshTimeoutMs?: number,
|
|
158
175
|
) {
|
|
159
176
|
this.mc = createChildMonitoringContext({
|
|
160
177
|
logger: subLogger,
|
|
161
178
|
namespace: "serializedStateManager",
|
|
162
179
|
});
|
|
163
180
|
|
|
181
|
+
this.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;
|
|
182
|
+
this.refreshTimer = new Timer(this.snapshotRefreshTimeoutMs, () =>
|
|
183
|
+
this.tryRefreshSnapshot(),
|
|
184
|
+
);
|
|
164
185
|
// special case handle. Obtaining the last saved op seq num to avoid
|
|
165
186
|
// refreshing the snapshot before we have processed it. It could cause
|
|
166
187
|
// a subsequent stashing to have a newer snapshot than allowed.
|
|
@@ -169,7 +190,7 @@ export class SerializedStateManager {
|
|
|
169
190
|
this.lastSavedOpSequenceNumber =
|
|
170
191
|
pendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;
|
|
171
192
|
}
|
|
172
|
-
containerEvent.
|
|
193
|
+
containerEvent.on("saved", () => this.updateSnapshotAndProcessedOpsMaybe());
|
|
173
194
|
}
|
|
174
195
|
|
|
175
196
|
public get offlineLoadEnabled(): boolean {
|
|
@@ -178,15 +199,17 @@ export class SerializedStateManager {
|
|
|
178
199
|
|
|
179
200
|
/**
|
|
180
201
|
* Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage
|
|
202
|
+
* only intended to be used for testing purposes.
|
|
203
|
+
* @returns The snapshot sequence number associated with the latest fetched snapshot
|
|
181
204
|
*/
|
|
182
|
-
public get
|
|
183
|
-
return this.
|
|
205
|
+
public get refreshSnapshotP(): Promise<number> | undefined {
|
|
206
|
+
return this._refreshSnapshotP;
|
|
184
207
|
}
|
|
185
208
|
|
|
186
209
|
/**
|
|
187
210
|
* Called whenever an incoming op is processed by the Container
|
|
188
211
|
*/
|
|
189
|
-
public addProcessedOp(message: ISequencedDocumentMessage) {
|
|
212
|
+
public addProcessedOp(message: ISequencedDocumentMessage): void {
|
|
190
213
|
if (this.offlineLoadEnabled) {
|
|
191
214
|
this.processedOps.push(message);
|
|
192
215
|
this.updateSnapshotAndProcessedOpsMaybe();
|
|
@@ -197,13 +220,16 @@ export class SerializedStateManager {
|
|
|
197
220
|
* This wraps the basic functionality of fetching the snapshot for this container during Container load.
|
|
198
221
|
*
|
|
199
222
|
* If we have pendingLocalState, we get the snapshot from there.
|
|
200
|
-
* Otherwise, fetch it from storage (according to specifiedVersion if provided)
|
|
223
|
+
* Otherwise, fetch it from storage (according to specifiedVersion if provided).
|
|
201
224
|
*
|
|
202
|
-
* @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage
|
|
225
|
+
* @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage.
|
|
203
226
|
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
|
|
204
227
|
* @returns The snapshot to boot the container from
|
|
205
228
|
*/
|
|
206
|
-
public async fetchSnapshot(specifiedVersion: string | undefined) {
|
|
229
|
+
public async fetchSnapshot(specifiedVersion: string | undefined): Promise<{
|
|
230
|
+
baseSnapshot: ISnapshot | ISnapshotTree;
|
|
231
|
+
version: IVersion | undefined;
|
|
232
|
+
}> {
|
|
207
233
|
if (this.pendingLocalState === undefined) {
|
|
208
234
|
const { baseSnapshot, version } = await getSnapshot(
|
|
209
235
|
this.mc,
|
|
@@ -224,6 +250,7 @@ export class SerializedStateManager {
|
|
|
224
250
|
snapshotBlobs,
|
|
225
251
|
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
226
252
|
};
|
|
253
|
+
this.refreshTimer.start();
|
|
227
254
|
}
|
|
228
255
|
return { baseSnapshot, version };
|
|
229
256
|
} else {
|
|
@@ -234,21 +261,7 @@ export class SerializedStateManager {
|
|
|
234
261
|
snapshotBlobs,
|
|
235
262
|
snapshotSequenceNumber: attributes.sequenceNumber,
|
|
236
263
|
};
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
this.refreshSnapshotP === undefined &&
|
|
240
|
-
this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true
|
|
241
|
-
) {
|
|
242
|
-
// Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
|
|
243
|
-
this.refreshSnapshotP = this.refreshLatestSnapshot(this.supportGetSnapshotApi());
|
|
244
|
-
this.refreshSnapshotP.catch((e) => {
|
|
245
|
-
this.mc.logger.sendErrorEvent({
|
|
246
|
-
eventName: "RefreshLatestSnapshotFailed",
|
|
247
|
-
error: e,
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
264
|
+
this.tryRefreshSnapshot();
|
|
252
265
|
const blobContents = new Map<string, ArrayBuffer>();
|
|
253
266
|
for (const [id, value] of Object.entries(snapshotBlobs)) {
|
|
254
267
|
blobContents.set(id, stringToBuffer(value, "utf8"));
|
|
@@ -265,13 +278,36 @@ export class SerializedStateManager {
|
|
|
265
278
|
}
|
|
266
279
|
}
|
|
267
280
|
|
|
281
|
+
private tryRefreshSnapshot(): void {
|
|
282
|
+
if (
|
|
283
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true &&
|
|
284
|
+
this._refreshSnapshotP === undefined &&
|
|
285
|
+
this.latestSnapshot === undefined
|
|
286
|
+
) {
|
|
287
|
+
// Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
|
|
288
|
+
this._refreshSnapshotP = this.refreshLatestSnapshot(this.supportGetSnapshotApi());
|
|
289
|
+
this._refreshSnapshotP
|
|
290
|
+
.catch(
|
|
291
|
+
(error: TelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>) => {
|
|
292
|
+
this.mc.logger.sendTelemetryEvent({
|
|
293
|
+
eventName: "RefreshLatestSnapshotFailed",
|
|
294
|
+
error,
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
)
|
|
298
|
+
.finally(() => {
|
|
299
|
+
this._refreshSnapshotP = undefined;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
268
304
|
/**
|
|
269
305
|
* Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.
|
|
270
306
|
* Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)
|
|
271
307
|
*
|
|
272
308
|
* @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)
|
|
273
309
|
*/
|
|
274
|
-
private async refreshLatestSnapshot(supportGetSnapshotApi: boolean): Promise<
|
|
310
|
+
private async refreshLatestSnapshot(supportGetSnapshotApi: boolean): Promise<number> {
|
|
275
311
|
this.latestSnapshot = await getLatestSnapshotInfo(
|
|
276
312
|
this.mc,
|
|
277
313
|
this.storageAdapter,
|
|
@@ -297,15 +333,16 @@ export class SerializedStateManager {
|
|
|
297
333
|
assert(snapshot !== undefined, 0x973 /* Snapshot should exist */);
|
|
298
334
|
}
|
|
299
335
|
|
|
300
|
-
this.updateSnapshotAndProcessedOpsMaybe();
|
|
336
|
+
return this.updateSnapshotAndProcessedOpsMaybe();
|
|
301
337
|
}
|
|
302
338
|
|
|
303
339
|
/**
|
|
304
340
|
* Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.
|
|
305
341
|
*/
|
|
306
|
-
private updateSnapshotAndProcessedOpsMaybe() {
|
|
342
|
+
private updateSnapshotAndProcessedOpsMaybe(): number {
|
|
343
|
+
const snapshotSequenceNumber = this.latestSnapshot?.snapshotSequenceNumber;
|
|
307
344
|
if (
|
|
308
|
-
|
|
345
|
+
snapshotSequenceNumber === undefined ||
|
|
309
346
|
this.processedOps.length === 0 ||
|
|
310
347
|
this.processedOps[this.processedOps.length - 1].sequenceNumber <
|
|
311
348
|
this.lastSavedOpSequenceNumber ||
|
|
@@ -313,9 +350,8 @@ export class SerializedStateManager {
|
|
|
313
350
|
) {
|
|
314
351
|
// can't refresh latest snapshot until we have processed the ops up to it.
|
|
315
352
|
// Pending state would be behind the latest snapshot.
|
|
316
|
-
return;
|
|
353
|
+
return -1;
|
|
317
354
|
}
|
|
318
|
-
const snapshotSequenceNumber = this.latestSnapshot.snapshotSequenceNumber;
|
|
319
355
|
const firstProcessedOpSequenceNumber = this.processedOps[0].sequenceNumber;
|
|
320
356
|
const lastProcessedOpSequenceNumber =
|
|
321
357
|
this.processedOps[this.processedOps.length - 1].sequenceNumber;
|
|
@@ -331,12 +367,14 @@ export class SerializedStateManager {
|
|
|
331
367
|
stashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,
|
|
332
368
|
});
|
|
333
369
|
this.latestSnapshot = undefined;
|
|
370
|
+
this.refreshTimer.restart();
|
|
334
371
|
} else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {
|
|
335
372
|
// Snapshot seq num is between the first and last processed op.
|
|
336
373
|
// Remove the ops that are already part of the snapshot
|
|
337
374
|
this.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);
|
|
338
375
|
this.snapshot = this.latestSnapshot;
|
|
339
376
|
this.latestSnapshot = undefined;
|
|
377
|
+
this.refreshTimer.restart();
|
|
340
378
|
this.mc.logger.sendTelemetryEvent({
|
|
341
379
|
eventName: "SnapshotRefreshed",
|
|
342
380
|
snapshotSequenceNumber,
|
|
@@ -345,6 +383,7 @@ export class SerializedStateManager {
|
|
|
345
383
|
this.processedOps.length === 0 ? undefined : this.processedOps[0].sequenceNumber,
|
|
346
384
|
});
|
|
347
385
|
}
|
|
386
|
+
return snapshotSequenceNumber;
|
|
348
387
|
}
|
|
349
388
|
|
|
350
389
|
/**
|
|
@@ -353,7 +392,7 @@ export class SerializedStateManager {
|
|
|
353
392
|
* base snapshot when attaching.
|
|
354
393
|
* @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
|
|
355
394
|
*/
|
|
356
|
-
public setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined) {
|
|
395
|
+
public setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined): void {
|
|
357
396
|
if (this.offlineLoadEnabled) {
|
|
358
397
|
assert(
|
|
359
398
|
this.snapshot === undefined,
|
|
@@ -365,12 +404,19 @@ export class SerializedStateManager {
|
|
|
365
404
|
".protocol" in baseSnapshot.trees
|
|
366
405
|
? baseSnapshot.trees[".protocol"].blobs.attributes
|
|
367
406
|
: baseSnapshot.blobs[".attributes"];
|
|
407
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
368
408
|
const attributes = JSON.parse(snapshotBlobs[attributesHash]);
|
|
369
409
|
assert(
|
|
410
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
370
411
|
attributes.sequenceNumber === 0,
|
|
371
412
|
0x939 /* trying to set a non attachment snapshot */,
|
|
372
413
|
);
|
|
373
|
-
this.snapshot = {
|
|
414
|
+
this.snapshot = {
|
|
415
|
+
...snapshot,
|
|
416
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
417
|
+
snapshotSequenceNumber: attributes.sequenceNumber as number,
|
|
418
|
+
};
|
|
419
|
+
this.refreshTimer.start();
|
|
374
420
|
}
|
|
375
421
|
}
|
|
376
422
|
|
|
@@ -450,11 +496,19 @@ export async function getLatestSnapshotInfo(
|
|
|
450
496
|
mc.logger,
|
|
451
497
|
{ eventName: "GetLatestSnapshotInfo" },
|
|
452
498
|
async () => {
|
|
499
|
+
// get the latest non cached snapshot version
|
|
500
|
+
const specifiedVersion: IVersion[] = await storageAdapter.getVersions(
|
|
501
|
+
// eslint-disable-next-line unicorn/no-null
|
|
502
|
+
null,
|
|
503
|
+
1,
|
|
504
|
+
"getLatestSnapshotInfo",
|
|
505
|
+
FetchSource.noCache,
|
|
506
|
+
);
|
|
453
507
|
const { baseSnapshot } = await getSnapshot(
|
|
454
508
|
mc,
|
|
455
509
|
storageAdapter,
|
|
456
510
|
supportGetSnapshotApi,
|
|
457
|
-
|
|
511
|
+
specifiedVersion[0]?.id,
|
|
458
512
|
);
|
|
459
513
|
|
|
460
514
|
const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);
|
|
@@ -549,6 +603,8 @@ export async function fetchISnapshotTree(
|
|
|
549
603
|
storageAdapter: Pick<IDocumentStorageService, "getSnapshotTree" | "getVersions">,
|
|
550
604
|
specifiedVersion: string | undefined,
|
|
551
605
|
): Promise<{ snapshot?: ISnapshotTree; version?: IVersion | undefined }> {
|
|
606
|
+
// API uses null
|
|
607
|
+
// eslint-disable-next-line unicorn/no-null
|
|
552
608
|
const versions = await storageAdapter.getVersions(specifiedVersion ?? null, 1);
|
|
553
609
|
const version = versions[0];
|
|
554
610
|
|
package/src/utils.ts
CHANGED
|
@@ -25,7 +25,11 @@ import {
|
|
|
25
25
|
isCombinedAppAndProtocolSummary,
|
|
26
26
|
readAndParse,
|
|
27
27
|
} from "@fluidframework/driver-utils/internal";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
LoggingError,
|
|
30
|
+
UsageError,
|
|
31
|
+
type IFluidErrorBase,
|
|
32
|
+
} from "@fluidframework/telemetry-utils/internal";
|
|
29
33
|
import { v4 as uuid } from "uuid";
|
|
30
34
|
|
|
31
35
|
import { ISerializableBlobContents } from "./containerStorageAdapter.js";
|
|
@@ -47,6 +51,7 @@ export interface ISnapshotTreeWithBlobContents extends ISnapshotTree {
|
|
|
47
51
|
* Interface to represent the parsed parts of IResolvedUrl.url to help
|
|
48
52
|
* in getting info about different parts of the url.
|
|
49
53
|
* May not be compatible or relevant for any Url Resolver
|
|
54
|
+
* @legacy
|
|
50
55
|
* @alpha
|
|
51
56
|
*/
|
|
52
57
|
export interface IParsedUrl {
|
|
@@ -76,6 +81,7 @@ export interface IParsedUrl {
|
|
|
76
81
|
* with urls of type: protocol://<string>/.../..?<querystring>
|
|
77
82
|
* @param url - This is the IResolvedUrl.url part of the resolved url.
|
|
78
83
|
* @returns The IParsedUrl representing the input URL, or undefined if the format was not supported
|
|
84
|
+
* @legacy
|
|
79
85
|
* @alpha
|
|
80
86
|
*/
|
|
81
87
|
export function tryParseCompatibleResolvedUrl(url: string): IParsedUrl | undefined {
|
|
@@ -150,9 +156,10 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): SnapshotWithBl
|
|
|
150
156
|
blobContents = { ...blobContents, ...innerSnapshot.snapshotBlobs };
|
|
151
157
|
break;
|
|
152
158
|
}
|
|
153
|
-
case SummaryType.Attachment:
|
|
159
|
+
case SummaryType.Attachment: {
|
|
154
160
|
treeNode.blobs[key] = summaryObject.id;
|
|
155
161
|
break;
|
|
162
|
+
}
|
|
156
163
|
case SummaryType.Blob: {
|
|
157
164
|
const blobId = uuid();
|
|
158
165
|
treeNode.blobs[key] = blobId;
|
|
@@ -163,11 +170,13 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): SnapshotWithBl
|
|
|
163
170
|
blobContents[blobId] = contentString;
|
|
164
171
|
break;
|
|
165
172
|
}
|
|
166
|
-
case SummaryType.Handle:
|
|
173
|
+
case SummaryType.Handle: {
|
|
167
174
|
throw new LoggingError(
|
|
168
175
|
"No handles should be there in summary in detached container!!",
|
|
169
176
|
);
|
|
177
|
+
}
|
|
170
178
|
default: {
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
171
180
|
unreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);
|
|
172
181
|
}
|
|
173
182
|
}
|
|
@@ -293,12 +302,13 @@ export const combineSnapshotTreeAndSnapshotBlobs = (
|
|
|
293
302
|
};
|
|
294
303
|
|
|
295
304
|
export function isDeltaStreamConnectionForbiddenError(
|
|
296
|
-
error:
|
|
305
|
+
error: unknown,
|
|
297
306
|
): error is DeltaStreamConnectionForbiddenError {
|
|
298
307
|
return (
|
|
299
308
|
typeof error === "object" &&
|
|
300
309
|
error !== null &&
|
|
301
|
-
error?.errorType ===
|
|
310
|
+
(error as Partial<IFluidErrorBase>)?.errorType ===
|
|
311
|
+
DriverErrorTypes.deltaStreamConnectionForbidden
|
|
302
312
|
);
|
|
303
313
|
}
|
|
304
314
|
|
|
@@ -329,9 +339,12 @@ export function getDetachedContainerStateFromSerializedContainer(
|
|
|
329
339
|
serializedContainer: string,
|
|
330
340
|
): IPendingDetachedContainerState {
|
|
331
341
|
const hasBlobsSummaryTree = ".hasAttachmentBlobs";
|
|
342
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
332
343
|
const parsedContainerState = JSON.parse(serializedContainer);
|
|
344
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
333
345
|
if (isPendingDetachedContainerState(parsedContainerState)) {
|
|
334
346
|
return parsedContainerState;
|
|
347
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
335
348
|
} else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {
|
|
336
349
|
const { baseSnapshot, snapshotBlobs } =
|
|
337
350
|
getSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);
|
|
@@ -354,16 +367,19 @@ export function getDetachedContainerStateFromSerializedContainer(
|
|
|
354
367
|
export function getAttachedContainerStateFromSerializedContainer(
|
|
355
368
|
serializedContainer: string | undefined,
|
|
356
369
|
): IPendingContainerState | undefined {
|
|
357
|
-
return serializedContainer
|
|
358
|
-
?
|
|
359
|
-
:
|
|
370
|
+
return serializedContainer === undefined
|
|
371
|
+
? undefined
|
|
372
|
+
: (JSON.parse(serializedContainer) as IPendingContainerState);
|
|
360
373
|
}
|
|
361
374
|
|
|
362
375
|
/**
|
|
363
376
|
* Ensures only a single instance of the provided async function is running.
|
|
364
377
|
* If there are multiple calls they will all get the same promise to wait on.
|
|
365
378
|
*/
|
|
366
|
-
|
|
379
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
380
|
+
export const runSingle = <A extends any[], R>(
|
|
381
|
+
func: (...args: A) => Promise<R>,
|
|
382
|
+
): ((...args: A) => Promise<R>) => {
|
|
367
383
|
let running:
|
|
368
384
|
| {
|
|
369
385
|
args: A;
|
|
@@ -373,7 +389,7 @@ export const runSingle = <A extends any[], R>(func: (...args: A) => Promise<R>)
|
|
|
373
389
|
// don't mark this function async, so we return the same promise,
|
|
374
390
|
// rather than one that is wrapped due to async
|
|
375
391
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
376
|
-
return (...args: A) => {
|
|
392
|
+
return (...args: A): Promise<R> => {
|
|
377
393
|
if (running !== undefined) {
|
|
378
394
|
if (!compareArrays(running.args, args)) {
|
|
379
395
|
return Promise.reject(
|