@fluidframework/container-runtime 0.56.0 → 0.57.0-51086

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/blobManager.d.ts.map +1 -1
  2. package/dist/blobManager.js +9 -1
  3. package/dist/blobManager.js.map +1 -1
  4. package/dist/connectionTelemetry.d.ts.map +1 -1
  5. package/dist/connectionTelemetry.js +6 -6
  6. package/dist/connectionTelemetry.js.map +1 -1
  7. package/dist/containerRuntime.d.ts +65 -25
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +149 -79
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/dataStore.d.ts +62 -0
  12. package/dist/dataStore.d.ts.map +1 -0
  13. package/dist/dataStore.js +135 -0
  14. package/dist/dataStore.js.map +1 -0
  15. package/dist/dataStoreContext.js.map +1 -1
  16. package/dist/dataStores.d.ts +9 -5
  17. package/dist/dataStores.d.ts.map +1 -1
  18. package/dist/dataStores.js +14 -19
  19. package/dist/dataStores.js.map +1 -1
  20. package/dist/garbageCollection.d.ts +47 -21
  21. package/dist/garbageCollection.d.ts.map +1 -1
  22. package/dist/garbageCollection.js +195 -61
  23. package/dist/garbageCollection.js.map +1 -1
  24. package/dist/index.d.ts +3 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +4 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/packageVersion.d.ts +1 -1
  29. package/dist/packageVersion.d.ts.map +1 -1
  30. package/dist/packageVersion.js +1 -1
  31. package/dist/packageVersion.js.map +1 -1
  32. package/dist/runningSummarizer.d.ts +1 -0
  33. package/dist/runningSummarizer.d.ts.map +1 -1
  34. package/dist/runningSummarizer.js +23 -15
  35. package/dist/runningSummarizer.js.map +1 -1
  36. package/dist/summarizerTypes.d.ts +6 -6
  37. package/dist/summarizerTypes.d.ts.map +1 -1
  38. package/dist/summarizerTypes.js.map +1 -1
  39. package/dist/summaryGenerator.d.ts +2 -1
  40. package/dist/summaryGenerator.d.ts.map +1 -1
  41. package/dist/summaryGenerator.js +46 -28
  42. package/dist/summaryGenerator.js.map +1 -1
  43. package/lib/blobManager.d.ts.map +1 -1
  44. package/lib/blobManager.js +9 -1
  45. package/lib/blobManager.js.map +1 -1
  46. package/lib/connectionTelemetry.d.ts.map +1 -1
  47. package/lib/connectionTelemetry.js +6 -6
  48. package/lib/connectionTelemetry.js.map +1 -1
  49. package/lib/containerRuntime.d.ts +65 -25
  50. package/lib/containerRuntime.d.ts.map +1 -1
  51. package/lib/containerRuntime.js +150 -80
  52. package/lib/containerRuntime.js.map +1 -1
  53. package/lib/dataStore.d.ts +62 -0
  54. package/lib/dataStore.d.ts.map +1 -0
  55. package/lib/dataStore.js +130 -0
  56. package/lib/dataStore.js.map +1 -0
  57. package/lib/dataStoreContext.js.map +1 -1
  58. package/lib/dataStores.d.ts +9 -5
  59. package/lib/dataStores.d.ts.map +1 -1
  60. package/lib/dataStores.js +13 -18
  61. package/lib/dataStores.js.map +1 -1
  62. package/lib/garbageCollection.d.ts +47 -21
  63. package/lib/garbageCollection.d.ts.map +1 -1
  64. package/lib/garbageCollection.js +197 -63
  65. package/lib/garbageCollection.js.map +1 -1
  66. package/lib/index.d.ts +3 -2
  67. package/lib/index.d.ts.map +1 -1
  68. package/lib/index.js +2 -1
  69. package/lib/index.js.map +1 -1
  70. package/lib/packageVersion.d.ts +1 -1
  71. package/lib/packageVersion.d.ts.map +1 -1
  72. package/lib/packageVersion.js +1 -1
  73. package/lib/packageVersion.js.map +1 -1
  74. package/lib/runningSummarizer.d.ts +1 -0
  75. package/lib/runningSummarizer.d.ts.map +1 -1
  76. package/lib/runningSummarizer.js +23 -15
  77. package/lib/runningSummarizer.js.map +1 -1
  78. package/lib/summarizerTypes.d.ts +6 -6
  79. package/lib/summarizerTypes.d.ts.map +1 -1
  80. package/lib/summarizerTypes.js.map +1 -1
  81. package/lib/summaryGenerator.d.ts +2 -1
  82. package/lib/summaryGenerator.d.ts.map +1 -1
  83. package/lib/summaryGenerator.js +46 -28
  84. package/lib/summaryGenerator.js.map +1 -1
  85. package/package.json +13 -13
  86. package/src/blobManager.ts +12 -1
  87. package/src/connectionTelemetry.ts +7 -6
  88. package/src/containerRuntime.ts +231 -103
  89. package/src/dataStore.ts +187 -0
  90. package/src/dataStoreContext.ts +1 -1
  91. package/src/dataStores.ts +18 -38
  92. package/src/garbageCollection.ts +283 -105
  93. package/src/index.ts +3 -1
  94. package/src/packageVersion.ts +1 -1
  95. package/src/runningSummarizer.ts +25 -16
  96. package/src/summarizerTypes.ts +6 -8
  97. package/src/summaryGenerator.ts +72 -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";
