@fluidframework/container-runtime 0.56.3 → 0.57.0-51086

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 (97) hide show
  1. package/dist/blobManager.d.ts.map +1 -1
  2. package/dist/blobManager.js +9 -1
  3. package/dist/blobManager.js.map +1 -1
  4. package/dist/connectionTelemetry.d.ts.map +1 -1
  5. package/dist/connectionTelemetry.js +6 -6
  6. package/dist/connectionTelemetry.js.map +1 -1
  7. package/dist/containerRuntime.d.ts +65 -24
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +139 -72
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/dataStore.d.ts +62 -0
  12. package/dist/dataStore.d.ts.map +1 -0
  13. package/dist/dataStore.js +135 -0
  14. package/dist/dataStore.js.map +1 -0
  15. package/dist/dataStoreContext.js.map +1 -1
  16. package/dist/dataStores.d.ts +9 -5
  17. package/dist/dataStores.d.ts.map +1 -1
  18. package/dist/dataStores.js +14 -19
  19. package/dist/dataStores.js.map +1 -1
  20. package/dist/garbageCollection.d.ts +47 -21
  21. package/dist/garbageCollection.d.ts.map +1 -1
  22. package/dist/garbageCollection.js +195 -61
  23. package/dist/garbageCollection.js.map +1 -1
  24. package/dist/index.d.ts +3 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +4 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/packageVersion.d.ts +1 -1
  29. package/dist/packageVersion.d.ts.map +1 -1
  30. package/dist/packageVersion.js +1 -1
  31. package/dist/packageVersion.js.map +1 -1
  32. package/dist/runningSummarizer.d.ts +1 -0
  33. package/dist/runningSummarizer.d.ts.map +1 -1
  34. package/dist/runningSummarizer.js +23 -15
  35. package/dist/runningSummarizer.js.map +1 -1
  36. package/dist/summarizerTypes.d.ts +4 -6
  37. package/dist/summarizerTypes.d.ts.map +1 -1
  38. package/dist/summarizerTypes.js.map +1 -1
  39. package/dist/summaryGenerator.d.ts +2 -1
  40. package/dist/summaryGenerator.d.ts.map +1 -1
  41. package/dist/summaryGenerator.js +46 -29
  42. package/dist/summaryGenerator.js.map +1 -1
  43. package/lib/blobManager.d.ts.map +1 -1
  44. package/lib/blobManager.js +9 -1
  45. package/lib/blobManager.js.map +1 -1
  46. package/lib/connectionTelemetry.d.ts.map +1 -1
  47. package/lib/connectionTelemetry.js +6 -6
  48. package/lib/connectionTelemetry.js.map +1 -1
  49. package/lib/containerRuntime.d.ts +65 -24
  50. package/lib/containerRuntime.d.ts.map +1 -1
  51. package/lib/containerRuntime.js +140 -73
  52. package/lib/containerRuntime.js.map +1 -1
  53. package/lib/dataStore.d.ts +62 -0
  54. package/lib/dataStore.d.ts.map +1 -0
  55. package/lib/dataStore.js +130 -0
  56. package/lib/dataStore.js.map +1 -0
  57. package/lib/dataStoreContext.js.map +1 -1
  58. package/lib/dataStores.d.ts +9 -5
  59. package/lib/dataStores.d.ts.map +1 -1
  60. package/lib/dataStores.js +13 -18
  61. package/lib/dataStores.js.map +1 -1
  62. package/lib/garbageCollection.d.ts +47 -21
  63. package/lib/garbageCollection.d.ts.map +1 -1
  64. package/lib/garbageCollection.js +197 -63
  65. package/lib/garbageCollection.js.map +1 -1
  66. package/lib/index.d.ts +3 -2
  67. package/lib/index.d.ts.map +1 -1
  68. package/lib/index.js +2 -1
  69. package/lib/index.js.map +1 -1
  70. package/lib/packageVersion.d.ts +1 -1
  71. package/lib/packageVersion.d.ts.map +1 -1
  72. package/lib/packageVersion.js +1 -1
  73. package/lib/packageVersion.js.map +1 -1
  74. package/lib/runningSummarizer.d.ts +1 -0
  75. package/lib/runningSummarizer.d.ts.map +1 -1
  76. package/lib/runningSummarizer.js +23 -15
  77. package/lib/runningSummarizer.js.map +1 -1
  78. package/lib/summarizerTypes.d.ts +4 -6
  79. package/lib/summarizerTypes.d.ts.map +1 -1
  80. package/lib/summarizerTypes.js.map +1 -1
  81. package/lib/summaryGenerator.d.ts +2 -1
  82. package/lib/summaryGenerator.d.ts.map +1 -1
  83. package/lib/summaryGenerator.js +46 -29
  84. package/lib/summaryGenerator.js.map +1 -1
  85. package/package.json +13 -13
  86. package/src/blobManager.ts +12 -1
  87. package/src/connectionTelemetry.ts +7 -6
  88. package/src/containerRuntime.ts +220 -93
  89. package/src/dataStore.ts +187 -0
  90. package/src/dataStoreContext.ts +1 -1
  91. package/src/dataStores.ts +18 -38
  92. package/src/garbageCollection.ts +283 -105
  93. package/src/index.ts +3 -1
  94. package/src/packageVersion.ts +1 -1
  95. package/src/runningSummarizer.ts +25 -16
  96. package/src/summarizerTypes.ts +4 -8
  97. package/src/summaryGenerator.ts +71 -23
