@fluidframework/container-runtime 0.52.0 → 0.54.0-47413

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 (132) hide show
  1. package/dist/containerHandleContext.d.ts +0 -1
  2. package/dist/containerHandleContext.d.ts.map +1 -1
  3. package/dist/containerHandleContext.js +0 -1
  4. package/dist/containerHandleContext.js.map +1 -1
  5. package/dist/containerRuntime.d.ts +43 -19
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +201 -111
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStoreContext.d.ts +33 -4
  10. package/dist/dataStoreContext.d.ts.map +1 -1
  11. package/dist/dataStoreContext.js +45 -17
  12. package/dist/dataStoreContext.js.map +1 -1
  13. package/dist/dataStores.d.ts +14 -10
  14. package/dist/dataStores.d.ts.map +1 -1
  15. package/dist/dataStores.js +73 -41
  16. package/dist/dataStores.js.map +1 -1
  17. package/dist/garbageCollection.d.ts +82 -15
  18. package/dist/garbageCollection.d.ts.map +1 -1
  19. package/dist/garbageCollection.js +359 -26
  20. package/dist/garbageCollection.js.map +1 -1
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +11 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/packageVersion.d.ts +1 -1
  26. package/dist/packageVersion.d.ts.map +1 -1
  27. package/dist/packageVersion.js +1 -1
  28. package/dist/packageVersion.js.map +1 -1
  29. package/dist/pendingStateManager.d.ts +0 -1
  30. package/dist/pendingStateManager.d.ts.map +1 -1
  31. package/dist/pendingStateManager.js +0 -36
  32. package/dist/pendingStateManager.js.map +1 -1
  33. package/dist/runningSummarizer.d.ts +3 -2
  34. package/dist/runningSummarizer.d.ts.map +1 -1
  35. package/dist/runningSummarizer.js +6 -6
  36. package/dist/runningSummarizer.js.map +1 -1
  37. package/dist/summarizer.d.ts +23 -3
  38. package/dist/summarizer.d.ts.map +1 -1
  39. package/dist/summarizer.js +135 -45
  40. package/dist/summarizer.js.map +1 -1
  41. package/dist/summarizerTypes.d.ts +3 -10
  42. package/dist/summarizerTypes.d.ts.map +1 -1
  43. package/dist/summarizerTypes.js.map +1 -1
  44. package/dist/summaryFormat.d.ts +10 -1
  45. package/dist/summaryFormat.d.ts.map +1 -1
  46. package/dist/summaryFormat.js +2 -1
  47. package/dist/summaryFormat.js.map +1 -1
  48. package/dist/summaryGenerator.d.ts.map +1 -1
  49. package/dist/summaryGenerator.js +1 -3
  50. package/dist/summaryGenerator.js.map +1 -1
  51. package/dist/summaryManager.d.ts +0 -15
  52. package/dist/summaryManager.d.ts.map +1 -1
  53. package/dist/summaryManager.js +1 -35
  54. package/dist/summaryManager.js.map +1 -1
  55. package/lib/containerHandleContext.d.ts +0 -1
  56. package/lib/containerHandleContext.d.ts.map +1 -1
  57. package/lib/containerHandleContext.js +0 -1
  58. package/lib/containerHandleContext.js.map +1 -1
  59. package/lib/containerRuntime.d.ts +43 -19
  60. package/lib/containerRuntime.d.ts.map +1 -1
  61. package/lib/containerRuntime.js +206 -117
  62. package/lib/containerRuntime.js.map +1 -1
  63. package/lib/dataStoreContext.d.ts +33 -4
  64. package/lib/dataStoreContext.d.ts.map +1 -1
  65. package/lib/dataStoreContext.js +45 -17
  66. package/lib/dataStoreContext.js.map +1 -1
  67. package/lib/dataStores.d.ts +14 -10
  68. package/lib/dataStores.d.ts.map +1 -1
  69. package/lib/dataStores.js +76 -44
  70. package/lib/dataStores.js.map +1 -1
  71. package/lib/garbageCollection.d.ts +82 -15
  72. package/lib/garbageCollection.d.ts.map +1 -1
  73. package/lib/garbageCollection.js +361 -28
  74. package/lib/garbageCollection.js.map +1 -1
  75. package/lib/index.d.ts +2 -2
  76. package/lib/index.d.ts.map +1 -1
  77. package/lib/index.js +2 -1
  78. package/lib/index.js.map +1 -1
  79. package/lib/packageVersion.d.ts +1 -1
  80. package/lib/packageVersion.d.ts.map +1 -1
  81. package/lib/packageVersion.js +1 -1
  82. package/lib/packageVersion.js.map +1 -1
  83. package/lib/pendingStateManager.d.ts +0 -1
  84. package/lib/pendingStateManager.d.ts.map +1 -1
  85. package/lib/pendingStateManager.js +0 -36
  86. package/lib/pendingStateManager.js.map +1 -1
  87. package/lib/runningSummarizer.d.ts +3 -2
  88. package/lib/runningSummarizer.d.ts.map +1 -1
  89. package/lib/runningSummarizer.js +6 -6
  90. package/lib/runningSummarizer.js.map +1 -1
  91. package/lib/summarizer.d.ts +23 -3
  92. package/lib/summarizer.d.ts.map +1 -1
  93. package/lib/summarizer.js +135 -45
  94. package/lib/summarizer.js.map +1 -1
  95. package/lib/summarizerTypes.d.ts +3 -10
  96. package/lib/summarizerTypes.d.ts.map +1 -1
  97. package/lib/summarizerTypes.js.map +1 -1
  98. package/lib/summaryFormat.d.ts +10 -1
  99. package/lib/summaryFormat.d.ts.map +1 -1
  100. package/lib/summaryFormat.js +1 -0
  101. package/lib/summaryFormat.js.map +1 -1
  102. package/lib/summaryGenerator.d.ts.map +1 -1
  103. package/lib/summaryGenerator.js +1 -3
  104. package/lib/summaryGenerator.js.map +1 -1
  105. package/lib/summaryManager.d.ts +0 -15
  106. package/lib/summaryManager.d.ts.map +1 -1
  107. package/lib/summaryManager.js +1 -34
  108. package/lib/summaryManager.js.map +1 -1
  109. package/package.json +14 -14
  110. package/src/containerHandleContext.ts +0 -1
  111. package/src/containerRuntime.ts +280 -140
  112. package/src/dataStoreContext.ts +59 -20
  113. package/src/dataStores.ts +116 -54
  114. package/src/garbageCollection.ts +492 -29
  115. package/src/index.ts +20 -2
  116. package/src/packageVersion.ts +1 -1
  117. package/src/pendingStateManager.ts +0 -43
  118. package/src/runningSummarizer.ts +12 -10
  119. package/src/summarizer.ts +154 -53
  120. package/src/summarizerTypes.ts +3 -11
  121. package/src/summaryFormat.ts +11 -1
  122. package/src/summaryGenerator.ts +2 -3
  123. package/src/summaryManager.ts +2 -49
  124. package/dist/localStorageFeatureGates.d.ts +0 -13
  125. package/dist/localStorageFeatureGates.d.ts.map +0 -1
  126. package/dist/localStorageFeatureGates.js +0 -31
  127. package/dist/localStorageFeatureGates.js.map +0 -1
  128. package/lib/localStorageFeatureGates.d.ts +0 -13
  129. package/lib/localStorageFeatureGates.d.ts.map +0 -1
  130. package/lib/localStorageFeatureGates.js +0 -27
  131. package/lib/localStorageFeatureGates.js.map +0 -1
  132. package/src/localStorageFeatureGates.ts +0 -27
