@fluidframework/container-runtime 0.52.0 → 0.54.0-47413

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 (132) hide show
  1. package/dist/containerHandleContext.d.ts +0 -1
  2. package/dist/containerHandleContext.d.ts.map +1 -1
  3. package/dist/containerHandleContext.js +0 -1
  4. package/dist/containerHandleContext.js.map +1 -1
  5. package/dist/containerRuntime.d.ts +43 -19
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +201 -111
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStoreContext.d.ts +33 -4
  10. package/dist/dataStoreContext.d.ts.map +1 -1
  11. package/dist/dataStoreContext.js +45 -17
  12. package/dist/dataStoreContext.js.map +1 -1
  13. package/dist/dataStores.d.ts +14 -10
  14. package/dist/dataStores.d.ts.map +1 -1
  15. package/dist/dataStores.js +73 -41
  16. package/dist/dataStores.js.map +1 -1
  17. package/dist/garbageCollection.d.ts +82 -15
  18. package/dist/garbageCollection.d.ts.map +1 -1
  19. package/dist/garbageCollection.js +359 -26
  20. package/dist/garbageCollection.js.map +1 -1
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +11 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/packageVersion.d.ts +1 -1
  26. package/dist/packageVersion.d.ts.map +1 -1
  27. package/dist/packageVersion.js +1 -1
  28. package/dist/packageVersion.js.map +1 -1
  29. package/dist/pendingStateManager.d.ts +0 -1
  30. package/dist/pendingStateManager.d.ts.map +1 -1
  31. package/dist/pendingStateManager.js +0 -36
  32. package/dist/pendingStateManager.js.map +1 -1
  33. package/dist/runningSummarizer.d.ts +3 -2
  34. package/dist/runningSummarizer.d.ts.map +1 -1
  35. package/dist/runningSummarizer.js +6 -6
  36. package/dist/runningSummarizer.js.map +1 -1
  37. package/dist/summarizer.d.ts +23 -3
  38. package/dist/summarizer.d.ts.map +1 -1
  39. package/dist/summarizer.js +135 -45
  40. package/dist/summarizer.js.map +1 -1
  41. package/dist/summarizerTypes.d.ts +3 -10
  42. package/dist/summarizerTypes.d.ts.map +1 -1
  43. package/dist/summarizerTypes.js.map +1 -1
  44. package/dist/summaryFormat.d.ts +10 -1
  45. package/dist/summaryFormat.d.ts.map +1 -1
  46. package/dist/summaryFormat.js +2 -1
  47. package/dist/summaryFormat.js.map +1 -1
  48. package/dist/summaryGenerator.d.ts.map +1 -1
  49. package/dist/summaryGenerator.js +1 -3
  50. package/dist/summaryGenerator.js.map +1 -1
  51. package/dist/summaryManager.d.ts +0 -15
  52. package/dist/summaryManager.d.ts.map +1 -1
  53. package/dist/summaryManager.js +1 -35
  54. package/dist/summaryManager.js.map +1 -1
  55. package/lib/containerHandleContext.d.ts +0 -1
  56. package/lib/containerHandleContext.d.ts.map +1 -1
  57. package/lib/containerHandleContext.js +0 -1
  58. package/lib/containerHandleContext.js.map +1 -1
  59. package/lib/containerRuntime.d.ts +43 -19
  60. package/lib/containerRuntime.d.ts.map +1 -1
  61. package/lib/containerRuntime.js +206 -117
  62. package/lib/containerRuntime.js.map +1 -1
  63. package/lib/dataStoreContext.d.ts +33 -4
  64. package/lib/dataStoreContext.d.ts.map +1 -1
  65. package/lib/dataStoreContext.js +45 -17
  66. package/lib/dataStoreContext.js.map +1 -1
  67. package/lib/dataStores.d.ts +14 -10
  68. package/lib/dataStores.d.ts.map +1 -1
  69. package/lib/dataStores.js +76 -44
  70. package/lib/dataStores.js.map +1 -1
  71. package/lib/garbageCollection.d.ts +82 -15
  72. package/lib/garbageCollection.d.ts.map +1 -1
  73. package/lib/garbageCollection.js +361 -28
  74. package/lib/garbageCollection.js.map +1 -1
  75. package/lib/index.d.ts +2 -2
  76. package/lib/index.d.ts.map +1 -1
  77. package/lib/index.js +2 -1
  78. package/lib/index.js.map +1 -1
  79. package/lib/packageVersion.d.ts +1 -1
  80. package/lib/packageVersion.d.ts.map +1 -1
  81. package/lib/packageVersion.js +1 -1
  82. package/lib/packageVersion.js.map +1 -1
  83. package/lib/pendingStateManager.d.ts +0 -1
  84. package/lib/pendingStateManager.d.ts.map +1 -1
  85. package/lib/pendingStateManager.js +0 -36
  86. package/lib/pendingStateManager.js.map +1 -1
  87. package/lib/runningSummarizer.d.ts +3 -2
  88. package/lib/runningSummarizer.d.ts.map +1 -1
  89. package/lib/runningSummarizer.js +6 -6
  90. package/lib/runningSummarizer.js.map +1 -1
  91. package/lib/summarizer.d.ts +23 -3
  92. package/lib/summarizer.d.ts.map +1 -1
  93. package/lib/summarizer.js +135 -45
  94. package/lib/summarizer.js.map +1 -1
  95. package/lib/summarizerTypes.d.ts +3 -10
  96. package/lib/summarizerTypes.d.ts.map +1 -1
  97. package/lib/summarizerTypes.js.map +1 -1
  98. package/lib/summaryFormat.d.ts +10 -1
  99. package/lib/summaryFormat.d.ts.map +1 -1
  100. package/lib/summaryFormat.js +1 -0
  101. package/lib/summaryFormat.js.map +1 -1
  102. package/lib/summaryGenerator.d.ts.map +1 -1
  103. package/lib/summaryGenerator.js +1 -3
  104. package/lib/summaryGenerator.js.map +1 -1
  105. package/lib/summaryManager.d.ts +0 -15
  106. package/lib/summaryManager.d.ts.map +1 -1
  107. package/lib/summaryManager.js +1 -34
  108. package/lib/summaryManager.js.map +1 -1
  109. package/package.json +14 -14
  110. package/src/containerHandleContext.ts +0 -1
  111. package/src/containerRuntime.ts +280 -140
  112. package/src/dataStoreContext.ts +59 -20
  113. package/src/dataStores.ts +116 -54
  114. package/src/garbageCollection.ts +492 -29
  115. package/src/index.ts +20 -2
  116. package/src/packageVersion.ts +1 -1
  117. package/src/pendingStateManager.ts +0 -43
  118. package/src/runningSummarizer.ts +12 -10
  119. package/src/summarizer.ts +154 -53
  120. package/src/summarizerTypes.ts +3 -11
  121. package/src/summaryFormat.ts +11 -1
  122. package/src/summaryGenerator.ts +2 -3
  123. package/src/summaryManager.ts +2 -49
  124. package/dist/localStorageFeatureGates.d.ts +0 -13
  125. package/dist/localStorageFeatureGates.d.ts.map +0 -1
  126. package/dist/localStorageFeatureGates.js +0 -31
  127. package/dist/localStorageFeatureGates.js.map +0 -1
  128. package/lib/localStorageFeatureGates.d.ts +0 -13
  129. package/lib/localStorageFeatureGates.d.ts.map +0 -1
  130. package/lib/localStorageFeatureGates.js +0 -27
  131. package/lib/localStorageFeatureGates.js.map +0 -1
  132. package/src/localStorageFeatureGates.ts +0 -27