@@ -46,6 +46,7 @@ import {
46
46
  TaggedLoggerAdapter,
47
47
  MonitoringContext,
48
48
  loggerToMonitoringContext,
49
+ TelemetryDataTag,
49
50
  } from "@fluidframework/telemetry-utils";
50
51
  import { DriverHeader, IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
51
52
  import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
@@ -64,7 +65,6 @@ import {
64
65
  ISummaryConfiguration,
65
66
  ISummaryContent,
66
67
  ISummaryTree,
67
- ITree,
68
68
  MessageType,
69
69
  SummaryType,
70
70
  } from "@fluidframework/protocol-definitions";
@@ -99,7 +99,6 @@ import {
99
99
  requestFluidObject,
100
100
  responseToException,
101
101
  seqFromTree,
102
- convertSummaryTreeToITree,
103
102
  } from "@fluidframework/runtime-utils";
104
103
  import { v4 as uuid } from "uuid";
105
104
  import { ContainerFluidHandleContext } from "./containerHandleContext";
@@ -145,8 +144,14 @@ import {
145
144
  IGarbageCollectionRuntime,
146
145
  IGarbageCollector,
147
146
  IGCStats,
148
- IUsedStateStats,
149
147
  } from "./garbageCollection";
148
+ import {
149
+ AliasResult,
150
+ channelToDataStore,
151
+ IDataStore,
152
+ IDataStoreAliasMessage,
153
+ isDataStoreAliasMessage,
154
+ } from "./dataStore";
150
155
 
151
156
  export enum ContainerMessageType {
152
157
  // An op to be delivered to store
@@ -283,12 +288,41 @@ export interface IContainerRuntimeOptions {
283
288
  * 3. "bypass" will skip the check entirely. This is not recommended.
284
289
  */
285
290
  loadSequenceNumberVerification?: "close" | "log" | "bypass";
291
+ /**
292
+ * Should the runtime use data store aliasing for creating root datastores.
293
+ * In case of aliasing conflicts, the runtime will raise an exception which does
294
+ * not effect the status of the container.
295
+ */
296
+ useDataStoreAliasing?: boolean;
286
297
  }
287
298
 
288
299
  type IRuntimeMessageMetadata = undefined | {
289
300
  batch?: boolean;
290
301
  };
291
302
 
303
+ /**
304
+ * The summary tree returned by the root node. It adds state relevant to the root of the tree.
305
+ */
306
+ export interface IRootSummaryTreeWithStats extends ISummaryTreeWithStats {
307
+ /** The garbage collection stats if GC ran, undefined otherwise. */
308
+ gcStats?: IGCStats;
309
+ }
310
+
311
+ /**
312
+ * Accepted header keys for requests coming to the runtime.
313
+ */
314
+ export enum RuntimeHeaders {
315
+ /** True to wait for a data store to be created and loaded before returning it. */
316
+ wait = "wait",
317
+ /**
318
+ * True if the request is from an external app. Used for GC to handle scenarios where a data store
319
+ * is deleted and requested via an external app.
320
+ */
321
+ externalRequest = "externalRequest",
322
+ /** True if the request is coming from an IFluidHandle. */
323
+ viaHandle = "viaHandle",
324
+ }
325
+
292
326
  /**
293
327
  * @deprecated
294
328
  * Untagged logger is unsupported going forward. There are old loaders with old ContainerContexts that only
@@ -297,10 +331,11 @@ type IRuntimeMessageMetadata = undefined | {
297
331
  */
298
332
  interface OldContainerContextWithLogger extends IContainerContext {
299
333
  logger: ITelemetryBaseLogger;
300
- };
334
+ }
301
335
 
302
336
  // Local storage key to set the default flush mode to TurnBased
303
337
  const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
338
+ const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
304
339
 
305
340
  export enum RuntimeMessage {
306
341
  FluidDataStoreOp = "component",
@@ -651,7 +686,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
651
686
  ): Promise<ContainerRuntime> {
652
687
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
653
688
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
654
- const passLogger = context.taggedLogger ?? new TaggedLoggerAdapter((context as
689
+ const passLogger = context.taggedLogger ?? new TaggedLoggerAdapter((context as
655
690
  OldContainerContextWithLogger).logger);
656
691
  const logger = ChildLogger.create(passLogger, undefined, {
657
692
  all: {
@@ -663,6 +698,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
663
698
  summaryOptions = {},
664
699
  gcOptions = {},
665
700
  loadSequenceNumberVerification = "close",
701
+ useDataStoreAliasing = false,
666
702
  } = runtimeOptions;
667
703
 
668
704
  // We pack at data store level only. If isolated channels are disabled,
@@ -756,6 +792,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
756
792
  summaryOptions,
757
793
  gcOptions,
758
794
  loadSequenceNumberVerification,
795
+ useDataStoreAliasing,
759
796
  },
760
797
  containerScope,
761
798
  logger,
@@ -853,6 +890,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
853
890
  private readonly summaryCollection: SummaryCollection;
854
891
 
855
892
  private readonly summarizerNode: IRootSummarizerNodeWithGC;
893
+ private readonly _aliasingEnabled: boolean;
856
894
 
857
895
  private _orderSequentiallyCalls: number = 0;
858
896
  private _flushMode: FlushMode;
@@ -873,23 +911,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
873
911
  }
874
912
 
875
913
  private get summaryConfiguration() {
876
- return {
914
+ return {
877
915
  // the defaults
878
916
  ... DefaultSummaryConfiguration,
879
917
  // the server provided values
880
918
  ... this.context?.serviceConfiguration?.summary,
881
919
  // the runtime configuration overrides
882
920
  ... this.runtimeOptions.summaryOptions?.summaryConfigOverrides,
883
- };
921
+ };
884
922
  }
885
923
 
886
924
  private _disposed = false;
887
925
  public get disposed() { return this._disposed; }
888
926
 
889
- private dirtyContainer = false;
927
+ private dirtyContainer: boolean;
890
928
  private emitDirtyDocumentEvent = true;
891
929
 
892
- private summarizerWarning = (warning: ContainerWarning) =>
930
+ private readonly summarizerWarning = (warning: ContainerWarning) =>
893
931
  this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
894
932
  /**
895
933
  * Summarizer is responsible for coordinating when to send generate and send summaries.
@@ -978,6 +1016,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
978
1016
  this.mc.config.getBoolean(turnBasedFlushModeKey) ?? false
979
1017
  ? FlushMode.TurnBased : FlushMode.Immediate;
980
1018
 
1019
+ this._aliasingEnabled =
1020
+ (this.mc.config.getBoolean(useDataStoreAliasingKey) ?? false) ||
1021
+ (runtimeOptions.useDataStoreAliasing ?? false);
1022
+
981
1023
  /**
982
1024
  * Function that return the current server timestamp. This is used by the garbage collector to set the
983
1025
  * time when a node becomes unreferenced.
@@ -994,6 +1036,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
994
1036
  this,
995
1037
  this.runtimeOptions.gcOptions,
996
1038
  (unusedRoutes: string[]) => this.dataStores.deleteUnusedRoutes(unusedRoutes),
1039
+ (nodePath: string) => this.dataStores.getNodePackagePath(nodePath),
997
1040
  getCurrentTimestamp,
998
1041
  this.closeFn,
999
1042
  context.baseSnapshot,
@@ -1047,7 +1090,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1047
1090
  (id: string) => this.summarizerNode.deleteChild(id),
1048
1091
  this.mc.logger,
1049
1092
  async () => this.garbageCollector.getDataStoreBaseGCDetails(),
1050
- (id: string) => this.garbageCollector.nodeChanged(id),
1093
+ (dataStorePath: string, packagePath?: readonly string[]) =>
1094
+ this.garbageCollector.nodeUpdated(dataStorePath, "Changed", packagePath),
1051
1095
  new Map<string, string>(dataStoreAliasMap),
1052
1096
  this.garbageCollector.writeDataAtRoot,
1053
1097
  );
@@ -1080,6 +1124,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1080
1124
 
1081
1125
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1082
1126
 
1127
+ const { attachState, pendingLocalState } = this.context;
1128
+ this.dirtyContainer = attachState !== AttachState.Attached
1129
+ || (pendingLocalState as IPendingLocalState)?.pendingStates.length > 0;
1130
+ this.context.updateDirtyContainerState(this.dirtyContainer);
1131
+
1083
1132
  // Map the deprecated generateSummaries flag to disableSummaries.
1084
1133
  if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
1085
1134
  this.runtimeOptions.summaryOptions.disableSummaries = true;
@@ -1305,18 +1354,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1305
1354
  return create404Response(request);
1306
1355
  }
1307
1356
  } else if (requestParser.pathParts.length > 0) {
1308
- /**
1309
- * If GC should run and this an external app request with "externalRequest" header, we need to return
1310
- * an error if the data store being requested is marked as unreferenced as per the data store's initial
1311
- * summary.
1312
- *
1313
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted
1314
- * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
1315
- */
1316
- const wait = typeof request.headers?.wait === "boolean" ? request.headers.wait : undefined;
1317
- const dataStore = request.headers?.externalRequest && this.garbageCollector.shouldRunGC
1318
- ? await this.getDataStoreIfInitiallyReferenced(id, wait)
1319
- : await this.getDataStore(id, wait);
1357
+ const dataStore = await this.getDataStoreFromRequest(id, request);
1320
1358
  const subRequest = requestParser.createSubRequest(1);
1321
1359
  // We always expect createSubRequest to include a leading slash, but asserting here to protect against
1322
1360
  // unintentionally modifying the url if that changes.
@@ -1331,6 +1369,36 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1331
1369
  }
1332
1370
  }
1333
1371
 
1372
+ private async getDataStoreFromRequest(id: string, request: IRequest): Promise<IFluidRouter> {
1373
+ const wait = typeof request.headers?.[RuntimeHeaders.wait] === "boolean"
1374
+ ? request.headers?.[RuntimeHeaders.wait]
1375
+ : true;
1376
+ const dataStoreContext = await this.dataStores.getDataStore(id, wait);
1377
+
1378
+ /**
1379
+ * If GC should run and this an external app request with "externalRequest" header, we need to return
1380
+ * an error if the data store being requested is marked as unreferenced as per the data store's base
1381
+ * GC data.
1382
+ *
1383
+ * This is a workaround to handle scenarios where a data store shared with an external app is deleted
1384
+ * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
1385
+ */
1386
+ if (request.headers?.[RuntimeHeaders.externalRequest] && this.garbageCollector.shouldRunGC) {
1387
+ // The data store is referenced if used routes in the base summary has a route to self.
1388
+ // Older documents may not have used routes in the summary. They are considered referenced.
1389
+ const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
1390
+ if (!(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))) {
1391
+ throw responseToException(create404Response(request), request);
1392
+ }
1393
+ }
1394
+
1395
+ const dataStoreChannel = await dataStoreContext.realize();
1396
+ // Let the garbage collector know that a data store was requested / loaded. Realize the data store first so
1397
+ // that the package path is available.
1398
+ this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", dataStoreContext.packagePath, request?.headers);
1399
+ return dataStoreChannel;
1400
+ }
1401
+
1334
1402
  private formMetadata(): IContainerRuntimeMetadata {
1335
1403
  return {
1336
1404
  ...this.createContainerMetadata,
@@ -1345,43 +1413,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1345
1413
  };
1346
1414
  }
1347
1415
 
1348
- /**
1349
- * Retrieves the runtime for a data store if it's referenced as per the initially summary that it is loaded with.
1350
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted and marked
1351
- * as unreferenced by GC.
1352
- * @param id - Id supplied during creating the data store.
1353
- * @param wait - True if you want to wait for it.
1354
- * @returns the data store runtime if the data store exists and is initially referenced; undefined otherwise.
1355
- */
1356
- private async getDataStoreIfInitiallyReferenced(id: string, wait = true): Promise<IFluidRouter> {
1357
- const dataStoreContext = await this.dataStores.getDataStore(id, wait);
1358
- // The data store is referenced if used routes in the initial summary has a route to self.
1359
- // Older documents may not have used routes in the summary. They are considered referenced.
1360
- const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
1361
- if (usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/")) {
1362
- return dataStoreContext.realize();
1363
- }
1364
-
1365
- // The data store is unreferenced. Throw a 404 response exception.
1366
- const request = { url: id };
1367
- throw responseToException(create404Response(request), request);
1368
- }
1369
-
1370
- /**
1371
- * Notifies this object to take the snapshot of the container.
1372
- * @deprecated - Use summarize to get summary of the container runtime.
1373
- */
1374
- public async snapshot(): Promise<ITree> {
1375
- const summaryResult = await this.summarize({
1376
- summaryLogger: this.logger,
1377
- fullTree: true,
1378
- trackState: false,
1379
- runGC: this.garbageCollector.shouldRunGC,
1380
- fullGC: true,
1381
- });
1382
- return convertSummaryTreeToITree(summaryResult.summary);
1383
- }
1384
-
1385
1416
  private addContainerStateToSummary(summaryTree: ISummaryTreeWithStats) {
1386
1417
  addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(this.formMetadata()));
1387
1418
  if (this.chunkMap.size > 0) {
@@ -1595,10 +1626,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1595
1626
  return context.realize();
1596
1627
  }
1597
1628
 
1598
- protected async getDataStore(id: string, wait = true): Promise<IFluidRouter> {
1599
- return (await this.dataStores.getDataStore(id, wait)).realize();
1600
- }
1601
-
1602
1629
  public setFlushMode(mode: FlushMode): void {
1603
1630
  if (mode === this._flushMode) {
1604
1631
  return;
@@ -1676,16 +1703,70 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1676
1703
  }
1677
1704
  }
1678
1705
 
1679
- public async createDataStore(pkg: string | string[]): Promise<IFluidRouter> {
1680
- return this._createDataStore(pkg, false /* isRoot */);
1706
+ public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
1707
+ const internalId = uuid();
1708
+ return channelToDataStore(
1709
+ await this._createDataStore(pkg, false /* isRoot */, internalId),
1710
+ internalId,
1711
+ this,
1712
+ this.dataStores,
1713
+ this.mc.logger);
1681
1714
  }
1682
1715
 
1683
- public async createRootDataStore(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
1716
+ /**
1717
+ * Creates a root datastore directly with a user generated id and attaches it to storage.
1718
+ * It is vulnerable to name collisions and should not be used.
1719
+ *
1720
+ * This method will be removed. See #6465.
1721
+ */
1722
+ private async createRootDataStoreLegacy(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
1684
1723
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1685
1724
  fluidDataStore.bindToContext();
1686
1725
  return fluidDataStore;
1687
1726
  }
1688
1727
 
1728
+ public async createRootDataStore(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
1729
+ return this._aliasingEnabled === true ?
1730
+ this.createAndAliasDataStore(pkg, rootDataStoreId) :
1731
+ this.createRootDataStoreLegacy(pkg, rootDataStoreId);
1732
+ }
1733
+
1734
+ /**
1735
+ * Creates a data store then attempts to alias it.
1736
+ * If aliasing fails, it will raise an exception.
1737
+ *
1738
+ * This method will be removed. See #6465.
1739
+ *
1740
+ * @param pkg - Package name of the data store
1741
+ * @param alias - Alias to be assigned to the data store
1742
+ * @param props - Properties for the data store
1743
+ * @returns - An aliased data store which can can be found / loaded by alias.
1744
+ */
1745
+ private async createAndAliasDataStore(pkg: string | string[], alias: string, props?: any): Promise<IFluidRouter> {
1746
+ const internalId = uuid();
1747
+ const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
1748
+ const aliasedDataStore = channelToDataStore(dataStore, internalId, this,this.dataStores, this.mc.logger);
1749
+ const result = await aliasedDataStore.trySetAlias(alias);
1750
+ if (result !== AliasResult.Success) {
1751
+ throw new GenericError(
1752
+ "dataStoreAliasFailure",
1753
+ undefined /* error */,
1754
+ {
1755
+ alias: {
1756
+ value: alias,
1757
+ tag: TelemetryDataTag.UserData,
1758
+ },
1759
+ internalId: {
1760
+ value: internalId,
1761
+ tag: TelemetryDataTag.PackageData,
1762
+ },
1763
+ aliasResult: result,
1764
+ });
1765
+ }
1766
+
1767
+ return aliasedDataStore;
1768
+ }
1769
+
1689
1770
  public createDetachedRootDataStore(
1690
1771
  pkg: Readonly<string[]>,
1691
1772
  rootDataStoreId: string): IFluidDataStoreContextDetached
@@ -1697,7 +1778,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1697
1778
  return this.dataStores.createDetachedDataStoreCore(pkg, false);
1698
1779
  }
1699
1780
 
1700
- public async _createDataStoreWithProps(
1781
+ /**
1782
+ * Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
1783
+ * It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
1784
+ *
1785
+ * This method will be removed. See #6465.
1786
+ */
1787
+ private async _createDataStoreWithPropsLegacy(
1701
1788
  pkg: string | string[],
1702
1789
  props?: any,
1703
1790
  id = uuid(),
@@ -1711,12 +1798,26 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1711
1798
  return fluidDataStore;
1712
1799
  }
1713
1800
 
1801
+ public async _createDataStoreWithProps(
1802
+ pkg: string | string[],
1803
+ props?: any,
1804
+ id = uuid(),
1805
+ isRoot = false,
1806
+ ): Promise<IFluidRouter> {
1807
+ return this._aliasingEnabled === true && isRoot ?
1808
+ this.createAndAliasDataStore(pkg, id, props) :
1809
+ this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
1810
+ }
1811
+
1714
1812
  private async _createDataStore(
1715
1813
  pkg: string | string[],
1716
1814
  isRoot: boolean,
1717
1815
  id = uuid(),
1816
+ props?: any,
1718
1817
  ): Promise<IFluidDataStoreChannel> {
1719
- return this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot).realize();
1818
+ return this.dataStores
1819
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
1820
+ .realize();
1720
1821
  }
1721
1822
 
1722
1823
  private canSendOps() {
@@ -1782,6 +1883,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1782
1883
  0x12e /* "Container Context should already be in attached state" */);
1783
1884
  this.emit("attached");
1784
1885
  }
