@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
package/src/containerContext.ts
CHANGED
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
AttachState,
|
|
8
8
|
IAudience,
|
|
9
9
|
ICriticalContainerError,
|
|
10
|
-
IDeltaManager,
|
|
11
10
|
} from "@fluidframework/container-definitions";
|
|
12
11
|
import {
|
|
13
12
|
IBatchMessage,
|
|
14
13
|
IContainerContext,
|
|
15
14
|
ILoader,
|
|
16
15
|
ILoaderOptions,
|
|
16
|
+
IDeltaManager,
|
|
17
17
|
} from "@fluidframework/container-definitions/internal";
|
|
18
18
|
import { type FluidObject } from "@fluidframework/core-interfaces";
|
|
19
19
|
import { type ISignalEnvelope } from "@fluidframework/core-interfaces/internal";
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
IVersion,
|
|
29
29
|
MessageType,
|
|
30
30
|
} from "@fluidframework/protocol-definitions";
|
|
31
|
-
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
31
|
+
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* {@inheritDoc @fluidframework/container-definitions#IContainerContext}
|
|
@@ -24,11 +24,16 @@ import {
|
|
|
24
24
|
ISummaryTree,
|
|
25
25
|
IVersion,
|
|
26
26
|
} from "@fluidframework/protocol-definitions";
|
|
27
|
-
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
27
|
+
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
28
28
|
|
|
29
29
|
import { IDetachedBlobStorage } from "./loader.js";
|
|
30
30
|
import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService.js";
|
|
31
31
|
import { RetriableDocumentStorageService } from "./retriableDocumentStorageService.js";
|
|
32
|
+
import type {
|
|
33
|
+
ISerializedStateManagerDocumentStorageService,
|
|
34
|
+
ISnapshotInfo,
|
|
35
|
+
} from "./serializedStateManager.js";
|
|
36
|
+
import { convertSnapshotInfoToSnapshot, getDocumentAttributes } from "./utils.js";
|
|
32
37
|
|
|
33
38
|
/**
|
|
34
39
|
* Stringified blobs from a summary/snapshot tree.
|
|
@@ -42,7 +47,9 @@ export interface ISerializableBlobContents {
|
|
|
42
47
|
* This class wraps the actual storage and make sure no wrong apis are called according to
|
|
43
48
|
* container attach state.
|
|
44
49
|
*/
|
|
45
|
-
export class ContainerStorageAdapter
|
|
50
|
+
export class ContainerStorageAdapter
|
|
51
|
+
implements ISerializedStateManagerDocumentStorageService, IDocumentStorageService, IDisposable
|
|
52
|
+
{
|
|
46
53
|
private _storageService: IDocumentStorageService & Partial<IDisposable>;
|
|
47
54
|
|
|
48
55
|
private _summarizeProtocolTree: boolean | undefined;
|
|
@@ -53,11 +60,20 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
53
60
|
return this._summarizeProtocolTree === true;
|
|
54
61
|
}
|
|
55
62
|
|
|
63
|
+
private _loadedGroupIdSnapshots: Record<string, ISnapshot> = {};
|
|
64
|
+
/**
|
|
65
|
+
* Any loading group id (virtualized) snapshot download from storage will be stored here.
|
|
66
|
+
*/
|
|
67
|
+
public get loadedGroupIdSnapshots(): Record<string, ISnapshot> {
|
|
68
|
+
return this._loadedGroupIdSnapshots;
|
|
69
|
+
}
|
|
70
|
+
|
|
56
71
|
/**
|
|
57
72
|
* An adapter that ensures we're using detachedBlobStorage up until we connect to a real service, and then
|
|
58
73
|
* after connecting to a real service augments it with retry and combined summary tree enforcement.
|
|
59
74
|
* @param detachedBlobStorage - The detached blob storage to use up until we connect to a real service
|
|
60
75
|
* @param logger - Telemetry logger
|
|
76
|
+
* @param loadingGroupIdSnapshotsFromPendingState - in offline mode, any loading group snapshots we've downloaded from the service that were stored in the pending state
|
|
61
77
|
* @param addProtocolSummaryIfMissing - a callback to permit the container to inspect the summary we're about to
|
|
62
78
|
* upload, and fix it up with a protocol tree if needed
|
|
63
79
|
* @param forceEnableSummarizeProtocolTree - Enforce uploading a protocol summary regardless of the service's policy
|
|
@@ -69,6 +85,7 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
69
85
|
* ArrayBufferLikes or utf8 encoded strings, containing blobs from a snapshot
|
|
70
86
|
*/
|
|
71
87
|
private readonly blobContents: { [id: string]: ArrayBufferLike | string } = {},
|
|
88
|
+
private loadingGroupIdSnapshotsFromPendingState: Record<string, ISnapshotInfo> | undefined,
|
|
72
89
|
private readonly addProtocolSummaryIfMissing: (summaryTree: ISummaryTree) => ISummaryTree,
|
|
73
90
|
forceEnableSummarizeProtocolTree: boolean | undefined,
|
|
74
91
|
) {
|
|
@@ -110,6 +127,10 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
110
127
|
}
|
|
111
128
|
}
|
|
112
129
|
|
|
130
|
+
public clearPendingState() {
|
|
131
|
+
this.loadingGroupIdSnapshotsFromPendingState = undefined;
|
|
132
|
+
}
|
|
133
|
+
|
|
113
134
|
public get policies(): IDocumentStorageServicePolicies | undefined {
|
|
114
135
|
// back-compat 0.40 containerRuntime requests policies even in detached container if storage is present
|
|
115
136
|
// and storage is always present in >=0.41.
|
|
@@ -127,12 +148,46 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
127
148
|
}
|
|
128
149
|
|
|
129
150
|
public async getSnapshot(snapshotFetchOptions?: ISnapshotFetchOptions): Promise<ISnapshot> {
|
|
130
|
-
|
|
131
|
-
|
|
151
|
+
let snapshot: ISnapshot;
|
|
152
|
+
if (
|
|
153
|
+
this.loadingGroupIdSnapshotsFromPendingState !== undefined &&
|
|
154
|
+
snapshotFetchOptions?.loadingGroupIds !== undefined
|
|
155
|
+
) {
|
|
156
|
+
const localSnapshot =
|
|
157
|
+
this.loadingGroupIdSnapshotsFromPendingState[
|
|
158
|
+
snapshotFetchOptions.loadingGroupIds[0]
|
|
159
|
+
];
|
|
160
|
+
assert(localSnapshot !== undefined, 0x970 /* Local snapshot must be present */);
|
|
161
|
+
const attributes = await getDocumentAttributes(this, localSnapshot.baseSnapshot);
|
|
162
|
+
snapshot = convertSnapshotInfoToSnapshot(localSnapshot, attributes.sequenceNumber);
|
|
163
|
+
} else {
|
|
164
|
+
if (this._storageService.getSnapshot === undefined) {
|
|
165
|
+
throw new UsageError(
|
|
166
|
+
"getSnapshot api should exist in internal storage in ContainerStorageAdapter",
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
snapshot = await this._storageService.getSnapshot(snapshotFetchOptions);
|
|
132
170
|
}
|
|
133
|
-
|
|
134
|
-
|
|
171
|
+
|
|
172
|
+
// Track the latest snapshot for each loading group id
|
|
173
|
+
const loadingGroupIds = snapshotFetchOptions?.loadingGroupIds;
|
|
174
|
+
assert(
|
|
175
|
+
snapshot.sequenceNumber !== undefined,
|
|
176
|
+
0x971 /* Snapshot must have sequence number */,
|
|
135
177
|
);
|
|
178
|
+
if (loadingGroupIds !== undefined) {
|
|
179
|
+
for (const loadingGroupId of loadingGroupIds) {
|
|
180
|
+
// Do we actually want to update the stored snapshot?
|
|
181
|
+
// What if the incoming snapshot is way newer than the stored snapshot?
|
|
182
|
+
// We only want to update the stored snapshot if the incoming snapshot is newer (stored sequence number < incoming sequence number)
|
|
183
|
+
const storedSeqNum =
|
|
184
|
+
this._loadedGroupIdSnapshots[loadingGroupId]?.sequenceNumber ?? -1;
|
|
185
|
+
if (storedSeqNum < snapshot.sequenceNumber) {
|
|
186
|
+
this._loadedGroupIdSnapshots[loadingGroupId] = snapshot;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return snapshot;
|
|
136
191
|
}
|
|
137
192
|
|
|
138
193
|
public async readBlob(id: string): Promise<ArrayBufferLike> {
|
package/src/contracts.ts
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
6
7
|
import {
|
|
7
|
-
IConnectionDetails,
|
|
8
|
-
ICriticalContainerError,
|
|
9
8
|
IDeltaQueue,
|
|
10
9
|
ReadOnlyInfo,
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
IFluidCodeDetails,
|
|
11
|
+
isFluidPackage,
|
|
12
|
+
IConnectionDetails,
|
|
13
|
+
} from "@fluidframework/container-definitions/internal";
|
|
13
14
|
import { IErrorBase, ITelemetryBaseProperties } from "@fluidframework/core-interfaces";
|
|
14
15
|
import { IContainerPackageInfo } from "@fluidframework/driver-definitions/internal";
|
|
15
16
|
import {
|
package/src/debugLogger.ts
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
ITelemetryBaseLogger,
|
|
10
10
|
ITelemetryBaseProperties,
|
|
11
11
|
} from "@fluidframework/core-interfaces";
|
|
12
|
-
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
13
12
|
import {
|
|
13
|
+
ITelemetryLoggerExt,
|
|
14
14
|
ITelemetryLoggerPropertyBags,
|
|
15
15
|
createMultiSinkLogger,
|
|
16
16
|
eventNamespaceSeparator,
|
package/src/deltaManager.ts
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
7
6
|
import {
|
|
8
7
|
ICriticalContainerError,
|
|
9
8
|
IDeltaManager,
|
|
10
9
|
IDeltaManagerEvents,
|
|
11
10
|
IDeltaQueue,
|
|
12
|
-
} from "@fluidframework/container-definitions";
|
|
11
|
+
} from "@fluidframework/container-definitions/internal";
|
|
13
12
|
import {
|
|
14
13
|
IEventProvider,
|
|
15
14
|
type ITelemetryBaseEvent,
|
|
@@ -17,10 +16,10 @@ import {
|
|
|
17
16
|
} from "@fluidframework/core-interfaces";
|
|
18
17
|
import { IThrottlingWarning } from "@fluidframework/core-interfaces/internal";
|
|
19
18
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
20
|
-
import { DriverErrorTypes } from "@fluidframework/driver-definitions";
|
|
21
19
|
import {
|
|
22
20
|
IDocumentDeltaStorageService,
|
|
23
21
|
IDocumentService,
|
|
22
|
+
DriverErrorTypes,
|
|
24
23
|
} from "@fluidframework/driver-definitions/internal";
|
|
25
24
|
import {
|
|
26
25
|
MessageType2,
|
|
@@ -38,8 +37,6 @@ import {
|
|
|
38
37
|
type ITelemetryErrorEventExt,
|
|
39
38
|
type ITelemetryGenericEventExt,
|
|
40
39
|
ITelemetryLoggerExt,
|
|
41
|
-
} from "@fluidframework/telemetry-utils";
|
|
42
|
-
import {
|
|
43
40
|
DataCorruptionError,
|
|
44
41
|
DataProcessingError,
|
|
45
42
|
UsageError,
|
|
@@ -47,6 +44,7 @@ import {
|
|
|
47
44
|
isFluidError,
|
|
48
45
|
normalizeError,
|
|
49
46
|
safeRaiseEvent,
|
|
47
|
+
EventEmitterWithErrorHandling,
|
|
50
48
|
} from "@fluidframework/telemetry-utils/internal";
|
|
51
49
|
import { v4 as uuid } from "uuid";
|
|
52
50
|
|
|
@@ -149,7 +147,7 @@ function logIfFalse(
|
|
|
149
147
|
* messages in order regardless of possible network conditions or timings causing out of order delivery.
|
|
150
148
|
*/
|
|
151
149
|
export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
152
|
-
extends
|
|
150
|
+
extends EventEmitterWithErrorHandling<IDeltaManagerInternalEvents>
|
|
153
151
|
implements
|
|
154
152
|
IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
|
|
155
153
|
IEventProvider<IDeltaManagerInternalEvents>
|
|
@@ -411,7 +409,16 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
411
409
|
private readonly _active: () => boolean,
|
|
412
410
|
createConnectionManager: (props: IConnectionManagerFactoryArgs) => TConnectionManager,
|
|
413
411
|
) {
|
|
414
|
-
super()
|
|
412
|
+
super((name, error) => {
|
|
413
|
+
this.logger.sendErrorEvent(
|
|
414
|
+
{
|
|
415
|
+
eventName: "DeltaManagerEventHandlerException",
|
|
416
|
+
name: typeof name === "string" ? name : undefined,
|
|
417
|
+
},
|
|
418
|
+
error,
|
|
419
|
+
);
|
|
420
|
+
this.close(normalizeError(error));
|
|
421
|
+
});
|
|
415
422
|
const props: IConnectionManagerFactoryArgs = {
|
|
416
423
|
incomingOpHandler: (messages: ISequencedDocumentMessage[], reason: string) => {
|
|
417
424
|
try {
|
|
@@ -550,7 +557,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
550
557
|
minSequenceNumber: number,
|
|
551
558
|
snapshotSequenceNumber: number,
|
|
552
559
|
handler: IDeltaHandlerStrategy,
|
|
553
|
-
prefetchType: "
|
|
560
|
+
prefetchType: "cached" | "all" | "none" = "none",
|
|
554
561
|
lastProcessedSequenceNumber: number = snapshotSequenceNumber,
|
|
555
562
|
) {
|
|
556
563
|
this.initSequenceNumber = snapshotSequenceNumber;
|
package/src/deltaQueue.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { TypedEventEmitter, performance } from "@fluid-internal/client-utils";
|
|
7
|
-
import { IDeltaQueue, IDeltaQueueEvents } from "@fluidframework/container-definitions";
|
|
7
|
+
import { IDeltaQueue, IDeltaQueueEvents } from "@fluidframework/container-definitions/internal";
|
|
8
8
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
9
9
|
import Deque from "double-ended-queue";
|
|
10
10
|
|
package/src/error.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
import { ContainerErrorTypes } from "@fluidframework/container-definitions/internal";
|
|
7
7
|
import { ITelemetryBaseProperties } from "@fluidframework/core-interfaces";
|
|
8
8
|
import { IThrottlingWarning } from "@fluidframework/core-interfaces/internal";
|
|
9
|
-
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
10
9
|
import {
|
|
11
10
|
IFluidErrorBase,
|
|
11
|
+
ITelemetryLoggerExt,
|
|
12
12
|
LoggingError,
|
|
13
13
|
wrapErrorAndLog,
|
|
14
14
|
} from "@fluidframework/telemetry-utils/internal";
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ILoader, LoaderHeader } from "@fluidframework/container-definitions/internal";
|
|
7
|
+
import { IRequest } from "@fluidframework/core-interfaces";
|
|
8
|
+
import { GenericError } from "@fluidframework/telemetry-utils/internal";
|
|
9
|
+
import type { IErrorBase } from "@fluidframework/core-interfaces";
|
|
10
|
+
|
|
11
|
+
/* eslint-disable jsdoc/check-indentation */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Loads container and leaves it in a state where it does not process any ops.
|
|
15
|
+
* Container instance returned by this function is in special mode where some functionality that is available in normal use will not work correctly
|
|
16
|
+
* with instance of container returned by this function. Some examples:
|
|
17
|
+
* 1. calling IContainer.connect() will have very little impact on this container as it will not process ops.
|
|
18
|
+
* 2. functionality like waitContainerToCatchUp() or waiting for ops in any other way would hand infinitely, as this container is not processing ops
|
|
19
|
+
* 3. No changes can be made to this container - they will be lost.
|
|
20
|
+
*
|
|
21
|
+
* If sequence number is provided, loads up to this sequence number and stops there, otherwise stops immediately after loading snapshot.
|
|
22
|
+
* In all cases, container is returned disconnected & paused, or an exception is thrown
|
|
23
|
+
* Notes:
|
|
24
|
+
* 1. Ignores LoaderHeader.loadMode headers. Container is always returned with ops applied upt to provided sequence number,
|
|
25
|
+
* or no ops applied at all (if sequence number is not provided)
|
|
26
|
+
* 2. This call can hang infinitately if disconnected from internet (or hit some other conditions, like 429 storm).
|
|
27
|
+
* Compare to Container.load() experience (with default settings) - it either returns failure right away, or succeeds, with
|
|
28
|
+
* ops fetching / delta connection happening in parallel / after container load flow, and thus providing an object (Container instance) to observe
|
|
29
|
+
* network connectivity issues / ability to cancel (IContainer.disconnect) or close container (IContainer.close)
|
|
30
|
+
* This flow needs to fetch ops (potentially connecting to delta connection), and any retriable errors on this path result in infinite retry.
|
|
31
|
+
* If you need to cancel that process, consider supplying AbortSignal parameter.
|
|
32
|
+
* @param loader - loader instance to use to load container
|
|
33
|
+
* @param request - request identifying container instance / load parameters. LoaderHeader.loadMode headers are ignored (see above)
|
|
34
|
+
* @param loadToSequenceNumber - optional sequence number. If provided, ops are processed up to this sequence number.
|
|
35
|
+
* @param signal - optional abort signal that can be used to cancel waiting for the ops.
|
|
36
|
+
* @returns IContainer instance
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
export async function loadContainerPaused(
|
|
41
|
+
loader: ILoader,
|
|
42
|
+
request: IRequest,
|
|
43
|
+
loadToSequenceNumber?: number,
|
|
44
|
+
signal?: AbortSignal,
|
|
45
|
+
) {
|
|
46
|
+
const container = await loader.resolve({
|
|
47
|
+
url: request.url,
|
|
48
|
+
headers: {
|
|
49
|
+
...request.headers,
|
|
50
|
+
// ensure we do not process any ops, such that we can examine container before ops starts to flow.
|
|
51
|
+
[LoaderHeader.loadMode]: { opsBeforeReturn: undefined, deltaConnection: "none" },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Force readonly mode - this will ensure we don't receive an error for the lack of join op
|
|
56
|
+
container.forceReadonly?.(true);
|
|
57
|
+
|
|
58
|
+
const dm = container.deltaManager;
|
|
59
|
+
const lastProcessedSequenceNumber = dm.initialSequenceNumber;
|
|
60
|
+
|
|
61
|
+
const pauseContainer = () => {
|
|
62
|
+
void dm.inbound.pause();
|
|
63
|
+
void dm.outbound.pause();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Happy path - we are already there.
|
|
67
|
+
if (
|
|
68
|
+
loadToSequenceNumber === undefined ||
|
|
69
|
+
lastProcessedSequenceNumber === loadToSequenceNumber
|
|
70
|
+
) {
|
|
71
|
+
// If we have already reached the desired sequence number, call pauseContainer() to pause immediately.
|
|
72
|
+
pauseContainer();
|
|
73
|
+
return container;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
77
|
+
if (lastProcessedSequenceNumber > loadToSequenceNumber) {
|
|
78
|
+
const error = new GenericError(
|
|
79
|
+
"Cannot satisfy request to pause the container at the specified sequence number. Most recent snapshot is newer than the specified sequence number.",
|
|
80
|
+
);
|
|
81
|
+
container.close(error);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let opHandler: () => void;
|
|
86
|
+
let onAbort: () => void;
|
|
87
|
+
let onClose: (error?: IErrorBase) => void;
|
|
88
|
+
|
|
89
|
+
const promise = new Promise<void>((resolve, rejectArg) => {
|
|
90
|
+
onAbort = () => rejectArg(new GenericError("Canceled due to cancellation request."));
|
|
91
|
+
onClose = (error?: IErrorBase) => rejectArg(error);
|
|
92
|
+
|
|
93
|
+
// We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
|
|
94
|
+
opHandler = () => {
|
|
95
|
+
// If there is a specified sequence number, keep processing until we reach it.
|
|
96
|
+
if (
|
|
97
|
+
loadToSequenceNumber !== undefined &&
|
|
98
|
+
dm.lastSequenceNumber >= loadToSequenceNumber
|
|
99
|
+
) {
|
|
100
|
+
// Pause op processing once we have processed the desired number of ops.
|
|
101
|
+
pauseContainer();
|
|
102
|
+
resolve();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
|
|
107
|
+
signal?.addEventListener("abort", onAbort);
|
|
108
|
+
container.on("op", opHandler);
|
|
109
|
+
container.on("closed", onClose);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// There are no guarantees on when ops will land in storage.
|
|
113
|
+
// No guarantees that driver implements ops caching (i.e. ops observed in previous session can be served from cache)
|
|
114
|
+
// or that browser will provide caching capabilities / keep the data (localStorage).
|
|
115
|
+
// Thus, we have to ensure we connect to delta storage in order to make forward progress with ops.
|
|
116
|
+
// We also instructed not to fetch / apply any ops from storage above (to be able to install callback above before ops are processed),
|
|
117
|
+
// connect() call will fetch ops as needed.
|
|
118
|
+
container.connect();
|
|
119
|
+
|
|
120
|
+
// Wait for the ops to be processed.
|
|
121
|
+
await promise
|
|
122
|
+
.catch((error) => {
|
|
123
|
+
container.close(error);
|
|
124
|
+
throw error;
|
|
125
|
+
})
|
|
126
|
+
.finally(() => {
|
|
127
|
+
// There is not much value in leaving delta connection on. We are not processing ops, we also can't advance to "connected" state because of it.
|
|
128
|
+
// We are not sending ops (due to forceReadonly() call above). We are holding collab window and any consensus-based processes.
|
|
129
|
+
// It's better not to have connection in such case, as there are only nagatives, and no positives.
|
|
130
|
+
container.disconnect();
|
|
131
|
+
|
|
132
|
+
container.off("op", opHandler);
|
|
133
|
+
container.off("closed", onClose);
|
|
134
|
+
signal?.removeEventListener("abort", onAbort);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return container;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* eslint-enable jsdoc/check-indentation */
|
package/src/loader.ts
CHANGED
|
@@ -26,11 +26,10 @@ import {
|
|
|
26
26
|
IUrlResolver,
|
|
27
27
|
} from "@fluidframework/driver-definitions/internal";
|
|
28
28
|
import { IClientDetails } from "@fluidframework/protocol-definitions";
|
|
29
|
-
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
30
29
|
import {
|
|
30
|
+
ITelemetryLoggerExt,
|
|
31
31
|
MonitoringContext,
|
|
32
32
|
PerformanceEvent,
|
|
33
|
-
UsageError,
|
|
34
33
|
createChildMonitoringContext,
|
|
35
34
|
mixinMonitoringContext,
|
|
36
35
|
sessionStorageConfigProvider,
|
|
@@ -42,7 +41,10 @@ import { DebugLogger } from "./debugLogger.js";
|
|
|
42
41
|
import { pkgVersion } from "./packageVersion.js";
|
|
43
42
|
import { ProtocolHandlerBuilder } from "./protocol.js";
|
|
44
43
|
import type { IPendingContainerState } from "./serializedStateManager.js";
|
|
45
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
getAttachedContainerStateFromSerializedContainer,
|
|
46
|
+
tryParseCompatibleResolvedUrl,
|
|
47
|
+
} from "./utils.js";
|
|
46
48
|
|
|
47
49
|
function ensureResolvedUrlDefined(
|
|
48
50
|
resolved: IResolvedUrl | undefined,
|
|
@@ -336,7 +338,7 @@ export class Loader implements IHostLoader {
|
|
|
336
338
|
return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName }, async () => {
|
|
337
339
|
return this.resolveCore(
|
|
338
340
|
request,
|
|
339
|
-
|
|
341
|
+
getAttachedContainerStateFromSerializedContainer(pendingLocalState),
|
|
340
342
|
);
|
|
341
343
|
});
|
|
342
344
|
}
|
|
@@ -369,24 +371,6 @@ export class Loader implements IHostLoader {
|
|
|
369
371
|
// If set in both query string and headers, use query string. Also write the value from the query string into the header either way.
|
|
370
372
|
request.headers[LoaderHeader.version] =
|
|
371
373
|
parsed.version ?? request.headers[LoaderHeader.version];
|
|
372
|
-
const fromSequenceNumber = request.headers[LoaderHeader.sequenceNumber] as
|
|
373
|
-
| number
|
|
374
|
-
| undefined;
|
|
375
|
-
const opsBeforeReturn = request.headers[LoaderHeader.loadMode]?.opsBeforeReturn as
|
|
376
|
-
| string
|
|
377
|
-
| undefined;
|
|
378
|
-
|
|
379
|
-
if (
|
|
380
|
-
opsBeforeReturn === "sequenceNumber" &&
|
|
381
|
-
(fromSequenceNumber === undefined || fromSequenceNumber < 0)
|
|
382
|
-
) {
|
|
383
|
-
// If opsBeforeReturn is set to "sequenceNumber", then fromSequenceNumber should be set to a non-negative integer.
|
|
384
|
-
throw new UsageError("sequenceNumber must be set to a non-negative integer");
|
|
385
|
-
} else if (opsBeforeReturn !== "sequenceNumber" && fromSequenceNumber !== undefined) {
|
|
386
|
-
// If opsBeforeReturn is not set to "sequenceNumber", then fromSequenceNumber should be undefined (default value).
|
|
387
|
-
// In this case, we should throw an error since opsBeforeReturn is not explicitly set to "sequenceNumber".
|
|
388
|
-
throw new UsageError('opsBeforeReturn must be set to "sequenceNumber"');
|
|
389
|
-
}
|
|
390
374
|
|
|
391
375
|
return this.loadContainer(request, resolvedAsFluid, pendingLocalState);
|
|
392
376
|
}
|
|
@@ -402,7 +386,6 @@ export class Loader implements IHostLoader {
|
|
|
402
386
|
version: request.headers?.[LoaderHeader.version] ?? undefined,
|
|
403
387
|
loadMode: request.headers?.[LoaderHeader.loadMode],
|
|
404
388
|
pendingLocalState,
|
|
405
|
-
loadToSequenceNumber: request.headers?.[LoaderHeader.sequenceNumber],
|
|
406
389
|
},
|
|
407
390
|
{
|
|
408
391
|
canReconnect: request.headers?.[LoaderHeader.reconnect],
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IRequest, ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
7
|
-
import { DriverErrorTypes } from "@fluidframework/driver-definitions";
|
|
8
7
|
import {
|
|
9
8
|
ILocationRedirectionError,
|
|
10
9
|
IUrlResolver,
|
|
10
|
+
DriverErrorTypes,
|
|
11
11
|
} from "@fluidframework/driver-definitions/internal";
|
|
12
12
|
import { createChildLogger } from "@fluidframework/telemetry-utils/internal";
|
|
13
13
|
|
package/src/packageVersion.ts
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -61,6 +61,10 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
|
|
|
61
61
|
sendProposal,
|
|
62
62
|
);
|
|
63
63
|
|
|
64
|
+
for (const [clientId, member] of this.quorum.getMembers()) {
|
|
65
|
+
audience.addMember(clientId, member.client);
|
|
66
|
+
}
|
|
67
|
+
|
|
64
68
|
// Join / leave signals are ignored for "write" clients in favor of join / leave ops
|
|
65
69
|
this.quorum.on("addMember", (clientId, details) =>
|
|
66
70
|
audience.addMember(clientId, details.client),
|
|
@@ -21,8 +21,11 @@ import {
|
|
|
21
21
|
ISummaryTree,
|
|
22
22
|
IVersion,
|
|
23
23
|
} from "@fluidframework/protocol-definitions";
|
|
24
|
-
import {
|
|
25
|
-
|
|
24
|
+
import {
|
|
25
|
+
ITelemetryLoggerExt,
|
|
26
|
+
GenericError,
|
|
27
|
+
UsageError,
|
|
28
|
+
} from "@fluidframework/telemetry-utils/internal";
|
|
26
29
|
|
|
27
30
|
export class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {
|
|
28
31
|
private _disposed = false;
|