@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
@@ -0,0 +1,151 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { unreachableCase } from "@fluidframework/common-utils";
8
+ import { AttachState } from "@fluidframework/container-definitions";
9
+ import { IRequest, IResponse } from "@fluidframework/core-interfaces";
10
+ import { AliasResult, IDataStore, IFluidDataStoreChannel } from "@fluidframework/runtime-definitions";
11
+ import { TelemetryDataTag } from "@fluidframework/telemetry-utils";
12
+ import { ContainerRuntime } from "./containerRuntime";
13
+ import { DataStores } from "./dataStores";
14
+
15
+ /**
16
+ * Interface for an op to be used for assigning an
17
+ * alias to a datastore
18
+ */
19
+ export interface IDataStoreAliasMessage {
20
+ /** The internal id of the datastore */
21
+ readonly internalId: string;
22
+ /** The alias name to be assigned to the datastore */
23
+ readonly alias: string;
24
+ }
25
+
26
+ /**
27
+ * Type guard that returns true if the given alias message is actually an instance of
28
+ * a class which implements @see IDataStoreAliasMessage
29
+ * @param maybeDataStoreAliasMessage - message object to be validated
30
+ * @returns True if the @see IDataStoreAliasMessage is fully implemented, false otherwise
31
+ */
32
+ export const isDataStoreAliasMessage = (
33
+ maybeDataStoreAliasMessage: any,
34
+ ): maybeDataStoreAliasMessage is IDataStoreAliasMessage => {
35
+ return typeof maybeDataStoreAliasMessage?.internalId === "string"
36
+ && typeof maybeDataStoreAliasMessage?.alias === "string";
37
+ };
38
+
39
+ export const channelToDataStore = (
40
+ fluidDataStoreChannel: IFluidDataStoreChannel,
41
+ internalId: string,
42
+ runtime: ContainerRuntime,
43
+ datastores: DataStores,
44
+ logger: ITelemetryLogger,
45
+ ): IDataStore => new DataStore(fluidDataStoreChannel, internalId, runtime, datastores, logger);
46
+
47
+ enum AliasState {
48
+ Aliased = "Aliased",
49
+ Aliasing = "Aliasing",
50
+ None = "None",
51
+ }
52
+
53
+ class DataStore implements IDataStore {
54
+ private aliasState: AliasState = AliasState.None;
55
+ private alias: string | undefined;
56
+
57
+ async trySetAlias(alias: string): Promise<AliasResult> {
58
+ switch (this.aliasState) {
59
+ // If we're already aliasing, throw an exception
60
+ case AliasState.Aliasing:
61
+ return "Aliasing";
62
+ // If this datastore is already aliased, return true only if this
63
+ // is a repeated call for the same alias
64
+ case AliasState.Aliased:
65
+ return this.alias === alias ? "Success" : "AlreadyAliased";
66
+ // There is no current or past alias operation for this datastore,
67
+ // it is safe to continue execution
68
+ case AliasState.None: break;
69
+ default: unreachableCase(this.aliasState);
70
+ }
71
+
72
+ this.aliasState = AliasState.Aliasing;
73
+ const message: IDataStoreAliasMessage = {
74
+ internalId: this.internalId,
75
+ alias,
76
+ };
77
+
78
+ this.fluidDataStoreChannel.bindToContext();
79
+
80
+ if (this.runtime.attachState === AttachState.Detached) {
81
+ const localResult = this.datastores.processAliasMessageCore(message);
82
+ // Explicitly Lock-out future attempts of aliasing,
83
+ // regardless of result
84
+ this.aliasState = AliasState.Aliased;
85
+ return localResult ? "Success" : "Conflict";
86
+ }
87
+
88
+ const aliased = await this.ackBasedPromise<boolean>((resolve) => {
89
+ this.runtime.submitDataStoreAliasOp(message, resolve);
90
+ }).then((succeeded) => {
91
+ // Explicitly Lock-out future attempts of aliasing,
92
+ // regardless of result
93
+ this.aliasState = AliasState.Aliased;
94
+ if (succeeded) {
95
+ this.alias = alias;
96
+ }
97
+
98
+ return succeeded;
99
+ }).catch((error) => {
100
+ this.logger.sendErrorEvent({
101
+ eventName: "AliasingException",
102
+ alias: {
103
+ value: alias,
104
+ tag: TelemetryDataTag.UserData,
105
+ },
106
+ internalId: {
107
+ value: this.internalId,
108
+ tag: TelemetryDataTag.PackageData,
109
+ },
110
+ }, error);
111
+ this.aliasState = AliasState.None;
112
+ return false;
113
+ });
114
+
115
+ return aliased ? "Success" : "Conflict";
116
+ }
117
+
118
+ async request(request: IRequest): Promise<IResponse> {
119
+ return this.fluidDataStoreChannel.request(request);
120
+ }
121
+
122
+ constructor(
123
+ private readonly fluidDataStoreChannel: IFluidDataStoreChannel,
124
+ private readonly internalId: string,
125
+ private readonly runtime: ContainerRuntime,
126
+ private datastores: DataStores,
127
+ private readonly logger: ITelemetryLogger,
128
+ ) { }
129
+ public get IFluidRouter() { return this.fluidDataStoreChannel; }
130
+
131
+ private async ackBasedPromise<T>(
132
+ executor: (resolve: (value: T | PromiseLike<T>) => void,
133
+ reject: (reason?: any) => void) => void,
134
+ ): Promise<T> {
135
+ let rejectBecauseDispose: () => void;
136
+ return new Promise<T>((resolve, reject) => {
137
+ rejectBecauseDispose =
138
+ () => reject(new Error("ContainerRuntime disposed while this ack-based Promise was pending"));
139
+
140
+ if (this.runtime.disposed) {
141
+ rejectBecauseDispose();
142
+ return;
143
+ }
144
+
145
+ this.runtime.on("dispose", rejectBecauseDispose);
146
+ executor(resolve, reject);
147
+ }).finally(() => {
148
+ this.runtime.off("dispose", rejectBecauseDispose);
149
+ });
150
+ }
151
+ }
@@ -106,11 +106,6 @@ export function createAttributesBlob(
106
106
 
107
107
  interface ISnapshotDetails {
108
108
  pkg: readonly string[];
109
- /**
110
- * This tells whether a data store is root. Root data stores are never collected.
111
- * Non-root data stores may be collected if they are not used.
112
- */
113
- isRootDataStore: boolean;
114
109
  snapshot?: ISnapshotTree;
115
110
  }
116
111
 
@@ -212,7 +207,10 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
212
207
  }
