@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,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.ContainerMessageType = void 0;
7
+ exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.ContainerMessageType = void 0;
8
8
  const container_definitions_1 = require("@fluidframework/container-definitions");
9
9
  const common_utils_1 = require("@fluidframework/common-utils");
10
10
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
@@ -32,6 +32,7 @@ const summarizerClientElection_1 = require("./summarizerClientElection");
32
32
  const throttler_1 = require("./throttler");
33
33
  const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator");
34
34
  const garbageCollection_1 = require("./garbageCollection");
35
+ const dataStore_1 = require("./dataStore");
35
36
  var ContainerMessageType;
36
37
  (function (ContainerMessageType) {
37
38
  // An op to be delivered to store
@@ -59,9 +60,24 @@ const DefaultSummaryConfiguration = {
59
60
  // the min of the two will be chosen
60
61
  maxAckWaitTime: 120000,
61
62
  };
62
- ;
63
+ /**
64
+ * Accepted header keys for requests coming to the runtime.
65
+ */
66
+ var RuntimeHeaders;
67
+ (function (RuntimeHeaders) {
68
+ /** True to wait for a data store to be created and loaded before returning it. */
69
+ RuntimeHeaders["wait"] = "wait";
70
+ /**
71
+ * True if the request is from an external app. Used for GC to handle scenarios where a data store
72
+ * is deleted and requested via an external app.
73
+ */
74
+ RuntimeHeaders["externalRequest"] = "externalRequest";
75
+ /** True if the request is coming from an IFluidHandle. */
76
+ RuntimeHeaders["viaHandle"] = "viaHandle";
77
+ })(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
63
78
  // Local storage key to set the default flush mode to TurnBased
64
79
  const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
80
+ const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
65
81
  var RuntimeMessage;
66
82
  (function (RuntimeMessage) {
67
83
  RuntimeMessage["FluidDataStoreOp"] = "component";
@@ -339,7 +355,7 @@ exports.getDeviceSpec = getDeviceSpec;
339
355
  */
340
356
  class ContainerRuntime extends common_utils_1.TypedEventEmitter {
341
357
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
342
- var _a, _b, _c, _d, _e;
358
+ var _a, _b, _c, _d, _e, _f, _g, _h;
343
359
  super();
344
360
  this.context = context;
345
361
  this.registry = registry;
@@ -353,7 +369,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
353
369
  this.flushTrigger = false;
354
370
  this.paused = false;
355
371
  this._disposed = false;
356
- this.dirtyContainer = false;
357
372
  this.emitDirtyDocumentEvent = true;
358
373
  this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
359
374
  /**
@@ -405,7 +420,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
405
420
  throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
406
421
  }
407
422
  };
408
- this.baseSummaryMessage = metadata === null || metadata === void 0 ? void 0 : metadata.message;
423
+ this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
409
424
  // If this is an existing container, we get values from metadata.
410
425
  // otherwise, we initialize them.
411
426
  if (existing) {
@@ -429,20 +444,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
429
444
  this.mc = telemetry_utils_1.loggerToMonitoringContext(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
430
445
  this._flushMode =
431
446
  ((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? runtime_definitions_1.FlushMode.TurnBased : runtime_definitions_1.FlushMode.Immediate;
447
+ this._aliasingEnabled =
448
+ ((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
449
+ ((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
450
+ this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
432
451
  /**
433
- * Function that return the current server timestamp. This is used by the garbage collector to set the
434
- * time when a node becomes unreferenced.
435
- * We use the timestamp of the last op for current timestamp. However, there can be cases where
436
- * we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
437
- * of this client's connection.
452
+ * Returns the timestamp of the last message seen by this client. This is used by garbage collector as
453
+ * the current reference timestamp for tracking unreferenced objects.
438
454
  */
439
- const getCurrentTimestamp = () => {
440
- var _a, _b, _c;
441
- const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
442
- const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
443
- 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();
444
- };
445
- this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
455
+ () => { 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) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
446
456
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
447
457
  this.summarizerNode = runtime_utils_1.createRootSummarizerNodeWithGC(telemetry_utils_1.ChildLogger.create(this.logger, "SummarizerNode"),
448
458
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
@@ -463,7 +473,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
463
473
  if (this.context.baseSnapshot) {
464
474
  this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
465
475
  }
466
- this.dataStores = new dataStores_1.DataStores(dataStores_1.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);
476
+ this.dataStores = new dataStores_1.DataStores(dataStores_1.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);
467
477
  this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
468
478
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
469
479
  this.deltaSender = this.deltaManager;
@@ -472,6 +482,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
472
482
  this.clearPartialChunks(clientId);
473
483
  });
474
484
  this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
485
+ const { attachState, pendingLocalState } = this.context;
486
+ this.dirtyContainer = attachState !== container_definitions_1.AttachState.Attached
487
+ || ((_e = pendingLocalState) === null || _e === void 0 ? void 0 : _e.pendingStates.length) > 0;
488
+ this.context.updateDirtyContainerState(this.dirtyContainer);
475
489
  // Map the deprecated generateSummaries flag to disableSummaries.
476
490
  if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
477
491
  this.runtimeOptions.summaryOptions.disableSummaries = true;
@@ -483,8 +497,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
483
497
  const orderedClientLogger = telemetry_utils_1.ChildLogger.create(this.logger, "OrderedClientElection");
484
498
  const orderedClientCollection = new orderedClientElection_1.OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
485
499
  const orderedClientElectionForSummarizer = new orderedClientElection_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, summarizerClientElection_1.SummarizerClientElection.isClientEligible);
486
- 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;
487
- const maxOpsSinceLastSummary = (_e = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _e !== void 0 ? _e : 7000;
500
+ 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;
501
+ const maxOpsSinceLastSummary = (_h = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _h !== void 0 ? _h : 7000;
488
502
  this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
489
503
  if (this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
490
504
  this._summarizer = new summarizer_1.Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => runWhileConnectedCoordinator_1.RunWhileConnectedCoordinator.create(runtime));
@@ -568,7 +582,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
568
582
  runtimeVersion: packageVersion_1.pkgVersion,
569
583
  },
570
584
  });
571
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", } = runtimeOptions;
585
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
572
586
  // We pack at data store level only. If isolated channels are disabled,
573
587
  // then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
574
588
  const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
@@ -636,6 +650,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
636
650
  summaryOptions,
637
651
  gcOptions,
638
652
  loadSequenceNumberVerification,
653
+ useDataStoreAliasing,
639
654
  }, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
640
655
  return runtime;
641
656
  }
@@ -775,7 +790,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
775
790
  * @param request - Request made to the handler.
776
791
  */
777
792
  async resolveHandle(request) {
778
- var _a, _b;
779
793
  try {
780
794
  const requestParser = runtime_utils_1.RequestParser.create(request);
781
795
  const id = requestParser.pathParts[0];
@@ -796,18 +810,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
796
810
  }
797
811
  }
798
812
  else if (requestParser.pathParts.length > 0) {
799
- /**
800
- * If GC should run and this an external app request with "externalRequest" header, we need to return
801
- * an error if the data store being requested is marked as unreferenced as per the data store's initial
802
- * summary.
803
- *
804
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted
805
- * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
806
- */
807
- const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a.wait) === "boolean" ? request.headers.wait : undefined;
808
- const dataStore = ((_b = request.headers) === null || _b === void 0 ? void 0 : _b.externalRequest) && this.garbageCollector.shouldRunGC
809
- ? await this.getDataStoreIfInitiallyReferenced(id, wait)
810
- : await this.getDataStore(id, wait);
813
+ const dataStore = await this.getDataStoreFromRequest(id, request);
811
814
  const subRequest = requestParser.createSubRequest(1);
812
815
  // We always expect createSubRequest to include a leading slash, but asserting here to protect against
813
816
  // unintentionally modifying the url if that changes.
@@ -820,46 +823,37 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
820
823
  return runtime_utils_1.exceptionToResponse(error);
821
824
  }