@@ -141,6 +157,10 @@ class ScheduleManagerCore {
141
157
  for (const pending of allPending) {
142
158
  this.trackPending(pending);
143
159
  }
160
+ // We are intentionally directly listening to the "op" to inspect system ops as well.
161
+ // If we do not observe system ops, we are likely to hit 0x296 assert when system ops
162
+ // precedes start of incomplete batch.
163
+ this.deltaManager.on("op", (message) => this.afterOpProcessing(message.sequenceNumber));
144
164
  }
145
165
  /**
146
166
  * The only public function in this class - called when we processed an op,
@@ -265,7 +285,7 @@ class ScheduleManager {
265
285
  this.logger = logger;
266
286
  this.hitError = false;
267
287
  this.deltaScheduler = new deltaScheduler_1.DeltaScheduler(this.deltaManager, telemetry_utils_1.ChildLogger.create(this.logger, "DeltaScheduler"));
268
- this.scheduler = new ScheduleManagerCore(deltaManager, logger);
288
+ void new ScheduleManagerCore(deltaManager, logger);
269
289
  }
270
290
  beforeOpProcessing(message) {
271
291
  var _a;
@@ -287,9 +307,6 @@ class ScheduleManager {
287
307
  var _a;
288
308
  // If this is no longer true, we need to revisit what we do where we set this.hitError.
289
309
  common_utils_1.assert(!this.hitError, 0x2a3 /* "container should be closed on any error" */);