@@ -6,15 +6,14 @@
6
6
  import { EventEmitter } from "events";
7
7
  import { ITelemetryGenericEvent, ITelemetryLogger } from "@fluidframework/common-definitions";
8
8
  import {
9
+ FluidObject,
10
+ IFluidConfiguration,
11
+ IFluidHandle,
12
+ IFluidHandleContext,
9
13
  IFluidObject,
10
14
  IFluidRouter,
11
- IFluidHandleContext,
12
- IFluidSerializer,
13
15
  IRequest,
14
16
  IResponse,
15
- IFluidHandle,
16
- IFluidConfiguration,
17
- FluidObject,
18
17
  } from "@fluidframework/core-interfaces";
19
18
  import {
20
19
  IAudience,
@@ -27,6 +26,7 @@ import {
27
26
  ICriticalContainerError,
28
27
  AttachState,
29
28
  ILoaderOptions,
29
+ LoaderHeader,
30
30
  } from "@fluidframework/container-definitions";
31
31
  import {
32
32
  IContainerRuntime,
@@ -45,18 +45,22 @@ import {
45
45
  PerformanceEvent,
46
46
  normalizeError,
47
47
  TaggedLoggerAdapter,
48
+ MonitoringContext,
49
+ loggerToMonitoringContext,
48
50
  } from "@fluidframework/telemetry-utils";
49
- import { IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
51
+ import { DriverHeader, IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
50
52
  import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
51
- import { DataCorruptionError, GenericError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
52
53
  import {
53
- BlobTreeEntry,
54
- TreeTreeEntry,
55
- } from "@fluidframework/protocol-base";
54
+ CreateProcessingError,
55
+ DataCorruptionError,
56
+ GenericError,
57
+ UsageError,
58
+ extractSafePropertiesFromMessage,
59
+ } from "@fluidframework/container-utils";
56
60
  import {
57
61
  IClientDetails,
58
62
  IDocumentMessage,
59
- IQuorum,
63
+ IQuorumClients,
60
64
  ISequencedDocumentMessage,
61
65
  ISignalMessage,
62
66
  ISummaryConfiguration,
@@ -90,19 +94,20 @@ import {
90
94
  addTreeToSummary,
91
95
  convertToSummaryTree,
92
96
  createRootSummarizerNodeWithGC,
93
- FluidSerializer,
94
97
  IRootSummarizerNodeWithGC,
95
98
  RequestParser,
96
99
  create404Response,
97
100
  exceptionToResponse,
101
+ requestFluidObject,
98
102
  responseToException,
99
103
  seqFromTree,
104
+ convertSummaryTreeToITree,
100
105
  } from "@fluidframework/runtime-utils";
101
106
  import { v4 as uuid } from "uuid";
102
107
  import { ContainerFluidHandleContext } from "./containerHandleContext";
103
108
  import { FluidDataStoreRegistry } from "./dataStoreRegistry";
104
109
  import { Summarizer } from "./summarizer";
105
- import { formRequestSummarizerFn, ISummarizerRequestOptions, SummaryManager } from "./summaryManager";
110
+ import { SummaryManager } from "./summaryManager";
106
111
  import { DeltaScheduler } from "./deltaScheduler";
107
112
  import { ReportOpPerfTelemetry, latencyThreshold } from "./connectionTelemetry";
108
113
  import { IPendingLocalState, PendingStateManager } from "./pendingStateManager";
@@ -110,17 +115,18 @@ import { pkgVersion } from "./packageVersion";
110
115
  import { BlobManager, IBlobManagerLoadInfo } from "./blobManager";
111
116
  import { DataStores, getSummaryForDatastores } from "./dataStores";
112
117
  import {
118
+ aliasBlobName,
113
119
  blobsTreeName,
114
120
  chunksBlobName,
115
121
  electedSummarizerBlobName,
116
122
  extractSummaryMetadataMessage,
117
123
  IContainerRuntimeMetadata,
124
+ ICreateContainerMetadata,
118
125
  ISummaryMetadataMessage,
119
126
  metadataBlobName,
120
127
  wrapSummaryInChannelsTree,
121
128
  } from "./summaryFormat";
122
129
  import { SummaryCollection } from "./summaryCollection";
123
- import { getLocalStorageFeatureGate } from "./localStorageFeatureGates";
124
130
  import { ISerializedElection, OrderedClientCollection, OrderedClientElection } from "./orderedClientElection";
125
131
  import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
126
132
  import {
@@ -137,6 +143,7 @@ import { formExponentialFn, Throttler } from "./throttler";
137
143
  import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
138
144
  import {
139
145
  GarbageCollector,
146
+ gcTreeKey,
140
147
  IGarbageCollectionRuntime,
141
148
  IGarbageCollector,
142
149
  IGCStats,
@@ -158,6 +165,9 @@ export enum ContainerMessageType {
158
165
 
159
166
  // Ties our new clientId to our old one on reconnect
160
167
  Rejoin = "rejoin",
168
+
169
+ // Sets the alias of a root data store
170
+ Alias = "alias",
161
171
  }
162
172
 
163
173
  export interface IChunkedOp {
@@ -282,13 +292,14 @@ type IRuntimeMessageMetadata = undefined | {
282
292
  };
283
293
 
284
294
  // Local storage key to set the default flush mode to TurnBased
285
- const turnBasedFlushModeKey = "FluidFlushModeTurnBased";
295
+ const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
286
296
 
287
297
  export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
288
298
  switch (message.type) {
289
299
  case ContainerMessageType.FluidDataStoreOp:
290
300
  case ContainerMessageType.ChunkedOp:
291
301
  case ContainerMessageType.Attach:
302
+ case ContainerMessageType.Alias:
292
303
  case ContainerMessageType.BlobAttach:
293
304
  case ContainerMessageType.Rejoin:
294
305
  case MessageType.Operation:
@@ -584,6 +595,19 @@ export class ScheduleManager {
584
595
  */
585
596
  export const agentSchedulerId = "_scheduler";
586
597
 
598
+ // safely check navigator and get the hardware spec value
599
+ export function getDeviceSpec() {
600
+ try {
601
+ if (typeof navigator === "object" && navigator !== null) {
602
+ return {
603
+ deviceMemory: (navigator as any).deviceMemory,
604
+ hardwareConcurrency: navigator.hardwareConcurrency,
605
+ };
606
+ }
607
+ } catch {
608
+ }
609
+ return {};
610
+ }
587
611
  /**
588
612
  * Represents the runtime of the container. Contains helper functions/state of the container.
589
613
  * It will define the store level mappings.
@@ -599,13 +623,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
599
623
  public get IContainerRuntime() { return this; }
600
624
  public get IFluidRouter() { return this; }
601
625
 
602
- // back-compat: Used by loader in <= 0.35
603
- /**
604
- * @internal
605
- * @deprecated Back-compat only. Used by the loader in versions earlier than 0.35.
606
- */
607
- public readonly runtimeVersion: string = pkgVersion;
608
-
609
626
  /**
610
627
  * Load the stores from a snapshot and returns the runtime.
611
628
  * @param context - Context of the container.
@@ -675,9 +692,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
675
692
  return readAndParse<T>(storage, blobId);
676
693
  }
677
694
  };
678
- const chunks = await tryFetchBlob<[string, string[]][]>(chunksBlobName) ?? [];
679
- const metadata = await tryFetchBlob<IContainerRuntimeMetadata>(metadataBlobName);
680
- const electedSummarizerData = await tryFetchBlob<ISerializedElection>(electedSummarizerBlobName);
695
+
696
+ const [chunks, metadata, electedSummarizerData, aliases] = await Promise.all([
697
+ tryFetchBlob<[string, string[]][]>(chunksBlobName),
698
+ tryFetchBlob<IContainerRuntimeMetadata>(metadataBlobName),
699
+ tryFetchBlob<ISerializedElection>(electedSummarizerBlobName),
700
+ tryFetchBlob<[string, string][]>(aliasBlobName),
701
+ ]);
702
+
681
703
  const loadExisting = existing === true || context.existing === true;
682
704
 
683
705
  // read snapshot blobs needed for BlobManager to load
@@ -716,7 +738,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
716
738
  registry,
717
739
  metadata,
718
740
  electedSummarizerData,
719
- chunks,
741
+ chunks ?? [],
742
+ aliases ?? [],
720
743
  {
721
744
  summaryOptions,
722
745
  gcOptions,
@@ -733,6 +756,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
733
756
  return runtime;
734
757
  }
735
758
 
759
+ /**
760
+ * @deprecated This will be removed in a later release. Deprecated in 0.53
761
+ */
736
762
  public get id(): string {
737
763
  return this.context.id;
738
764
  }
@@ -805,13 +831,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
805
831
  return this.context.attachState;
806
832
  }
807
833
 
808
- // Back compat: 0.28, can be removed in 0.29
809
- public readonly IFluidSerializer: IFluidSerializer;
810
-
811
- public readonly IFluidHandleContext: IFluidHandleContext;
834
+ public get IFluidHandleContext(): IFluidHandleContext {
835
+ return this.handleContext;
836
+ }
837
+ private readonly handleContext: ContainerFluidHandleContext;
812
838
 
813
839
  // internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
814
- private readonly _logger: ITelemetryLogger;
840
+ private readonly mc: MonitoringContext;
815
841
  private readonly summarizerClientElection?: SummarizerClientElection;
816
842
  /**
817
843
  * summaryManager will only be created if this client is permitted to spawn a summarizing client
@@ -824,7 +850,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
824
850
  private readonly summarizerNode: IRootSummarizerNodeWithGC;
825
851
 
826
852
  private _orderSequentiallyCalls: number = 0;
827
- private _flushMode = ContainerRuntime.defaultFlushMode;
853
+ private _flushMode: FlushMode;
828
854
  private needsFlush = false;
829
855
  private flushTrigger = false;
830
856
 
@@ -874,6 +900,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
874
900
 
875
901
  private readonly dataStores: DataStores;
876
902
 
903
+ /**
904
+ * True, if GC data should be written at root of the summary tree.
905
+ * False, if data stores should write GC blobs in their summary tree.
906
+ */
907
+ public get writeGCDataAtRoot(): boolean {
908
+ return this.garbageCollector.writeDataAtRoot;
909
+ }
910
+
877
911
  /**
878
912
  * True if generating summaries with isolated channels is
879
913
  * explicitly disabled. This only affects how summaries are written,
@@ -883,21 +917,26 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
883
917
  /** The message in the metadata of the base summary this container is loaded from. */
884
918
  private readonly baseSummaryMessage: ISummaryMetadataMessage | undefined;
885
919
 
886
- private static get defaultFlushMode(): FlushMode {
887
- return getLocalStorageFeatureGate(turnBasedFlushModeKey) ? FlushMode.TurnBased : FlushMode.Immediate;
888
- }
889
-
890
920
  private get summarizer(): Summarizer {
891
921
  assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
892
922
  return this._summarizer;
893
923
  }
894
924
 
925
+ private get summariesDisabled(): boolean {
926
+ return this.runtimeOptions.summaryOptions.disableSummaries === true ||
927
+ this.runtimeOptions.summaryOptions.summaryConfigOverrides?.disableSummaries === true;
928
+ }
929
+
930
+ private readonly createContainerMetadata: ICreateContainerMetadata;
931
+ private summaryCount: number | undefined;
932
+
895
933
  private constructor(
896
934
  private readonly context: IContainerContext,
897
935
  private readonly registry: IFluidDataStoreRegistry,
898
936
  metadata: IContainerRuntimeMetadata | undefined,
899
937
  electedSummarizerData: ISerializedElection | undefined,
900
938
  chunks: [string, string[]][],
939
+ dataStoreAliasMap: [string, string][],
901
940
  private readonly runtimeOptions: Readonly<Required<IContainerRuntimeOptions>>,
902
941
  private readonly containerScope: FluidObject,
903
942
  public readonly logger: ITelemetryLogger,
@@ -907,25 +946,58 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
907
946
  private _storage?: IDocumentStorageService,
908
947
  ) {
909
948
  super();
910
-
911
949
  this.baseSummaryMessage = metadata?.message;
912
950
 
951
+ // If this is an existing container, we get values from metadata.
952
+ // otherwise, we initialize them.
953
+ if (existing) {
954
+ this.createContainerMetadata = {
955
+ createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
956
+ createContainerTimestamp: metadata?.createContainerTimestamp,
957
+ };
958
+ this.summaryCount = metadata?.summaryCount;
959
+ } else {
960
+ this.createContainerMetadata = {
961
+ createContainerRuntimeVersion: pkgVersion,
962
+ createContainerTimestamp: Date.now(),
963
+ };
964
+ }
965
+
913
966
  // Default to false (enabled).
914
967
  this.disableIsolatedChannels = this.runtimeOptions.summaryOptions.disableIsolatedChannels ?? false;
915
968
 
916
969
  this._connected = this.context.connected;
917
970
  this.chunkMap = new Map<string, string[]>(chunks);
918
971
 
919
- this.IFluidHandleContext = new ContainerFluidHandleContext("", this);
920
- this.IFluidSerializer = new FluidSerializer(this.IFluidHandleContext);
921
-
922
- this._logger = ChildLogger.create(this.logger, "ContainerRuntime");
923
-
972
+ this.handleContext = new ContainerFluidHandleContext("", this);
973
+
974
+ this.mc = loggerToMonitoringContext(
975
+ ChildLogger.create(this.logger, "ContainerRuntime"));
976
+
977
+ this._flushMode =
978
+ this.mc.config.getBoolean(turnBasedFlushModeKey) ?? false
979
+ ? FlushMode.TurnBased : FlushMode.Immediate;
980
+
981
+ /**
982
+ * Function that return the current server timestamp. This is used by the garbage collector to set the
983
+ * time when a node becomes unreferenced.
984
+ * We use the timestamp of the last op for current timestamp. However, there can be cases where
985
+ * we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
986
+ * of this client's connection.
987
+ */
988
+ const getCurrentTimestamp = () => {
989
+ const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
990
+ const timestamp = client?.timestamp;
991
+ return this.deltaManager.lastMessage?.timestamp ?? timestamp ?? Date.now();
992
+ };
924
993
  this.garbageCollector = GarbageCollector.create(
925
994
  this,
926
995
  this.runtimeOptions.gcOptions,
927
996
  (unusedRoutes: string[]) => this.dataStores.deleteUnusedRoutes(unusedRoutes),
928
- this._logger,
997
+ getCurrentTimestamp,
998
+ context.baseSnapshot,
999
+ async <T>(id: string) => readAndParse<T>(this.storage, id),
1000
+ this.mc.logger,
929
1001
  existing,
930
1002
  metadata,
931
1003
  );
@@ -948,8 +1020,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
948
1020
  throwOnFailure: true,
949
1021
  // If GC should not run, let the summarizer node know so that it does not track GC state.
950
1022
  gcDisabled: !this.garbageCollector.shouldRunGC,
951
- // The max duration for which objects can be unreferenced before they are eligible for deletion.
952
- maxUnreferencedDurationMs: this.runtimeOptions.gcOptions.maxUnreferencedDurationMs,
953
1023
  },
954
1024
  );
955
1025
 
@@ -974,10 +1044,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
974
1044
  getInitialGCSummaryDetailsFn,
975
1045
  ),
976
1046
  (id: string) => this.summarizerNode.deleteChild(id),
977
- this._logger);
1047
+ this.mc.logger,
1048
+ async () => this.garbageCollector.getDataStoreBaseGCDetails(),
1049
+ (id: string) => this.garbageCollector.nodeChanged(id),
1050
+ new Map<string, string>(dataStoreAliasMap),
1051
+ );
978
1052
 
979
1053
  this.blobManager = new BlobManager(
980
- this.IFluidHandleContext,
1054
+ this.handleContext,
981
1055
  blobManagerSnapshot,
982
1056
  () => this.storage,
983
1057
  (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }),
@@ -1002,54 +1076,34 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1002
1076
  this.clearPartialChunks(clientId);
1003
1077
  });
1004
1078
 
1005
- this.context.quorum.on("addProposal", (proposal) => {
1006
- if (proposal.key === "code" || proposal.key === "code2") {
1007
- this.emit("codeDetailsProposed", proposal.value, proposal);
1008
- }
1009
- });
1010
-
1011
1079
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1012
1080
 
1013
- // Only create a SummaryManager if summaries are enabled and we are not the summarizer client
1014
1081
  // Map the deprecated generateSummaries flag to disableSummaries.
1015
1082
  if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
1016
1083
  this.runtimeOptions.summaryOptions.disableSummaries = true;
1017
1084
  }
1018
- if (this.summariesDisabled()) {
1019
- this._logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
1020
- } else {
1021
- const maxOpsSinceLastSummary = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary ?? 7000;
1022
- const defaultAction = () => {
1023
- if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
1024
- this.logger.sendErrorEvent({eventName: "SummaryStatus:Behind"});
1025
- // unregister default to no log on every op after falling behind
1026
- // and register summary ack handler to re-register this handler
1027
- // after successful summary
1028
- this.summaryCollection.once(MessageType.SummaryAck, () => {
1029
- this.logger.sendTelemetryEvent({eventName: "SummaryStatus:CaughtUp"});
1030
- // we've caught up, so re-register the default action to monitor for
1031
- // falling behind, and unregister ourself
1032
- this.summaryCollection.on("default", defaultAction);
1033
- });
1034
- this.summaryCollection.off("default", defaultAction);
1035
- }
1036
- };
1037
1085
 
1038
- this.summaryCollection.on("default", defaultAction);
1039
- const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
1086
+ if (this.summariesDisabled) {
1087
+ this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
1088
+ }
1089
+ else {
1090
+ const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
1040
1091
  const orderedClientCollection = new OrderedClientCollection(
1041
1092
  orderedClientLogger,
1042
1093
  this.context.deltaManager,
1043
1094
  this.context.quorum,
1044
1095
  );
1045
1096
  const orderedClientElectionForSummarizer = new OrderedClientElection(
1097
+
1046
1098
  orderedClientLogger,
1047
1099
  orderedClientCollection,
1048
1100
  electedSummarizerData ?? this.context.deltaManager.lastSequenceNumber,
1049
1101
  SummarizerClientElection.isClientEligible,
1050
1102
  );
1051
- const summarizerClientElectionEnabled = getLocalStorageFeatureGate("summarizerClientElection") ??
1103
+ const summarizerClientElectionEnabled =
1104
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection") ??
1052
1105
  this.runtimeOptions.summaryOptions?.summarizerClientElection === true;
1106
+ const maxOpsSinceLastSummary = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary ?? 7000;
1053
1107
  this.summarizerClientElection = new SummarizerClientElection(
1054
1108
  orderedClientLogger,
1055
1109
  this.summaryCollection,
@@ -1064,27 +1118,39 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1064
1118
  this /* ISummarizerRuntime */,
1065
1119
  () => this.summaryConfiguration,
1066
1120
  this /* ISummarizerInternalsProvider */,
1067
- this.IFluidHandleContext,
1121
+ this.handleContext,
1068
1122
  this.summaryCollection,
1069
1123
  async (runtime: IConnectableRuntime) => RunWhileConnectedCoordinator.create(runtime),
1070
1124
  );
1071
- } else if (SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)) {
1125
+ }
1126
+ else if (SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)) {
1127
+ // Only create a SummaryManager and SummarizerClientElection
1128
+ // if summaries are enabled and we are not the summarizer client.
1129
+ const defaultAction = () => {
1130
+ if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
1131
+ this.logger.sendErrorEvent({eventName: "SummaryStatus:Behind"});
1132
+ // unregister default to no log on every op after falling behind
1133
+ // and register summary ack handler to re-register this handler
1134
+ // after successful summary
1135
+ this.summaryCollection.once(MessageType.SummaryAck, () => {
1136
+ this.logger.sendTelemetryEvent({eventName: "SummaryStatus:CaughtUp"});
1137
+ // we've caught up, so re-register the default action to monitor for
1138
+ // falling behind, and unregister ourself
1139
+ this.summaryCollection.on("default", defaultAction);
1140
+ });
1141
+ this.summaryCollection.off("default", defaultAction);
1142
+ }
1143
+ };
1144
+
1145
+ this.summaryCollection.on("default", defaultAction);
1146
+
1072
1147
  // Create the SummaryManager and mark the initial state
