@fluidframework/container-runtime 0.56.7 → 0.57.1

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 (101) 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 +68 -28
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +148 -89
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/dataStore.d.ts +27 -0
  12. package/dist/dataStore.d.ts.map +1 -0
  13. package/dist/dataStore.js +113 -0
  14. package/dist/dataStore.js.map +1 -0
  15. package/dist/dataStoreContext.d.ts +1 -7
  16. package/dist/dataStoreContext.d.ts.map +1 -1
  17. package/dist/dataStoreContext.js +10 -6
  18. package/dist/dataStoreContext.js.map +1 -1
  19. package/dist/dataStores.d.ts +9 -5
  20. package/dist/dataStores.d.ts.map +1 -1
  21. package/dist/dataStores.js +14 -19
  22. package/dist/dataStores.js.map +1 -1
  23. package/dist/garbageCollection.d.ts +66 -27
  24. package/dist/garbageCollection.d.ts.map +1 -1
  25. package/dist/garbageCollection.js +272 -97
  26. package/dist/garbageCollection.js.map +1 -1
  27. package/dist/index.d.ts +2 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/packageVersion.d.ts +1 -1
  32. package/dist/packageVersion.js +1 -1
  33. package/dist/packageVersion.js.map +1 -1
  34. package/dist/runningSummarizer.d.ts +1 -0
  35. package/dist/runningSummarizer.d.ts.map +1 -1
  36. package/dist/runningSummarizer.js +23 -15
  37. package/dist/runningSummarizer.js.map +1 -1
  38. package/dist/summarizerTypes.d.ts +4 -6
  39. package/dist/summarizerTypes.d.ts.map +1 -1
  40. package/dist/summarizerTypes.js.map +1 -1
  41. package/dist/summaryGenerator.d.ts +2 -1
  42. package/dist/summaryGenerator.d.ts.map +1 -1
  43. package/dist/summaryGenerator.js +46 -29
  44. package/dist/summaryGenerator.js.map +1 -1
  45. package/lib/blobManager.d.ts.map +1 -1
  46. package/lib/blobManager.js +9 -1
  47. package/lib/blobManager.js.map +1 -1
  48. package/lib/connectionTelemetry.d.ts.map +1 -1
  49. package/lib/connectionTelemetry.js +6 -6
  50. package/lib/connectionTelemetry.js.map +1 -1
  51. package/lib/containerRuntime.d.ts +68 -28
  52. package/lib/containerRuntime.d.ts.map +1 -1
  53. package/lib/containerRuntime.js +149 -90
  54. package/lib/containerRuntime.js.map +1 -1
  55. package/lib/dataStore.d.ts +27 -0
  56. package/lib/dataStore.d.ts.map +1 -0
  57. package/lib/dataStore.js +108 -0
  58. package/lib/dataStore.js.map +1 -0
  59. package/lib/dataStoreContext.d.ts +1 -7
  60. package/lib/dataStoreContext.d.ts.map +1 -1
  61. package/lib/dataStoreContext.js +10 -6
  62. package/lib/dataStoreContext.js.map +1 -1
  63. package/lib/dataStores.d.ts +9 -5
  64. package/lib/dataStores.d.ts.map +1 -1
  65. package/lib/dataStores.js +13 -18
  66. package/lib/dataStores.js.map +1 -1
  67. package/lib/garbageCollection.d.ts +66 -27
  68. package/lib/garbageCollection.d.ts.map +1 -1
  69. package/lib/garbageCollection.js +274 -99
  70. package/lib/garbageCollection.js.map +1 -1
  71. package/lib/index.d.ts +2 -2
  72. package/lib/index.d.ts.map +1 -1
  73. package/lib/index.js +1 -1
  74. package/lib/index.js.map +1 -1
  75. package/lib/packageVersion.d.ts +1 -1
  76. package/lib/packageVersion.js +1 -1
  77. package/lib/packageVersion.js.map +1 -1
  78. package/lib/runningSummarizer.d.ts +1 -0
  79. package/lib/runningSummarizer.d.ts.map +1 -1
  80. package/lib/runningSummarizer.js +23 -15
  81. package/lib/runningSummarizer.js.map +1 -1
  82. package/lib/summarizerTypes.d.ts +4 -6
  83. package/lib/summarizerTypes.d.ts.map +1 -1
  84. package/lib/summarizerTypes.js.map +1 -1
  85. package/lib/summaryGenerator.d.ts +2 -1
  86. package/lib/summaryGenerator.d.ts.map +1 -1
  87. package/lib/summaryGenerator.js +46 -29
  88. package/lib/summaryGenerator.js.map +1 -1
  89. package/package.json +13 -13
  90. package/src/blobManager.ts +12 -1
  91. package/src/connectionTelemetry.ts +7 -6
  92. package/src/containerRuntime.ts +244 -115
  93. package/src/dataStore.ts +151 -0
  94. package/src/dataStoreContext.ts +11 -14
  95. package/src/dataStores.ts +23 -38
  96. package/src/garbageCollection.ts +385 -150
  97. package/src/index.ts +2 -1
  98. package/src/packageVersion.ts +1 -1
  99. package/src/runningSummarizer.ts +25 -16
  100. package/src/summarizerTypes.ts +4 -8
  101. package/src/summaryGenerator.ts +71 -23
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.ContainerMessageType = void 0;
3
+ exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.ContainerMessageType = void 0;
4
4
  const container_definitions_1 = require("@fluidframework/container-definitions");
