@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
@@ -0,0 +1,187 @@
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 { IFluidRouter, IRequest, IResponse } from "@fluidframework/core-interfaces";
10
+ import { 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
+ /**
40
+ * Encapsulates the return codes of the aliasing API
41
+ */
42
+ export enum AliasResult {
43
+ /**
44
+ * The datastore has been successfully aliased
45
+ */
46
+ Success = "Success",
47
+ /**
48
+ * There is already a datastore bound to the provided alias
49
+ */
50
+ Conflict = "Conflict",
51
+ /**
52
+ * The datastore is currently in the process of being aliased
53
+ */
54
+ Aliasing = "Aliasing",
55
+ /**
56
+ * The datastore has been attempted to be aliased before
57
+ */
58
+ AlreadyAliased = "AlreadyAliased",
59
+ }
60
+
61
+ /**
62
+ * A fluid router with the capability of being assigned an alias
63
+ */
64
+ export interface IDataStore extends IFluidRouter {
65
+ /**
66
+ * Attempt to assign an alias to the datastore.
67
+ * If the operation succeeds, the datastore can be referenced
68
+ * by the supplied alias.
69
+ *
70
+ * @param alias - Given alias for this datastore.
71
+ */
72
+ trySetAlias(alias: string): Promise<AliasResult>;
73
+ }
74
+
75
+ export const channelToDataStore = (
76
+ fluidDataStoreChannel: IFluidDataStoreChannel,
77
+ internalId: string,
78
+ runtime: ContainerRuntime,
79
+ datastores: DataStores,
80
+ logger: ITelemetryLogger,
81
+ ): IDataStore => new DataStore(fluidDataStoreChannel, internalId, runtime, datastores, logger);
82
+
83
+ enum AliasState {
84
+ Aliased = "Aliased",
85
+ Aliasing = "Aliasing",
86
+ None = "None",
87
+ }
88
+
89
+ class DataStore implements IDataStore {
90
+ private aliasState: AliasState = AliasState.None;
91
+ private alias: string | undefined;
92
+
93
+ async trySetAlias(alias: string): Promise<AliasResult> {
94
+ switch (this.aliasState) {
95
+ // If we're already aliasing, throw an exception
96
+ case AliasState.Aliasing:
97
+ return AliasResult.Aliasing;
98
+ // If this datastore is already aliased, return true only if this
99
+ // is a repeated call for the same alias
100
+ case AliasState.Aliased:
101
+ return this.alias === alias ? AliasResult.Success : AliasResult.AlreadyAliased;
102
+ // There is no current or past alias operation for this datastore,
103
+ // it is safe to continue execution
104
+ case AliasState.None: break;
105
+ default: unreachableCase(this.aliasState);
106
+ }
107
+
108
+ this.aliasState = AliasState.Aliasing;
109
+ const message: IDataStoreAliasMessage = {
110
+ internalId: this.internalId,
111
+ alias,
112
+ };
113
+
114
+ this.fluidDataStoreChannel.bindToContext();
115
+
116
+ if (this.runtime.attachState === AttachState.Detached) {
117
+ const localResult = this.datastores.processAliasMessageCore(message);
118
+ // Explicitly Lock-out future attempts of aliasing,
119
+ // regardless of result
120
+ this.aliasState = AliasState.Aliased;
121
+ return localResult ? AliasResult.Success : AliasResult.Conflict;
122
+ }
123
+
124
+ const aliased = await this.ackBasedPromise<boolean>((resolve) => {
125
+ this.runtime.submitDataStoreAliasOp(message, resolve);
126
+ }).then((succeeded) => {
127
+ // Explicitly Lock-out future attempts of aliasing,
128
+ // regardless of result
129
+ this.aliasState = AliasState.Aliased;
130
+ if (succeeded) {
131
+ this.alias = alias;
132
+ }
133
+
134
+ return succeeded;
135
+ }).catch((error) => {
136
+ this.logger.sendErrorEvent({
137
+ eventName: "AliasingException",
138
+ alias: {
139
+ value: alias,
140
+ tag: TelemetryDataTag.UserData,
141
+ },
142
+ internalId: {
143
+ value: this.internalId,
144
+ tag: TelemetryDataTag.PackageData,
145
+ },
146
+ }, error);
147
+ this.aliasState = AliasState.None;
148
+ return false;
149
+ });
150
+
151
+ return aliased ? AliasResult.Success : AliasResult.Conflict;
152
+ }
153
+
154
+ async request(request: IRequest): Promise<IResponse> {
155
+ return this.fluidDataStoreChannel.request(request);
156
+ }
157
+
158
+ constructor(
159
+ private readonly fluidDataStoreChannel: IFluidDataStoreChannel,
160
+ private readonly internalId: string,
161
+ private readonly runtime: ContainerRuntime,
162
+ private datastores: DataStores,
163
+ private readonly logger: ITelemetryLogger,
164
+ ) { }
165
+ public get IFluidRouter() { return this.fluidDataStoreChannel; }
166
+
167
+ private async ackBasedPromise<T>(
168
+ executor: (resolve: (value: T | PromiseLike<T>) => void,
169
+ reject: (reason?: any) => void) => void,
170
+ ): Promise<T> {
171
+ let rejectBecauseDispose: () => void;
172
+ return new Promise<T>((resolve, reject) => {
173
+ rejectBecauseDispose =
174
+ () => reject(new Error("ContainerRuntime disposed while this ack-based Promise was pending"));
175
+
176
+ if (this.runtime.disposed) {
177
+ rejectBecauseDispose();
178
+ return;
179
+ }
180
+
181
+ this.runtime.on("dispose", rejectBecauseDispose);
182
+ executor(resolve, reject);
183
+ }).finally(() => {
184
+ this.runtime.off("dispose", rejectBecauseDispose);
185
+ });
186
+ }
187
+ }
@@ -750,7 +750,7 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
750
750
  });
