@fluidframework/container-runtime 0.53.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 (98) hide show
  1. package/dist/containerRuntime.d.ts +25 -16
  2. package/dist/containerRuntime.d.ts.map +1 -1
  3. package/dist/containerRuntime.js +125 -77
  4. package/dist/containerRuntime.js.map +1 -1
  5. package/dist/dataStoreContext.d.ts +29 -3
  6. package/dist/dataStoreContext.d.ts.map +1 -1
  7. package/dist/dataStoreContext.js +29 -4
  8. package/dist/dataStoreContext.js.map +1 -1
  9. package/dist/dataStores.d.ts +7 -3
  10. package/dist/dataStores.d.ts.map +1 -1
  11. package/dist/dataStores.js +54 -5
  12. package/dist/dataStores.js.map +1 -1
  13. package/dist/garbageCollection.d.ts +22 -2
  14. package/dist/garbageCollection.d.ts.map +1 -1
  15. package/dist/garbageCollection.js +112 -34
  16. package/dist/garbageCollection.js.map +1 -1
  17. package/dist/packageVersion.d.ts +1 -1
  18. package/dist/packageVersion.d.ts.map +1 -1
  19. package/dist/packageVersion.js +1 -1
  20. package/dist/packageVersion.js.map +1 -1
  21. package/dist/runningSummarizer.d.ts +3 -2
  22. package/dist/runningSummarizer.d.ts.map +1 -1
  23. package/dist/runningSummarizer.js +6 -6
  24. package/dist/runningSummarizer.js.map +1 -1
  25. package/dist/summarizer.d.ts +22 -0
  26. package/dist/summarizer.d.ts.map +1 -1
  27. package/dist/summarizer.js +135 -33
  28. package/dist/summarizer.js.map +1 -1
  29. package/dist/summarizerTypes.d.ts +1 -8
  30. package/dist/summarizerTypes.d.ts.map +1 -1
  31. package/dist/summarizerTypes.js.map +1 -1
  32. package/dist/summaryFormat.d.ts +1 -0
  33. package/dist/summaryFormat.d.ts.map +1 -1
  34. package/dist/summaryFormat.js +2 -1
  35. package/dist/summaryFormat.js.map +1 -1
  36. package/dist/summaryManager.d.ts +0 -15
  37. package/dist/summaryManager.d.ts.map +1 -1
  38. package/dist/summaryManager.js +1 -35
  39. package/dist/summaryManager.js.map +1 -1
  40. package/lib/containerRuntime.d.ts +25 -16
  41. package/lib/containerRuntime.d.ts.map +1 -1
  42. package/lib/containerRuntime.js +131 -83
  43. package/lib/containerRuntime.js.map +1 -1
  44. package/lib/dataStoreContext.d.ts +29 -3
  45. package/lib/dataStoreContext.d.ts.map +1 -1
  46. package/lib/dataStoreContext.js +29 -4
  47. package/lib/dataStoreContext.js.map +1 -1
  48. package/lib/dataStores.d.ts +7 -3
  49. package/lib/dataStores.d.ts.map +1 -1
  50. package/lib/dataStores.js +54 -5
  51. package/lib/dataStores.js.map +1 -1
  52. package/lib/garbageCollection.d.ts +22 -2
  53. package/lib/garbageCollection.d.ts.map +1 -1
  54. package/lib/garbageCollection.js +114 -36
  55. package/lib/garbageCollection.js.map +1 -1
  56. package/lib/packageVersion.d.ts +1 -1
  57. package/lib/packageVersion.d.ts.map +1 -1
  58. package/lib/packageVersion.js +1 -1
  59. package/lib/packageVersion.js.map +1 -1
  60. package/lib/runningSummarizer.d.ts +3 -2
  61. package/lib/runningSummarizer.d.ts.map +1 -1
  62. package/lib/runningSummarizer.js +6 -6
  63. package/lib/runningSummarizer.js.map +1 -1
  64. package/lib/summarizer.d.ts +22 -0
  65. package/lib/summarizer.d.ts.map +1 -1
  66. package/lib/summarizer.js +135 -33
  67. package/lib/summarizer.js.map +1 -1
  68. package/lib/summarizerTypes.d.ts +1 -8
  69. package/lib/summarizerTypes.d.ts.map +1 -1
  70. package/lib/summarizerTypes.js.map +1 -1
  71. package/lib/summaryFormat.d.ts +1 -0
  72. package/lib/summaryFormat.d.ts.map +1 -1
  73. package/lib/summaryFormat.js +1 -0
  74. package/lib/summaryFormat.js.map +1 -1
  75. package/lib/summaryManager.d.ts +0 -15
  76. package/lib/summaryManager.d.ts.map +1 -1
  77. package/lib/summaryManager.js +1 -34
  78. package/lib/summaryManager.js.map +1 -1
  79. package/package.json +13 -13
  80. package/src/containerRuntime.ts +176 -93
  81. package/src/dataStoreContext.ts +44 -6
  82. package/src/dataStores.ts +84 -4
  83. package/src/garbageCollection.ts +137 -46
  84. package/src/packageVersion.ts +1 -1
  85. package/src/runningSummarizer.ts +12 -10
  86. package/src/summarizer.ts +154 -38
  87. package/src/summarizerTypes.ts +2 -9
  88. package/src/summaryFormat.ts +1 -0
  89. package/src/summaryManager.ts +2 -49
  90. package/dist/localStorageFeatureGates.d.ts +0 -13
  91. package/dist/localStorageFeatureGates.d.ts.map +0 -1
  92. package/dist/localStorageFeatureGates.js +0 -31
  93. package/dist/localStorageFeatureGates.js.map +0 -1
  94. package/lib/localStorageFeatureGates.d.ts +0 -13
  95. package/lib/localStorageFeatureGates.d.ts.map +0 -1
  96. package/lib/localStorageFeatureGates.js +0 -27
  97. package/lib/localStorageFeatureGates.js.map +0 -1
  98. package/src/localStorageFeatureGates.ts +0 -27