822
825
  }
823
- formMetadata() {
824
- var _a;
825
- return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
826
- // The last message processed at the time of summary. If there are no messages, nothing has changed from
827
- // the base summary we loaded from. So, use the message from its metadata blob.
828
- message: (_a = summaryFormat_1.extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.baseSummaryMessage, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
829
- }
830
- /**
831
- * Retrieves the runtime for a data store if it's referenced as per the initially summary that it is loaded with.
832
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted and marked
833
- * as unreferenced by GC.
834
- * @param id - Id supplied during creating the data store.
835
- * @param wait - True if you want to wait for it.
836
- * @returns the data store runtime if the data store exists and is initially referenced; undefined otherwise.
837
- */
838
- async getDataStoreIfInitiallyReferenced(id, wait = true) {
826
+ async getDataStoreFromRequest(id, request) {
827
+ var _a, _b, _c;
828
+ const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
829
+ ? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait] : true;
839
830
  const dataStoreContext = await this.dataStores.getDataStore(id, wait);
840
- // The data store is referenced if used routes in the initial summary has a route to self.
841
- // Older documents may not have used routes in the summary. They are considered referenced.
842
- const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
843
- if (usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/")) {
844
- return dataStoreContext.realize();
831
+ /**
832
+ * If GC should run and this an external app request with "externalRequest" header, we need to return
833
+ * an error if the data store being requested is marked as unreferenced as per the data store's base
834
+ * GC data.
835
+ *
836
+ * This is a workaround to handle scenarios where a data store shared with an external app is deleted
837
+ * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
838
+ */
839
+ if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
840
+ // The data store is referenced if used routes in the base summary has a route to self.
841
+ // Older documents may not have used routes in the summary. They are considered referenced.
842
+ const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
843
+ if (!(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))) {
844
+ throw runtime_utils_1.responseToException(runtime_utils_1.create404Response(request), request);
845
+ }
845
846
  }
846
- // The data store is unreferenced. Throw a 404 response exception.
847
- const request = { url: id };
848
- throw runtime_utils_1.responseToException(runtime_utils_1.create404Response(request), request);
847
+ const dataStoreChannel = await dataStoreContext.realize();
848
+ this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request === null || request === void 0 ? void 0 : request.headers);
849
+ return dataStoreChannel;
849
850
  }