751
751
  }
752
752
 
753
- private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
753
+ private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
754
754
  let tree: ISnapshotTree | undefined;
755
755
  let isRootDataStore = true;
756
756
 
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,11 @@ 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: (dataStorePath: string, packagePath?: readonly string[]) => void,
119
95
  private readonly aliasMap: Map<string, string>,
120
96
  private readonly writeGCDataAtRoot: boolean,
121
97
  private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
@@ -290,7 +266,7 @@ export class DataStores implements IDisposable {
290
266
  }
291
267
  }
292
268
 
293
- private processAliasMessageCore(aliasMessage: IDataStoreAliasMessage): boolean {
269
+ public processAliasMessageCore(aliasMessage: IDataStoreAliasMessage): boolean {
294
270
  if (this.alreadyProcessed(aliasMessage.alias)) {
295
271
  return false;
296
272
  }
@@ -411,7 +387,7 @@ export class DataStores implements IDisposable {
411
387
  context.process(transformed, local, localMessageMetadata);
412
388
 
413
389
  // Notify that a data store changed. This is used to detect if a deleted data store is being used.
414
- this.dataStoreChanged(envelope.address);
390
+ this.dataStoreChanged(`/${envelope.address}`, context.isLoaded ? context.packagePath : undefined);
415
391
  }
416
392
 
417
393
  public async getDataStore(id: string, wait: boolean): Promise<FluidDataStoreContext> {
@@ -582,9 +558,8 @@ export class DataStores implements IDisposable {
582
558
  * @param usedRoutes - The routes that are used in all data stores in this Container.
583
559
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
584
560
  * 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
561
  */
587
- public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): IUsedStateStats {
562
+ public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number) {
588
563
  // Get a map of data store ids to routes used in it.
589
564
  const usedDataStoreRoutes = unpackChildNodesUsedRoutes(usedRoutes);
590
565
 
@@ -597,13 +572,6 @@ export class DataStores implements IDisposable {
597
572
  for (const [contextId, context] of this.contexts) {
598
573
  context.updateUsedRoutes(usedDataStoreRoutes.get(contextId) ?? [], gcTimestamp);
599
574
  }
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
575
  }
608
576
 
609
577
  /**
@@ -635,6 +603,18 @@ export class DataStores implements IDisposable {
635
603
  }
636
604
  return outboundRoutes;
637
605
  }
606
+
607
+ /**
608
+ * Returns the package path of the node with the given path. This is used by GC to log when an inactive / deleted
609
+ * node is used.
610
+ */
611
+ public getNodePackagePath(nodePath: string): readonly string[] | undefined {
612
+ // Currently, only return the data store package path for the node since GC is only interested in data stores.
613
+ const dataStoreId = nodePath.split("/")[1];
614
+ const context = this.contexts.get(dataStoreId);
615
+ assert(context !== undefined, "Data store with given id does not exist");
616
+ return context.isLoaded ? context.packagePath : undefined;
617
+ }
638
618
  }
639
619
 
640
620
  export function getSummaryForDatastores(