@@ -30,7 +30,7 @@ import { BlobTreeEntry } from "@fluidframework/protocol-base";
30
30
  import {
31
31
  IClientDetails,
32
32
  IDocumentMessage,
33
- IQuorum,
33
+ IQuorumClients,
34
34
  ISequencedDocumentMessage,
35
35
  ISnapshotTree,
36
36
  ITreeEntry,
@@ -67,6 +67,7 @@ import {
67
67
  ThresholdCounter,
68
68
  } from "@fluidframework/telemetry-utils";
69
69
  import { CreateProcessingError } from "@fluidframework/container-utils";
70
+
70
71
  import { ContainerRuntime } from "./containerRuntime";
71
72
  import {
72
73
  dataStoreAttributesBlobName,
@@ -188,6 +189,11 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
188
189
  return this._containerRuntime.disableIsolatedChannels;
189
190
  }
190
191
 
192
+ /** Tells whether GC data will be written at the root of the summary tree. If so, data store should not write it. */
193
+ protected get writeGCDataAtRoot(): boolean {
194
+ return this._containerRuntime.writeGCDataAtRoot;
195
+ }
196
+
191
197
  protected registry: IFluidDataStoreRegistry | undefined;
192
198
 
193
199
  protected detachedRuntimeCreation = false;
@@ -381,7 +387,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
381
387
  this.channel?.processSignal(message, local);
382
388
  }