850
- /**
851
- * Notifies this object to take the snapshot of the container.
852
- * @deprecated - Use summarize to get summary of the container runtime.
853
- */
854
- async snapshot() {
855
- const summaryResult = await this.summarize({
856
- summaryLogger: this.logger,
857
- fullTree: true,
858
- trackState: false,
859
- runGC: this.garbageCollector.shouldRunGC,
860
- fullGC: true,
861
- });
862
- return runtime_utils_1.convertSummaryTreeToITree(summaryResult.summary);
851
+ formMetadata() {
852
+ var _a;
853
+ return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
854
+ // The last message processed at the time of summary. If there are no new messages, use the message from the
855
+ // last summary.
856
+ message: (_a = summaryFormat_1.extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
863
857
  }
864
858
  addContainerStateToSummary(summaryTree) {
865
859
  var _a;
@@ -1022,9 +1016,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1022
1016
  common_utils_1.assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
1023
1017
  return context.realize();
1024
1018
  }
1025
- async getDataStore(id, wait = true) {
1026
- return (await this.dataStores.getDataStore(id, wait)).realize();
1027
- }
1028
1019
  setFlushMode(mode) {
1029
1020
  if (mode === this._flushMode) {
1030
1021
  return;
@@ -1089,28 +1080,84 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1089
1080
  }
1090
1081
  }
1091
1082
  async createDataStore(pkg) {
1092
- return this._createDataStore(pkg, false /* isRoot */);
1083
+ const internalId = uuid_1.v4();
1084
+ return dataStore_1.channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
1093
1085
  }
1094
- async createRootDataStore(pkg, rootDataStoreId) {
1086
+ /**
1087
+ * Creates a root datastore directly with a user generated id and attaches it to storage.
1088
+ * It is vulnerable to name collisions and should not be used.
1089
+ *
1090
+ * This method will be removed. See #6465.
1091
+ */
1092
+ async createRootDataStoreLegacy(pkg, rootDataStoreId) {
1095
1093
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1096
1094
  fluidDataStore.bindToContext();
1097
1095
  return fluidDataStore;
1098
1096
  }
1097
+ async createRootDataStore(pkg, rootDataStoreId) {
1098
+ return this._aliasingEnabled === true ?
1099
+ this.createAndAliasDataStore(pkg, rootDataStoreId) :
1100
+ this.createRootDataStoreLegacy(pkg, rootDataStoreId);
1101
+ }
1102
+ /**
1103
+ * Creates a data store then attempts to alias it.
1104
+ * If aliasing fails, it will raise an exception.
1105
+ *
1106
+ * This method will be removed. See #6465.
1107
+ *
1108
+ * @param pkg - Package name of the data store
1109
+ * @param alias - Alias to be assigned to the data store
1110
+ * @param props - Properties for the data store
1111
+ * @returns - An aliased data store which can can be found / loaded by alias.
1112
+ */
1113
+ async createAndAliasDataStore(pkg, alias, props) {
1114
+ const internalId = uuid_1.v4();
1115
+ const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
1116
+ const aliasedDataStore = dataStore_1.channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
1117
+ const result = await aliasedDataStore.trySetAlias(alias);
1118
+ if (result !== dataStore_1.AliasResult.Success) {
1119
+ throw new container_utils_1.GenericError("dataStoreAliasFailure", undefined /* error */, {
1120
+ alias: {
1121
+ value: alias,
1122
+ tag: telemetry_utils_1.TelemetryDataTag.UserData,
1123
+ },
1124
+ internalId: {
1125
+ value: internalId,
1126
+ tag: telemetry_utils_1.TelemetryDataTag.PackageData,
1127
+ },
1128
+ aliasResult: result,
1129
+ });
1130
+ }
1131
+ return aliasedDataStore;
1132
+ }
1099
1133
  createDetachedRootDataStore(pkg, rootDataStoreId) {
1100
1134
  return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
1101
1135
  }
1102
1136
  createDetachedDataStore(pkg) {
1103
1137
  return this.dataStores.createDetachedDataStoreCore(pkg, false);
1104
1138
  }
1105
- async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
1139
+ /**
1140
+ * Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
1141
+ * It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
1142
+ *
1143
+ * This method will be removed. See #6465.
1144
+ */
1145
+ async _createDataStoreWithPropsLegacy(pkg, props, id = uuid_1.v4(), isRoot = false) {
1106
1146
  const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
1107
1147
  if (isRoot) {
1108
1148
  fluidDataStore.bindToContext();
1109
1149
  }
1110
1150
  return fluidDataStore;
1111
1151
  }
1112
- async _createDataStore(pkg, isRoot, id = uuid_1.v4()) {
1113
- return this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot).realize();
1152
+ async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
1153
+ return this._aliasingEnabled === true && isRoot ?
1154
+ this.createAndAliasDataStore(pkg, id, props) :
1155
+ this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
1156
+ }
1157
+ async _createDataStore(pkg, isRoot, id = uuid_1.v4(), props) {
1158
+ return this.dataStores
1159
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
1160
+ .realize();
1114
1161
  }
1115
1162
  canSendOps() {
1116
1163
  return this.connected && !this.deltaManager.readOnlyInfo.readonly;
@@ -1168,6 +1215,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1168
1215
  common_utils_1.assert(this.attachState === container_definitions_1.AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
1169
1216
  this.emit("attached");
1170
1217
  }
1218
+ if (attachState === container_definitions_1.AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
1219
+ this.updateDocumentDirtyState(false);
1220
+ }
1171
1221
  this.dataStores.setAttachState(attachState);
1172
1222
  }
1173
1223
  /**
@@ -1214,13 +1264,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1214
1264
  */
1215
1265
  async summarize(options) {
1216
1266
  this.verifyNotClosed();
1217
- const { summaryLogger, fullTree = false, trackState = true, runGC = true, runSweep, fullGC } = options;
1267
+ const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
1268
+ let gcStats;
1218
1269
  if (runGC) {
1219
- await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1270
+ gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1220
1271
  }
1221
1272
  const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
1222
1273
  common_utils_1.assert(summarizeResult.summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1223
- return summarizeResult;
1274
+ return Object.assign(Object.assign({}, summarizeResult), { gcStats });
1224
1275
  }
1225
1276
  /**
1226
1277
  * Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
@@ -1245,7 +1296,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1245
1296
  * @param usedRoutes - The routes that are used in all nodes in this Container.
1246
1297
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
1247
1298
  * unreferenced as part of this GC run, this should be used to update the time when it happens.
1248
- * @returns the statistics of the used state of the data stores.
1249
1299
  */
1250
1300
  updateUsedRoutes(usedRoutes, gcTimestamp) {
1251
1301
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
@@ -1279,7 +1329,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1279
1329
  * @param options - options controlling how the summary is generated or submitted
1280
1330
  */
1281
1331
  async submitSummary(options) {
1282
- var _a;
1332
+ var _a, _b;
1283
1333
  const { fullTree, refreshLatestAck, summaryLogger } = options;
1284
1334
  if (refreshLatestAck) {
1285
1335
  const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
@@ -1343,9 +1393,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1343
1393
  const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
1344
1394
  try {
1345
1395
  summarizeResult = await this.summarize({
1346
- summaryLogger,
1347
1396
  fullTree: fullTree || forcedFullTree,
1348
1397
  trackState: true,
1398
+ summaryLogger,
1349
1399
  runGC: this.garbageCollector.shouldRunGC,
1350
1400
  });
1351
1401
  }
@@ -1353,13 +1403,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1353
1403
  return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error };
1354
1404
  }
1355
1405
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
1406
+ // Now that we have generated the summary, update the message at last summary to the last message processed.
1407
+ this.messageAtLastSummary = this.deltaManager.lastMessage;
1356
1408
  // Counting dataStores and handles
1357
1409
  // Because handles are unchanged dataStores in the current logic,
1358
1410
  // summarized dataStore count is total dataStore count minus handle count
1359
1411
  const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[runtime_definitions_1.channelsTreeName];
1360
1412
  common_utils_1.assert(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1361
1413
  const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
1362
- const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
1414
+ 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);
1363
1415
  const generateSummaryData = {
1364
1416
  referenceSequenceNumber: summaryRefSeqNum,
1365
1417
  summaryTree,
@@ -1375,7 +1427,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1375
1427
  const summaryContext = lastAck === undefined
1376
1428
  ? {
1377
1429
  proposalHandle: undefined,
1378
- ackHandle: (_a = this.context.getLoadedFromVersion()) === null || _a === void 0 ? void 0 : _a.id,
1430
+ ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
1379
1431
  referenceSequenceNumber: summaryRefSeqNum,
1380
1432
  }
1381
1433
  : {
@@ -1470,6 +1522,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1470
1522
  };
1471
1523
  this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
1472
1524
  }
1525
+ submitDataStoreAliasOp(contents, localOpMetadata) {
1526
+ const aliasMessage = contents;
1527
+ if (!dataStore_1.isDataStoreAliasMessage(aliasMessage)) {
1528
+ throw new container_utils_1.UsageError("malformedDataStoreAliasMessage");
1529
+ }
1530
+ this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
1531
+ }
1473
1532
  async uploadBlob(blob) {
1474
1533
  this.verifyNotClosed();
1475
1534
  return this.blobManager.createBlob(blob);