1886
+
1887
+ if (attachState === AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
1888
+ this.updateDocumentDirtyState(false);
1889
+ }
1785
1890
  this.dataStores.setAttachState(attachState);
1786
1891
  }
1787
1892
 
@@ -1837,32 +1942,40 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1837
1942
  * Returns a summary of the runtime at the current sequence number.
1838
1943
  */
1839
1944
  public async summarize(options: {
1840
- /** Logger to use for correlated summary events */
1841
- summaryLogger: ITelemetryLogger,
1842
1945
  /** True to generate the full tree with no handle reuse optimizations; defaults to false */
1843
1946
  fullTree?: boolean,
1844
1947
  /** True to track the state for this summary in the SummarizerNodes; defaults to true */
1845
1948
  trackState?: boolean,
1949
+ /** Logger to use for correlated summary events */
1950
+ summaryLogger?: ITelemetryLogger,
1846
1951
  /** True to run garbage collection before summarizing; defaults to true */
1847
1952
  runGC?: boolean,
1848
1953
  /** True to generate full GC data */
1849
1954
  fullGC?: boolean,
1850
1955
  /** True to run GC sweep phase after the mark phase */
1851
1956
  runSweep?: boolean,
1852
- }): Promise<ISummaryTreeWithStats> {
1957
+ }): Promise<IRootSummaryTreeWithStats> {
1853
1958
  this.verifyNotClosed();
1854
1959
 
1855
- const { summaryLogger, fullTree = false, trackState = true, runGC = true, runSweep, fullGC } = options;
1856
-
1960
+ const {
1961
+ fullTree = false,
1962
+ trackState = true,
1963
+ summaryLogger = this.logger,
1964
+ runGC = this.garbageCollector.shouldRunGC,
1965
+ runSweep,
1966
+ fullGC,
1967
+ } = options;
1968
+
1969
+ let gcStats: IGCStats | undefined;
1857
1970
  if (runGC) {
1858
- await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1971
+ gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1859
1972
  }
1860
1973
 
1861
1974
  const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
1862
1975
  assert(summarizeResult.summary.type === SummaryType.Tree,
1863
1976
  0x12f /* "Container Runtime's summarize should always return a tree" */);
1864
1977
 
1865
- return summarizeResult as ISummaryTreeWithStats;
1978
+ return { ...summarizeResult, gcStats } as IRootSummaryTreeWithStats;
1866
1979
  }
