@fluidframework/container-loader 2.0.0-dev.4.4.0.162253 → 2.0.0-dev.5.2.0.169897
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 +28 -0
- package/README.md +27 -3
- package/dist/connectionManager.d.ts +3 -2
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +32 -13
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +15 -3
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +24 -1
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +74 -44
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +81 -111
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +3 -7
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +3 -3
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +6 -15
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +8 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +21 -9
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +42 -31
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +2 -3
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +2 -3
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +9 -7
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +47 -61
- 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/retriableDocumentStorageService.d.ts +3 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +8 -1
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts +3 -2
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +33 -14
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +15 -3
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +25 -2
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +74 -44
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +82 -112
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -2
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +3 -7
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +3 -3
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +6 -15
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +8 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +21 -9
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +44 -33
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +2 -3
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +2 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +9 -7
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +47 -61
- 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/retriableDocumentStorageService.d.ts +3 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts +2 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +7 -1
- package/lib/utils.js.map +1 -1
- package/package.json +16 -18
- package/src/connectionManager.ts +40 -22
- package/src/connectionStateHandler.ts +52 -8
- package/src/container.ts +191 -159
- package/src/containerContext.ts +3 -9
- package/src/containerStorageAdapter.ts +8 -20
- package/src/contracts.ts +10 -0
- package/src/deltaManager.ts +59 -37
- package/src/deltaQueue.ts +2 -3
- package/src/index.ts +1 -8
- package/src/loader.ts +85 -83
- package/src/packageVersion.ts +1 -1
- package/src/retriableDocumentStorageService.ts +3 -2
- package/src/utils.ts +15 -1
package/src/container.ts
CHANGED
|
@@ -7,11 +7,7 @@
|
|
|
7
7
|
import merge from "lodash/merge";
|
|
8
8
|
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
|
-
import {
|
|
11
|
-
ITelemetryLogger,
|
|
12
|
-
ITelemetryProperties,
|
|
13
|
-
TelemetryEventCategory,
|
|
14
|
-
} from "@fluidframework/common-definitions";
|
|
10
|
+
import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/common-definitions";
|
|
15
11
|
import { assert, performance, unreachableCase } from "@fluidframework/common-utils";
|
|
16
12
|
import { IRequest, IResponse, IFluidRouter, FluidObject } from "@fluidframework/core-interfaces";
|
|
17
13
|
import {
|
|
@@ -29,23 +25,24 @@ import {
|
|
|
29
25
|
IFluidCodeDetails,
|
|
30
26
|
isFluidCodeDetails,
|
|
31
27
|
IBatchMessage,
|
|
28
|
+
ICodeDetailsLoader,
|
|
29
|
+
IHostLoader,
|
|
32
30
|
} from "@fluidframework/container-definitions";
|
|
33
31
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
34
32
|
import {
|
|
35
33
|
IAnyDriverError,
|
|
36
34
|
IDocumentService,
|
|
35
|
+
IDocumentServiceFactory,
|
|
37
36
|
IDocumentStorageService,
|
|
38
|
-
IFluidResolvedUrl,
|
|
39
37
|
IResolvedUrl,
|
|
38
|
+
IUrlResolver,
|
|
40
39
|
} from "@fluidframework/driver-definitions";
|
|
41
40
|
import {
|
|
42
41
|
readAndParse,
|
|
43
42
|
OnlineStatus,
|
|
44
43
|
isOnline,
|
|
45
|
-
ensureFluidResolvedUrl,
|
|
46
44
|
combineAppAndProtocolSummary,
|
|
47
45
|
runWithRetry,
|
|
48
|
-
isFluidResolvedUrl,
|
|
49
46
|
isCombinedAppAndProtocolSummary,
|
|
50
47
|
} from "@fluidframework/driver-utils";
|
|
51
48
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
@@ -80,13 +77,14 @@ import {
|
|
|
80
77
|
MonitoringContext,
|
|
81
78
|
loggerToMonitoringContext,
|
|
82
79
|
wrapError,
|
|
80
|
+
ITelemetryLoggerExt,
|
|
83
81
|
} from "@fluidframework/telemetry-utils";
|
|
84
82
|
import { Audience } from "./audience";
|
|
85
83
|
import { ContainerContext } from "./containerContext";
|
|
86
84
|
import { ReconnectMode, IConnectionManagerFactoryArgs, getPackageName } from "./contracts";
|
|
87
85
|
import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
88
86
|
import { DeltaManagerProxy } from "./deltaManagerProxy";
|
|
89
|
-
import {
|
|
87
|
+
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
|
|
90
88
|
import { pkgVersion } from "./packageVersion";
|
|
91
89
|
import {
|
|
92
90
|
ContainerStorageAdapter,
|
|
@@ -117,44 +115,82 @@ const dirtyContainerEvent = "dirty";
|
|
|
117
115
|
const savedContainerEvent = "saved";
|
|
118
116
|
|
|
119
117
|
/**
|
|
120
|
-
* @deprecated this is an internal interface and will not longer be exported in future versions
|
|
121
118
|
* @internal
|
|
122
119
|
*/
|
|
123
|
-
export interface
|
|
124
|
-
/**
|
|
125
|
-
* Disables the Container from reconnecting if false, allows reconnect otherwise.
|
|
126
|
-
*/
|
|
127
|
-
canReconnect?: boolean;
|
|
120
|
+
export interface IContainerLoadProps {
|
|
128
121
|
/**
|
|
129
|
-
*
|
|
122
|
+
* The resolved url of the container being loaded
|
|
130
123
|
*/
|
|
131
|
-
|
|
132
|
-
resolvedUrl: IFluidResolvedUrl;
|
|
124
|
+
readonly resolvedUrl: IResolvedUrl;
|
|
133
125
|
/**
|
|
134
126
|
* Control which snapshot version to load from. See IParsedUrl for detailed information.
|
|
135
127
|
*/
|
|
136
|
-
version: string | undefined;
|
|
128
|
+
readonly version: string | undefined;
|
|
137
129
|
/**
|
|
138
130
|
* Loads the Container in paused state if true, unpaused otherwise.
|
|
139
131
|
*/
|
|
140
|
-
loadMode?: IContainerLoadMode;
|
|
132
|
+
readonly loadMode?: IContainerLoadMode;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The pending state serialized from a pervious container instance
|
|
136
|
+
*/
|
|
137
|
+
readonly pendingLocalState?: IPendingContainerState;
|
|
141
138
|
}
|
|
142
139
|
|
|
143
140
|
/**
|
|
144
|
-
* @deprecated this is an internal interface and will not longer be exported in future versions
|
|
145
141
|
* @internal
|
|
146
142
|
*/
|
|
147
|
-
export interface
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
export interface IContainerCreateProps {
|
|
144
|
+
/**
|
|
145
|
+
* Disables the Container from reconnecting if false, allows reconnect otherwise.
|
|
146
|
+
*/
|
|
147
|
+
readonly canReconnect?: boolean;
|
|
150
148
|
/**
|
|
151
149
|
* Client details provided in the override will be merged over the default client.
|
|
152
150
|
*/
|
|
153
|
-
clientDetailsOverride?: IClientDetails;
|
|
151
|
+
readonly clientDetailsOverride?: IClientDetails;
|
|
152
|
+
|
|
154
153
|
/**
|
|
155
|
-
*
|
|
154
|
+
* The url resolver used by the loader for resolving external urls
|
|
155
|
+
* into Fluid urls such that the container specified by the
|
|
156
|
+
* external url can be loaded.
|
|
156
157
|
*/
|
|
157
|
-
|
|
158
|
+
readonly urlResolver: IUrlResolver;
|
|
159
|
+
/**
|
|
160
|
+
* The document service factory take the Fluid url provided
|
|
161
|
+
* by the resolved url and constructs all the necessary services
|
|
162
|
+
* for communication with the container's server.
|
|
163
|
+
*/
|
|
164
|
+
readonly documentServiceFactory: IDocumentServiceFactory;
|
|
165
|
+
/**
|
|
166
|
+
* The code loader handles loading the necessary code
|
|
167
|
+
* for running a container once it is loaded.
|
|
168
|
+
*/
|
|
169
|
+
readonly codeLoader: ICodeDetailsLoader;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A property bag of options used by various layers
|
|
173
|
+
* to control features
|
|
174
|
+
*/
|
|
175
|
+
readonly options: ILoaderOptions;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Scope is provided to all container and is a set of shared
|
|
179
|
+
* services for container's to integrate with their host environment.
|
|
180
|
+
*/
|
|
181
|
+
readonly scope: FluidObject;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The logger downstream consumers should construct their loggers from
|
|
185
|
+
*/
|
|
186
|
+
readonly subLogger: ITelemetryLoggerExt;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Blobs storage for detached containers.
|
|
190
|
+
*/
|
|
191
|
+
readonly detachedBlobStorage?: IDetachedBlobStorage;
|
|
192
|
+
|
|
193
|
+
readonly protocolHandlerBuilder?: ProtocolHandlerBuilder;
|
|
158
194
|
}
|
|
159
195
|
|
|
160
196
|
/**
|
|
@@ -260,7 +296,7 @@ const getCodeProposal =
|
|
|
260
296
|
* @param action - functor to call and measure
|
|
261
297
|
*/
|
|
262
298
|
export async function ReportIfTooLong(
|
|
263
|
-
logger:
|
|
299
|
+
logger: ITelemetryLoggerExt,
|
|
264
300
|
eventName: string,
|
|
265
301
|
action: () => Promise<ITelemetryProperties>,
|
|
266
302
|
) {
|
|
@@ -274,7 +310,6 @@ export async function ReportIfTooLong(
|
|
|
274
310
|
/**
|
|
275
311
|
* State saved by a container at close time, to be used to load a new instance
|
|
276
312
|
* of the container to the same state
|
|
277
|
-
* @deprecated this is an internal interface and will not longer be exported in future versions
|
|
278
313
|
* @internal
|
|
279
314
|
*/
|
|
280
315
|
export interface IPendingContainerState {
|
|
@@ -301,9 +336,6 @@ export interface IPendingContainerState {
|
|
|
301
336
|
|
|
302
337
|
const summarizerClientType = "summarizer";
|
|
303
338
|
|
|
304
|
-
/**
|
|
305
|
-
* @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
|
|
306
|
-
*/
|
|
307
339
|
export class Container
|
|
308
340
|
extends EventEmitterWithErrorHandling<IContainerEvents>
|
|
309
341
|
implements IContainer, IContainerExperimental
|
|
@@ -315,20 +347,15 @@ export class Container
|
|
|
315
347
|
* @internal
|
|
316
348
|
*/
|
|
317
349
|
public static async load(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
pendingLocalState?: IPendingContainerState,
|
|
321
|
-
protocolHandlerBuilder?: ProtocolHandlerBuilder,
|
|
350
|
+
loadProps: IContainerLoadProps,
|
|
351
|
+
createProps: IContainerCreateProps,
|
|
322
352
|
): Promise<Container> {
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
serializedContainerState: pendingLocalState,
|
|
330
|
-
},
|
|
331
|
-
protocolHandlerBuilder,
|
|
353
|
+
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
354
|
+
|
|
355
|
+
const container = new Container(createProps, loadProps);
|
|
356
|
+
|
|
357
|
+
const disableRecordHeapSize = container.mc.config.getBoolean(
|
|
358
|
+
"Fluid.Loader.DisableRecordHeapSize",
|
|
332
359
|
);
|
|
333
360
|
|
|
334
361
|
return PerformanceEvent.timedExecAsync(
|
|
@@ -336,14 +363,12 @@ export class Container
|
|
|
336
363
|
{ eventName: "Load" },
|
|
337
364
|
async (event) =>
|
|
338
365
|
new Promise<Container>((resolve, reject) => {
|
|
339
|
-
const version = loadOptions.version;
|
|
340
|
-
|
|
341
366
|
const defaultMode: IContainerLoadMode = { opsBeforeReturn: "cached" };
|
|
342
367
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
343
368
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
344
369
|
const mode: IContainerLoadMode = pendingLocalState
|
|
345
|
-
? { ...(
|
|
346
|
-
:
|
|
370
|
+
? { ...(loadMode ?? defaultMode), opsBeforeReturn: undefined }
|
|
371
|
+
: loadMode ?? defaultMode;
|
|
347
372
|
|
|
348
373
|
const onClosed = (err?: ICriticalContainerError) => {
|
|
349
374
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
@@ -354,13 +379,13 @@ export class Container
|
|
|
354
379
|
container.on("closed", onClosed);
|
|
355
380
|
|
|
356
381
|
container
|
|
357
|
-
.load(version, mode, pendingLocalState)
|
|
382
|
+
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
358
383
|
.finally(() => {
|
|
359
384
|
container.removeListener("closed", onClosed);
|
|
360
385
|
})
|
|
361
386
|
.then(
|
|
362
387
|
(props) => {
|
|
363
|
-
event.end({ ...props, ...
|
|
388
|
+
event.end({ ...props, ...loadMode });
|
|
364
389
|
resolve(container);
|
|
365
390
|
},
|
|
366
391
|
(error) => {
|
|
@@ -374,6 +399,7 @@ export class Container
|
|
|
374
399
|
);
|
|
375
400
|
}),
|
|
376
401
|
{ start: true, end: true, cancel: "generic" },
|
|
402
|
+
disableRecordHeapSize !== true /* recordHeapSize */,
|
|
377
403
|
);
|
|
378
404
|
}
|
|
379
405
|
|
|
@@ -381,11 +407,10 @@ export class Container
|
|
|
381
407
|
* Create a new container in a detached state.
|
|
382
408
|
*/
|
|
383
409
|
public static async createDetached(
|
|
384
|
-
|
|
410
|
+
createProps: IContainerCreateProps,
|
|
385
411
|
codeDetails: IFluidCodeDetails,
|
|
386
|
-
protocolHandlerBuilder?: ProtocolHandlerBuilder,
|
|
387
412
|
): Promise<Container> {
|
|
388
|
-
const container = new Container(
|
|
413
|
+
const container = new Container(createProps);
|
|
389
414
|
|
|
390
415
|
return PerformanceEvent.timedExecAsync(
|
|
391
416
|
container.mc.logger,
|
|
@@ -403,11 +428,10 @@ export class Container
|
|
|
403
428
|
* snapshot from a previous detached container.
|
|
404
429
|
*/
|
|
405
430
|
public static async rehydrateDetachedFromSnapshot(
|
|
406
|
-
|
|
431
|
+
createProps: IContainerCreateProps,
|
|
407
432
|
snapshot: string,
|
|
408
|
-
protocolHandlerBuilder?: ProtocolHandlerBuilder,
|
|
409
433
|
): Promise<Container> {
|
|
410
|
-
const container = new Container(
|
|
434
|
+
const container = new Container(createProps);
|
|
411
435
|
|
|
412
436
|
return PerformanceEvent.timedExecAsync(
|
|
413
437
|
container.mc.logger,
|
|
@@ -421,14 +445,30 @@ export class Container
|
|
|
421
445
|
);
|
|
422
446
|
}
|
|
423
447
|
|
|
424
|
-
public subLogger: TelemetryLogger;
|
|
425
|
-
|
|
426
448
|
// Tells if container can reconnect on losing fist connection
|
|
427
449
|
// If false, container gets closed on loss of connection.
|
|
428
|
-
private readonly _canReconnect: boolean
|
|
450
|
+
private readonly _canReconnect: boolean;
|
|
451
|
+
private readonly clientDetailsOverride: IClientDetails | undefined;
|
|
452
|
+
private readonly urlResolver: IUrlResolver;
|
|
453
|
+
private readonly serviceFactory: IDocumentServiceFactory;
|
|
454
|
+
private readonly codeLoader: ICodeDetailsLoader;
|
|
455
|
+
public readonly options: ILoaderOptions;
|
|
456
|
+
private readonly scope: FluidObject;
|
|
457
|
+
public subLogger: TelemetryLogger;
|
|
458
|
+
private readonly detachedBlobStorage: IDetachedBlobStorage | undefined;
|
|
459
|
+
private readonly protocolHandlerBuilder: ProtocolHandlerBuilder;
|
|
429
460
|
|
|
430
461
|
private readonly mc: MonitoringContext;
|
|
431
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Used by the RelativeLoader to spawn a new Container for the same document. Used to create the summarizing client.
|
|
465
|
+
* @internal
|
|
466
|
+
*/
|
|
467
|
+
public readonly clone: (
|
|
468
|
+
loadProps: IContainerLoadProps,
|
|
469
|
+
createParamOverrides: Partial<IContainerCreateProps>,
|
|
470
|
+
) => Promise<Container>;
|
|
471
|
+
|
|
432
472
|
/**
|
|
433
473
|
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
434
474
|
*
|
|
@@ -478,7 +518,6 @@ export class Container
|
|
|
478
518
|
return this.storageAdapter;
|
|
479
519
|
}
|
|
480
520
|
|
|
481
|
-
private readonly clientDetailsOverride: IClientDetails | undefined;
|
|
482
521
|
private readonly _deltaManager: DeltaManager<ConnectionManager>;
|
|
483
522
|
private service: IDocumentService | undefined;
|
|
484
523
|
|
|
@@ -503,7 +542,6 @@ export class Container
|
|
|
503
542
|
private readonly connectionTransitionTimes: number[] = [];
|
|
504
543
|
private messageCountAfterDisconnection: number = 0;
|
|
505
544
|
private _loadedFromVersion: IVersion | undefined;
|
|
506
|
-
private _resolvedUrl: IFluidResolvedUrl | undefined;
|
|
507
545
|
private attachStarted = false;
|
|
508
546
|
private _dirtyContainer = false;
|
|
509
547
|
private readonly savedOps: ISequencedDocumentMessage[] = [];
|
|
@@ -527,7 +565,18 @@ export class Container
|
|
|
527
565
|
}
|
|
528
566
|
|
|
529
567
|
public get resolvedUrl(): IResolvedUrl | undefined {
|
|
530
|
-
|
|
568
|
+
/**
|
|
569
|
+
* All attached containers will have a document service,
|
|
570
|
+
* this is required, as attached containers are attached to
|
|
571
|
+
* a service. Detached containers will neither have a document
|
|
572
|
+
* service or a resolved url as they only exist locally.
|
|
573
|
+
* in order to create a document service a resolved url must
|
|
574
|
+
* first be obtained, this is how the container is identified.
|
|
575
|
+
* Because of this, the document service's resolved url
|
|
576
|
+
* is always the same as the containers, as we had to
|
|
577
|
+
* obtain the resolved url, and then create the service from it.
|
|
578
|
+
*/
|
|
579
|
+
return this.service?.resolvedUrl;
|
|
531
580
|
}
|
|
532
581
|
|
|
533
582
|
public get loadedFromVersion(): IVersion | undefined {
|
|
@@ -632,20 +681,6 @@ export class Container
|
|
|
632
681
|
return this._dirtyContainer;
|
|
633
682
|
}
|
|
634
683
|
|
|
635
|
-
private get serviceFactory() {
|
|
636
|
-
return this.loader.services.documentServiceFactory;
|
|
637
|
-
}
|
|
638
|
-
private get urlResolver() {
|
|
639
|
-
return this.loader.services.urlResolver;
|
|
640
|
-
}
|
|
641
|
-
public readonly options: ILoaderOptions;
|
|
642
|
-
private get scope() {
|
|
643
|
-
return this.loader.services.scope;
|
|
644
|
-
}
|
|
645
|
-
private get codeLoader() {
|
|
646
|
-
return this.loader.services.codeLoader;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
684
|
/**
|
|
650
685
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
651
686
|
*/
|
|
@@ -685,9 +720,8 @@ export class Container
|
|
|
685
720
|
* @internal
|
|
686
721
|
*/
|
|
687
722
|
constructor(
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
private readonly protocolHandlerBuilder?: ProtocolHandlerBuilder,
|
|
723
|
+
createProps: IContainerCreateProps,
|
|
724
|
+
loadProps?: Pick<IContainerLoadProps, "pendingLocalState">,
|
|
691
725
|
) {
|
|
692
726
|
super((name, error) => {
|
|
693
727
|
this.mc.logger.sendErrorEvent(
|
|
@@ -699,11 +733,46 @@ export class Container
|
|
|
699
733
|
);
|
|
700
734
|
});
|
|
701
735
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
736
|
+
const {
|
|
737
|
+
canReconnect,
|
|
738
|
+
clientDetailsOverride,
|
|
739
|
+
urlResolver,
|
|
740
|
+
documentServiceFactory,
|
|
741
|
+
codeLoader,
|
|
742
|
+
options,
|
|
743
|
+
scope,
|
|
744
|
+
subLogger,
|
|
745
|
+
detachedBlobStorage,
|
|
746
|
+
protocolHandlerBuilder,
|
|
747
|
+
} = createProps;
|
|
748
|
+
|
|
749
|
+
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
750
|
+
const pendingLocalState = loadProps?.pendingLocalState;
|
|
751
|
+
|
|
752
|
+
this._canReconnect = canReconnect ?? true;
|
|
753
|
+
this.clientDetailsOverride = clientDetailsOverride;
|
|
754
|
+
this.urlResolver = urlResolver;
|
|
755
|
+
this.serviceFactory = documentServiceFactory;
|
|
756
|
+
this.codeLoader = codeLoader;
|
|
757
|
+
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
758
|
+
// all clients that were loaded from the same loader (including summarizer clients).
|
|
759
|
+
// Tracking alternative ways to handle this in AB#4129.
|
|
760
|
+
this.options = { ...options };
|
|
761
|
+
this.scope = scope;
|
|
762
|
+
this.detachedBlobStorage = detachedBlobStorage;
|
|
763
|
+
this.protocolHandlerBuilder =
|
|
764
|
+
protocolHandlerBuilder ?? ((...args) => new ProtocolHandler(...args, new Audience()));
|
|
765
|
+
|
|
766
|
+
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
767
|
+
this.clone = async (
|
|
768
|
+
_loadProps: IContainerLoadProps,
|
|
769
|
+
createParamOverrides: Partial<IContainerCreateProps>,
|
|
770
|
+
) => {
|
|
771
|
+
return Container.load(_loadProps, {
|
|
772
|
+
...createProps,
|
|
773
|
+
...createParamOverrides,
|
|
774
|
+
});
|
|
775
|
+
};
|
|
707
776
|
|
|
708
777
|
// Create logger for data stores to use
|
|
709
778
|
const type = this.client.details.type;
|
|
@@ -713,15 +782,15 @@ export class Container
|
|
|
713
782
|
}`;
|
|
714
783
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
715
784
|
// We assign the id later so property getter is used.
|
|
716
|
-
this.subLogger = ChildLogger.create(
|
|
785
|
+
this.subLogger = ChildLogger.create(subLogger, undefined, {
|
|
717
786
|
all: {
|
|
718
787
|
clientType, // Differentiating summarizer container from main container
|
|
719
788
|
containerId: uuid(),
|
|
720
|
-
docId: () => this.
|
|
789
|
+
docId: () => this.resolvedUrl?.id,
|
|
721
790
|
containerAttachState: () => this._attachState,
|
|
722
791
|
containerLifecycleState: () => this._lifecycleState,
|
|
723
792
|
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
724
|
-
serializedContainer:
|
|
793
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
725
794
|
},
|
|
726
795
|
// we need to be judicious with our logging here to avoid generating too much data
|
|
727
796
|
// all data logged here should be broadly applicable, and not specific to a
|
|
@@ -747,13 +816,6 @@ export class Container
|
|
|
747
816
|
// Prefix all events in this file with container-loader
|
|
748
817
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
|
|
749
818
|
|
|
750
|
-
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
751
|
-
// all clients that were loaded from the same loader (including summarizer clients).
|
|
752
|
-
// Tracking alternative ways to handle this in AB#4129.
|
|
753
|
-
this.options = {
|
|
754
|
-
...this.loader.services.options,
|
|
755
|
-
};
|
|
756
|
-
|
|
757
819
|
this._deltaManager = this.createDeltaManager();
|
|
758
820
|
|
|
759
821
|
this.connectionStateHandler = createConnectionStateHandler(
|
|
@@ -774,7 +836,7 @@ export class Container
|
|
|
774
836
|
}
|
|
775
837
|
},
|
|
776
838
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
777
|
-
maxClientLeaveWaitTime:
|
|
839
|
+
maxClientLeaveWaitTime: options.maxClientLeaveWaitTime,
|
|
778
840
|
logConnectionIssue: (
|
|
779
841
|
eventName: string,
|
|
780
842
|
category: TelemetryEventCategory,
|
|
@@ -811,7 +873,7 @@ export class Container
|
|
|
811
873
|
},
|
|
812
874
|
},
|
|
813
875
|
this.deltaManager,
|
|
814
|
-
|
|
876
|
+
pendingLocalState?.clientId,
|
|
815
877
|
);
|
|
816
878
|
|
|
817
879
|
this.on(savedContainerEvent, () => {
|
|
@@ -830,12 +892,12 @@ export class Container
|
|
|
830
892
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
831
893
|
const forceEnableSummarizeProtocolTree =
|
|
832
894
|
this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
833
|
-
|
|
895
|
+
options.summarizeProtocolTree;
|
|
834
896
|
|
|
835
897
|
this.storageAdapter = new ContainerStorageAdapter(
|
|
836
|
-
|
|
898
|
+
detachedBlobStorage,
|
|
837
899
|
this.mc.logger,
|
|
838
|
-
|
|
900
|
+
pendingLocalState?.snapshotBlobs,
|
|
839
901
|
addProtocolSummaryIfMissing,
|
|
840
902
|
forceEnableSummarizeProtocolTree,
|
|
841
903
|
);
|
|
@@ -869,8 +931,8 @@ export class Container
|
|
|
869
931
|
return this.protocolHandler.quorum;
|
|
870
932
|
}
|
|
871
933
|
|
|
872
|
-
public dispose
|
|
873
|
-
this._deltaManager.
|
|
934
|
+
public dispose(error?: ICriticalContainerError) {
|
|
935
|
+
this._deltaManager.dispose(error);
|
|
874
936
|
this.verifyClosed();
|
|
875
937
|
}
|
|
876
938
|
|
|
@@ -921,15 +983,6 @@ export class Container
|
|
|
921
983
|
this._protocolHandler?.close();
|
|
922
984
|
|
|
923
985
|
this.connectionStateHandler.dispose();
|
|
924
|
-
|
|
925
|
-
this._context?.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
926
|
-
|
|
927
|
-
this.storageAdapter.dispose();
|
|
928
|
-
|
|
929
|
-
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
930
|
-
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
931
|
-
// Driver need to ensure all caches are cleared on critical errors
|
|
932
|
-
this.service?.dispose(error);
|
|
933
986
|
} catch (exception) {
|
|
934
987
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
935
988
|
}
|
|
@@ -1049,10 +1102,7 @@ export class Container
|
|
|
1049
1102
|
const protocolSummary = this.captureProtocolSummary();
|
|
1050
1103
|
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1051
1104
|
|
|
1052
|
-
if (
|
|
1053
|
-
this.loader.services.detachedBlobStorage &&
|
|
1054
|
-
this.loader.services.detachedBlobStorage.size > 0
|
|
1055
|
-
) {
|
|
1105
|
+
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
1056
1106
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
1057
1107
|
type: SummaryType.Blob,
|
|
1058
1108
|
content: "true",
|
|
@@ -1082,8 +1132,7 @@ export class Container
|
|
|
1082
1132
|
|
|
1083
1133
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
1084
1134
|
const hasAttachmentBlobs =
|
|
1085
|
-
this.
|
|
1086
|
-
this.loader.services.detachedBlobStorage.size > 0;
|
|
1135
|
+
this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
1087
1136
|
|
|
1088
1137
|
try {
|
|
1089
1138
|
assert(
|
|
@@ -1114,11 +1163,11 @@ export class Container
|
|
|
1114
1163
|
}
|
|
1115
1164
|
|
|
1116
1165
|
// Actually go and create the resolved document
|
|
1117
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
1118
|
-
ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
1119
1166
|
if (this.service === undefined) {
|
|
1167
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
1120
1168
|
assert(
|
|
1121
|
-
this.client.details.type !== summarizerClientType
|
|
1169
|
+
this.client.details.type !== summarizerClientType &&
|
|
1170
|
+
createNewResolvedUrl !== undefined,
|
|
1122
1171
|
0x2c4 /* "client should not be summarizer before container is created" */,
|
|
1123
1172
|
);
|
|
1124
1173
|
this.service = await runWithRetry(
|
|
@@ -1136,15 +1185,12 @@ export class Container
|
|
|
1136
1185
|
}, // progress
|
|
1137
1186
|
);
|
|
1138
1187
|
}
|
|
1139
|
-
const resolvedUrl = this.service.resolvedUrl;
|
|
1140
|
-
ensureFluidResolvedUrl(resolvedUrl);
|
|
1141
|
-
this._resolvedUrl = resolvedUrl;
|
|
1142
1188
|
await this.storageAdapter.connectToService(this.service);
|
|
1143
1189
|
|
|
1144
1190
|
if (hasAttachmentBlobs) {
|
|
1145
1191
|
// upload blobs to storage
|
|
1146
1192
|
assert(
|
|
1147
|
-
!!this.
|
|
1193
|
+
!!this.detachedBlobStorage,
|
|
1148
1194
|
0x24e /* "assertion for type narrowing" */,
|
|
1149
1195
|
);
|
|
1150
1196
|
|
|
@@ -1152,13 +1198,12 @@ export class Container
|
|
|
1152
1198
|
// support blob handles that only know about the local IDs
|
|
1153
1199
|
const redirectTable = new Map<string, string>();
|
|
1154
1200
|
// if new blobs are added while uploading, upload them too
|
|
1155
|
-
while (redirectTable.size < this.
|
|
1156
|
-
const newIds = this.
|
|
1201
|
+
while (redirectTable.size < this.detachedBlobStorage.size) {
|
|
1202
|
+
const newIds = this.detachedBlobStorage
|
|
1157
1203
|
.getBlobIds()
|
|
1158
1204
|
.filter((id) => !redirectTable.has(id));
|
|
1159
1205
|
for (const id of newIds) {
|
|
1160
|
-
const blob =
|
|
1161
|
-
await this.loader.services.detachedBlobStorage.readBlob(id);
|
|
1206
|
+
const blob = await this.detachedBlobStorage.readBlob(id);
|
|
1162
1207
|
const response = await this.storageAdapter.createBlob(blob);
|
|
1163
1208
|
redirectTable.set(id, response.id);
|
|
1164
1209
|
}
|
|
@@ -1197,12 +1242,8 @@ export class Container
|
|
|
1197
1242
|
} catch (error) {
|
|
1198
1243
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
1199
1244
|
const newError = normalizeError(error);
|
|
1200
|
-
|
|
1201
|
-
if (isFluidResolvedUrl(resolvedUrl)) {
|
|
1202
|
-
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
1203
|
-
}
|
|
1245
|
+
newError.addTelemetryProperties({ resolvedUrl: this.resolvedUrl?.url });
|
|
1204
1246
|
this.close(newError);
|
|
1205
|
-
this.dispose?.(newError);
|
|
1206
1247
|
throw newError;
|
|
1207
1248
|
}
|
|
1208
1249
|
},
|
|
@@ -1349,7 +1390,6 @@ export class Container
|
|
|
1349
1390
|
// pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
|
|
1350
1391
|
const error = new GenericError("Existing context does not satisfy incoming proposal");
|
|
1351
1392
|
this.close(error);
|
|
1352
|
-
this.dispose?.(error);
|
|
1353
1393
|
}
|
|
1354
1394
|
|
|
1355
1395
|
private async getVersion(version: string | null): Promise<IVersion | undefined> {
|
|
@@ -1357,15 +1397,7 @@ export class Container
|
|
|
1357
1397
|
return versions[0];
|
|
1358
1398
|
}
|
|
1359
1399
|
|
|
1360
|
-
private recordConnectStartTime() {
|
|
1361
|
-
if (this.connectionTransitionTimes[ConnectionState.Disconnected] === undefined) {
|
|
1362
|
-
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
1400
|
private connectToDeltaStream(args: IConnectionArgs) {
|
|
1367
|
-
this.recordConnectStartTime();
|
|
1368
|
-
|
|
1369
1401
|
// All agents need "write" access, including summarizer.
|
|
1370
1402
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
1371
1403
|
args.mode = "write";
|
|
@@ -1382,13 +1414,11 @@ export class Container
|
|
|
1382
1414
|
private async load(
|
|
1383
1415
|
specifiedVersion: string | undefined,
|
|
1384
1416
|
loadMode: IContainerLoadMode,
|
|
1417
|
+
resolvedUrl: IResolvedUrl,
|
|
1385
1418
|
pendingLocalState?: IPendingContainerState,
|
|
1386
1419
|
) {
|
|
1387
|
-
if (this._resolvedUrl === undefined) {
|
|
1388
|
-
throw new Error("Attempting to load without a resolved url");
|
|
1389
|
-
}
|
|
1390
1420
|
this.service = await this.serviceFactory.createDocumentService(
|
|
1391
|
-
|
|
1421
|
+
resolvedUrl,
|
|
1392
1422
|
this.subLogger,
|
|
1393
1423
|
this.client.details.type === summarizerClientType,
|
|
1394
1424
|
);
|
|
@@ -1420,7 +1450,6 @@ export class Container
|
|
|
1420
1450
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
1421
1451
|
this.storageAdapter.connectToService(this.service).catch((error) => {
|
|
1422
1452
|
this.close(error);
|
|
1423
|
-
this.dispose?.(error);
|
|
1424
1453
|
});
|
|
1425
1454
|
}
|
|
1426
1455
|
|
|
@@ -1603,8 +1632,7 @@ export class Container
|
|
|
1603
1632
|
private async rehydrateDetachedFromSnapshot(detachedContainerSnapshot: ISummaryTree) {
|
|
1604
1633
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
1605
1634
|
assert(
|
|
1606
|
-
!!this.
|
|
1607
|
-
this.loader.services.detachedBlobStorage.size > 0,
|
|
1635
|
+
!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
|
|
1608
1636
|
0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */,
|
|
1609
1637
|
);
|
|
1610
1638
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
@@ -1701,10 +1729,7 @@ export class Container
|
|
|
1701
1729
|
attributes: IDocumentAttributes,
|
|
1702
1730
|
quorumSnapshot: IQuorumSnapshot,
|
|
1703
1731
|
): void {
|
|
1704
|
-
const protocolHandlerBuilder
|
|
1705
|
-
this.protocolHandlerBuilder ??
|
|
1706
|
-
((...args) => new ProtocolHandler(...args, new Audience()));
|
|
1707
|
-
const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) =>
|
|
1732
|
+
const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) =>
|
|
1708
1733
|
this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })),
|
|
1709
1734
|
);
|
|
1710
1735
|
|
|
@@ -1733,7 +1758,6 @@ export class Container
|
|
|
1733
1758
|
this.processCodeProposal().catch((error) => {
|
|
1734
1759
|
const normalizedError = normalizeError(error);
|
|
1735
1760
|
this.close(normalizedError);
|
|
1736
|
-
this.dispose?.(normalizedError);
|
|
1737
1761
|
throw error;
|
|
1738
1762
|
});
|
|
1739
1763
|
}
|
|
@@ -1844,6 +1868,14 @@ export class Container
|
|
|
1844
1868
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1845
1869
|
});
|
|
1846
1870
|
|
|
1871
|
+
deltaManager.on("establishingConnection", (reason: string) => {
|
|
1872
|
+
this.connectionStateHandler.establishingConnection(reason);
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
deltaManager.on("cancelEstablishingConnection", (reason: string) => {
|
|
1876
|
+
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1877
|
+
});
|
|
1878
|
+
|
|
1847
1879
|
deltaManager.on("disconnect", (reason: string, error?: IAnyDriverError) => {
|
|
1848
1880
|
this.collabWindowTracker?.stopSequenceNumberUpdate();
|
|
1849
1881
|
if (!this.closed) {
|
|
@@ -1920,8 +1952,8 @@ export class Container
|
|
|
1920
1952
|
durationFromDisconnected =
|
|
1921
1953
|
time - this.connectionTransitionTimes[ConnectionState.Disconnected];
|
|
1922
1954
|
durationFromDisconnected = TelemetryLogger.formatTick(durationFromDisconnected);
|
|
1923
|
-
} else {
|
|
1924
|
-
// This info is of most
|
|
1955
|
+
} else if (value === ConnectionState.CatchingUp) {
|
|
1956
|
+
// This info is of most interesting while Catching Up.
|
|
1925
1957
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
1926
1958
|
if (this.deltaManager.hasCheckpointSequenceNumber) {
|
|
1927
1959
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
@@ -2013,7 +2045,6 @@ export class Container
|
|
|
2013
2045
|
{ messageType: type },
|
|
2014
2046
|
);
|
|
2015
2047
|
this.close(newError);
|
|
2016
|
-
this.dispose?.(newError);
|
|
2017
2048
|
return -1;
|
|
2018
2049
|
}
|
|
2019
2050
|
}
|
|
@@ -2183,7 +2214,8 @@ export class Container
|
|
|
2183
2214
|
|
|
2184
2215
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
2185
2216
|
// are set. Global requests will still go directly to the loader
|
|
2186
|
-
const
|
|
2217
|
+
const maybeLoader: FluidObject<IHostLoader> = this.scope;
|
|
2218
|
+
const loader = new RelativeLoader(this, maybeLoader.ILoader);
|
|
2187
2219
|
this._context = await ContainerContext.createOrLoad(
|
|
2188
2220
|
this,
|
|
2189
2221
|
this.scope,
|
|
@@ -2200,7 +2232,7 @@ export class Container
|
|
|
2200
2232
|
(batch: IBatchMessage[], referenceSequenceNumber?: number) =>
|
|
2201
2233
|
this.submitBatch(batch, referenceSequenceNumber),
|
|
2202
2234
|
(message) => this.submitSignal(message),
|
|
2203
|
-
(error?: ICriticalContainerError) => this.dispose
|
|
2235
|
+
(error?: ICriticalContainerError) => this.dispose(error),
|
|
2204
2236
|
(error?: ICriticalContainerError) => this.close(error),
|
|
2205
2237
|
Container.version,
|
|
2206
2238
|
(dirty: boolean) => this.updateDirtyContainerState(dirty),
|