@fluidframework/container-loader 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917
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 +14 -0
- package/README.md +45 -4
- package/closeAndGetPendingLocalState.md +51 -0
- package/dist/connectionManager.d.ts +2 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +59 -17
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +4 -4
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +7 -0
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +44 -4
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +160 -105
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +18 -8
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +47 -4
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +41 -2
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +87 -11
- 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/deltaManager.d.ts +4 -5
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +7 -10
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts +10 -22
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaManagerProxy.js +14 -50
- package/dist/deltaManagerProxy.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +10 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +25 -16
- package/dist/loader.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 +1 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +4 -2
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +6 -2
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +7 -4
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts +2 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +60 -18
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +4 -4
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +7 -0
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +44 -4
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +164 -109
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +18 -8
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +48 -5
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +41 -2
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +85 -11
- 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.map +1 -1
- package/lib/deltaManager.d.ts +4 -5
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +7 -10
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts +10 -22
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaManagerProxy.js +14 -50
- package/lib/deltaManagerProxy.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +10 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +24 -16
- package/lib/loader.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 +1 -0
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +3 -1
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +6 -2
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js +7 -4
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +2 -1
- package/lib/utils.js.map +1 -1
- package/package.json +64 -56
- package/src/connectionManager.ts +65 -24
- package/src/connectionStateHandler.ts +17 -5
- package/src/container.ts +239 -137
- package/src/containerContext.ts +74 -11
- package/src/containerStorageAdapter.ts +113 -9
- package/src/contracts.ts +2 -2
- package/src/deltaManager.ts +12 -14
- package/src/deltaManagerProxy.ts +18 -73
- package/src/index.ts +3 -3
- package/src/loader.ts +31 -26
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +4 -1
- package/src/protocolTreeDocumentStorageService.ts +6 -3
- package/src/utils.ts +7 -4
package/src/containerContext.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import { LazyPromise } from "@fluidframework/common-utils";
|
|
7
|
+
import { assert, LazyPromise, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
8
8
|
import {
|
|
9
9
|
IAudience,
|
|
10
10
|
IContainerContext,
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
IProvideFluidCodeDetailsComparer,
|
|
22
22
|
ICodeDetailsLoader,
|
|
23
23
|
IFluidModuleWithDetails,
|
|
24
|
-
ISnapshotTreeWithBlobContents,
|
|
25
24
|
IBatchMessage,
|
|
26
25
|
} from "@fluidframework/container-definitions";
|
|
27
26
|
import { IRequest, IResponse, FluidObject } from "@fluidframework/core-interfaces";
|
|
@@ -42,10 +41,21 @@ import {
|
|
|
42
41
|
ISummaryContent,
|
|
43
42
|
} from "@fluidframework/protocol-definitions";
|
|
44
43
|
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
44
|
+
import { UsageError } from "@fluidframework/container-utils";
|
|
45
45
|
import { Container } from "./container";
|
|
46
46
|
|
|
47
47
|
const PackageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Events that {@link ContainerContext} can emit through its lifecycle.
|
|
51
|
+
*
|
|
52
|
+
* "runtimeInstantiated" - When an {@link @fluidframework/container-definitions#IRuntime} has been instantiated (by
|
|
53
|
+
* calling instantiateRuntime() on the runtime factory), and this._runtime is set.
|
|
54
|
+
*
|
|
55
|
+
* "disposed" - When its dispose() method is called. The {@link ContainerContext} is no longer usable at that point.
|
|
56
|
+
*/
|
|
57
|
+
type ContextLifecycleEvents = "runtimeInstantiated" | "disposed";
|
|
58
|
+
|
|
49
59
|
export class ContainerContext implements IContainerContext {
|
|
50
60
|
public static async createOrLoad(
|
|
51
61
|
container: Container,
|
|
@@ -57,8 +67,8 @@ export class ContainerContext implements IContainerContext {
|
|
|
57
67
|
quorum: IQuorum,
|
|
58
68
|
loader: ILoader,
|
|
59
69
|
submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
|
|
60
|
-
submitSummaryFn: (summaryOp: ISummaryContent) => number,
|
|
61
|
-
submitBatchFn: (batch: IBatchMessage[]) => number,
|
|
70
|
+
submitSummaryFn: (summaryOp: ISummaryContent, referenceSequenceNumber?: number) => number,
|
|
71
|
+
submitBatchFn: (batch: IBatchMessage[], referenceSequenceNumber?: number) => number,
|
|
62
72
|
submitSignalFn: (contents: any) => void,
|
|
63
73
|
disposeFn: (error?: ICriticalContainerError) => void,
|
|
64
74
|
closeFn: (error?: ICriticalContainerError) => void,
|
|
@@ -92,6 +102,7 @@ export class ContainerContext implements IContainerContext {
|
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
public readonly taggedLogger: ITelemetryLogger;
|
|
105
|
+
public readonly supportedFeatures: ReadonlyMap<string, unknown>;
|
|
95
106
|
|
|
96
107
|
public get clientId(): string | undefined {
|
|
97
108
|
return this.container.clientId;
|
|
@@ -170,12 +181,46 @@ export class ContainerContext implements IContainerContext {
|
|
|
170
181
|
|
|
171
182
|
private readonly _fluidModuleP: Promise<IFluidModuleWithDetails>;
|
|
172
183
|
|
|
184
|
+
/**
|
|
185
|
+
* {@inheritDoc @fluidframework/container-definitions#IContainerContext.getEntryPoint}
|
|
186
|
+
*/
|
|
187
|
+
public async getEntryPoint?(): Promise<FluidObject | undefined> {
|
|
188
|
+
if (this._disposed) {
|
|
189
|
+
throw new UsageError("The context is already disposed");
|
|
190
|
+
}
|
|
191
|
+
if (this._runtime !== undefined) {
|
|
192
|
+
return this._runtime?.getEntryPoint?.();
|
|
193
|
+
}
|
|
194
|
+
return new Promise<FluidObject | undefined>((resolve, reject) => {
|
|
195
|
+
const runtimeInstantiatedHandler = () => {
|
|
196
|
+
assert(
|
|
197
|
+
this._runtime !== undefined,
|
|
198
|
+
0x5a3 /* runtimeInstantiated fired but runtime is still undefined */,
|
|
199
|
+
);
|
|
200
|
+
resolve(this._runtime.getEntryPoint?.());
|
|
201
|
+
this.lifecycleEvents.off("disposed", disposedHandler);
|
|
202
|
+
};
|
|
203
|
+
const disposedHandler = () => {
|
|
204
|
+
reject(new Error("ContainerContext was disposed"));
|
|
205
|
+
this.lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
206
|
+
};
|
|
207
|
+
this.lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
208
|
+
this.lifecycleEvents.once("disposed", disposedHandler);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Emits events about the container context's lifecycle.
|
|
214
|
+
* Use it to coordinate things inside the ContainerContext class.
|
|
215
|
+
*/
|
|
216
|
+
private readonly lifecycleEvents = new TypedEventEmitter<ContextLifecycleEvents>();
|
|
217
|
+
|
|
173
218
|
constructor(
|
|
174
219
|
private readonly container: Container,
|
|
175
220
|
public readonly scope: FluidObject,
|
|
176
221
|
private readonly codeLoader: ICodeDetailsLoader,
|
|
177
222
|
private readonly _codeDetails: IFluidCodeDetails,
|
|
178
|
-
private _baseSnapshot: ISnapshotTree | undefined,
|
|
223
|
+
private readonly _baseSnapshot: ISnapshotTree | undefined,
|
|
179
224
|
public readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
|
|
180
225
|
quorum: IQuorum,
|
|
181
226
|
public readonly loader: ILoader,
|
|
@@ -185,9 +230,15 @@ export class ContainerContext implements IContainerContext {
|
|
|
185
230
|
batch: boolean,
|
|
186
231
|
appData: any,
|
|
187
232
|
) => number,
|
|
188
|
-
public readonly submitSummaryFn: (
|
|
233
|
+
public readonly submitSummaryFn: (
|
|
234
|
+
summaryOp: ISummaryContent,
|
|
235
|
+
referenceSequenceNumber?: number,
|
|
236
|
+
) => number,
|
|
189
237
|
/** @returns clientSequenceNumber of last message in a batch */
|
|
190
|
-
public readonly submitBatchFn: (
|
|
238
|
+
public readonly submitBatchFn: (
|
|
239
|
+
batch: IBatchMessage[],
|
|
240
|
+
referenceSequenceNumber?: number,
|
|
241
|
+
) => number,
|
|
191
242
|
public readonly submitSignalFn: (contents: any) => void,
|
|
192
243
|
public readonly disposeFn: (error?: ICriticalContainerError) => void,
|
|
193
244
|
public readonly closeFn: (error?: ICriticalContainerError) => void,
|
|
@@ -202,6 +253,15 @@ export class ContainerContext implements IContainerContext {
|
|
|
202
253
|
this._fluidModuleP = new LazyPromise<IFluidModuleWithDetails>(async () =>
|
|
203
254
|
this.loadCodeModule(_codeDetails),
|
|
204
255
|
);
|
|
256
|
+
|
|
257
|
+
this.supportedFeatures = new Map([
|
|
258
|
+
/**
|
|
259
|
+
* This version of the loader accepts `referenceSequenceNumber`, provided by the container runtime,
|
|
260
|
+
* as a parameter to the `submitBatchFn` and `submitSummaryFn` functions.
|
|
261
|
+
* This is then used to set the reference sequence numbers of the submitted ops in the DeltaManager.
|
|
262
|
+
*/
|
|
263
|
+
["referenceSequenceNumbers", true],
|
|
264
|
+
]);
|
|
205
265
|
this.attachListener();
|
|
206
266
|
}
|
|
207
267
|
|
|
@@ -223,6 +283,7 @@ export class ContainerContext implements IContainerContext {
|
|
|
223
283
|
}
|
|
224
284
|
this._disposed = true;
|
|
225
285
|
|
|
286
|
+
this.lifecycleEvents.emit("disposed");
|
|
226
287
|
this.runtime.dispose(error);
|
|
227
288
|
this._quorum.dispose();
|
|
228
289
|
this.deltaManager.dispose();
|
|
@@ -313,10 +374,8 @@ export class ContainerContext implements IContainerContext {
|
|
|
313
374
|
return true;
|
|
314
375
|
}
|
|
315
376
|
|
|
316
|
-
public
|
|
317
|
-
this.
|
|
318
|
-
this.runtime.notifyAttaching?.(snapshot);
|
|
319
|
-
this.runtime.setAttachState(AttachState.Attaching);
|
|
377
|
+
public async notifyOpReplay(message: ISequencedDocumentMessage): Promise<void> {
|
|
378
|
+
return this.runtime.notifyOpReplay?.(message);
|
|
320
379
|
}
|
|
321
380
|
|
|
322
381
|
// #region private
|
|
@@ -340,9 +399,13 @@ export class ContainerContext implements IContainerContext {
|
|
|
340
399
|
{ eventName: "InstantiateRuntime" },
|
|
341
400
|
async () => runtimeFactory.instantiateRuntime(this, existing),
|
|
342
401
|
);
|
|
402
|
+
this.lifecycleEvents.emit("runtimeInstantiated");
|
|
343
403
|
}
|
|
344
404
|
|
|
345
405
|
private attachListener() {
|
|
406
|
+
this.container.once("attaching", () => {
|
|
407
|
+
this.runtime.setAttachState(AttachState.Attaching);
|
|
408
|
+
});
|
|
346
409
|
this.container.once("attached", () => {
|
|
347
410
|
this.runtime.setAttachState(AttachState.Attached);
|
|
348
411
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import { assert } from "@fluidframework/common-utils";
|
|
7
|
+
import { assert, bufferToString, stringToBuffer } from "@fluidframework/common-utils";
|
|
8
8
|
import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
|
|
9
9
|
import {
|
|
10
10
|
FetchSource,
|
|
@@ -25,20 +25,51 @@ import { IDetachedBlobStorage } from "./loader";
|
|
|
25
25
|
import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService";
|
|
26
26
|
import { RetriableDocumentStorageService } from "./retriableDocumentStorageService";
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Stringified blobs from a summary/snapshot tree.
|
|
30
|
+
* @deprecated this is an internal interface and will not longer be exported in future versions
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export interface ISerializableBlobContents {
|
|
34
|
+
[id: string]: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
/**
|
|
29
38
|
* This class wraps the actual storage and make sure no wrong apis are called according to
|
|
30
39
|
* container attach state.
|
|
31
40
|
*/
|
|
32
41
|
export class ContainerStorageAdapter implements IDocumentStorageService, IDisposable {
|
|
33
|
-
private readonly blobContents: { [id: string]: ArrayBufferLike } = {};
|
|
34
42
|
private _storageService: IDocumentStorageService & Partial<IDisposable>;
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
private _summarizeProtocolTree: boolean | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the adapter will enforce sending combined summary trees.
|
|
47
|
+
*/
|
|
48
|
+
public get summarizeProtocolTree() {
|
|
49
|
+
return this._summarizeProtocolTree === true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* An adapter that ensures we're using detachedBlobStorage up until we connect to a real service, and then
|
|
54
|
+
* after connecting to a real service augments it with retry and combined summary tree enforcement.
|
|
55
|
+
* @param detachedBlobStorage - The detached blob storage to use up until we connect to a real service
|
|
56
|
+
* @param logger - Telemetry logger
|
|
57
|
+
* @param addProtocolSummaryIfMissing - a callback to permit the container to inspect the summary we're about to
|
|
58
|
+
* upload, and fix it up with a protocol tree if needed
|
|
59
|
+
* @param forceEnableSummarizeProtocolTree - Enforce uploading a protocol summary regardless of the service's policy
|
|
60
|
+
*/
|
|
61
|
+
public constructor(
|
|
37
62
|
detachedBlobStorage: IDetachedBlobStorage | undefined,
|
|
38
63
|
private readonly logger: ITelemetryLogger,
|
|
39
|
-
|
|
64
|
+
/**
|
|
65
|
+
* ArrayBufferLikes or utf8 encoded strings, containing blobs from a snapshot
|
|
66
|
+
*/
|
|
67
|
+
private readonly blobContents: { [id: string]: ArrayBufferLike | string } = {},
|
|
68
|
+
private readonly addProtocolSummaryIfMissing: (summaryTree: ISummaryTree) => ISummaryTree,
|
|
69
|
+
forceEnableSummarizeProtocolTree: boolean | undefined,
|
|
40
70
|
) {
|
|
41
71
|
this._storageService = new BlobOnlyStorage(detachedBlobStorage, logger);
|
|
72
|
+
this._summarizeProtocolTree = forceEnableSummarizeProtocolTree;
|
|
42
73
|
}
|
|
43
74
|
|
|
44
75
|
disposed: boolean = false;
|
|
@@ -58,11 +89,13 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
58
89
|
this.logger,
|
|
59
90
|
));
|
|
60
91
|
|
|
61
|
-
|
|
92
|
+
this._summarizeProtocolTree =
|
|
93
|
+
this._summarizeProtocolTree ?? service.policies?.summarizeProtocolTree;
|
|
94
|
+
if (this.summarizeProtocolTree) {
|
|
62
95
|
this.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
|
|
63
96
|
this._storageService = new ProtocolTreeStorageService(
|
|
64
97
|
retriableStorage,
|
|
65
|
-
this.
|
|
98
|
+
this.addProtocolSummaryIfMissing,
|
|
66
99
|
);
|
|
67
100
|
}
|
|
68
101
|
|
|
@@ -107,9 +140,13 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
107
140
|
}
|
|
108
141
|
|
|
109
142
|
public async readBlob(id: string): Promise<ArrayBufferLike> {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
|
|
143
|
+
const maybeBlob = this.blobContents[id];
|
|
144
|
+
if (maybeBlob !== undefined) {
|
|
145
|
+
if (typeof maybeBlob === "string") {
|
|
146
|
+
const blob = stringToBuffer(maybeBlob, "utf8");
|
|
147
|
+
return blob;
|
|
148
|
+
}
|
|
149
|
+
return maybeBlob;
|
|
113
150
|
}
|
|
114
151
|
return this._storageService.readBlob(id);
|
|
115
152
|
}
|
|
@@ -191,3 +228,70 @@ class BlobOnlyStorage implements IDocumentStorageService {
|
|
|
191
228
|
}
|
|
192
229
|
}
|
|
193
230
|
}
|
|
231
|
+
|
|
232
|
+
// runtime will write a tree to the summary containing only "attachment" type entries
|
|
233
|
+
// which reference attachment blobs by ID. However, some drivers do not support this type
|
|
234
|
+
// and will convert them to "blob" type entries. We want to avoid saving these to reduce
|
|
235
|
+
// the size of stashed change blobs.
|
|
236
|
+
const blobsTreeName = ".blobs";
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get blob contents of a snapshot tree from storage (or, ideally, cache)
|
|
240
|
+
*/
|
|
241
|
+
export async function getBlobContentsFromTree(
|
|
242
|
+
snapshot: ISnapshotTree,
|
|
243
|
+
storage: IDocumentStorageService,
|
|
244
|
+
): Promise<ISerializableBlobContents> {
|
|
245
|
+
const blobs = {};
|
|
246
|
+
await getBlobContentsFromTreeCore(snapshot, blobs, storage);
|
|
247
|
+
return blobs;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function getBlobContentsFromTreeCore(
|
|
251
|
+
tree: ISnapshotTree,
|
|
252
|
+
blobs: ISerializableBlobContents,
|
|
253
|
+
storage: IDocumentStorageService,
|
|
254
|
+
root = true,
|
|
255
|
+
) {
|
|
256
|
+
const treePs: Promise<any>[] = [];
|
|
257
|
+
for (const [key, subTree] of Object.entries(tree.trees)) {
|
|
258
|
+
if (!root || key !== blobsTreeName) {
|
|
259
|
+
treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage, false));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
for (const id of Object.values(tree.blobs)) {
|
|
263
|
+
const blob = await storage.readBlob(id);
|
|
264
|
+
// ArrayBufferLike will not survive JSON.stringify()
|
|
265
|
+
blobs[id] = bufferToString(blob, "utf8");
|
|
266
|
+
}
|
|
267
|
+
return Promise.all(treePs);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Extract blob contents from a snapshot tree with blob contents
|
|
272
|
+
*/
|
|
273
|
+
export function getBlobContentsFromTreeWithBlobContents(
|
|
274
|
+
snapshot: ISnapshotTreeWithBlobContents,
|
|
275
|
+
): ISerializableBlobContents {
|
|
276
|
+
const blobs = {};
|
|
277
|
+
getBlobContentsFromTreeWithBlobContentsCore(snapshot, blobs);
|
|
278
|
+
return blobs;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function getBlobContentsFromTreeWithBlobContentsCore(
|
|
282
|
+
tree: ISnapshotTreeWithBlobContents,
|
|
283
|
+
blobs: ISerializableBlobContents,
|
|
284
|
+
root = true,
|
|
285
|
+
) {
|
|
286
|
+
for (const [key, subTree] of Object.entries(tree.trees)) {
|
|
287
|
+
if (!root || key !== blobsTreeName) {
|
|
288
|
+
getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs, false);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (const id of Object.values(tree.blobs)) {
|
|
292
|
+
const blob = tree.blobsContents[id];
|
|
293
|
+
assert(blob !== undefined, 0x2ec /* "Blob must be present in blobsContents" */);
|
|
294
|
+
// ArrayBufferLike will not survive JSON.stringify()
|
|
295
|
+
blobs[id] = bufferToString(blob, "utf8");
|
|
296
|
+
}
|
|
297
|
+
}
|
package/src/contracts.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { ITelemetryProperties } from "@fluidframework/common-definitions";
|
|
|
7
7
|
import {
|
|
8
8
|
IDeltaQueue,
|
|
9
9
|
ReadOnlyInfo,
|
|
10
|
-
|
|
10
|
+
IConnectionDetailsInternal,
|
|
11
11
|
ICriticalContainerError,
|
|
12
12
|
IFluidCodeDetails,
|
|
13
13
|
isFluidPackage,
|
|
@@ -144,7 +144,7 @@ export interface IConnectionManagerFactoryArgs {
|
|
|
144
144
|
/**
|
|
145
145
|
* Called whenever new connection to rely service is established
|
|
146
146
|
*/
|
|
147
|
-
readonly connectHandler: (connection:
|
|
147
|
+
readonly connectHandler: (connection: IConnectionDetailsInternal) => void;
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
150
|
* Called whenever ping/pong messages are roundtripped on connection.
|
package/src/deltaManager.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
IDeltaQueue,
|
|
19
19
|
ICriticalContainerError,
|
|
20
20
|
IThrottlingWarning,
|
|
21
|
-
|
|
21
|
+
IConnectionDetailsInternal,
|
|
22
22
|
} from "@fluidframework/container-definitions";
|
|
23
23
|
import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
24
24
|
import { normalizeError, logIfFalse, safeRaiseEvent } from "@fluidframework/telemetry-utils";
|
|
@@ -41,8 +41,9 @@ import {
|
|
|
41
41
|
extractSafePropertiesFromMessage,
|
|
42
42
|
DataProcessingError,
|
|
43
43
|
} from "@fluidframework/container-utils";
|
|
44
|
-
import { DeltaQueue } from "./deltaQueue";
|
|
45
44
|
import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
|
|
45
|
+
import { DeltaQueue } from "./deltaQueue";
|
|
46
|
+
import { OnlyValidTermValue } from "./protocol";
|
|
46
47
|
|
|
47
48
|
export interface IConnectionArgs {
|
|
48
49
|
mode?: ConnectionMode;
|
|
@@ -57,6 +58,7 @@ export interface IConnectionArgs {
|
|
|
57
58
|
export interface IDeltaManagerInternalEvents extends IDeltaManagerEvents {
|
|
58
59
|
(event: "throttled", listener: (error: IThrottlingWarning) => void);
|
|
59
60
|
(event: "closed" | "disposed", listener: (error?: ICriticalContainerError) => void);
|
|
61
|
+
(event: "connect", listener: (details: IConnectionDetailsInternal, opsBehind?: number) => void);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
/**
|
|
@@ -122,7 +124,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
122
124
|
private lastObservedSeqNumber: number = 0;
|
|
123
125
|
private lastProcessedSequenceNumber: number = 0;
|
|
124
126
|
private lastProcessedMessage: ISequencedDocumentMessage | undefined;
|
|
125
|
-
private baseTerm: number = 0;
|
|
126
127
|
|
|
127
128
|
/** count number of noops sent by the client which may not be acked */
|
|
128
129
|
private noOpCount: number = 0;
|
|
@@ -186,10 +187,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
186
187
|
return this.lastObservedSeqNumber;
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
public get referenceTerm(): number {
|
|
190
|
-
return this.baseTerm;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
190
|
public get minimumSequenceNumber(): number {
|
|
194
191
|
return this.minSequenceNumber;
|
|
195
192
|
}
|
|
@@ -230,11 +227,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
230
227
|
batch = false,
|
|
231
228
|
metadata?: any,
|
|
232
229
|
compression?: string,
|
|
230
|
+
referenceSequenceNumber?: number,
|
|
233
231
|
) {
|
|
232
|
+
// Back-compat ADO:3455
|
|
233
|
+
const backCompatRefSeqNum = referenceSequenceNumber ?? this.lastProcessedSequenceNumber;
|
|
234
234
|
const messagePartial: Omit<IDocumentMessage, "clientSequenceNumber"> = {
|
|
235
235
|
contents,
|
|
236
236
|
metadata,
|
|
237
|
-
referenceSequenceNumber:
|
|
237
|
+
referenceSequenceNumber: backCompatRefSeqNum,
|
|
238
238
|
type,
|
|
239
239
|
compression,
|
|
240
240
|
};
|
|
@@ -358,7 +358,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
358
358
|
this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
|
|
359
359
|
closeHandler: (error: any) => this.close(error),
|
|
360
360
|
disconnectHandler: (reason: string) => this.disconnectHandler(reason),
|
|
361
|
-
connectHandler: (connection:
|
|
361
|
+
connectHandler: (connection: IConnectionDetailsInternal) =>
|
|
362
|
+
this.connectHandler(connection),
|
|
362
363
|
pongHandler: (latency: number) => this.emit("pong", latency),
|
|
363
364
|
readonlyChangeHandler: (readonly?: boolean) =>
|
|
364
365
|
safeRaiseEvent(this, this.logger, "readonly", readonly),
|
|
@@ -401,7 +402,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
401
402
|
// - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
|
|
402
403
|
}
|
|
403
404
|
|
|
404
|
-
private connectHandler(connection:
|
|
405
|
+
private connectHandler(connection: IConnectionDetailsInternal) {
|
|
405
406
|
this.refreshDelayInfo(this.deltaStreamDelayId);
|
|
406
407
|
|
|
407
408
|
const props = this.connectionManager.connectionVerboseProps;
|
|
@@ -460,13 +461,11 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
460
461
|
public async attachOpHandler(
|
|
461
462
|
minSequenceNumber: number,
|
|
462
463
|
sequenceNumber: number,
|
|
463
|
-
term: number,
|
|
464
464
|
handler: IDeltaHandlerStrategy,
|
|
465
465
|
prefetchType: "cached" | "all" | "none" = "none",
|
|
466
466
|
) {
|
|
467
467
|
this.initSequenceNumber = sequenceNumber;
|
|
468
468
|
this.lastProcessedSequenceNumber = sequenceNumber;
|
|
469
|
-
this.baseTerm = term;
|
|
470
469
|
this.minSequenceNumber = minSequenceNumber;
|
|
471
470
|
this.lastQueuedSequenceNumber = sequenceNumber;
|
|
472
471
|
this.lastObservedSeqNumber = sequenceNumber;
|
|
@@ -981,9 +980,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
981
980
|
|
|
982
981
|
// Back-compat for older server with no term
|
|
983
982
|
if (message.term === undefined) {
|
|
984
|
-
message.term =
|
|
983
|
+
message.term = OnlyValidTermValue;
|
|
985
984
|
}
|
|
986
|
-
this.baseTerm = message.term;
|
|
987
985
|
|
|
988
986
|
if (this.handler === undefined) {
|
|
989
987
|
throw new Error("Attempted to process an inbound message without a handler attached");
|
package/src/deltaManagerProxy.ts
CHANGED
|
@@ -5,20 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
IDeltaManager,
|
|
8
|
-
IDeltaManagerEvents,
|
|
9
8
|
IDeltaQueue,
|
|
10
|
-
IDeltaSender,
|
|
11
9
|
IDeltaQueueEvents,
|
|
12
|
-
ReadOnlyInfo,
|
|
13
10
|
} from "@fluidframework/container-definitions";
|
|
14
11
|
import { EventForwarder } from "@fluidframework/common-utils";
|
|
15
12
|
import {
|
|
16
|
-
IClientConfiguration,
|
|
17
|
-
IClientDetails,
|
|
18
13
|
IDocumentMessage,
|
|
19
14
|
ISequencedDocumentMessage,
|
|
20
15
|
ISignalMessage,
|
|
21
16
|
} from "@fluidframework/protocol-definitions";
|
|
17
|
+
import { DeltaManagerProxyBase } from "@fluidframework/container-utils";
|
|
22
18
|
|
|
23
19
|
/**
|
|
24
20
|
* Proxy to the real IDeltaQueue - used to restrict access
|
|
@@ -78,87 +74,36 @@ export class DeltaQueueProxy<T>
|
|
|
78
74
|
* Proxy to the real IDeltaManager - used to restrict access
|
|
79
75
|
*/
|
|
80
76
|
export class DeltaManagerProxy
|
|
81
|
-
extends
|
|
77
|
+
extends DeltaManagerProxyBase
|
|
82
78
|
implements IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>
|
|
83
79
|
{
|
|
84
|
-
public
|
|
85
|
-
|
|
86
|
-
public readonly inboundSignal: IDeltaQueue<ISignalMessage>;
|
|
87
|
-
|
|
88
|
-
public get IDeltaSender(): IDeltaSender {
|
|
89
|
-
return this;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public get minimumSequenceNumber(): number {
|
|
93
|
-
return this.deltaManager.minimumSequenceNumber;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
public get lastSequenceNumber(): number {
|
|
97
|
-
return this.deltaManager.lastSequenceNumber;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
public get lastMessage() {
|
|
101
|
-
return this.deltaManager.lastMessage;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
public get lastKnownSeqNumber() {
|
|
105
|
-
return this.deltaManager.lastKnownSeqNumber;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
public get initialSequenceNumber(): number {
|
|
109
|
-
return this.deltaManager.initialSequenceNumber;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public get hasCheckpointSequenceNumber() {
|
|
113
|
-
return this.deltaManager.hasCheckpointSequenceNumber;
|
|
80
|
+
public get inbound(): IDeltaQueue<ISequencedDocumentMessage> {
|
|
81
|
+
return this._inbound;
|
|
114
82
|
}
|
|
83
|
+
private readonly _inbound: IDeltaQueue<ISequencedDocumentMessage>;
|
|
115
84
|
|
|
116
|
-
public get
|
|
117
|
-
return this.
|
|
85
|
+
public get outbound(): IDeltaQueue<IDocumentMessage[]> {
|
|
86
|
+
return this._outbound;
|
|
118
87
|
}
|
|
88
|
+
private readonly _outbound: IDeltaQueue<IDocumentMessage[]>;
|
|
119
89
|
|
|
120
|
-
public get
|
|
121
|
-
return this.
|
|
90
|
+
public get inboundSignal(): IDeltaQueue<ISignalMessage> {
|
|
91
|
+
return this._inboundSignal;
|
|
122
92
|
}
|
|
93
|
+
private readonly _inboundSignal: IDeltaQueue<ISignalMessage>;
|
|
123
94
|
|
|
124
|
-
|
|
125
|
-
return this.deltaManager.maxMessageSize;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
public get serviceConfiguration(): IClientConfiguration | undefined {
|
|
129
|
-
return this.deltaManager.serviceConfiguration;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
public get active(): boolean {
|
|
133
|
-
return this.deltaManager.active;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public get readOnlyInfo(): ReadOnlyInfo {
|
|
137
|
-
return this.deltaManager.readOnlyInfo;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
constructor(
|
|
141
|
-
private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
|
|
142
|
-
) {
|
|
95
|
+
constructor(deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>) {
|
|
143
96
|
super(deltaManager);
|
|
144
97
|
|
|
145
|
-
this.
|
|
146
|
-
this.
|
|
147
|
-
this.
|
|
98
|
+
this._inbound = new DeltaQueueProxy(deltaManager.inbound);
|
|
99
|
+
this._outbound = new DeltaQueueProxy(deltaManager.outbound);
|
|
100
|
+
this._inboundSignal = new DeltaQueueProxy(deltaManager.inboundSignal);
|
|
148
101
|
}
|
|
149
102
|
|
|
150
103
|
public dispose(): void {
|
|
151
|
-
this.
|
|
152
|
-
this.
|
|
153
|
-
this.
|
|
104
|
+
this._inbound.dispose();
|
|
105
|
+
this._outbound.dispose();
|
|
106
|
+
this._inboundSignal.dispose();
|
|
154
107
|
super.dispose();
|
|
155
108
|
}
|
|
156
|
-
|
|
157
|
-
public submitSignal(content: any): void {
|
|
158
|
-
return this.deltaManager.submitSignal(content);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
public flush(): void {
|
|
162
|
-
return this.deltaManager.flush();
|
|
163
|
-
}
|
|
164
109
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
export { ConnectionState } from "./connectionState";
|
|
7
7
|
export {
|
|
8
|
-
Container,
|
|
9
|
-
IContainerLoadOptions,
|
|
10
8
|
IContainerConfig,
|
|
9
|
+
IContainerLoadOptions,
|
|
11
10
|
IPendingContainerState,
|
|
12
11
|
waitContainerToCatchUp,
|
|
13
12
|
} from "./container";
|
|
13
|
+
export { ISerializableBlobContents } from "./containerStorageAdapter";
|
|
14
14
|
export {
|
|
15
15
|
ICodeDetailsLoader,
|
|
16
16
|
IDetachedBlobStorage,
|
|
@@ -19,6 +19,6 @@ export {
|
|
|
19
19
|
ILoaderProps,
|
|
20
20
|
ILoaderServices,
|
|
21
21
|
Loader,
|
|
22
|
-
|
|
22
|
+
requestResolvedObjectFromContainer,
|
|
23
23
|
} from "./loader";
|
|
24
24
|
export { IProtocolHandler, ProtocolHandlerBuilder } from "./protocol";
|