5
5
  const common_utils_1 = require("@fluidframework/common-utils");
6
6
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
@@ -28,6 +28,7 @@ const summarizerClientElection_1 = require("./summarizerClientElection");
28
28
  const throttler_1 = require("./throttler");
29
29
  const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator");
30
30
  const garbageCollection_1 = require("./garbageCollection");
31
+ const dataStore_1 = require("./dataStore");
31
32
  var ContainerMessageType;
32
33
  (function (ContainerMessageType) {
33
34
  // An op to be delivered to store
@@ -55,9 +56,24 @@ const DefaultSummaryConfiguration = {
55
56
  // the min of the two will be chosen
56
57
  maxAckWaitTime: 120000,
57
58
  };
58
- ;
59
+ /**
60
+ * Accepted header keys for requests coming to the runtime.
61
+ */
62
+ var RuntimeHeaders;
63
+ (function (RuntimeHeaders) {
64
+ /** True to wait for a data store to be created and loaded before returning it. */
65
+ RuntimeHeaders["wait"] = "wait";
66
+ /**
67
+ * True if the request is from an external app. Used for GC to handle scenarios where a data store
68
+ * is deleted and requested via an external app.
69
+ */
70
+ RuntimeHeaders["externalRequest"] = "externalRequest";
71
+ /** True if the request is coming from an IFluidHandle. */
72
+ RuntimeHeaders["viaHandle"] = "viaHandle";
73
+ })(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
59
74
  // Local storage key to set the default flush mode to TurnBased
60
75
  const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
76
+ const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
61
77
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
62
78
  var RuntimeMessage;
63
79
  (function (RuntimeMessage) {
@@ -336,7 +352,7 @@ exports.getDeviceSpec = getDeviceSpec;
336
352
  */
337
353
  class ContainerRuntime extends common_utils_1.TypedEventEmitter {
338
354
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
339
- var _a, _b, _c, _d, _e, _f;
355
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
340
356
  super();
341
357
  this.context = context;
342
358
  this.registry = registry;
@@ -352,7 +368,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
352
368
  this.paused = false;
353
369
  this.consecutiveReconnects = 0;
354
370
  this._disposed = false;
355
- this.dirtyContainer = false;
356
371
  this.emitDirtyDocumentEvent = true;
357
372
  this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
358
373
  /**
@@ -404,7 +419,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
404
419
  throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
405
420
  }
406
421
  };
407
- this.baseSummaryMessage = metadata === null || metadata === void 0 ? void 0 : metadata.message;
422
+ this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
408
423
  // If this is an existing container, we get values from metadata.
409
424
  // otherwise, we initialize them.
410
425
  if (existing) {
@@ -428,21 +443,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
428
443
  this.mc = telemetry_utils_1.loggerToMonitoringContext(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
429
444
  this._flushMode =
430
445
  ((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? runtime_definitions_1.FlushMode.TurnBased : runtime_definitions_1.FlushMode.Immediate;
446
+ this._aliasingEnabled =
447
+ ((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
448
+ ((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
449
+ this.maxConsecutiveReconnects = (_e = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _e !== void 0 ? _e : this.defaultMaxConsecutiveReconnects;
450
+ this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
431
451
  /**
432
- * Function that return the current server timestamp. This is used by the garbage collector to set the
433
- * time when a node becomes unreferenced.
434
- * We use the timestamp of the last op for current timestamp. However, there can be cases where
435
- * we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
436
- * 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.
437
454
  */
438
- const getCurrentTimestamp = () => {
439
- var _a, _b, _c;
440
- const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
441
- const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
442
- 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();
443
- };
444
- this.maxConsecutiveReconnects = (_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
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
+ || ((_f = pendingLocalState) === null || _f === void 0 ? void 0 : _f.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 = (_d = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _d !== void 0 ? _d : ((_e = this.runtimeOptions.summaryOptions) === null || _e === void 0 ? void 0 : _e.summarizerClientElection) === true;
487
- const maxOpsSinceLastSummary = (_f = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _f !== void 0 ? _f : 7000;
500
+ const summarizerClientElectionEnabled = (_g = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _g !== void 0 ? _g : ((_h = this.runtimeOptions.summaryOptions) === null || _h === void 0 ? void 0 : _h.summarizerClientElection) === true;
501
+ const maxOpsSinceLastSummary = (_j = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _j !== void 0 ? _j : 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;
@@ -1064,9 +1058,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1064
1058
  common_utils_1.assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
1065
1059
  return context.realize();
1066
1060
  }
1067
- async getDataStore(id, wait = true) {
1068
- return (await this.dataStores.getDataStore(id, wait)).realize();
1069
- }
1070
1061
  setFlushMode(mode) {
1071
1062
  if (mode === this._flushMode) {
1072
1063
  return;
@@ -1131,28 +1122,84 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1131
1122
  }
1132
1123
  }
1133
1124
  async createDataStore(pkg) {
1134
- return this._createDataStore(pkg, false /* isRoot */);
1125
+ const internalId = uuid_1.v4();
1126
+ return dataStore_1.channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
1135
1127
  }
1136
- async createRootDataStore(pkg, rootDataStoreId) {
1128
+ /**
1129
+ * Creates a root datastore directly with a user generated id and attaches it to storage.
1130
+ * It is vulnerable to name collisions and should not be used.
1131
+ *
1132
+ * This method will be removed. See #6465.
1133
+ */
1134
+ async createRootDataStoreLegacy(pkg, rootDataStoreId) {
1137
1135
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1138
1136
  fluidDataStore.bindToContext();
1139
1137
  return fluidDataStore;
1140
1138
  }
1139
+ async createRootDataStore(pkg, rootDataStoreId) {
1140
+ return this._aliasingEnabled === true ?
1141
+ this.createAndAliasDataStore(pkg, rootDataStoreId) :
1142
+ this.createRootDataStoreLegacy(pkg, rootDataStoreId);
1143
+ }
1144
+ /**
1145
+ * Creates a data store then attempts to alias it.
1146
+ * If aliasing fails, it will raise an exception.
1147
+ *
1148
+ * This method will be removed. See #6465.
1149
+ *
1150
+ * @param pkg - Package name of the data store
1151
+ * @param alias - Alias to be assigned to the data store
1152
+ * @param props - Properties for the data store
1153
+ * @returns - An aliased data store which can can be found / loaded by alias.
1154
+ */
1155
+ async createAndAliasDataStore(pkg, alias, props) {
1156
+ const internalId = uuid_1.v4();
1157
+ const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
1158
+ const aliasedDataStore = dataStore_1.channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
1159
+ const result = await aliasedDataStore.trySetAlias(alias);
1160
+ if (result !== "Success") {
1161
+ throw new container_utils_1.GenericError("dataStoreAliasFailure", undefined /* error */, {
1162
+ alias: {
1163
+ value: alias,
1164
+ tag: telemetry_utils_1.TelemetryDataTag.UserData,
1165
+ },
1166
+ internalId: {
1167
+ value: internalId,
1168
+ tag: telemetry_utils_1.TelemetryDataTag.PackageData,
1169
+ },
1170
+ aliasResult: result,
1171
+ });
1172
+ }
1173
+ return aliasedDataStore;
1174
+ }
1141
1175
  createDetachedRootDataStore(pkg, rootDataStoreId) {
1142
1176
  return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
1143
1177
  }
1144
1178
  createDetachedDataStore(pkg) {
1145
1179
  return this.dataStores.createDetachedDataStoreCore(pkg, false);
1146
1180
  }
1147
- async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
1181
+ /**
1182
+ * Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
1183
+ * It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
1184
+ *
1185
+ * This method will be removed. See #6465.
1186
+ */
1187
+ async _createDataStoreWithPropsLegacy(pkg, props, id = uuid_1.v4(), isRoot = false) {
1148
1188
  const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
1149
1189
  if (isRoot) {
1150
1190
  fluidDataStore.bindToContext();
1151
1191
  }
1152
- return fluidDataStore;
1192
+ return dataStore_1.channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
1153
1193
  }
1154
- async _createDataStore(pkg, isRoot, id = uuid_1.v4()) {
1155
- return this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot).realize();
1194
+ async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
1195
+ return this._aliasingEnabled === true && isRoot ?
1196
+ this.createAndAliasDataStore(pkg, id, props) :
1197
+ this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
1198
+ }
1199
+ async _createDataStore(pkg, isRoot, id = uuid_1.v4(), props) {
1200
+ return this.dataStores
1201
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
1202
+ .realize();
1156
1203
  }
1157
1204
  canSendOps() {
1158
1205
  return this.connected && !this.deltaManager.readOnlyInfo.readonly;
@@ -1210,6 +1257,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1210
1257
  common_utils_1.assert(this.attachState === container_definitions_1.AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
1211
1258
  this.emit("attached");
1212
1259
  }
1260
+ if (attachState === container_definitions_1.AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
1261
+ this.updateDocumentDirtyState(false);
1262
+ }
1213
1263
  this.dataStores.setAttachState(attachState);
1214
1264
  }
1215
1265
  /**
@@ -1256,13 +1306,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1256
1306
  */
1257
1307
  async summarize(options) {
1258
1308
  this.verifyNotClosed();
1259
- const { summaryLogger, fullTree = false, trackState = true, runGC = true, runSweep, fullGC } = options;
1309
+ const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
1310
+ let gcStats;
1260
1311
  if (runGC) {
1261
- await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1312
+ gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1262
1313
  }
1263
1314
  const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
1264
1315
  common_utils_1.assert(summarizeResult.summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1265
- return summarizeResult;
1316
+ return Object.assign(Object.assign({}, summarizeResult), { gcStats });
1266
1317
  }
1267
1318
  /**
1268
1319
  * Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
@@ -1287,7 +1338,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1287
1338
  * @param usedRoutes - The routes that are used in all nodes in this Container.
1288
1339
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
1289
1340
  * unreferenced as part of this GC run, this should be used to update the time when it happens.
1290
- * @returns the statistics of the used state of the data stores.
1291
1341
  */
1292
1342
  updateUsedRoutes(usedRoutes, gcTimestamp) {
1293
1343
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
@@ -1321,7 +1371,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1321
1371
  * @param options - options controlling how the summary is generated or submitted
1322
1372
  */
1323
1373
  async submitSummary(options) {
1324
- var _a;
1374
+ var _a, _b;
1325
1375
  const { fullTree, refreshLatestAck, summaryLogger } = options;
1326
1376
  if (refreshLatestAck) {
1327
1377
  const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
@@ -1385,9 +1435,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1385
1435
  const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
1386
1436
  try {
1387
1437
  summarizeResult = await this.summarize({
1388
- summaryLogger,
1389
1438
  fullTree: fullTree || forcedFullTree,
1390
1439
  trackState: true,
1440
+ summaryLogger,
1391
1441
  runGC: this.garbageCollector.shouldRunGC,
1392
1442
  });
1393
1443
  }
@@ -1395,13 +1445,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1395
1445
  return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error };
1396
1446
  }
1397
1447
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
1448
+ // Now that we have generated the summary, update the message at last summary to the last message processed.
1449
+ this.messageAtLastSummary = this.deltaManager.lastMessage;
1398
1450
  // Counting dataStores and handles
1399
1451
  // Because handles are unchanged dataStores in the current logic,
1400
1452
  // summarized dataStore count is total dataStore count minus handle count
1401
1453
  const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[runtime_definitions_1.channelsTreeName];
1402
1454
  common_utils_1.assert(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1403
1455
  const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
1404
- const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
1456
+ 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);
1405
1457
  const generateSummaryData = {
1406
1458
  referenceSequenceNumber: summaryRefSeqNum,
1407
1459
  summaryTree,
@@ -1417,7 +1469,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1417
1469
  const summaryContext = lastAck === undefined
1418
1470
  ? {
1419
1471
  proposalHandle: undefined,
1420
- ackHandle: (_a = this.context.getLoadedFromVersion()) === null || _a === void 0 ? void 0 : _a.id,
1472
+ ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
1421
1473
  referenceSequenceNumber: summaryRefSeqNum,
1422
1474
  }
1423
1475
  : {
@@ -1512,6 +1564,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1512
1564
  };
1513
1565
  this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
1514
1566
  }
1567
+ submitDataStoreAliasOp(contents, localOpMetadata) {
1568
+ const aliasMessage = contents;
1569
+ if (!dataStore_1.isDataStoreAliasMessage(aliasMessage)) {
1570
+ throw new container_utils_1.UsageError("malformedDataStoreAliasMessage");
1571
+ }
1572
+ this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
1573
+ }
1515
1574
  async uploadBlob(blob) {
1516
1575
  this.verifyNotClosed();
1517
1576
  return this.blobManager.createBlob(blob);