@@ -26,6 +26,7 @@ import {
26
26
  ICriticalContainerError,
27
27
  AttachState,
28
28
  ILoaderOptions,
29
+ LoaderHeader,
29
30
  } from "@fluidframework/container-definitions";
30
31
  import {
31
32
  IContainerRuntime,
@@ -44,14 +45,22 @@ import {
44
45
  PerformanceEvent,
45
46
  normalizeError,
46
47
  TaggedLoggerAdapter,
48
+ MonitoringContext,
49
+ loggerToMonitoringContext,
47
50
  } from "@fluidframework/telemetry-utils";
48
- import { IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
51
+ import { DriverHeader, IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
49
52
  import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
50
- import { DataCorruptionError, GenericError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
53
+ import {
54
+ CreateProcessingError,
55
+ DataCorruptionError,
56
+ GenericError,
57
+ UsageError,
58
+ extractSafePropertiesFromMessage,
59
+ } from "@fluidframework/container-utils";
51
60
  import {
52
61
  IClientDetails,
53
62
  IDocumentMessage,
54
- IQuorum,
63
+ IQuorumClients,
55
64
  ISequencedDocumentMessage,
56
65
  ISignalMessage,
57
66
  ISummaryConfiguration,
@@ -89,6 +98,7 @@ import {
89
98
  RequestParser,
90
99
  create404Response,
91
100
  exceptionToResponse,
101
+ requestFluidObject,
92
102
  responseToException,
93
103
  seqFromTree,
94
104
  convertSummaryTreeToITree,
@@ -97,7 +107,7 @@ import { v4 as uuid } from "uuid";
97
107
  import { ContainerFluidHandleContext } from "./containerHandleContext";
98
108
  import { FluidDataStoreRegistry } from "./dataStoreRegistry";
99
109
  import { Summarizer } from "./summarizer";
100
- import { formRequestSummarizerFn, ISummarizerRequestOptions, SummaryManager } from "./summaryManager";
110
+ import { SummaryManager } from "./summaryManager";
101
111
  import { DeltaScheduler } from "./deltaScheduler";
102
112
  import { ReportOpPerfTelemetry, latencyThreshold } from "./connectionTelemetry";
103
113
  import { IPendingLocalState, PendingStateManager } from "./pendingStateManager";
@@ -105,6 +115,7 @@ import { pkgVersion } from "./packageVersion";
105
115
  import { BlobManager, IBlobManagerLoadInfo } from "./blobManager";
106
116
  import { DataStores, getSummaryForDatastores } from "./dataStores";
107
117
  import {
118
+ aliasBlobName,
108
119
  blobsTreeName,
109
120
  chunksBlobName,
110
121
  electedSummarizerBlobName,
@@ -116,7 +127,6 @@ import {
116
127
  wrapSummaryInChannelsTree,
117
128
  } from "./summaryFormat";
118
129
  import { SummaryCollection } from "./summaryCollection";
119
- import { getLocalStorageFeatureGate } from "./localStorageFeatureGates";
120
130
  import { ISerializedElection, OrderedClientCollection, OrderedClientElection } from "./orderedClientElection";
121
131
  import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
122
132
  import {
@@ -155,6 +165,9 @@ export enum ContainerMessageType {
155
165
 
156
166
  // Ties our new clientId to our old one on reconnect
157
167
  Rejoin = "rejoin",
168
+
169
+ // Sets the alias of a root data store
170
+ Alias = "alias",
158
171
  }
159
172
 
160
173
  export interface IChunkedOp {
@@ -279,13 +292,14 @@ type IRuntimeMessageMetadata = undefined | {
279
292
  };
280
293
 
281
294
  // Local storage key to set the default flush mode to TurnBased
282
- const turnBasedFlushModeKey = "FluidFlushModeTurnBased";
295
+ const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
283
296
 
284
297
  export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
285
298
  switch (message.type) {
286
299
  case ContainerMessageType.FluidDataStoreOp:
287
300
  case ContainerMessageType.ChunkedOp:
288
301
  case ContainerMessageType.Attach:
302
+ case ContainerMessageType.Alias:
289
303
  case ContainerMessageType.BlobAttach:
290
304
  case ContainerMessageType.Rejoin:
291
305
  case MessageType.Operation:
@@ -609,13 +623,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
609
623
  public get IContainerRuntime() { return this; }
610
624
  public get IFluidRouter() { return this; }
611
625
 
612
- // back-compat: Used by loader in <= 0.35
613
- /**
614
- * @internal
615
- * @deprecated Back-compat only. Used by the loader in versions earlier than 0.35.
616
- */
617
- public readonly runtimeVersion: string = pkgVersion;
618
-
619
626
  /**
620
627
  * Load the stores from a snapshot and returns the runtime.
621
628
  * @param context - Context of the container.
@@ -685,9 +692,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
685
692
  return readAndParse<T>(storage, blobId);
686
693
  }
687
694
  };
688
- const chunks = await tryFetchBlob<[string, string[]][]>(chunksBlobName) ?? [];
689
- const metadata = await tryFetchBlob<IContainerRuntimeMetadata>(metadataBlobName);
690
- 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
+
691
703
  const loadExisting = existing === true || context.existing === true;
692
704
 
693
705
  // read snapshot blobs needed for BlobManager to load
@@ -726,7 +738,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
726
738
  registry,
727
739
  metadata,
728
740
  electedSummarizerData,
729
- chunks,
741
+ chunks ?? [],
742
+ aliases ?? [],
730
743
  {
731
744
  summaryOptions,
732
745
  gcOptions,
@@ -743,6 +756,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
743
756
  return runtime;
744
757
  }
745
758
 
759
+ /**
760
+ * @deprecated This will be removed in a later release. Deprecated in 0.53
761
+ */
746
762
  public get id(): string {
747
763
  return this.context.id;
748
764
  }
@@ -815,10 +831,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
815
831
  return this.context.attachState;
816
832
  }
817
833
 
818
- public readonly IFluidHandleContext: IFluidHandleContext;
834
+ public get IFluidHandleContext(): IFluidHandleContext {
835
+ return this.handleContext;
836
+ }
837
+ private readonly handleContext: ContainerFluidHandleContext;
819
838
 
820
839
  // internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
821
- private readonly _logger: ITelemetryLogger;
840
+ private readonly mc: MonitoringContext;
822
841
  private readonly summarizerClientElection?: SummarizerClientElection;
823
842
  /**
824
843
  * summaryManager will only be created if this client is permitted to spawn a summarizing client
@@ -831,7 +850,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
831
850
  private readonly summarizerNode: IRootSummarizerNodeWithGC;
832
851
 
833
852
  private _orderSequentiallyCalls: number = 0;
834
- private _flushMode = ContainerRuntime.defaultFlushMode;
853
+ private _flushMode: FlushMode;
835
854
  private needsFlush = false;
836
855
  private flushTrigger = false;
837
856
 
@@ -898,15 +917,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
898
917
  /** The message in the metadata of the base summary this container is loaded from. */
899
918
  private readonly baseSummaryMessage: ISummaryMetadataMessage | undefined;
900
919
 
901
- private static get defaultFlushMode(): FlushMode {
902
- return getLocalStorageFeatureGate(turnBasedFlushModeKey) ? FlushMode.TurnBased : FlushMode.Immediate;
903
- }
904
-
905
920
  private get summarizer(): Summarizer {
906
921
  assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
907
922
  return this._summarizer;
908
923
  }
909
924
 
925
+ private get summariesDisabled(): boolean {
926
+ return this.runtimeOptions.summaryOptions.disableSummaries === true ||
927
+ this.runtimeOptions.summaryOptions.summaryConfigOverrides?.disableSummaries === true;
928
+ }
929
+
910
930
  private readonly createContainerMetadata: ICreateContainerMetadata;
911
931
  private summaryCount: number | undefined;
912
932
 
@@ -916,6 +936,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
916
936
  metadata: IContainerRuntimeMetadata | undefined,
917
937
  electedSummarizerData: ISerializedElection | undefined,
918
938
  chunks: [string, string[]][],
939
+ dataStoreAliasMap: [string, string][],
919
940
  private readonly runtimeOptions: Readonly<Required<IContainerRuntimeOptions>>,
920
941
  private readonly containerScope: FluidObject,
921
942
  public readonly logger: ITelemetryLogger,
@@ -948,19 +969,26 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
948
969
  this._connected = this.context.connected;
949
970
  this.chunkMap = new Map<string, string[]>(chunks);
950
971
 
951
- this.IFluidHandleContext = new ContainerFluidHandleContext("", this);
972
+ this.handleContext = new ContainerFluidHandleContext("", this);
973
+
974
+ this.mc = loggerToMonitoringContext(
975
+ ChildLogger.create(this.logger, "ContainerRuntime"));
952
976
 
953
- this._logger = ChildLogger.create(this.logger, "ContainerRuntime");
977
+ this._flushMode =
978
+ this.mc.config.getBoolean(turnBasedFlushModeKey) ?? false
979
+ ? FlushMode.TurnBased : FlushMode.Immediate;
954
980
 
955
981
  /**
956
982
  * Function that return the current server timestamp. This is used by the garbage collector to set the
957
983
  * time when a node becomes unreferenced.
958
- * For now, we use the timestamp of the last op for gcTimestamp. However, there can be cases where
984
+ * We use the timestamp of the last op for current timestamp. However, there can be cases where
959
985
  * we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
960
- * of this client's connection - https://github.com/microsoft/FluidFramework/issues/8375.
986
+ * of this client's connection.
961
987
  */
962
- const getCurrentTimestamp = () => {
963
- return this.deltaManager.lastMessage?.timestamp ?? Date.now();
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();
964
992
  };
965
993
  this.garbageCollector = GarbageCollector.create(
966
994
  this,
@@ -969,7 +997,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
969
997
  getCurrentTimestamp,
970
998
  context.baseSnapshot,
971
999
  async <T>(id: string) => readAndParse<T>(this.storage, id),
972
- this._logger,
1000
+ this.mc.logger,
973
1001
  existing,
974
1002
  metadata,
975
1003
  );
@@ -1016,13 +1044,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1016
1044
  getInitialGCSummaryDetailsFn,
1017
1045
  ),
1018
1046
  (id: string) => this.summarizerNode.deleteChild(id),
1019
- this._logger,
1047
+ this.mc.logger,
1020
1048
  async () => this.garbageCollector.getDataStoreBaseGCDetails(),
1021
1049
  (id: string) => this.garbageCollector.nodeChanged(id),
1050
+ new Map<string, string>(dataStoreAliasMap),
1022
1051
  );
1023
1052
 
1024
1053
  this.blobManager = new BlobManager(
1025
- this.IFluidHandleContext,
1054
+ this.handleContext,
1026
1055
  blobManagerSnapshot,
1027
1056
  () => this.storage,
1028
1057
  (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }),
@@ -1049,46 +1078,32 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1049
1078
 
1050
1079
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1051
1080
 
1052
- // Only create a SummaryManager if summaries are enabled and we are not the summarizer client
1053
1081
  // Map the deprecated generateSummaries flag to disableSummaries.
1054
1082
  if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
1055
1083
  this.runtimeOptions.summaryOptions.disableSummaries = true;
1056
1084
  }
1057
- if (this.summariesDisabled()) {
1058
- this._logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
1059
- } else {
1060
- const maxOpsSinceLastSummary = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary ?? 7000;
1061
- const defaultAction = () => {
1062
- if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
1063
- this.logger.sendErrorEvent({eventName: "SummaryStatus:Behind"});
1064
- // unregister default to no log on every op after falling behind
1065
- // and register summary ack handler to re-register this handler
1066
- // after successful summary
1067
- this.summaryCollection.once(MessageType.SummaryAck, () => {
1068
- this.logger.sendTelemetryEvent({eventName: "SummaryStatus:CaughtUp"});
1069
- // we've caught up, so re-register the default action to monitor for
1070
- // falling behind, and unregister ourself
1071
- this.summaryCollection.on("default", defaultAction);
1072
- });
1073
- this.summaryCollection.off("default", defaultAction);
1074
- }
1075
- };
1076
1085
 
1077
- this.summaryCollection.on("default", defaultAction);
1078
- 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");
1079
1091
  const orderedClientCollection = new OrderedClientCollection(
1080
1092
  orderedClientLogger,
1081
1093
  this.context.deltaManager,
1082
1094
  this.context.quorum,
1083
1095
  );
1084
1096
  const orderedClientElectionForSummarizer = new OrderedClientElection(
1097
+
1085
1098
  orderedClientLogger,
1086
1099
  orderedClientCollection,
1087
1100
  electedSummarizerData ?? this.context.deltaManager.lastSequenceNumber,
1088
1101
  SummarizerClientElection.isClientEligible,
1089
1102
  );
1090
- const summarizerClientElectionEnabled = getLocalStorageFeatureGate("summarizerClientElection") ??
1103
+ const summarizerClientElectionEnabled =
1104
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection") ??
1091
1105
  this.runtimeOptions.summaryOptions?.summarizerClientElection === true;
1106
+ const maxOpsSinceLastSummary = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary ?? 7000;
1092
1107
  this.summarizerClientElection = new SummarizerClientElection(
1093
1108
  orderedClientLogger,
1094
1109
  this.summaryCollection,
@@ -1103,27 +1118,39 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1103
1118
  this /* ISummarizerRuntime */,
1104
1119
  () => this.summaryConfiguration,
1105
1120
  this /* ISummarizerInternalsProvider */,
1106
- this.IFluidHandleContext,
1121
+ this.handleContext,
1107
1122
  this.summaryCollection,
1108
1123
  async (runtime: IConnectableRuntime) => RunWhileConnectedCoordinator.create(runtime),
1109
1124
  );
1110
- } 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
+
1111
1147
  // Create the SummaryManager and mark the initial state
1112
- const requestOptions: ISummarizerRequestOptions =
1113
- {
1114
- cache: false,
1115
- reconnect: false,
1116
- summarizingClient: true,
1117
- };
1118
1148
  this.summaryManager = new SummaryManager(
1119
1149
  this.summarizerClientElection,
1120
1150
  this, // IConnectedState
1121
1151
  this.summaryCollection,
1122
1152
  this.logger,
1123
- formRequestSummarizerFn(
1124
- this.context.loader,
1125
- this.context.deltaManager.lastSequenceNumber,
1126
- requestOptions),
1153
+ this.formRequestSummarizerFn(this.context.loader),
1127
1154
  new Throttler(
1128
1155
  60 * 1000, // 60 sec delay window
1129
1156
  30 * 1000, // 30 sec max delay
@@ -1143,8 +1170,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1143
1170
  this.deltaManager.on("readonly", (readonly: boolean) => {
1144
1171
  // we accumulate ops while being in read-only state.
1145
1172
  // once user gets write permissions and we have active connection, flush all pending ops.
1146
- // eslint-disable-next-line max-len
1147
- assert(readonly === this.deltaManager.readOnlyInfo.readonly, 0x124 /* "inconsistent readonly property/event state" */);
1173
+ assert(readonly === this.deltaManager.readOnlyInfo.readonly,
1174
+ 0x124 /* "inconsistent readonly property/event state" */);
1148
1175
 
1149
1176
  // We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
1150
1177
  // when we either never send an op, or attempted to send it but we know for sure it was not
@@ -1363,6 +1390,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1363
1390
  addBlobToSummary(summaryTree, chunksBlobName, content);
1364
1391
  }
1365
1392
 
1393
+ const dataStoreAliases = this.dataStores.aliases();
1394
+ if (dataStoreAliases.size > 0) {
1395
+ addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
1396
+ }
1397
+
1366
1398
  if (this.summarizerClientElection) {
1367
1399
  const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
1368
1400
  addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
@@ -1442,6 +1474,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1442
1474
  return this.dataStores.applyStashedOp(op);
1443
1475
  case ContainerMessageType.Attach:
1444
1476
  return this.dataStores.applyStashedAttachOp(op as unknown as IAttachMessage);
1477
+ case ContainerMessageType.Alias:
1445
1478
  case ContainerMessageType.BlobAttach:
1446
1479
  return;
1447
1480
  case ContainerMessageType.ChunkedOp:
@@ -1468,7 +1501,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1468
1501
 
1469
1502
  this.dataStores.setConnectionState(connected, clientId);
1470
1503
 
1471
- raiseConnectedEvent(this._logger, this, connected, clientId);
1504
+ raiseConnectedEvent(this.mc.logger, this, connected, clientId);
1472
1505
  }
1473
1506
 
1474
1507
  public process(messageArg: ISequencedDocumentMessage, local: boolean) {
@@ -1510,6 +1543,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1510
1543
  case ContainerMessageType.Attach:
1511
1544
  this.dataStores.processAttachMessage(message, local || localAck);
1512
1545
  break;
1546
+ case ContainerMessageType.Alias:
1547
+ this.processAliasMessage(message, localOpMetadata, local);
1548
+ break;
1513
1549
  case ContainerMessageType.FluidDataStoreOp:
1514
1550
  // if localAck === true, treat this as a local op because it's one we sent on a previous container
1515
1551
  this.dataStores.processFluidDataStoreOp(message, local || localAck, localOpMetadata);
@@ -1529,6 +1565,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1529
1565
  }
1530
1566
  }
1531
1567
 
1568
+ private processAliasMessage(
1569
+ message: ISequencedDocumentMessage,
1570
+ localOpMetadata: unknown,
1571
+ local: boolean,
1572
+ ) {
1573
+ this.dataStores.processAliasMessage(message, localOpMetadata, local);
1574
+ }
1575
+
1532
1576
  public processSignal(message: ISignalMessage, local: boolean) {
1533
1577
  const envelope = message.content as ISignalEnvelope;
1534
1578
  const transformed: IInboundSignalMessage = {
@@ -1592,6 +1636,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1592
1636
  }
1593
1637
 
1594
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
+
1595
1646
  return this.deltaSender.flush();
1596
1647
  }
1597
1648
 
@@ -1611,9 +1662,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1611
1662
 
1612
1663
  try {
1613
1664
  this.trackOrderSequentiallyCalls(callback);
1614
- } finally {
1615
1665
  this.flush();
1616
1666
  this.setFlushMode(savedFlushMode);
1667
+ } catch(error) {
1668
+ this.closeFn(CreateProcessingError(error, "orderSequentially"));
1617
1669
  }
1618
1670
  }
1619
1671
 
@@ -1673,7 +1725,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1673
1725
  return this.connected && !this.deltaManager.readonly;
1674
1726
  }
1675
1727
 
1676
- public getQuorum(): IQuorum {
1728
+ public getQuorum(): IQuorumClients {
1677
1729
  return this.context.quorum;
1678
1730
  }
1679
1731
 
@@ -1845,7 +1897,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1845
1897
  }
1846
1898
 
1847
1899
  /**
1848
- * 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.
1849
1901
  * @returns the statistics of the garbage collection run.
1850
1902
  */
1851
1903
  public async collectGarbage(
@@ -1861,6 +1913,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1861
1913
  return this.garbageCollector.collectGarbage(options);
1862
1914
  }
1863
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
+
1864
1926
  /**
1865
1927
  * Generates the summary tree, uploads it to storage, and then submits the summarize op.
1866
1928
  * This is intended to be called by the summarizer, since it is the implementation of
@@ -2093,10 +2155,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2093
2155
  this.dirtyContainer = dirty;
2094
2156
  if (this.emitDirtyDocumentEvent) {
2095
2157
  this.emit(dirty ? "dirty" : "saved");
2096
- // back-compat: Loader API added in 0.35 only
2097
- if (this.context.updateDirtyContainerState !== undefined) {
2098
- this.context.updateDirtyContainerState(dirty);
2099
- }
2158
+ this.context.updateDirtyContainerState(dirty);
2100
2159
  }
2101
2160
  }
2102
2161
 
@@ -2215,7 +2274,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2215
2274
  // That might be not what caller hopes to get, but we can look deeper if telemetry tells us it's a problem.
2216
2275
  const middleOfBatch = this.flushMode === FlushMode.TurnBased && this.needsFlush;
2217
2276
  if (middleOfBatch) {
2218
- this._logger.sendErrorEvent({ eventName: "submitSystemMessageError", type });
2277
+ this.mc.logger.sendErrorEvent({ eventName: "submitSystemMessageError", type });
2219
2278
  }
2220
2279
 
2221
2280
  return this.context.submitFn(
@@ -2269,6 +2328,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2269
2328
  this.dataStores.resubmitDataStoreOp(content, localOpMetadata);
2270
2329
  break;
2271
2330
  case ContainerMessageType.Attach:
2331
+ case ContainerMessageType.Alias:
2272
2332
  this.submit(type, content, localOpMetadata);
2273
2333
  break;
2274
2334
  case ContainerMessageType.ChunkedOp:
@@ -2314,7 +2374,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2314
2374
  * @returns downloaded snapshot's reference sequence number
2315
2375
  */
2316
2376
  private async refreshLatestSummaryAckFromServer(summaryLogger: ITelemetryLogger): Promise<number> {
2317
- const snapshot = await this.fetchSnapshotFromStorage(this.id, summaryLogger, {
2377
+ const snapshot = await this.fetchSnapshotFromStorage(null, summaryLogger, {
2318
2378
  eventName: "RefreshLatestSummaryGetSnapshot",
2319
2379
  fetchLatest: true,
2320
2380
  });
@@ -2336,7 +2396,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2336
2396
  return snapshotRefSeq;
2337
2397
  }
2338
2398
 
2339
- private async fetchSnapshotFromStorage(versionId: string, logger: ITelemetryLogger, event: ITelemetryGenericEvent) {
2399
+ private async fetchSnapshotFromStorage(
2400
+ versionId: string | null, logger: ITelemetryLogger, event: ITelemetryGenericEvent) {
2340
2401
  return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
2341
2402
  const stats: { getVersionDuration?: number; getSnapshotDuration?: number } = {};
2342
2403
  const trace = Trace.start();
@@ -2358,14 +2419,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2358
2419
  return this.pendingStateManager.getLocalState();
2359
2420
  }
2360
2421
 
2361
- /**
2362
- * @returns true if summaries are explicitly disabled for this ContainerRuntime, false otherwise
2363
- */
2364
- public summariesDisabled(): boolean {
2365
- return this.runtimeOptions.summaryOptions.disableSummaries === true ||
2366
- this.runtimeOptions.summaryOptions.summaryConfigOverrides?.disableSummaries === true;
2367
- }
2368
-
2369
2422
  public readonly summarizeOnDemand: ISummarizer["summarizeOnDemand"] = (...args) => {
2370
2423
  if (this.clientDetails.type === summarizerClientType) {
2371
2424
  return this.summarizer.summarizeOnDemand(...args);
@@ -2375,8 +2428,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2375
2428
  // If we're not the summarizer, and we don't have a summaryManager, we expect that
2376
2429
  // disableSummaries is turned on. We are throwing instead of returning a failure here,
2377
2430
  // because it is a misuse of the API rather than an expected failure.
2378
- throw new Error(
2379
- `Can't summarize, disableSummaries: ${this.summariesDisabled()}`,
2431
+ throw new UsageError(
2432
+ `Can't summarize, disableSummaries: ${this.summariesDisabled}`,
2380
2433
  );
2381
2434
  }
2382
2435
  };
@@ -2390,11 +2443,41 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2390
2443
  // If we're not the summarizer, and we don't have a summaryManager, we expect that
2391
2444
  // generateSummaries is turned off. We are throwing instead of returning a failure here,
2392
2445
  // because it is a misuse of the API rather than an expected failure.
2393
- throw new Error(
2394
- `Can't summarize, disableSummaries: ${this.summariesDisabled()}`,
2446
+ throw new UsageError(
2447
+ `Can't summarize, disableSummaries: ${this.summariesDisabled}`,
2395
2448
  );
2396
2449
  }
2397
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
+ }
2398
2481
  }
2399
2482
 
2400
2483
  /**