290
- // Let the scheduler know how far we progressed, to decide if op processing
291
- // should be paused or not.
292
- this.scheduler.afterOpProcessing(message.sequenceNumber);
293
310
  if (error) {
294
311
  // We assume here that loader will close container and stop processing all future ops.
295
312
  // This is implicit dependency. If this flow changes, this code might no longer be correct.
@@ -338,7 +355,7 @@ exports.getDeviceSpec = getDeviceSpec;
338
355
  */
339
356
  class ContainerRuntime extends common_utils_1.TypedEventEmitter {
340
357
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
341
- var _a, _b, _c, _d, _e;
358
+ var _a, _b, _c, _d, _e, _f, _g, _h;
342
359
  super();
343
360
  this.context = context;
344
361
  this.registry = registry;
@@ -352,7 +369,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
352
369
  this.flushTrigger = false;
353
370
  this.paused = false;
354
371
  this._disposed = false;
355
- this.dirtyContainer = false;
356
372
  this.emitDirtyDocumentEvent = true;
357
373
  this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
358
374
  /**
@@ -428,6 +444,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
428
444
  this.mc = telemetry_utils_1.loggerToMonitoringContext(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
429
445
  this._flushMode =
430
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);
431
450
  /**
432
451
  * Function that return the current server timestamp. This is used by the garbage collector to set the
433
452
  * time when a node becomes unreferenced.
@@ -441,7 +460,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
441
460
  const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
442
461
  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
462
  };
444
- 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);
463
+ this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
445
464
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
446
465
  this.summarizerNode = runtime_utils_1.createRootSummarizerNodeWithGC(telemetry_utils_1.ChildLogger.create(this.logger, "SummarizerNode"),
447
466
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
@@ -462,7 +481,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
462
481
  if (this.context.baseSnapshot) {
463
482
  this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
464
483
  }
465
- 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);
484
+ 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(), (dataStorePath, packagePath) => this.garbageCollector.nodeUpdated(dataStorePath, "Changed", packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
466
485
  this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
467
486
  this.scheduleManager = new ScheduleManager(context.deltaManager, this, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
468
487
  this.deltaSender = this.deltaManager;
@@ -471,6 +490,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
471
490
  this.clearPartialChunks(clientId);
472
491
  });
473
492
  this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
493
+ const { attachState, pendingLocalState } = this.context;
494
+ this.dirtyContainer = attachState !== container_definitions_1.AttachState.Attached
495
+ || ((_e = pendingLocalState) === null || _e === void 0 ? void 0 : _e.pendingStates.length) > 0;
496
+ this.context.updateDirtyContainerState(this.dirtyContainer);
474
497
  // Map the deprecated generateSummaries flag to disableSummaries.
475
498
  if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
476
499
  this.runtimeOptions.summaryOptions.disableSummaries = true;
@@ -482,8 +505,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
482
505
  const orderedClientLogger = telemetry_utils_1.ChildLogger.create(this.logger, "OrderedClientElection");
483
506
  const orderedClientCollection = new orderedClientElection_1.OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
484
507
  const orderedClientElectionForSummarizer = new orderedClientElection_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, summarizerClientElection_1.SummarizerClientElection.isClientEligible);
485
- 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;
486
- const maxOpsSinceLastSummary = (_e = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _e !== void 0 ? _e : 7000;
508
+ 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;
509
+ const maxOpsSinceLastSummary = (_h = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _h !== void 0 ? _h : 7000;
487
510
  this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
488
511
  if (this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
489
512
  this._summarizer = new summarizer_1.Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => runWhileConnectedCoordinator_1.RunWhileConnectedCoordinator.create(runtime));
@@ -567,7 +590,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
567
590
  runtimeVersion: packageVersion_1.pkgVersion,
568
591
  },
569
592
  });
570
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", } = runtimeOptions;
593
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
571
594
  // We pack at data store level only. If isolated channels are disabled,
572
595
  // then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
573
596
  const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
@@ -635,6 +658,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
635
658
  summaryOptions,
636
659
  gcOptions,
637
660
  loadSequenceNumberVerification,
661
+ useDataStoreAliasing,
638
662
  }, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
639
663
  return runtime;
640
664
  }
@@ -774,7 +798,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
774
798
  * @param request - Request made to the handler.
775
799
  */
776
800
  async resolveHandle(request) {
777
- var _a, _b;
778
801
  try {
779
802
  const requestParser = runtime_utils_1.RequestParser.create(request);
780
803
  const id = requestParser.pathParts[0];
@@ -795,18 +818,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
795
818
  }
796
819
  }
797
820
  else if (requestParser.pathParts.length > 0) {
798
- /**
799
- * If GC should run and this an external app request with "externalRequest" header, we need to return
800
- * an error if the data store being requested is marked as unreferenced as per the data store's initial
801
- * summary.
802
- *
803
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted
804
- * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
805
- */
806
- const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a.wait) === "boolean" ? request.headers.wait : undefined;
807
- const dataStore = ((_b = request.headers) === null || _b === void 0 ? void 0 : _b.externalRequest) && this.garbageCollector.shouldRunGC
808
- ? await this.getDataStoreIfInitiallyReferenced(id, wait)
809
- : await this.getDataStore(id, wait);
821
+ const dataStore = await this.getDataStoreFromRequest(id, request);
810
822
  const subRequest = requestParser.createSubRequest(1);
811
823
  // We always expect createSubRequest to include a leading slash, but asserting here to protect against
812
824
  // unintentionally modifying the url if that changes.
@@ -819,6 +831,33 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
819
831
  return runtime_utils_1.exceptionToResponse(error);
820
832
  }
821
833
  }