1073
- const requestOptions: ISummarizerRequestOptions =
1074
- {
1075
- cache: false,
1076
- reconnect: false,
1077
- summarizingClient: true,
1078
- };
1079
1148
  this.summaryManager = new SummaryManager(
1080
1149
  this.summarizerClientElection,
1081
1150
  this, // IConnectedState
1082
1151
  this.summaryCollection,
1083
1152
  this.logger,
1084
- formRequestSummarizerFn(
1085
- this.context.loader,
1086
- this.context.deltaManager.lastSequenceNumber,
1087
- requestOptions),
1153
+ this.formRequestSummarizerFn(this.context.loader),
1088
1154
  new Throttler(
1089
1155
  60 * 1000, // 60 sec delay window
1090
1156
  30 * 1000, // 30 sec max delay
@@ -1104,7 +1170,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1104
1170
  this.deltaManager.on("readonly", (readonly: boolean) => {
1105
1171
  // we accumulate ops while being in read-only state.
1106
1172
  // once user gets write permissions and we have active connection, flush all pending ops.
1107
- assert(readonly === this.deltaManager.readonly, 0x124 /* "inconsistent readonly property/event state" */);
1173
+ assert(readonly === this.deltaManager.readOnlyInfo.readonly,
1174
+ 0x124 /* "inconsistent readonly property/event state" */);
1108
1175
 
1109
1176
  // We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
1110
1177
  // when we either never send an op, or attempted to send it but we know for sure it was not
@@ -1127,6 +1194,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1127
1194
  this.deltaManager.on("op", this.onOp);
1128
1195
  }
1129
1196
 
1197
+ // logging hardware telemetry
1198
+ logger.sendTelemetryEvent({
1199
+ eventName:"DeviceSpec",
1200
+ ...getDeviceSpec(),
1201
+ });
1202
+
1203
+ // logging container load stats
1204
+ this.logger.sendTelemetryEvent({
1205
+ eventName: "ContainerLoadStats",
1206
+ ...this.createContainerMetadata,
1207
+ ...this.dataStores.containerLoadStats,
1208
+ summaryCount: this.summaryCount,
1209
+ summaryFormatVersion: metadata?.summaryFormatVersion,
1210
+ disableIsolatedChannels: metadata?.disableIsolatedChannels,
1211
+ gcVersion: metadata?.gcFeature,
1212
+ });
1213
+
1130
1214
  ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
1131
1215
  }
1132
1216
 
@@ -1251,6 +1335,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1251
1335
 
1252
1336
  private formMetadata(): IContainerRuntimeMetadata {
1253
1337
  return {
1338
+ ...this.createContainerMetadata,
1339
+ summaryCount: this.summaryCount,
1254
1340
  summaryFormatVersion: 1,
1255
1341
  disableIsolatedChannels: this.disableIsolatedChannels || undefined,
1256
1342
  gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
@@ -1287,26 +1373,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1287
1373
  * @deprecated - Use summarize to get summary of the container runtime.
1288
1374
  */
1289
1375
  public async snapshot(): Promise<ITree> {
1290
- if (this.garbageCollector.shouldRunGC) {
1291
- await this.collectGarbage({ logger: this.logger, fullGC: true /* fullGC */ });
1292
- }
1293
-
1294
- const root: ITree = { entries: [] };
1295
- const entries = await this.dataStores.snapshot();
1296
-
1297
- if (this.disableIsolatedChannels) {
1298
- root.entries = root.entries.concat(entries);
1299
- } else {
1300
- root.entries.push(new TreeTreeEntry(channelsTreeName, { entries }));
1301
- }
1302
-
1303
- root.entries.push(new BlobTreeEntry(metadataBlobName, JSON.stringify(this.formMetadata())));
1304
-
1305
- if (this.chunkMap.size > 0) {
1306
- root.entries.push(new BlobTreeEntry(chunksBlobName, JSON.stringify([...this.chunkMap])));
1307
- }
1308
-
1309
- return root;
1376
+ const summaryResult = await this.summarize({
1377
+ summaryLogger: this.logger,
1378
+ fullTree: true,
1379
+ trackState: false,
1380
+ runGC: this.garbageCollector.shouldRunGC,
1381
+ fullGC: true,
1382
+ });
1383
+ return convertSummaryTreeToITree(summaryResult.summary);
1310
1384
  }
1311
1385
 
1312
1386
  private addContainerBlobsToSummary(summaryTree: ISummaryTreeWithStats) {
@@ -1316,6 +1390,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1316
1390
  addBlobToSummary(summaryTree, chunksBlobName, content);
1317
1391
  }
1318
1392
 
1393
+ const dataStoreAliases = this.dataStores.aliases();
1394
+ if (dataStoreAliases.size > 0) {
1395
+ addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
1396
+ }
1397
+
1319
1398
  if (this.summarizerClientElection) {
1320
1399
  const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
1321
1400
  addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
@@ -1328,6 +1407,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1328
1407
  const blobsTree = convertToSummaryTree(snapshot, false);
1329
1408
  addTreeToSummary(summaryTree, blobsTreeName, blobsTree);
1330
1409
  }
1410
+
1411
+ if (this.writeGCDataAtRoot) {
1412
+ const gcSummary = this.garbageCollector.summarize();
1413
+ if (gcSummary !== undefined) {
1414
+ addTreeToSummary(summaryTree, gcTreeKey, gcSummary);
1415
+ }
1416
+ }
1331
1417
  }
1332
1418
 
1333
1419
  private replayPendingStates() {
@@ -1388,6 +1474,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1388
1474
  return this.dataStores.applyStashedOp(op);
1389
1475
  case ContainerMessageType.Attach:
1390
1476
  return this.dataStores.applyStashedAttachOp(op as unknown as IAttachMessage);
1477
+ case ContainerMessageType.Alias:
1391
1478
  case ContainerMessageType.BlobAttach:
1392
1479
  return;
1393
1480
  case ContainerMessageType.ChunkedOp:
@@ -1414,7 +1501,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1414
1501
 
1415
1502
  this.dataStores.setConnectionState(connected, clientId);
1416
1503
 
1417
- raiseConnectedEvent(this._logger, this, connected, clientId);
1504
+ raiseConnectedEvent(this.mc.logger, this, connected, clientId);
1418
1505
  }
1419
1506
 
1420
1507
  public process(messageArg: ISequencedDocumentMessage, local: boolean) {
@@ -1456,6 +1543,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1456
1543
  case ContainerMessageType.Attach:
1457
1544
  this.dataStores.processAttachMessage(message, local || localAck);
1458
1545
  break;
1546
+ case ContainerMessageType.Alias:
1547
+ this.processAliasMessage(message, localOpMetadata, local);
1548
+ break;
1459
1549
  case ContainerMessageType.FluidDataStoreOp:
1460
1550
  // if localAck === true, treat this as a local op because it's one we sent on a previous container
1461
1551
  this.dataStores.processFluidDataStoreOp(message, local || localAck, localOpMetadata);
@@ -1475,6 +1565,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1475
1565
  }
1476
1566
  }
1477
1567
 
1568
+ private processAliasMessage(
1569
+ message: ISequencedDocumentMessage,
1570
+ localOpMetadata: unknown,
1571
+ local: boolean,
1572
+ ) {
1573
+ this.dataStores.processAliasMessage(message, localOpMetadata, local);
1574
+ }
1575
+
1478
1576
  public processSignal(message: ISignalMessage, local: boolean) {
1479
1577
  const envelope = message.content as ISignalEnvelope;
1480
1578
  const transformed: IInboundSignalMessage = {
@@ -1538,6 +1636,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1538
1636
  }
1539
1637
 
1540
1638
  this.needsFlush = false;
1639
+
1640
+ // Did we disconnect in the middle of turn-based batch?
1641
+ // If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
1642
+ if (!this.canSendOps()) {
1643
+ return;
1644
+ }
1645
+
1541
1646
  return this.deltaSender.flush();
1542
1647
  }
1543
1648
 
@@ -1557,9 +1662,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1557
1662
 
1558
1663
  try {
1559
1664
  this.trackOrderSequentiallyCalls(callback);
1560
- } finally {
1561
1665
  this.flush();
1562
1666
  this.setFlushMode(savedFlushMode);
1667
+ } catch(error) {
1668
+ this.closeFn(CreateProcessingError(error, "orderSequentially"));
1563
1669
  }
1564
1670
  }
1565
1671
 
@@ -1619,7 +1725,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1619
1725
  return this.connected && !this.deltaManager.readonly;
1620
1726
  }