213
208
 
214
209
  public async isRoot(): Promise<boolean> {
215
- return (await this.getInitialSnapshotDetails()).isRootDataStore;
210
+ // This call updates this.isRootDataStore if it has not yet been updated
211
+ // The initial value is stored in the initial snapshot of the data store
212
+ await this.getInitialSnapshotDetails();
213
+ return this.isRootDataStore;
216
214
  }
217
215
 
218
216
  protected registry: IFluidDataStoreRegistry | undefined;
@@ -225,6 +223,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
225
223
  protected channelDeferred: Deferred<IFluidDataStoreChannel> | undefined;
226
224
  private _baseSnapshot: ISnapshotTree | undefined;
227
225
  protected _attachState: AttachState;
226
+ protected isRootDataStore: boolean = false;
228
227
  protected readonly summarizerNode: ISummarizerNodeWithGC;
229
228
  private readonly subLogger: ITelemetryLogger;
230
229
  private readonly thresholdOpsCounter: ThresholdCounter;
@@ -450,8 +449,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
450
449
  }
451
450
 
452
451
  // Add data store's attributes to the summary.
453
- const { pkg, isRootDataStore } = await this.getInitialSnapshotDetails();
454
- const attributes = createAttributes(pkg, isRootDataStore, this.disableIsolatedChannels);
452
+ const { pkg } = await this.getInitialSnapshotDetails();
453
+ const attributes = createAttributes(pkg, this.isRootDataStore, this.disableIsolatedChannels);
455
454
  addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