383
389
 
384
- public getQuorum(): IQuorum {
390
+ public getQuorum(): IQuorumClients {
385
391
  return this._containerRuntime.getQuorum();
386
392
  }
387
393
 
@@ -416,8 +422,10 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
416
422
  const attributes = createAttributes(pkg, isRootDataStore, this.disableIsolatedChannels);
417
423
  addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
418
424
 
419
- // Add GC details to the summary.
420
- addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
425
+ // Add GC data to the summary if it's not written at the root.
426
+ if (!this.writeGCDataAtRoot) {
427
+ addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
428
+ }
421
429
 
422
430
  // If we are not referenced, mark the summary tree as unreferenced. Also, update unreferenced blob
423
431
  // size in the summary stats with the blobs size of this data store.
@@ -489,9 +497,19 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
489
497
  }
490
498
  }
491
499
 
500
+ /**
501
+ * Called when a new outbound reference is added to another node. This is used by garbage collection to identify
502
+ * all references added in the system.
503
+ * @param srcHandle - The handle of the node that added the reference.
504
+ * @param outboundHandle - The handle of the outbound node that is referenced.
505
+ */
506
+ public addedGCOutboundReference(srcHandle: IFluidHandle, outboundHandle: IFluidHandle) {
507
+ this._containerRuntime.addedGCOutboundReference(srcHandle, outboundHandle);
508
+ }
509
+
492
510
  /**
493
511
  * Updates the used routes of the channel and its child contexts. The channel must be loaded before calling this.
494
- * It is called in these two scenarions:
512
+ * It is called in these two scenarios:
495
513
  * 1. When the used routes of the data store is updated and the data store is loaded.
496
514
  * 2. When the data store is realized. This updates the channel's used routes as per last GC run.
497
515
  */
@@ -575,7 +593,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
575
593
  try
576
594
  {
577
595
  assert(!this.detachedRuntimeCreation, 0x148 /* "Detached runtime creation on runtime bind" */);
578
- assert(this.channelDeferred !== undefined, 0x149 /* "Undefined channel defferal" */);
596
+ assert(this.channelDeferred !== undefined, 0x149 /* "Undefined channel deferral" */);
579
597
  assert(this.pkg !== undefined, 0x14a /* "Undefined package path" */);
580
598
 
581
599
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -627,6 +645,13 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
627
645
 
628
646
  protected abstract getInitialSnapshotDetails(): Promise<ISnapshotDetails>;
629
647
 
648
+ /**
649
+ * @deprecated - Sets the datastore as root, for aliasing purposes: #7948
650
+ * This method should not be used outside of the aliasing context.
651
+ * It will be removed, as the source of truth for this flag will be the aliasing blob.
652
+ */
653
+ public abstract setRoot(): void;
654
+
630
655
  public abstract getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails>;
631
656
 
632
657
  public reSubmit(contents: any, localOpMetadata: unknown) {
@@ -672,9 +697,12 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
672
697
  }
673
698
 
674
699
  export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
700
+ private isRootDataStore: boolean | undefined;
701
+
675
702
  constructor(
676
703
  id: string,
677
704
  private readonly initSnapshotValue: ISnapshotTree | string | undefined,
705
+ private readonly getBaseSummaryGCDetails: () => Promise<IGarbageCollectionSummaryDetails | undefined>,
678
706
  runtime: ContainerRuntime,
679
707
  storage: IDocumentStorageService,
680
708
  scope: FluidObject,
@@ -742,7 +770,7 @@ export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
742
770
  * data stores in older documents are not garbage collected incorrectly. This may lead to additional
743
771
  * roots in the document but they won't break.
744
772
  */
745
- isRootDataStore = attributes.isRootDataStore ?? true;
773
+ isRootDataStore = this.isRootDataStore === true || (attributes.isRootDataStore ?? true);
746
774
 
747
775
  if (hasIsolatedChannels(attributes)) {
748
776
  tree = tree.trees[channelsTreeName];
@@ -760,16 +788,7 @@ export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
760
788
  });
761
789
 
762
790
  private readonly gcDetailsInInitialSummaryP = new LazyPromise<IGarbageCollectionSummaryDetails>(async () => {
763
- // If the initial snapshot is undefined or string, the snapshot is in old format and won't have GC details.
764
- if (!(!this.initSnapshotValue || typeof this.initSnapshotValue === "string")
765
- && this.initSnapshotValue.blobs[gcBlobKey] !== undefined) {
766
- return readAndParse<IGarbageCollectionSummaryDetails>(
767
- this.storage,
768
- this.initSnapshotValue.blobs[gcBlobKey],
769
- );
770
- } else {
771
- return {};
772
- }
791
+ return (await this.getBaseSummaryGCDetails()) ?? {};
773
792
  });
774
793
 
775
794
  protected async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
@@ -783,6 +802,15 @@ export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
783
802
  public generateAttachMessage(): IAttachMessage {
784
803
  throw new Error("Cannot attach remote store");
785
804
  }
805
+
806
+ /**
807
+ * @deprecated - Sets the datastore as root, for aliasing purposes: #7948
808
+ * This method should not be used outside of the aliasing context.
809
+ * It will be removed, as the source of truth for this flag will be the aliasing blob.
810
+ */
811
+ public setRoot(): void {
812
+ this.isRootDataStore = true;
813
+ }
786
814
  }
