@fluidframework/container-runtime 0.56.5 → 0.57.0

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 (95) 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 +67 -26
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +146 -87
  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 +66 -27
  21. package/dist/garbageCollection.d.ts.map +1 -1
  22. package/dist/garbageCollection.js +272 -97
  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.js +1 -1
  30. package/dist/packageVersion.js.map +1 -1
  31. package/dist/runningSummarizer.d.ts +1 -0
  32. package/dist/runningSummarizer.d.ts.map +1 -1
  33. package/dist/runningSummarizer.js +23 -15
  34. package/dist/runningSummarizer.js.map +1 -1
  35. package/dist/summarizerTypes.d.ts +4 -6
  36. package/dist/summarizerTypes.d.ts.map +1 -1
  37. package/dist/summarizerTypes.js.map +1 -1
  38. package/dist/summaryGenerator.d.ts +2 -1
  39. package/dist/summaryGenerator.d.ts.map +1 -1
  40. package/dist/summaryGenerator.js +46 -29
  41. package/dist/summaryGenerator.js.map +1 -1
  42. package/lib/blobManager.d.ts.map +1 -1
  43. package/lib/blobManager.js +9 -1
  44. package/lib/blobManager.js.map +1 -1
  45. package/lib/connectionTelemetry.d.ts.map +1 -1
  46. package/lib/connectionTelemetry.js +6 -6
  47. package/lib/connectionTelemetry.js.map +1 -1
  48. package/lib/containerRuntime.d.ts +67 -26
  49. package/lib/containerRuntime.d.ts.map +1 -1
  50. package/lib/containerRuntime.js +147 -88
  51. package/lib/containerRuntime.js.map +1 -1
  52. package/lib/dataStore.d.ts +62 -0
  53. package/lib/dataStore.d.ts.map +1 -0
  54. package/lib/dataStore.js +130 -0
  55. package/lib/dataStore.js.map +1 -0
  56. package/lib/dataStoreContext.js.map +1 -1
  57. package/lib/dataStores.d.ts +9 -5
  58. package/lib/dataStores.d.ts.map +1 -1
  59. package/lib/dataStores.js +13 -18
  60. package/lib/dataStores.js.map +1 -1
  61. package/lib/garbageCollection.d.ts +66 -27
  62. package/lib/garbageCollection.d.ts.map +1 -1
  63. package/lib/garbageCollection.js +274 -99
  64. package/lib/garbageCollection.js.map +1 -1
  65. package/lib/index.d.ts +3 -2
  66. package/lib/index.d.ts.map +1 -1
  67. package/lib/index.js +2 -1
  68. package/lib/index.js.map +1 -1
  69. package/lib/packageVersion.d.ts +1 -1
  70. package/lib/packageVersion.js +1 -1
  71. package/lib/packageVersion.js.map +1 -1
  72. package/lib/runningSummarizer.d.ts +1 -0
  73. package/lib/runningSummarizer.d.ts.map +1 -1
  74. package/lib/runningSummarizer.js +23 -15
  75. package/lib/runningSummarizer.js.map +1 -1
  76. package/lib/summarizerTypes.d.ts +4 -6
  77. package/lib/summarizerTypes.d.ts.map +1 -1
  78. package/lib/summarizerTypes.js.map +1 -1
  79. package/lib/summaryGenerator.d.ts +2 -1
  80. package/lib/summaryGenerator.d.ts.map +1 -1
  81. package/lib/summaryGenerator.js +46 -29
  82. package/lib/summaryGenerator.js.map +1 -1
  83. package/package.json +13 -13
  84. package/src/blobManager.ts +12 -1
  85. package/src/connectionTelemetry.ts +7 -6
  86. package/src/containerRuntime.ts +244 -113
  87. package/src/dataStore.ts +187 -0
  88. package/src/dataStoreContext.ts +1 -1
  89. package/src/dataStores.ts +23 -38
  90. package/src/garbageCollection.ts +385 -150
  91. package/src/index.ts +3 -1
  92. package/src/packageVersion.ts +1 -1
  93. package/src/runningSummarizer.ts +25 -16
  94. package/src/summarizerTypes.ts +4 -8
  95. package/src/summaryGenerator.ts +71 -23
@@ -4,13 +4,13 @@
4
4
  */