456
455
 
457
456
  // Add GC data to the summary if it's not written at the root.
@@ -729,7 +728,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
729
728
  }
730
729
 
731
730
  export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
732
- private isRootDataStore: boolean | undefined;
733
731
  private readonly initSnapshotValue: ISnapshotTree | string | undefined;
734
732
  private readonly baseGCDetailsP: Promise<IGarbageCollectionDetailsBase>;
735
733
 
@@ -750,7 +748,7 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
750
748
  });
751
749
  }
752
750
 
753
- private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
751
+ private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
754
752
  let tree: ISnapshotTree | undefined;
755
753
  let isRootDataStore = true;
756
754
 
@@ -804,11 +802,12 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
804
802
  }
805
803
  }
806
804
 
805
+ this.isRootDataStore = isRootDataStore;
806
+
807
807
  return {
808
808
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
809
809
  pkg: this.pkg!,
810
810
  snapshot: tree,
811
- isRootDataStore,
812
811
  };
813
812
  });
814
813
 
@@ -846,7 +845,6 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
846
845
  */
847
846
  export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
848
847
  private readonly snapshotTree: ISnapshotTree | undefined;
849
- protected isRootDataStore: boolean | undefined;
850
848
  /**
851
849
  * @deprecated 0.16 Issue #1635, #3631
852
850
  */
@@ -862,7 +860,7 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
862
860
  );
863
861
 
864
862
  this.snapshotTree = props.snapshotTree;
865
- this.isRootDataStore = props.isRootDataStore;
863
+ this.isRootDataStore = props.isRootDataStore ?? false;
866
864
  this.createProps = props.createProps;
867
865
  this.attachListeners();
868
866
  }
@@ -938,7 +936,6 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
938
936
  return {
939
937
  pkg: this.pkg,
940
938
  snapshot,
941
- isRootDataStore: this.isRootDataStore,
942
939
  };
943
940
  }
944
941
 
package/src/dataStores.ts CHANGED
@@ -50,34 +50,10 @@ import {
50
50
  LocalDetachedFluidDataStoreContext,
51
51
  } from "./dataStoreContext";
52
52
  import { IContainerRuntimeMetadata, nonDataStorePaths, rootHasIsolatedChannels } from "./summaryFormat";
53
- import { IUsedStateStats } from "./garbageCollection";
53
+ import { IDataStoreAliasMessage, isDataStoreAliasMessage } from "./dataStore";
54
54
 
55
55
  type PendingAliasResolve = (success: boolean) => void;
56
56
 