787
815
 
788
816
  /**
@@ -850,8 +878,10 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
850
878
  );
851
879
  addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
852
880
 
853
- // Add GC details to the summary.
854
- addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
881
+ // Add GC data to the summary if it's not written at the root.
882
+ if (!this.writeGCDataAtRoot) {
883
+ addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
884
+ }
855
885
 
856
886
  // Attach message needs the summary in ITree format. Convert the ISummaryTree into an ITree.
857
887
  const snapshot = convertSummaryTreeToITree(summarizeResult.summary);
@@ -882,7 +912,7 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
882
912
  // If there is no isRootDataStore in the attributes blob, set it to true. This ensures that data
883
913
  // stores in older documents are not garbage collected incorrectly. This may lead to additional
884
914
  // roots in the document but they won't break.
885
- this.isRootDataStore = attributes.isRootDataStore ?? true;
915
+ this.isRootDataStore = this.isRootDataStore || (attributes.isRootDataStore ?? true);
886
916
  }
887
917
  }
888
918
  assert(this.pkg !== undefined, 0x152 /* "pkg should be available in local data store" */);
@@ -900,6 +930,15 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
900
930
  // Local data store does not have initial summary.
901
931
  return {};
902
932
  }
933
+
934
+ /**
935
+ * @deprecated - Sets the datastore as root, for aliasing purposes: #7948
936
+ * This method should not be used outside of the aliasing context.
937
+ * It will be removed, as the source of truth for this flag will be the aliasing blob.
938
+ */
939
+ public setRoot(): void {
940
+ this.isRootDataStore = true;
941
+ }
903
942
  }
904
943
 