1867
1980
 
1868
1981
  /**
@@ -1890,9 +2003,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1890
2003
  * @param usedRoutes - The routes that are used in all nodes in this Container.
1891
2004
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
1892
2005
  * unreferenced as part of this GC run, this should be used to update the time when it happens.
1893
- * @returns the statistics of the used state of the data stores.
1894
2006
  */
1895
- public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): IUsedStateStats {
2007
+ public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number) {
1896
2008
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
1897
2009
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1898
2010
  // always referenced, so the used routes is only self-route (empty string).
@@ -2008,15 +2120,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2008
2120
  }
2009
2121
 
2010
2122
  const trace = Trace.start();
2011
- let summarizeResult: ISummaryTreeWithStats;
2123
+ let summarizeResult: IRootSummaryTreeWithStats;
2012
2124
  // If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
2013
2125
  // state of all the nodes.
2014
2126
  const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
2015
2127
  try {
2016
2128
  summarizeResult = await this.summarize({
2017
- summaryLogger,
2018
2129
  fullTree: fullTree || forcedFullTree,
2019
2130
  trackState: true,
2131
+ summaryLogger,
2020
2132
  runGC: this.garbageCollector.shouldRunGC,
2021
2133
  });
2022
2134
  } catch (error) {
@@ -2036,6 +2148,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2036
2148
  const summaryStats: IGeneratedSummaryStats = {
2037
2149
  dataStoreCount: this.dataStores.size,
2038
2150
  summarizedDataStoreCount: this.dataStores.size - handleCount,
2151
+ gcStateUpdatedDataStoreCount: summarizeResult.gcStats?.updatedDataStoreCount,
2039
2152
  ...partialStats,
2040
2153
  };
2041
2154
  const generateSummaryData = {
@@ -2176,6 +2289,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2176
2289
  this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
2177
2290
  }
2178
2291
 
2292
+ public submitDataStoreAliasOp(contents: any, localOpMetadata: unknown): void {
2293
+ const aliasMessage = contents as IDataStoreAliasMessage;
2294
+ if (!isDataStoreAliasMessage(aliasMessage)) {
2295
+ throw new UsageError("malformedDataStoreAliasMessage");
2296
+ }
2297
+
2298
+ this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
2299
+ }
2300
+
2179
2301
  public async uploadBlob(blob: ArrayBufferLike): Promise<IFluidHandle<ArrayBufferLike>> {
2180
2302
  this.verifyNotClosed();
2181
2303
  return this.blobManager.createBlob(blob);
@@ -2404,20 +2526,25 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2404
2526
 
2405
2527
  private async fetchSnapshotFromStorage(
2406
2528
  versionId: string | null, logger: ITelemetryLogger, event: ITelemetryGenericEvent) {
2407
- return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
2408
- const stats: { getVersionDuration?: number; getSnapshotDuration?: number } = {};
2409
- const trace = Trace.start();
2410
-
2411
- const versions = await this.storage.getVersions(versionId, 1);
2412
- assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
2413
- stats.getVersionDuration = trace.trace().duration;
2414
-
2415
- const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
2416
- assert(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
2417
- stats.getSnapshotDuration = trace.trace().duration;
2418
-
2419
- perfEvent.end(stats);
2420
- return maybeSnapshot;
2529
+ return PerformanceEvent.timedExecAsync(
2530
+ logger, event, async (perfEvent: {
2531
+ end: (arg0: {
2532
+ getVersionDuration?: number | undefined;
2533
+ getSnapshotDuration?: number | undefined;
2534
+ }) => void; }) => {
2535
+ const stats: { getVersionDuration?: number; getSnapshotDuration?: number } = {};
2536
+ const trace = Trace.start();
2537
+
2538
+ const versions = await this.storage.getVersions(versionId, 1);
2539
+ assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
2540
+ stats.getVersionDuration = trace.trace().duration;
2541
+
2542
+ const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
2543
+ assert(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
2544
+ stats.getSnapshotDuration = trace.trace().duration;
2545
+
2546
+ perfEvent.end(stats);
2547
+ return maybeSnapshot;
2421
2548
  });
2422
2549
  }
2423
2550