1621
1727
 
1622
- public getQuorum(): IQuorum {
1728
+ public getQuorum(): IQuorumClients {
1623
1729
  return this.context.quorum;
1624
1730
  }
1625
1731
 
@@ -1777,25 +1883,21 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1777
1883
  * Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
1778
1884
  * After GC has run, called to notify this container's nodes of routes that are used in it.
1779
1885
  * @param usedRoutes - The routes that are used in all nodes in this Container.
1886
+ * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
1887
+ * unreferenced as part of this GC run, this should be used to update the time when it happens.
1780
1888
  * @returns the statistics of the used state of the data stores.
1781
1889
  */
1782
- public updateUsedRoutes(usedRoutes: string[]): IUsedStateStats {
1890
+ public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): IUsedStateStats {
1783
1891
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
1784
1892
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1785
1893
  // always referenced, so the used routes is only self-route (empty string).
1786
1894
  this.summarizerNode.updateUsedRoutes([""]);
1787
1895
 
1788
- return this.dataStores.updateUsedRoutes(
1789
- usedRoutes,
1790
- // For now, we use the timestamp of the last op for gcTimestamp. However, there can be cases where
1791
- // we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
1792
- // of this client's connection - https://github.com/microsoft/FluidFramework/issues/7152.
1793
- this.deltaManager.lastMessage?.timestamp,
1794
- );
1896
+ return this.dataStores.updateUsedRoutes(usedRoutes, gcTimestamp);
1795
1897
  }
1796
1898
 
1797
1899
  /**
1798
- * Runs garbage collection and udpates the reference / used state of the nodes in the container.
1900
+ * Runs garbage collection and updates the reference / used state of the nodes in the container.
1799
1901
  * @returns the statistics of the garbage collection run.
1800
1902
  */
1801
1903
  public async collectGarbage(
@@ -1811,6 +1913,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1811
1913
  return this.garbageCollector.collectGarbage(options);
1812
1914
  }
1813
1915
 
1916
+ /**
1917
+ * Called when a new outbound reference is added to another node. This is used by garbage collection to identify
1918
+ * all references added in the system.
1919
+ * @param srcHandle - The handle of the node that added the reference.
1920
+ * @param outboundHandle - The handle of the outbound node that is referenced.
1921
+ */
1922
+ public addedGCOutboundReference(srcHandle: IFluidHandle, outboundHandle: IFluidHandle) {
1923
+ this.garbageCollector.addedOutboundReference(srcHandle.absolutePath, outboundHandle.absolutePath);
1924
+ }
1925
+
1814
1926
  /**
1815
1927
  * Generates the summary tree, uploads it to storage, and then submits the summarize op.
1816
1928
  * This is intended to be called by the summarizer, since it is the implementation of
@@ -1883,6 +1995,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1883
1995
  return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error: continueResult.error };
1884
1996
  }
1885
1997
 
1998
+ // increment summary count
1999
+ if (this.summaryCount !== undefined) {
2000
+ this.summaryCount++;
2001
+ } else {
2002
+ this.summaryCount = 1;
2003
+ }
2004
+
1886
2005
  const trace = Trace.start();
1887
2006
  let summarizeResult: ISummaryTreeWithStats;
1888
2007
  try {
@@ -2036,10 +2155,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2036
2155
  this.dirtyContainer = dirty;
2037
2156
  if (this.emitDirtyDocumentEvent) {
2038
2157
  this.emit(dirty ? "dirty" : "saved");
2039
- // back-compat: Loader API added in 0.35 only
2040
- if (this.context.updateDirtyContainerState !== undefined) {
2041
- this.context.updateDirtyContainerState(dirty);
2042
- }
2158
+ this.context.updateDirtyContainerState(dirty);
2043
2159
  }
2044
2160
  }
2045
2161
 
@@ -2158,7 +2274,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2158
2274
  // That might be not what caller hopes to get, but we can look deeper if telemetry tells us it's a problem.
2159
2275
  const middleOfBatch = this.flushMode === FlushMode.TurnBased && this.needsFlush;
2160
2276
  if (middleOfBatch) {
2161
- this._logger.sendErrorEvent({ eventName: "submitSystemMessageError", type });
2277
+ this.mc.logger.sendErrorEvent({ eventName: "submitSystemMessageError", type });
2162
2278
  }
2163
2279
 
2164
2280
  return this.context.submitFn(
@@ -2212,6 +2328,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2212
2328
  this.dataStores.resubmitDataStoreOp(content, localOpMetadata);
2213
2329
  break;
2214
2330
  case ContainerMessageType.Attach:
2331
+ case ContainerMessageType.Alias:
2215
2332
  this.submit(type, content, localOpMetadata);
2216
2333
  break;
2217
2334
  case ContainerMessageType.ChunkedOp:
@@ -2257,7 +2374,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2257
2374
  * @returns downloaded snapshot's reference sequence number
2258
2375
  */
2259
2376
  private async refreshLatestSummaryAckFromServer(summaryLogger: ITelemetryLogger): Promise<number> {
2260
- const snapshot = await this.fetchSnapshotFromStorage(this.id, summaryLogger, {
2377
+ const snapshot = await this.fetchSnapshotFromStorage(null, summaryLogger, {
2261
2378
  eventName: "RefreshLatestSummaryGetSnapshot",
2262
2379
  fetchLatest: true,
2263
2380
  });
@@ -2279,7 +2396,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2279
2396
  return snapshotRefSeq;
2280
2397
  }
2281
2398
 
2282
- private async fetchSnapshotFromStorage(versionId: string, logger: ITelemetryLogger, event: ITelemetryGenericEvent) {
2399
+ private async fetchSnapshotFromStorage(
2400
+ versionId: string | null, logger: ITelemetryLogger, event: ITelemetryGenericEvent) {
2283
2401
  return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
2284
2402
  const stats: { getVersionDuration?: number; getSnapshotDuration?: number } = {};
2285
2403
  const trace = Trace.start();
@@ -2301,14 +2419,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2301
2419
  return this.pendingStateManager.getLocalState();
2302
2420
  }
2303
2421
 
2304
- /**
2305
- * @returns true if summaries are explicitly disabled for this ContainerRuntime, false otherwise
2306
- */
2307
- public summariesDisabled(): boolean {
2308
- return this.runtimeOptions.summaryOptions.disableSummaries === true ||
2309
- this.runtimeOptions.summaryOptions.summaryConfigOverrides?.disableSummaries === true;
2310
- }
2311
-
2312
2422
  public readonly summarizeOnDemand: ISummarizer["summarizeOnDemand"] = (...args) => {
2313
2423
  if (this.clientDetails.type === summarizerClientType) {
2314
2424
  return this.summarizer.summarizeOnDemand(...args);
@@ -2318,8 +2428,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2318
2428
  // If we're not the summarizer, and we don't have a summaryManager, we expect that
2319
2429
  // disableSummaries is turned on. We are throwing instead of returning a failure here,
2320
2430
  // because it is a misuse of the API rather than an expected failure.
2321
- throw new Error(
2322
- `Can't summarize, disableSummaries: ${this.summariesDisabled()}`,
2431
+ throw new UsageError(
2432
+ `Can't summarize, disableSummaries: ${this.summariesDisabled}`,
2323
2433
  );
2324
2434
  }
2325
2435
  };
@@ -2333,11 +2443,41 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2333
2443
  // If we're not the summarizer, and we don't have a summaryManager, we expect that
2334
2444
  // generateSummaries is turned off. We are throwing instead of returning a failure here,
2335
2445
  // because it is a misuse of the API rather than an expected failure.
2336
- throw new Error(
2337
- `Can't summarize, disableSummaries: ${this.summariesDisabled()}`,
2446
+ throw new UsageError(
2447
+ `Can't summarize, disableSummaries: ${this.summariesDisabled}`,
2338
2448
  );
2339
2449
  }
2340
2450
  };
2451
+
2452
+ /**
2453
+ * * Forms a function that will request a Summarizer.
2454
+ * @param loaderRouter - the loader acting as an IFluidRouter
2455
+ * */
2456
+ private formRequestSummarizerFn(loaderRouter: IFluidRouter) {
2457
+ return async () => {
2458
+ const request: IRequest = {
2459
+ headers: {
2460
+ [LoaderHeader.cache]: false,
2461
+ [LoaderHeader.clientDetails]: {
2462
+ capabilities: { interactive: false },
2463
+ type: summarizerClientType,
2464
+ },
2465
+ [DriverHeader.summarizingClient]: true,
2466
+ [LoaderHeader.reconnect]: false,
2467
+ },
2468
+ url: "/_summarizer",
2469
+ };
2470
+
2471
+ const fluidObject = await requestFluidObject<FluidObject<ISummarizer>>(loaderRouter, request);
2472
+ const summarizer = fluidObject.ISummarizer;
2473
+
2474
+ if (!summarizer) {
2475
+ throw new UsageError("Fluid object does not implement ISummarizer");
2476
+ }
2477
+
2478
+ return summarizer;
2479
+ };
2480
+ }
2341
2481
  }
2342
2482
 
2343
2483
  /**