@fluidframework/container-loader 2.0.0-dev.4.4.0.162574 → 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.
Files changed (114) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +27 -3
  3. package/dist/connectionManager.d.ts +3 -2
  4. package/dist/connectionManager.d.ts.map +1 -1
  5. package/dist/connectionManager.js +32 -13
  6. package/dist/connectionManager.js.map +1 -1
  7. package/dist/connectionStateHandler.d.ts +15 -3
  8. package/dist/connectionStateHandler.d.ts.map +1 -1
  9. package/dist/connectionStateHandler.js +24 -1
  10. package/dist/connectionStateHandler.js.map +1 -1
  11. package/dist/container.d.ts +74 -44
  12. package/dist/container.d.ts.map +1 -1
  13. package/dist/container.js +81 -111
  14. package/dist/container.js.map +1 -1
  15. package/dist/containerContext.d.ts +2 -2
  16. package/dist/containerContext.d.ts.map +1 -1
  17. package/dist/containerContext.js +3 -7
  18. package/dist/containerContext.js.map +1 -1
  19. package/dist/containerStorageAdapter.d.ts +3 -3
  20. package/dist/containerStorageAdapter.d.ts.map +1 -1
  21. package/dist/containerStorageAdapter.js +6 -15
  22. package/dist/containerStorageAdapter.js.map +1 -1
  23. package/dist/contracts.d.ts +8 -0
  24. package/dist/contracts.d.ts.map +1 -1
  25. package/dist/contracts.js.map +1 -1
  26. package/dist/deltaManager.d.ts +21 -9
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +42 -31
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/deltaQueue.d.ts +2 -3
  31. package/dist/deltaQueue.d.ts.map +1 -1
  32. package/dist/deltaQueue.js +2 -3
  33. package/dist/deltaQueue.js.map +1 -1
  34. package/dist/index.d.ts +1 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/loader.d.ts +9 -7
  38. package/dist/loader.d.ts.map +1 -1
  39. package/dist/loader.js +47 -61
  40. package/dist/loader.js.map +1 -1
  41. package/dist/packageVersion.d.ts +1 -1
  42. package/dist/packageVersion.js +1 -1
  43. package/dist/packageVersion.js.map +1 -1
  44. package/dist/retriableDocumentStorageService.d.ts +3 -2
  45. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  46. package/dist/retriableDocumentStorageService.js.map +1 -1
  47. package/dist/tsdoc-metadata.json +11 -0
  48. package/dist/utils.d.ts +2 -0
  49. package/dist/utils.d.ts.map +1 -1
  50. package/dist/utils.js +8 -1
  51. package/dist/utils.js.map +1 -1
  52. package/lib/connectionManager.d.ts +3 -2
  53. package/lib/connectionManager.d.ts.map +1 -1
  54. package/lib/connectionManager.js +33 -14
  55. package/lib/connectionManager.js.map +1 -1
  56. package/lib/connectionStateHandler.d.ts +15 -3
  57. package/lib/connectionStateHandler.d.ts.map +1 -1
  58. package/lib/connectionStateHandler.js +25 -2
  59. package/lib/connectionStateHandler.js.map +1 -1
  60. package/lib/container.d.ts +74 -44
  61. package/lib/container.d.ts.map +1 -1
  62. package/lib/container.js +82 -112
  63. package/lib/container.js.map +1 -1
  64. package/lib/containerContext.d.ts +2 -2
  65. package/lib/containerContext.d.ts.map +1 -1
  66. package/lib/containerContext.js +3 -7
  67. package/lib/containerContext.js.map +1 -1
  68. package/lib/containerStorageAdapter.d.ts +3 -3
  69. package/lib/containerStorageAdapter.d.ts.map +1 -1
  70. package/lib/containerStorageAdapter.js +6 -15
  71. package/lib/containerStorageAdapter.js.map +1 -1
  72. package/lib/contracts.d.ts +8 -0
  73. package/lib/contracts.d.ts.map +1 -1
  74. package/lib/contracts.js.map +1 -1
  75. package/lib/deltaManager.d.ts +21 -9
  76. package/lib/deltaManager.d.ts.map +1 -1
  77. package/lib/deltaManager.js +44 -33
  78. package/lib/deltaManager.js.map +1 -1
  79. package/lib/deltaQueue.d.ts +2 -3
  80. package/lib/deltaQueue.d.ts.map +1 -1
  81. package/lib/deltaQueue.js +2 -3
  82. package/lib/deltaQueue.js.map +1 -1
  83. package/lib/index.d.ts +1 -2
  84. package/lib/index.d.ts.map +1 -1
  85. package/lib/index.js +1 -1
  86. package/lib/index.js.map +1 -1
  87. package/lib/loader.d.ts +9 -7
  88. package/lib/loader.d.ts.map +1 -1
  89. package/lib/loader.js +47 -61
  90. package/lib/loader.js.map +1 -1
  91. package/lib/packageVersion.d.ts +1 -1
  92. package/lib/packageVersion.js +1 -1
  93. package/lib/packageVersion.js.map +1 -1
  94. package/lib/retriableDocumentStorageService.d.ts +3 -2
  95. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  96. package/lib/retriableDocumentStorageService.js.map +1 -1
  97. package/lib/utils.d.ts +2 -0
  98. package/lib/utils.d.ts.map +1 -1
  99. package/lib/utils.js +7 -1
  100. package/lib/utils.js.map +1 -1
  101. package/package.json +16 -18
  102. package/src/connectionManager.ts +40 -22
  103. package/src/connectionStateHandler.ts +52 -8
  104. package/src/container.ts +191 -159
  105. package/src/containerContext.ts +3 -9
  106. package/src/containerStorageAdapter.ts +8 -20
  107. package/src/contracts.ts +10 -0
  108. package/src/deltaManager.ts +59 -37
  109. package/src/deltaQueue.ts +2 -3
  110. package/src/index.ts +1 -8
  111. package/src/loader.ts +85 -83
  112. package/src/packageVersion.ts +1 -1
  113. package/src/retriableDocumentStorageService.ts +3 -2
  114. 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 { ILoaderOptions, Loader, RelativeLoader } from "./loader";
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 IContainerLoadOptions {
124
- /**
125
- * Disables the Container from reconnecting if false, allows reconnect otherwise.
126
- */
127
- canReconnect?: boolean;
120
+ export interface IContainerLoadProps {
128
121
  /**
129
- * Client details provided in the override will be merged over the default client.
122
+ * The resolved url of the container being loaded
130
123
  */
131
- clientDetailsOverride?: IClientDetails;
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 IContainerConfig {
148
- resolvedUrl?: IFluidResolvedUrl;
149
- canReconnect?: boolean;
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
- * Serialized state from a previous instance of this container
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
- serializedContainerState?: IPendingContainerState;
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: ITelemetryLogger,
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
- loader: Loader,
319
- loadOptions: IContainerLoadOptions,
320
- pendingLocalState?: IPendingContainerState,
321
- protocolHandlerBuilder?: ProtocolHandlerBuilder,
350
+ loadProps: IContainerLoadProps,
351
+ createProps: IContainerCreateProps,
322
352
  ): Promise<Container> {
323
- const container = new Container(
324
- loader,
325
- {
326
- clientDetailsOverride: loadOptions.clientDetailsOverride,
327
- resolvedUrl: loadOptions.resolvedUrl,
328
- canReconnect: loadOptions.canReconnect,
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
- ? { ...(loadOptions.loadMode ?? defaultMode), opsBeforeReturn: undefined }
346
- : loadOptions.loadMode ?? defaultMode;
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, ...loadOptions.loadMode });
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
- loader: Loader,
410
+ createProps: IContainerCreateProps,
385
411
  codeDetails: IFluidCodeDetails,
386
- protocolHandlerBuilder?: ProtocolHandlerBuilder,
387
412
  ): Promise<Container> {
388
- const container = new Container(loader, {}, protocolHandlerBuilder);
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
- loader: Loader,
431
+ createProps: IContainerCreateProps,
407
432
  snapshot: string,
408
- protocolHandlerBuilder?: ProtocolHandlerBuilder,
409
433
  ): Promise<Container> {
410
- const container = new Container(loader, {}, protocolHandlerBuilder);
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 = true;
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
- return this._resolvedUrl;
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
- private readonly loader: Loader,
689
- config: IContainerConfig,
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
- this.clientDetailsOverride = config.clientDetailsOverride;
703
- this._resolvedUrl = config.resolvedUrl;
704
- if (config.canReconnect !== undefined) {
705
- this._canReconnect = config.canReconnect;
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(loader.services.subLogger, undefined, {
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._resolvedUrl?.id ?? undefined,
789
+ docId: () => this.resolvedUrl?.id,
721
790
  containerAttachState: () => this._attachState,
722
791
  containerLifecycleState: () => this._lifecycleState,
723
792
  containerConnectionState: () => ConnectionState[this.connectionState],
724
- serializedContainer: config.serializedContainerState !== undefined,
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: this.loader.services.options.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
- config.serializedContainerState?.clientId,
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
- this.loader.services.options.summarizeProtocolTree;
895
+ options.summarizeProtocolTree;
834
896
 
835
897
  this.storageAdapter = new ContainerStorageAdapter(
836
- this.loader.services.detachedBlobStorage,
898
+ detachedBlobStorage,
837
899
  this.mc.logger,
838
- config.serializedContainerState?.snapshotBlobs,
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?(error?: ICriticalContainerError) {
873
- this._deltaManager.close(error, true /* doDispose */);
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.loader.services.detachedBlobStorage !== undefined &&
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.loader.services.detachedBlobStorage,
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.loader.services.detachedBlobStorage.size) {
1156
- const newIds = this.loader.services.detachedBlobStorage
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
- const resolvedUrl = this.resolvedUrl;
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
- this._resolvedUrl,
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.loader.services.detachedBlobStorage &&
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 interest on establishing connection only.
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 loader = new RelativeLoader(this, this.loader);
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?.(error),
2235
+ (error?: ICriticalContainerError) => this.dispose(error),
2204
2236
  (error?: ICriticalContainerError) => this.close(error),
2205
2237
  Container.version,
2206
2238
  (dirty: boolean) => this.updateDirtyContainerState(dirty),