5
5
  import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
6
6
  import { assert, Trace, TypedEventEmitter, unreachableCase, performance, } from "@fluidframework/common-utils";
7
- import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
7
+ import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, loggerToMonitoringContext, TelemetryDataTag, } from "@fluidframework/telemetry-utils";
8
8
  import { DriverHeader } from "@fluidframework/driver-definitions";
9
9
  import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
10
10
  import { DataCorruptionError, GenericError, UsageError, extractSafePropertiesFromMessage, } from "@fluidframework/container-utils";
11
11
  import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
12
12
  import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
13
- import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, convertSummaryTreeToITree, } from "@fluidframework/runtime-utils";
13
+ import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, } from "@fluidframework/runtime-utils";
14
14
  import { v4 as uuid } from "uuid";
15
15
  import { ContainerFluidHandleContext } from "./containerHandleContext";
16
16
  import { FluidDataStoreRegistry } from "./dataStoreRegistry";
@@ -29,6 +29,7 @@ import { SummarizerClientElection, summarizerClientType } from "./summarizerClie
29
29
  import { formExponentialFn, Throttler } from "./throttler";
30
30
  import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
31
31
  import { GarbageCollector, gcTreeKey, } from "./garbageCollection";
32
+ import { AliasResult, channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
32
33
  export var ContainerMessageType;
33
34
  (function (ContainerMessageType) {
34
35
  // An op to be delivered to store
@@ -56,9 +57,24 @@ const DefaultSummaryConfiguration = {
56
57
  // the min of the two will be chosen
57
58
  maxAckWaitTime: 120000,
58
59
  };
59
- ;
60
+ /**
61
+ * Accepted header keys for requests coming to the runtime.
62
+ */
63
+ export var RuntimeHeaders;
64
+ (function (RuntimeHeaders) {
65
+ /** True to wait for a data store to be created and loaded before returning it. */
66
+ RuntimeHeaders["wait"] = "wait";
67
+ /**
68
+ * True if the request is from an external app. Used for GC to handle scenarios where a data store
69
+ * is deleted and requested via an external app.
70
+ */
71
+ RuntimeHeaders["externalRequest"] = "externalRequest";
72
+ /** True if the request is coming from an IFluidHandle. */
73
+ RuntimeHeaders["viaHandle"] = "viaHandle";
74
+ })(RuntimeHeaders || (RuntimeHeaders = {}));
60
75
  // Local storage key to set the default flush mode to TurnBased
61
76
  const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
77
+ const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
62
78
  export var RuntimeMessage;
63
79
  (function (RuntimeMessage) {
64
80
  RuntimeMessage["FluidDataStoreOp"] = "component";
@@ -332,7 +348,7 @@ export function getDeviceSpec() {
332
348
  */
333
349
  export class ContainerRuntime extends TypedEventEmitter {
334
350
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
335
- var _a, _b, _c, _d, _e;
351
+ var _a, _b, _c, _d, _e, _f, _g, _h;
336
352
  super();
337
353
  this.context = context;
338
354
  this.registry = registry;
@@ -346,7 +362,6 @@ export class ContainerRuntime extends TypedEventEmitter {
346
362
  this.flushTrigger = false;
347
363
  this.paused = false;
348
364
  this._disposed = false;
349
- this.dirtyContainer = false;
350
365
  this.emitDirtyDocumentEvent = true;
351
366
  this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
352
367
  /**
@@ -398,7 +413,7 @@ export class ContainerRuntime extends TypedEventEmitter {
398
413
  throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
399
414
  }
400
415
  };
401
- this.baseSummaryMessage = metadata === null || metadata === void 0 ? void 0 : metadata.message;
416
+ this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
402
417
  // If this is an existing container, we get values from metadata.
403
418
  // otherwise, we initialize them.
404
419
  if (existing) {
@@ -422,20 +437,15 @@ export class ContainerRuntime extends TypedEventEmitter {
422
437
  this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
423
438
  this._flushMode =
424
439
  ((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? FlushMode.TurnBased : FlushMode.Immediate;
440
+ this._aliasingEnabled =
441
+ ((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
442
+ ((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
443
+ this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
425
444
  /**
426
- * Function that return the current server timestamp. This is used by the garbage collector to set the
427
- * time when a node becomes unreferenced.
428
- * We use the timestamp of the last op for current timestamp. However, there can be cases where
429
- * we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
430
- * of this client's connection.
445
+ * Returns the timestamp of the last message seen by this client. This is used by garbage collector as
446
+ * the current reference timestamp for tracking unreferenced objects.
431
447
  */
432
- const getCurrentTimestamp = () => {
433
- var _a, _b, _c;
434
- const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
435
- const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
436
- return (_c = (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : timestamp) !== null && _c !== void 0 ? _c : Date.now();
437
- };
438
- this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
448
+ () => { var _a, _b, _c; return (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : (_c = this.messageAtLastSummary) === null || _c === void 0 ? void 0 : _c.timestamp; }, () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; }, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
439
449
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
440
450
  this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
441
451
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
@@ -456,7 +466,7 @@ export class ContainerRuntime extends TypedEventEmitter {
456
466
  if (this.context.baseSnapshot) {
457
467
  this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
458
468
  }
459
- this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getDataStoreBaseGCDetails(), (id) => this.garbageCollector.nodeChanged(id), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
469
+ this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getDataStoreBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
460
470
  this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
461
471
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
462
472
  this.deltaSender = this.deltaManager;
@@ -465,6 +475,10 @@ export class ContainerRuntime extends TypedEventEmitter {
465
475
  this.clearPartialChunks(clientId);
466
476
  });
467
477
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
478
+ const { attachState, pendingLocalState } = this.context;
479
+ this.dirtyContainer = attachState !== AttachState.Attached
480
+ || ((_e = pendingLocalState) === null || _e === void 0 ? void 0 : _e.pendingStates.length) > 0;
481
+ this.context.updateDirtyContainerState(this.dirtyContainer);
468
482
  // Map the deprecated generateSummaries flag to disableSummaries.
469
483
  if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
470
484
  this.runtimeOptions.summaryOptions.disableSummaries = true;
@@ -476,8 +490,8 @@ export class ContainerRuntime extends TypedEventEmitter {
476
490
  const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
477
491
  const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
478
492
  const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
479
- const summarizerClientElectionEnabled = (_c = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _c !== void 0 ? _c : ((_d = this.runtimeOptions.summaryOptions) === null || _d === void 0 ? void 0 : _d.summarizerClientElection) === true;
480
- const maxOpsSinceLastSummary = (_e = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _e !== void 0 ? _e : 7000;
493
+ const summarizerClientElectionEnabled = (_f = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _f !== void 0 ? _f : ((_g = this.runtimeOptions.summaryOptions) === null || _g === void 0 ? void 0 : _g.summarizerClientElection) === true;
494
+ const maxOpsSinceLastSummary = (_h = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _h !== void 0 ? _h : 7000;
481
495
  this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
482
496
  if (this.context.clientDetails.type === summarizerClientType) {
483
497
  this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
@@ -561,7 +575,7 @@ export class ContainerRuntime extends TypedEventEmitter {
561
575
  runtimeVersion: pkgVersion,
562
576
  },
563
577
  });
564
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", } = runtimeOptions;
578
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
565
579
  // We pack at data store level only. If isolated channels are disabled,
566
580
  // then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
567
581
  const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
@@ -629,6 +643,7 @@ export class ContainerRuntime extends TypedEventEmitter {
629
643
  summaryOptions,
630
644
  gcOptions,
631
645
  loadSequenceNumberVerification,
646
+ useDataStoreAliasing,
632
647
  }, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
633
648
  return runtime;
634
649
  }
@@ -768,7 +783,6 @@ export class ContainerRuntime extends TypedEventEmitter {
768
783
  * @param request - Request made to the handler.
769
784
  */
770
785
  async resolveHandle(request) {
771
- var _a, _b;
772
786
  try {
773
787
  const requestParser = RequestParser.create(request);
774
788
  const id = requestParser.pathParts[0];
@@ -789,18 +803,7 @@ export class ContainerRuntime extends TypedEventEmitter {
789
803
  }
790
804
  }
791
805
  else if (requestParser.pathParts.length > 0) {
792
- /**
793
- * If GC should run and this an external app request with "externalRequest" header, we need to return
794
- * an error if the data store being requested is marked as unreferenced as per the data store's initial
795
- * summary.
796
- *
797
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted
798
- * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
799
- */
800
- const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a.wait) === "boolean" ? request.headers.wait : undefined;
801
- const dataStore = ((_b = request.headers) === null || _b === void 0 ? void 0 : _b.externalRequest) && this.garbageCollector.shouldRunGC
802
- ? await this.getDataStoreIfInitiallyReferenced(id, wait)
803
- : await this.getDataStore(id, wait);
806
+ const dataStore = await this.getDataStoreFromRequest(id, request);
804
807
  const subRequest = requestParser.createSubRequest(1);
805
808
  // We always expect createSubRequest to include a leading slash, but asserting here to protect against
806
809
  // unintentionally modifying the url if that changes.
@@ -813,46 +816,37 @@ export class ContainerRuntime extends TypedEventEmitter {
813
816
  return exceptionToResponse(error);
814
817
  }
815
818
  }
816
- formMetadata() {
817
- var _a;
818
- return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
819
- // The last message processed at the time of summary. If there are no messages, nothing has changed from
820
- // the base summary we loaded from. So, use the message from its metadata blob.
821
- message: (_a = extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.baseSummaryMessage, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
822
- }
823
- /**
824
- * Retrieves the runtime for a data store if it's referenced as per the initially summary that it is loaded with.
825
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted and marked
826
- * as unreferenced by GC.
827
- * @param id - Id supplied during creating the data store.
828
- * @param wait - True if you want to wait for it.
829
- * @returns the data store runtime if the data store exists and is initially referenced; undefined otherwise.
830
- */
831
- async getDataStoreIfInitiallyReferenced(id, wait = true) {
819
+ async getDataStoreFromRequest(id, request) {
820
+ var _a, _b, _c;
821
+ const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
822
+ ? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait] : true;
832
823
  const dataStoreContext = await this.dataStores.getDataStore(id, wait);
833
- // The data store is referenced if used routes in the initial summary has a route to self.
834
- // Older documents may not have used routes in the summary. They are considered referenced.
835
- const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
836
- if (usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/")) {
837
- return dataStoreContext.realize();
824
+ /**
825
+ * If GC should run and this an external app request with "externalRequest" header, we need to return
826
+ * an error if the data store being requested is marked as unreferenced as per the data store's base
827
+ * GC data.
828
+ *
829
+ * This is a workaround to handle scenarios where a data store shared with an external app is deleted
830
+ * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
831
+ */
832
+ if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
833
+ // The data store is referenced if used routes in the base summary has a route to self.
834
+ // Older documents may not have used routes in the summary. They are considered referenced.
835
+ const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
836
+ if (!(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))) {
837
+ throw responseToException(create404Response(request), request);
838
+ }
838
839
  }
839
- // The data store is unreferenced. Throw a 404 response exception.
840
- const request = { url: id };
841
- throw responseToException(create404Response(request), request);
840
+ const dataStoreChannel = await dataStoreContext.realize();
841
+ this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request === null || request === void 0 ? void 0 : request.headers);
842
+ return dataStoreChannel;
842
843
  }
843
- /**
844
- * Notifies this object to take the snapshot of the container.
845
- * @deprecated - Use summarize to get summary of the container runtime.
846
- */
847
- async snapshot() {
848
- const summaryResult = await this.summarize({
849
- summaryLogger: this.logger,
850
- fullTree: true,
851
- trackState: false,
852
- runGC: this.garbageCollector.shouldRunGC,
853
- fullGC: true,
854
- });
855
- return convertSummaryTreeToITree(summaryResult.summary);
844
+ formMetadata() {
845
+ var _a;
846
+ return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
847
+ // The last message processed at the time of summary. If there are no new messages, use the message from the
848
+ // last summary.
849
+ message: (_a = extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
856
850
  }
857
851
  addContainerStateToSummary(summaryTree) {
858
852
  var _a;
@@ -1015,9 +1009,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1015
1009
  assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
1016
1010
  return context.realize();
1017
1011
  }
1018
- async getDataStore(id, wait = true) {
1019
- return (await this.dataStores.getDataStore(id, wait)).realize();
1020
- }
1021
1012
  setFlushMode(mode) {
1022
1013
  if (mode === this._flushMode) {
1023
1014
  return;
@@ -1082,28 +1073,84 @@ export class ContainerRuntime extends TypedEventEmitter {
1082
1073
  }
1083
1074
  }
1084
1075
  async createDataStore(pkg) {
1085
- return this._createDataStore(pkg, false /* isRoot */);
1076
+ const internalId = uuid();
1077
+ return channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
1086
1078
  }
1087
- async createRootDataStore(pkg, rootDataStoreId) {
1079
+ /**
1080
+ * Creates a root datastore directly with a user generated id and attaches it to storage.
1081
+ * It is vulnerable to name collisions and should not be used.
1082
+ *
1083
+ * This method will be removed. See #6465.
1084
+ */
1085
+ async createRootDataStoreLegacy(pkg, rootDataStoreId) {
1088
1086
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1089
1087
  fluidDataStore.bindToContext();
1090
1088
  return fluidDataStore;
1091
1089
  }
1090
+ async createRootDataStore(pkg, rootDataStoreId) {
1091
+ return this._aliasingEnabled === true ?
1092
+ this.createAndAliasDataStore(pkg, rootDataStoreId) :
1093
+ this.createRootDataStoreLegacy(pkg, rootDataStoreId);
1094
+ }
1095
+ /**
1096
+ * Creates a data store then attempts to alias it.
1097
+ * If aliasing fails, it will raise an exception.
1098
+ *
1099
+ * This method will be removed. See #6465.
1100
+ *
1101
+ * @param pkg - Package name of the data store
1102
+ * @param alias - Alias to be assigned to the data store
1103
+ * @param props - Properties for the data store
1104
+ * @returns - An aliased data store which can can be found / loaded by alias.
1105
+ */
1106
+ async createAndAliasDataStore(pkg, alias, props) {
1107
+ const internalId = uuid();
1108
+ const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
1109
+ const aliasedDataStore = channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
1110
+ const result = await aliasedDataStore.trySetAlias(alias);
1111
+ if (result !== AliasResult.Success) {
1112
+ throw new GenericError("dataStoreAliasFailure", undefined /* error */, {
1113
+ alias: {
1114
+ value: alias,
1115
+ tag: TelemetryDataTag.UserData,
1116
+ },
1117
+ internalId: {
1118
+ value: internalId,
1119
+ tag: TelemetryDataTag.PackageData,
1120
+ },
1121
+ aliasResult: result,
1122
+ });
1123
+ }
1124
+ return aliasedDataStore;
1125
+ }
1092
1126
  createDetachedRootDataStore(pkg, rootDataStoreId) {
1093
1127
  return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
1094
1128
  }
1095
1129
  createDetachedDataStore(pkg) {
1096
1130
  return this.dataStores.createDetachedDataStoreCore(pkg, false);
1097
1131
  }
1098
- async _createDataStoreWithProps(pkg, props, id = uuid(), isRoot = false) {
1132
+ /**
1133
+ * Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
1134
+ * It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
1135
+ *
1136
+ * This method will be removed. See #6465.
1137
+ */
1138
+ async _createDataStoreWithPropsLegacy(pkg, props, id = uuid(), isRoot = false) {
1099
1139
  const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
1100
1140
  if (isRoot) {
1101
1141
  fluidDataStore.bindToContext();
1102
1142
  }
1103
1143
  return fluidDataStore;
1104
1144
  }
1105
- async _createDataStore(pkg, isRoot, id = uuid()) {
1106
- return this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot).realize();
1145
+ async _createDataStoreWithProps(pkg, props, id = uuid(), isRoot = false) {
1146
+ return this._aliasingEnabled === true && isRoot ?
1147
+ this.createAndAliasDataStore(pkg, id, props) :
1148
+ this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
1149
+ }
1150
+ async _createDataStore(pkg, isRoot, id = uuid(), props) {
1151
+ return this.dataStores
1152
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
1153
+ .realize();
1107
1154
  }
1108
1155
  canSendOps() {
1109
1156
  return this.connected && !this.deltaManager.readOnlyInfo.readonly;
@@ -1161,6 +1208,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1161
1208
  assert(this.attachState === AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
1162
1209
  this.emit("attached");
1163
1210
  }
1211
+ if (attachState === AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
1212
+ this.updateDocumentDirtyState(false);
1213
+ }
1164
1214
  this.dataStores.setAttachState(attachState);
1165
1215
  }
1166
1216
  /**
@@ -1207,13 +1257,14 @@ export class ContainerRuntime extends TypedEventEmitter {
1207
1257
  */
1208
1258
  async summarize(options) {
1209
1259
  this.verifyNotClosed();
1210
- const { summaryLogger, fullTree = false, trackState = true, runGC = true, runSweep, fullGC } = options;
1260
+ const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
1261
+ let gcStats;
1211
1262
  if (runGC) {
1212
- await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1263
+ gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1213
1264
  }
1214
1265
  const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
1215
1266
  assert(summarizeResult.summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1216
- return summarizeResult;
1267
+ return Object.assign(Object.assign({}, summarizeResult), { gcStats });
1217
1268
  }
1218
1269
  /**
1219
1270
  * Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
@@ -1238,7 +1289,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1238
1289
  * @param usedRoutes - The routes that are used in all nodes in this Container.
1239
1290
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
1240
1291
  * unreferenced as part of this GC run, this should be used to update the time when it happens.
1241
- * @returns the statistics of the used state of the data stores.
1242
1292
  */
1243
1293
  updateUsedRoutes(usedRoutes, gcTimestamp) {
1244
1294
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
@@ -1272,7 +1322,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1272
1322
  * @param options - options controlling how the summary is generated or submitted
1273
1323
  */
1274
1324
  async submitSummary(options) {
1275
- var _a;
1325
+ var _a, _b;
1276
1326
  const { fullTree, refreshLatestAck, summaryLogger } = options;
1277
1327
  if (refreshLatestAck) {
1278
1328
  const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
@@ -1336,9 +1386,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1336
1386
  const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
1337
1387
  try {
1338
1388
  summarizeResult = await this.summarize({
1339
- summaryLogger,
1340
1389
  fullTree: fullTree || forcedFullTree,
1341
1390
  trackState: true,
1391
+ summaryLogger,
1342
1392
  runGC: this.garbageCollector.shouldRunGC,
1343
1393
  });
1344
1394
  }
@@ -1346,13 +1396,15 @@ export class ContainerRuntime extends TypedEventEmitter {
1346
1396
  return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error };
1347
1397
  }
1348
1398
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
1399
+ // Now that we have generated the summary, update the message at last summary to the last message processed.
1400
+ this.messageAtLastSummary = this.deltaManager.lastMessage;
1349
1401
  // Counting dataStores and handles
1350
1402
  // Because handles are unchanged dataStores in the current logic,
1351
1403
  // summarized dataStore count is total dataStore count minus handle count
1352
1404
  const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[channelsTreeName];
1353
1405
  assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1354
1406
  const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === SummaryType.Handle).length;
1355
- const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
1407
+ const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_a = summarizeResult.gcStats) === null || _a === void 0 ? void 0 : _a.updatedDataStoreCount }, partialStats);
1356
1408
  const generateSummaryData = {
1357
1409
  referenceSequenceNumber: summaryRefSeqNum,
1358
1410
  summaryTree,
@@ -1368,7 +1420,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1368
1420
  const summaryContext = lastAck === undefined
1369
1421
  ? {
1370
1422
  proposalHandle: undefined,
1371
- ackHandle: (_a = this.context.getLoadedFromVersion()) === null || _a === void 0 ? void 0 : _a.id,
1423
+ ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
1372
1424
  referenceSequenceNumber: summaryRefSeqNum,
1373
1425
  }
1374
1426
  : {
@@ -1463,6 +1515,13 @@ export class ContainerRuntime extends TypedEventEmitter {
1463
1515
  };
1464
1516
  this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
1465
1517
  }
1518
+ submitDataStoreAliasOp(contents, localOpMetadata) {
1519
+ const aliasMessage = contents;
1520
+ if (!isDataStoreAliasMessage(aliasMessage)) {
1521
+ throw new UsageError("malformedDataStoreAliasMessage");
1522
+ }
1523
+ this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
1524
+ }
1466
1525
  async uploadBlob(blob) {
1467
1526
  this.verifyNotClosed();
1468
1527
  return this.blobManager.createBlob(blob);