57
- /**
58
- * Interface for an op to be used for assigning an
59
- * alias to a datastore
60
- */
61
- interface IDataStoreAliasMessage {
62
- /** The internal id of the datastore */
63
- readonly internalId: string;
64
- /** The alias name to be assigned to the datastore */
65
- readonly alias: string;
66
- }
67
-
68
- /**
69
- * Type guard that returns true if the given alias message is actually an instance of
70
- * a class which implements @see IDataStoreAliasMessage
71
- * @param maybeDataStoreAliasMessage - message object to be validated
72
- * @returns True if the @see IDataStoreAliasMessage is fully implemented, false otherwise
73
- */
74
- const isDataStoreAliasMessage = (
75
- maybeDataStoreAliasMessage: any,
76
- ): maybeDataStoreAliasMessage is IDataStoreAliasMessage => {
77
- return typeof maybeDataStoreAliasMessage?.internalId === "string"
78
- && typeof maybeDataStoreAliasMessage?.alias === "string";
79
- };
80
-
81
57
  /**
82
58
  * This class encapsulates data store handling. Currently it is only used by the container runtime,
83
59
  * but eventually could be hosted on any channel once we formalize the channel api boundary.
@@ -111,11 +87,12 @@ export class DataStores implements IDisposable {
111
87
  private readonly runtime: ContainerRuntime,
112
88
  private readonly submitAttachFn: (attachContent: any) => void,
113
89
  private readonly getCreateChildSummarizerNodeFn:
114
- (id: string, createParam: CreateChildSummarizerNodeParam) => CreateChildSummarizerNodeFn,
90
+ (id: string, createParam: CreateChildSummarizerNodeParam) => CreateChildSummarizerNodeFn,
115
91
  private readonly deleteChildSummarizerNodeFn: (id: string) => void,
116
92
  baseLogger: ITelemetryBaseLogger,
117
93
  getBaseGCDetails: () => Promise<Map<string, IGarbageCollectionDetailsBase>>,
118
- private readonly dataStoreChanged: (id: string) => void,
94
+ private readonly dataStoreChanged: (
95
+ dataStorePath: string, timestampMs: number, packagePath?: readonly string[]) => void,
119
96
  private readonly aliasMap: Map<string, string>,
120
97
  private readonly writeGCDataAtRoot: boolean,
121
98
  private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
@@ -290,7 +267,7 @@ export class DataStores implements IDisposable {
290
267
  }
291
268
  }
292
269
 
293
- private processAliasMessageCore(aliasMessage: IDataStoreAliasMessage): boolean {
270
+ public processAliasMessageCore(aliasMessage: IDataStoreAliasMessage): boolean {
294
271
  if (this.alreadyProcessed(aliasMessage.alias)) {
295
272
  return false;
296
273
  }
@@ -411,7 +388,11 @@ export class DataStores implements IDisposable {
411
388
  context.process(transformed, local, localMessageMetadata);
412
389
 
413
390
  // Notify that a data store changed. This is used to detect if a deleted data store is being used.
414
- this.dataStoreChanged(envelope.address);
391
+ this.dataStoreChanged(
392
+ `/${envelope.address}`,
393
+ message.timestamp,
394
+ context.isLoaded ? context.packagePath : undefined,
395
+ );
415
396
  }
416
397
 
417
398
  public async getDataStore(id: string, wait: boolean): Promise<FluidDataStoreContext> {
@@ -582,9 +563,8 @@ export class DataStores implements IDisposable {
582
563
  * @param usedRoutes - The routes that are used in all data stores in this Container.
583
564
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
584
565
  * unreferenced as part of this GC run, this should be used to update the time when it happens.
585
- * @returns the statistics of the used state of the data stores.
586
566
  */
587
- public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): IUsedStateStats {
567
+ public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number) {
588
568
  // Get a map of data store ids to routes used in it.
589
569
  const usedDataStoreRoutes = unpackChildNodesUsedRoutes(usedRoutes);
590
570
 
@@ -597,13 +577,6 @@ export class DataStores implements IDisposable {
597
577
  for (const [contextId, context] of this.contexts) {
598
578
  context.updateUsedRoutes(usedDataStoreRoutes.get(contextId) ?? [], gcTimestamp);
599
579
  }
600
-
601
- // Return the number of data stores that are unused.
602
- const dataStoreCount = this.contexts.size;
603
- return {
604
- totalNodeCount: dataStoreCount,
605
- unusedNodeCount: dataStoreCount - usedDataStoreRoutes.size,
606
- };
607
580
  }
608
581
 
609
582
  /**
@@ -635,6 +608,18 @@ export class DataStores implements IDisposable {
635
608
  }
636
609
  return outboundRoutes;
637
610
  }
611
+
612
+ /**
613
+ * Returns the package path of the node with the given path. This is used by GC to log when an inactive / deleted
614
+ * node is used.
615
+ */
616
+ public getNodePackagePath(nodePath: string): readonly string[] | undefined {
617
+ // Currently, only return the data store package path for the node since GC is only interested in data stores.
618
+ const dataStoreId = nodePath.split("/")[1];
619
+ const context = this.contexts.get(dataStoreId);
620
+ assert(context !== undefined, 0x2b9 /* "Data store with given id does not exist" */);
621
+ return context.isLoaded ? context.packagePath : undefined;
622
+ }
638
623
  }
639
624
 
640
625
  export function getSummaryForDatastores(