905
944
  /**
package/src/dataStores.ts CHANGED
@@ -8,8 +8,6 @@ import { DataCorruptionError, extractSafePropertiesFromMessage } from "@fluidfra
8
8
  import {
9
9
  ISequencedDocumentMessage,
10
10
  ISnapshotTree,
11
- ITreeEntry,
12
- SummaryType,
13
11
  } from "@fluidframework/protocol-definitions";
14
12
  import {
15
13
  channelsTreeName,
@@ -21,6 +19,7 @@ import {
21
19
  IFluidDataStoreChannel,
22
20
  IFluidDataStoreContextDetached,
23
21
  IGarbageCollectionData,
22
+ IGarbageCollectionSummaryDetails,
24
23
  IInboundSignalMessage,
25
24
  InboundAttachMessage,
26
25
  ISummarizeResult,
@@ -28,7 +27,6 @@ import {
28
27
  } from "@fluidframework/runtime-definitions";
29
28
  import {
30
29
  convertSnapshotTreeToSummaryTree,
31
- convertSummaryTreeToITree,
32
30
  convertToSummaryTree,
33
31
  create404Response,
34
32
  responseToException,
@@ -37,10 +35,9 @@ import {
37
35
  import { ChildLogger, TelemetryDataTag } from "@fluidframework/telemetry-utils";
38
36
  import { AttachState } from "@fluidframework/container-definitions";
39
37
  import { BlobCacheStorageService, buildSnapshotTree } from "@fluidframework/driver-utils";
40
- import { assert, Lazy } from "@fluidframework/common-utils";
38
+ import { assert, Lazy, LazyPromise } from "@fluidframework/common-utils";
41
39
  import { v4 as uuid } from "uuid";
42
- import { TreeTreeEntry } from "@fluidframework/protocol-base";
43
- import { GCDataBuilder, getChildNodesUsedRoutes } from "@fluidframework/garbage-collector";
40
+ import { GCDataBuilder, unpackChildNodesUsedRoutes } from "@fluidframework/garbage-collector";
44
41
  import { DataStoreContexts } from "./dataStoreContexts";
45
42
  import { ContainerRuntime } from "./containerRuntime";
46
43
  import {
@@ -53,6 +50,32 @@ import {
53
50
  import { IContainerRuntimeMetadata, nonDataStorePaths, rootHasIsolatedChannels } from "./summaryFormat";
54
51
  import { IUsedStateStats } from "./garbageCollection";
55
52
 
53
+ type PendingAliasResolve = (success: boolean) => void;
54
+
55
+ /**
56
+ * Interface for an op to be used for assigning an
57
+ * alias to a datastore
58
+ */
59
+ interface IDataStoreAliasMessage {
60
+ /** The internal id of the datastore */
61
+ readonly internalId: string;
62
+ /** The alias name to be assigned to the datastore */
63
+ readonly alias: string;
64
+ }
65
+
66
+ /**
67
+ * Type guard that returns true if the given alias message is actually an instance of
68
+ * a class which implements @see IDataStoreAliasMessage
69
+ * @param maybeDataStoreAliasMessage - message object to be validated
70
+ * @returns True if the @see IDataStoreAliasMessage is fully implemented, false otherwise
71
+ */
72
+ const isDataStoreAliasMessage = (
73
+ maybeDataStoreAliasMessage: any,
74
+ ): maybeDataStoreAliasMessage is IDataStoreAliasMessage => {
75
+ return typeof maybeDataStoreAliasMessage?.internalId === "string"
76
+ && typeof maybeDataStoreAliasMessage?.alias === "string";
77
+ };
78
+
56
79
  /**
57
80
  * This class encapsulates data store handling. Currently it is only used by the container runtime,
58
81
  * but eventually could be hosted on any channel once we formalize the channel api boundary.
@@ -67,6 +90,13 @@ export class DataStores implements IDisposable {
67
90
 
68
91
  private readonly disposeOnce = new Lazy<void>(() => this.contexts.dispose());
69
92
 
93
+ public readonly containerLoadStats: {
94
+ // number of dataStores during loadContainer
95
+ readonly containerLoadDataStoreCount: number;
96
+ // number of unreferenced dataStores during loadContainer
97
+ readonly referencedDataStoreCount: number;
98
+ };
99
+
70
100
  constructor(
71
101
  private readonly baseSnapshot: ISnapshotTree | undefined,
72
102
  private readonly runtime: ContainerRuntime,
@@ -75,12 +105,24 @@ export class DataStores implements IDisposable {
75
105
  (id: string, createParam: CreateChildSummarizerNodeParam) => CreateChildSummarizerNodeFn,
76
106
  private readonly deleteChildSummarizerNodeFn: (id: string) => void,
77
107
  baseLogger: ITelemetryBaseLogger,
108
+ getDataStoreBaseGCDetails: () => Promise<Map<string, IGarbageCollectionSummaryDetails>>,
109
+ private readonly dataStoreChanged: (id: string) => void,
110
+ private readonly aliasMap: Map<string, string>,
78
111
  private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
79
112
  ) {
80
113
  this.logger = ChildLogger.create(baseLogger);
114
+
115
+ const baseDataStoresGCDetailsP = new LazyPromise(async () => {
116
+ return getDataStoreBaseGCDetails();
117
+ });
118
+ // Returns the base summary GC details for the data store with the given id.
119
+ const dataStoreBaseGCDetails = async (dataStoreId: string) => {
120
+ const baseGCDetails = await baseDataStoresGCDetailsP;
121
+ return baseGCDetails.get(dataStoreId);
122
+ };
123
+
81
124
  // Extract stores stored inside the snapshot
82
125
  const fluidDataStores = new Map<string, ISnapshotTree>();
83
-
84
126
  if (baseSnapshot) {
85
127
  for (const [key, value] of Object.entries(baseSnapshot.trees)) {
86
128
  fluidDataStores.set(key, value);
@@ -101,6 +143,7 @@ export class DataStores implements IDisposable {
101
143
  dataStoreContext = new RemotedFluidDataStoreContext(
102
144
  key,
103
145
  value,
146
+ async () => dataStoreBaseGCDetails(key),
104
147
  this.runtime,
105
148
  this.runtime.storage,
106
149
  this.runtime.scope,
@@ -124,11 +167,14 @@ export class DataStores implements IDisposable {
124
167
  }
125
168
  this.contexts.addBoundOrRemoted(dataStoreContext);
126
169
  }
127
- this.logger.sendTelemetryEvent({
128
- eventName: "ContainerLoadStats",
129
- dataStoreCount: fluidDataStores.size,
170
+ this.containerLoadStats = {
171
+ containerLoadDataStoreCount: fluidDataStores.size,
130
172
  referencedDataStoreCount: fluidDataStores.size - unreferencedDataStoreCount,
131
- });
173
+ };
174
+ }
175
+
176
+ public aliases(): ReadonlyMap<string, string> {
177
+ return this.aliasMap;
132
178
  }
133
179
 
134
180
  public processAttachMessage(message: ISequencedDocumentMessage, local: boolean) {
@@ -170,6 +216,8 @@ export class DataStores implements IDisposable {
170
216
  const remotedFluidDataStoreContext = new RemotedFluidDataStoreContext(
171
217
  attachMessage.id,
172
218
  snapshotTree,
219
+ // New data stores begin with empty GC details since GC hasn't run on them yet.
220
+ async () => { return {}; },
173
221
  this.runtime,
174
222
  new BlobCacheStorageService(this.runtime.storage, flatBlobs),
175
223
  this.runtime.scope,
@@ -188,7 +236,6 @@ export class DataStores implements IDisposable {
188
236
  }),
189
237
  pkg);
190
238
 
191
- // Resolve pending gets and store off any new ones
192
239
  this.contexts.addBoundOrRemoted(remotedFluidDataStoreContext);
193
240
 
194
241
  // Equivalent of nextTick() - Prefetch once all current ops have completed
@@ -196,6 +243,55 @@ export class DataStores implements IDisposable {
196
243
  Promise.resolve().then(async () => remotedFluidDataStoreContext.realize());
197
244
  }
198
245
 
246
+ public processAliasMessage(
247
+ message: ISequencedDocumentMessage,
248
+ localOpMetadata: unknown,
249
+ local: boolean,
250
+ ): void {
251
+ const aliasMessage = message.contents as IDataStoreAliasMessage;
252
+ if (!isDataStoreAliasMessage(aliasMessage)) {
253
+ throw new DataCorruptionError(
254
+ "malformedDataStoreAliasMessage",
255
+ {
256
+ ...extractSafePropertiesFromMessage(message),
257
+ },
258
+ );
259
+ }
260
+
261
+ const resolve = localOpMetadata as PendingAliasResolve;
262
+ const aliasResult = this.processAliasMessageCore(aliasMessage);
263
+ if (local) {
264
+ resolve(aliasResult);
265
+ }
266
+ }
267
+
268
+ private processAliasMessageCore(aliasMessage: IDataStoreAliasMessage): boolean {
269
+ const existingMapping = this.aliasMap.get(aliasMessage.alias);
270
+ if (existingMapping !== undefined) {
271
+ return false;
272
+ }
273
+
274
+ // Unlikely scenario, but we may receive an alias OP with the alias value
275
+ // equal to one of the ids supplied to `createRootDataStore` in the past
276
+ const maybeContextWithAliasAsId = this.contexts.get(aliasMessage.alias);
277
+ if (maybeContextWithAliasAsId !== undefined) {
278
+ return false;
279
+ }
280
+
281
+ const currentContext = this.contexts.get(aliasMessage.internalId);
282
+ if (currentContext === undefined) {
283
+ this.logger.sendErrorEvent({
284
+ eventName: "AliasFluidDataStoreNotFound",
285
+ fluidDataStoreId: aliasMessage.internalId,
286
+ });
287
+ return false;
288
+ }
289
+
290
+ this.aliasMap.set(aliasMessage.alias, currentContext.id);
291
+ currentContext.setRoot();
292
+ return true;
293
+ }
294
+
199
295
  public bindFluidDataStore(fluidDataStoreRuntime: IFluidDataStoreChannel): void {
200
296
  const id = fluidDataStoreRuntime.id;
201
297
  const localContext = this.contexts.getUnbound(id);
@@ -281,11 +377,15 @@ export class DataStores implements IDisposable {
281
377
  const context = this.contexts.get(envelope.address);
282
378
  assert(!!context, 0x162 /* "There should be a store context for the op" */);