834
+ async getDataStoreFromRequest(id, request) {
835
+ var _a, _b, _c;
836
+ const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
837
+ ? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait] : true;
838
+ const dataStoreContext = await this.dataStores.getDataStore(id, wait);
839
+ /**
840
+ * If GC should run and this an external app request with "externalRequest" header, we need to return
841
+ * an error if the data store being requested is marked as unreferenced as per the data store's base
842
+ * GC data.
843
+ *
844
+ * This is a workaround to handle scenarios where a data store shared with an external app is deleted
845
+ * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
846
+ */
847
+ if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
848
+ // The data store is referenced if used routes in the base summary has a route to self.
849
+ // Older documents may not have used routes in the summary. They are considered referenced.
850
+ const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
851
+ if (!(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))) {
852
+ throw runtime_utils_1.responseToException(runtime_utils_1.create404Response(request), request);
853
+ }
854
+ }
855
+ const dataStoreChannel = await dataStoreContext.realize();
856
+ // Let the garbage collector know that a data store was requested / loaded. Realize the data store first so
857
+ // that the package path is available.
858
+ this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", dataStoreContext.packagePath, request === null || request === void 0 ? void 0 : request.headers);
859
+ return dataStoreChannel;
860
+ }
822
861
  formMetadata() {
823
862
  var _a;
824
863
  return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
@@ -826,40 +865,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
826
865
  // the base summary we loaded from. So, use the message from its metadata blob.
827
866
  message: (_a = summaryFormat_1.extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.baseSummaryMessage, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
828
867
  }
829
- /**
830
- * Retrieves the runtime for a data store if it's referenced as per the initially summary that it is loaded with.
831
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted and marked
832
- * as unreferenced by GC.
833
- * @param id - Id supplied during creating the data store.
834
- * @param wait - True if you want to wait for it.
835
- * @returns the data store runtime if the data store exists and is initially referenced; undefined otherwise.
836
- */
837
- async getDataStoreIfInitiallyReferenced(id, wait = true) {
838
- const dataStoreContext = await this.dataStores.getDataStore(id, wait);
839
- // The data store is referenced if used routes in the initial summary has a route to self.
840
- // Older documents may not have used routes in the summary. They are considered referenced.
841
- const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
842
- if (usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/")) {
843
- return dataStoreContext.realize();
844
- }
845
- // The data store is unreferenced. Throw a 404 response exception.
846
- const request = { url: id };
847
- throw runtime_utils_1.responseToException(runtime_utils_1.create404Response(request), request);
848
- }
849
- /**
850
- * Notifies this object to take the snapshot of the container.
851
- * @deprecated - Use summarize to get summary of the container runtime.
852
- */
853
- async snapshot() {
854
- const summaryResult = await this.summarize({
855
- summaryLogger: this.logger,
856
- fullTree: true,
857
- trackState: false,
858
- runGC: this.garbageCollector.shouldRunGC,
859
- fullGC: true,
860
- });
861
- return runtime_utils_1.convertSummaryTreeToITree(summaryResult.summary);
862
- }
863
868
  addContainerStateToSummary(summaryTree) {
864
869
  var _a;
865
870
  runtime_utils_1.addBlobToSummary(summaryTree, summaryFormat_1.metadataBlobName, JSON.stringify(this.formMetadata()));
@@ -1021,9 +1026,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1021
1026
  common_utils_1.assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
1022
1027
  return context.realize();
1023
1028
  }
1024
- async getDataStore(id, wait = true) {
1025
- return (await this.dataStores.getDataStore(id, wait)).realize();
1026
- }
1027
1029
  setFlushMode(mode) {
1028
1030
  if (mode === this._flushMode) {
1029
1031
  return;
@@ -1088,28 +1090,84 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1088
1090
  }
1089
1091
  }
1090
1092
  async createDataStore(pkg) {
1091
- return this._createDataStore(pkg, false /* isRoot */);
1093
+ const internalId = uuid_1.v4();
1094
+ return dataStore_1.channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
1092
1095
  }
1093
- async createRootDataStore(pkg, rootDataStoreId) {
1096
+ /**
1097
+ * Creates a root datastore directly with a user generated id and attaches it to storage.
1098
+ * It is vulnerable to name collisions and should not be used.
1099
+ *
1100
+ * This method will be removed. See #6465.
1101
+ */
1102
+ async createRootDataStoreLegacy(pkg, rootDataStoreId) {
1094
1103
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
1095
1104
  fluidDataStore.bindToContext();
1096
1105
  return fluidDataStore;
1097
1106
  }
1107
+ async createRootDataStore(pkg, rootDataStoreId) {
1108
+ return this._aliasingEnabled === true ?
1109
+ this.createAndAliasDataStore(pkg, rootDataStoreId) :
1110
+ this.createRootDataStoreLegacy(pkg, rootDataStoreId);
1111
+ }
1112
+ /**
1113
+ * Creates a data store then attempts to alias it.
1114
+ * If aliasing fails, it will raise an exception.
1115
+ *
1116
+ * This method will be removed. See #6465.
1117
+ *
1118
+ * @param pkg - Package name of the data store
1119
+ * @param alias - Alias to be assigned to the data store
1120
+ * @param props - Properties for the data store
1121
+ * @returns - An aliased data store which can can be found / loaded by alias.
1122
+ */
1123
+ async createAndAliasDataStore(pkg, alias, props) {
1124
+ const internalId = uuid_1.v4();
1125
+ const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
1126
+ const aliasedDataStore = dataStore_1.channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
1127
+ const result = await aliasedDataStore.trySetAlias(alias);
1128
+ if (result !== dataStore_1.AliasResult.Success) {
1129
+ throw new container_utils_1.GenericError("dataStoreAliasFailure", undefined /* error */, {
1130
+ alias: {
1131
+ value: alias,
1132
+ tag: telemetry_utils_1.TelemetryDataTag.UserData,
1133
+ },
1134
+ internalId: {
1135
+ value: internalId,
1136
+ tag: telemetry_utils_1.TelemetryDataTag.PackageData,
1137
+ },
1138
+ aliasResult: result,
1139
+ });
1140
+ }
1141
+ return aliasedDataStore;
1142
+ }
1098
1143
  createDetachedRootDataStore(pkg, rootDataStoreId) {
1099
1144
  return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
1100
1145
  }
1101
1146
  createDetachedDataStore(pkg) {
1102
1147
  return this.dataStores.createDetachedDataStoreCore(pkg, false);
1103
1148
  }
1104
- async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
1149
+ /**
1150
+ * Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
1151
+ * It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
1152
+ *
1153
+ * This method will be removed. See #6465.
1154
+ */
1155
+ async _createDataStoreWithPropsLegacy(pkg, props, id = uuid_1.v4(), isRoot = false) {
1105
1156
  const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
1106
1157
  if (isRoot) {
1107
1158
  fluidDataStore.bindToContext();
1108
1159
  }
1109
1160
  return fluidDataStore;
1110
1161
  }
1111
- async _createDataStore(pkg, isRoot, id = uuid_1.v4()) {
1112
- return this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot).realize();
1162
+ async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
1163
+ return this._aliasingEnabled === true && isRoot ?
1164
+ this.createAndAliasDataStore(pkg, id, props) :
1165
+ this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
1166
+ }
1167
+ async _createDataStore(pkg, isRoot, id = uuid_1.v4(), props) {
1168
+ return this.dataStores
1169
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
1170
+ .realize();
1113
1171
  }
1114
1172
  canSendOps() {
1115
1173
  return this.connected && !this.deltaManager.readOnlyInfo.readonly;
@@ -1167,6 +1225,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1167
1225
  common_utils_1.assert(this.attachState === container_definitions_1.AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
1168
1226
  this.emit("attached");
1169
1227
  }
1228
+ if (attachState === container_definitions_1.AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
1229
+ this.updateDocumentDirtyState(false);
1230
+ }
1170
1231
  this.dataStores.setAttachState(attachState);
1171
1232
  }
1172
1233
  /**
@@ -1213,13 +1274,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1213
1274
  */
1214
1275
  async summarize(options) {
1215
1276
  this.verifyNotClosed();
1216
- const { summaryLogger, fullTree = false, trackState = true, runGC = true, runSweep, fullGC } = options;
1277
+ const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
1278
+ let gcStats;
1217
1279
  if (runGC) {
1218
- await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1280
+ gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
1219
1281
  }
1220
1282
  const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
1221
1283
  common_utils_1.assert(summarizeResult.summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1222
- return summarizeResult;
1284
+ return Object.assign(Object.assign({}, summarizeResult), { gcStats });
1223
1285
  }
1224
1286
  /**
1225
1287
  * Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
@@ -1244,7 +1306,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1244
1306
  * @param usedRoutes - The routes that are used in all nodes in this Container.
1245
1307
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
1246
1308
  * unreferenced as part of this GC run, this should be used to update the time when it happens.
1247
- * @returns the statistics of the used state of the data stores.
1248
1309
  */
1249
1310
  updateUsedRoutes(usedRoutes, gcTimestamp) {
1250
1311
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
@@ -1278,7 +1339,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1278
1339
  * @param options - options controlling how the summary is generated or submitted
1279
1340
  */
1280
1341
  async submitSummary(options) {
1281
- var _a;
1342
+ var _a, _b;
1282
1343
  const { fullTree, refreshLatestAck, summaryLogger } = options;
1283
1344
  if (refreshLatestAck) {
1284
1345
  const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
@@ -1337,13 +1398,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1337
1398
  }
1338
1399
  const trace = common_utils_1.Trace.start();
1339
1400
  let summarizeResult;
1401
+ // If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
1402
+ // state of all the nodes.
1403
+ const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
1340
1404
  try {
1341
1405
  summarizeResult = await this.summarize({
1342
- summaryLogger,
1343
- // If the GC state needs to be reset, we need to regenerate the summary and update the unreferenced
1344
- // state of all the nodes.
1345
- fullTree: fullTree || this.garbageCollector.summaryStateNeedsReset,
1406
+ fullTree: fullTree || forcedFullTree,
1346
1407
  trackState: true,
1408
+ summaryLogger,
1347
1409
  runGC: this.garbageCollector.shouldRunGC,
1348
1410
  });
1349
1411
  }
@@ -1357,12 +1419,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1357
1419
  const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[runtime_definitions_1.channelsTreeName];
1358
1420
  common_utils_1.assert(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1359
1421
  const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
1360
- const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
1422
+ 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);
1361
1423
  const generateSummaryData = {
1362
1424
  referenceSequenceNumber: summaryRefSeqNum,
1363
1425
  summaryTree,
1364
1426
  summaryStats,
1365
1427
  generateDuration: trace.trace().duration,
1428
+ forcedFullTree,
1366
1429
  };
1367
1430
  continueResult = checkContinue();
1368
1431
  if (!continueResult.continue) {
@@ -1372,7 +1435,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1372
1435
  const summaryContext = lastAck === undefined
1373
1436
  ? {
1374
1437
  proposalHandle: undefined,
1375
- ackHandle: (_a = this.context.getLoadedFromVersion()) === null || _a === void 0 ? void 0 : _a.id,
1438
+ ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
1376
1439
  referenceSequenceNumber: summaryRefSeqNum,
1377
1440
  }
1378
1441
  : {
@@ -1467,6 +1530,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1467
1530
  };
1468
1531
  this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
1469
1532
  }
1533
+ submitDataStoreAliasOp(contents, localOpMetadata) {
1534
+ const aliasMessage = contents;
1535
+ if (!dataStore_1.isDataStoreAliasMessage(aliasMessage)) {
1536
+ throw new container_utils_1.UsageError("malformedDataStoreAliasMessage");
1537
+ }
1538
+ this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
1539
+ }
1470
1540
  async uploadBlob(blob) {
1471
1541
  this.verifyNotClosed();
1472
1542
  return this.blobManager.createBlob(blob);