283
379
  context.process(transformed, local, localMessageMetadata);
380
+
381
+ // Notify that a data store changed. This is used to detect if a deleted data store is being used.
382
+ this.dataStoreChanged(envelope.address);
284
383
  }
285
384
 
286
385
  public async getDataStore(id: string, wait: boolean): Promise<FluidDataStoreContext> {
287
- const context = await this.contexts.getBoundOrRemoted(id, wait);
386
+ const internalId = this.aliasMap.get(id) ?? id;
288
387
 
388
+ const context = await this.contexts.getBoundOrRemoted(internalId, wait);
289
389
  if (context === undefined) {
290
390
  // The requested data store does not exits. Throw a 404 response exception.
291
391
  const request = { url: id };
@@ -339,44 +439,6 @@ export class DataStores implements IDisposable {
339
439
  }
340
440
  }
341
441
 
342
- /**
343
- * Notifies this object to take the snapshot of the container.
344
- * @deprecated - Use summarize to get summary of the container runtime.
345
- */
346
- public async snapshot(): Promise<ITreeEntry[]> {
347
- // Iterate over each store and ask it to snapshot
348
- const fluidDataStoreSnapshotsP = Array.from(this.contexts).map(async ([fluidDataStoreId, value]) => {
349
- const summaryTree = await value.summarize(true /* fullTree */, false /* trackState */);
350
- assert(
351
- summaryTree.summary.type === SummaryType.Tree,
352
- 0x164 /* "summarize should always return a tree when fullTree is true" */);
353
- // back-compat summary - Remove this once snapshot is removed.
354
- const snapshot = convertSummaryTreeToITree(summaryTree.summary);
355
-
356
- // If ID exists then previous commit is still valid
357
- return {
358
- fluidDataStoreId,
359
- snapshot,
360
- };
361
- });
362
-
363
- const entries: ITreeEntry[] = [];
364
-
365
- // Add in module references to the store snapshots
366
- const fluidDataStoreSnapshots = await Promise.all(fluidDataStoreSnapshotsP);
367
-
368
- // Sort for better diffing of snapshots (in replay tool, used to find bugs in snapshotting logic)
369
- fluidDataStoreSnapshots.sort((a, b) => a?.fluidDataStoreId.localeCompare(b.fluidDataStoreId));
370
-
371
- for (const fluidDataStoreSnapshot of fluidDataStoreSnapshots) {
372
- entries.push(new TreeTreeEntry(
373
- fluidDataStoreSnapshot.fluidDataStoreId,
374
- fluidDataStoreSnapshot.snapshot,
375
- ));
376
- }
377
- return entries;
378
- }
379
-
380
442
  public get size(): number {
381
443
  return this.contexts.size;
382
444
  }
@@ -439,8 +501,8 @@ export class DataStores implements IDisposable {
439
501
  /**
440
502
  * Generates data used for garbage collection. It does the following:
441
503
  * 1. Calls into each child data store context to get its GC data.
442
- * 2. Prefixs the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
443
- * idenfied as belonging to the child.
504
+ * 2. Prefixes the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
505
+ * identified as belonging to the child.
444
506
  * 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
445
507
  * the GC data of this channel.
446
508
  * @param fullGC - true to bypass optimizations and force full generation of GC data.
@@ -474,7 +536,7 @@ export class DataStores implements IDisposable {
474
536
  */
475
537
  public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): IUsedStateStats {
476
538
  // Get a map of data store ids to routes used in it.
477
- const usedDataStoreRoutes = getChildNodesUsedRoutes(usedRoutes);
539
+ const usedDataStoreRoutes = unpackChildNodesUsedRoutes(usedRoutes);
478
540
 
479
541
  // Verify that the used routes are correct.
480
542
  for (const [id] of usedDataStoreRoutes) {