@fluidframework/container-runtime 0.54.2 → 0.56.0-49831

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 (94) hide show
  1. package/.eslintrc.js +1 -1
  2. package/dist/blobManager.js +1 -1
  3. package/dist/blobManager.js.map +1 -1
  4. package/dist/containerRuntime.d.ts +20 -14
  5. package/dist/containerRuntime.d.ts.map +1 -1
  6. package/dist/containerRuntime.js +51 -58
  7. package/dist/containerRuntime.js.map +1 -1
  8. package/dist/dataStoreContext.d.ts +57 -33
  9. package/dist/dataStoreContext.d.ts.map +1 -1
  10. package/dist/dataStoreContext.js +44 -54
  11. package/dist/dataStoreContext.js.map +1 -1
  12. package/dist/dataStores.d.ts +12 -2
  13. package/dist/dataStores.d.ts.map +1 -1
  14. package/dist/dataStores.js +106 -32
  15. package/dist/dataStores.js.map +1 -1
  16. package/dist/garbageCollection.d.ts +49 -14
  17. package/dist/garbageCollection.d.ts.map +1 -1
  18. package/dist/garbageCollection.js +122 -26
  19. package/dist/garbageCollection.js.map +1 -1
  20. package/dist/index.d.ts +7 -7
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +12 -17
  23. package/dist/index.js.map +1 -1
  24. package/dist/packageVersion.d.ts +1 -1
  25. package/dist/packageVersion.d.ts.map +1 -1
  26. package/dist/packageVersion.js +1 -1
  27. package/dist/packageVersion.js.map +1 -1
  28. package/dist/runningSummarizer.d.ts.map +1 -1
  29. package/dist/runningSummarizer.js +2 -9
  30. package/dist/runningSummarizer.js.map +1 -1
  31. package/dist/summaryFormat.d.ts +2 -0
  32. package/dist/summaryFormat.d.ts.map +1 -1
  33. package/dist/summaryFormat.js +2 -4
  34. package/dist/summaryFormat.js.map +1 -1
  35. package/dist/summaryGenerator.d.ts +0 -5
  36. package/dist/summaryGenerator.d.ts.map +1 -1
  37. package/dist/summaryGenerator.js.map +1 -1
  38. package/dist/summaryManager.d.ts +1 -0
  39. package/dist/summaryManager.d.ts.map +1 -1
  40. package/dist/summaryManager.js +7 -2
  41. package/dist/summaryManager.js.map +1 -1
  42. package/garbageCollection.md +33 -0
  43. package/lib/blobManager.js +1 -1
  44. package/lib/blobManager.js.map +1 -1
  45. package/lib/containerRuntime.d.ts +20 -14
  46. package/lib/containerRuntime.d.ts.map +1 -1
  47. package/lib/containerRuntime.js +51 -58
  48. package/lib/containerRuntime.js.map +1 -1
  49. package/lib/dataStoreContext.d.ts +57 -33
  50. package/lib/dataStoreContext.d.ts.map +1 -1
  51. package/lib/dataStoreContext.js +42 -52
  52. package/lib/dataStoreContext.js.map +1 -1
  53. package/lib/dataStores.d.ts +12 -2
  54. package/lib/dataStores.d.ts.map +1 -1
  55. package/lib/dataStores.js +107 -33
  56. package/lib/dataStores.js.map +1 -1
  57. package/lib/garbageCollection.d.ts +49 -14
  58. package/lib/garbageCollection.d.ts.map +1 -1
  59. package/lib/garbageCollection.js +122 -26
  60. package/lib/garbageCollection.js.map +1 -1
  61. package/lib/index.d.ts +7 -7
  62. package/lib/index.d.ts.map +1 -1
  63. package/lib/index.js +6 -7
  64. package/lib/index.js.map +1 -1
  65. package/lib/packageVersion.d.ts +1 -1
  66. package/lib/packageVersion.d.ts.map +1 -1
  67. package/lib/packageVersion.js +1 -1
  68. package/lib/packageVersion.js.map +1 -1
  69. package/lib/runningSummarizer.d.ts.map +1 -1
  70. package/lib/runningSummarizer.js +2 -9
  71. package/lib/runningSummarizer.js.map +1 -1
  72. package/lib/summaryFormat.d.ts +2 -0
  73. package/lib/summaryFormat.d.ts.map +1 -1
  74. package/lib/summaryFormat.js +2 -4
  75. package/lib/summaryFormat.js.map +1 -1
  76. package/lib/summaryGenerator.d.ts +0 -5
  77. package/lib/summaryGenerator.d.ts.map +1 -1
  78. package/lib/summaryGenerator.js.map +1 -1
  79. package/lib/summaryManager.d.ts +1 -0
  80. package/lib/summaryManager.d.ts.map +1 -1
  81. package/lib/summaryManager.js +7 -2
  82. package/lib/summaryManager.js.map +1 -1
  83. package/package.json +24 -23
  84. package/src/blobManager.ts +1 -1
  85. package/src/containerRuntime.ts +69 -65
  86. package/src/dataStoreContext.ts +105 -129
  87. package/src/dataStores.ts +118 -68
  88. package/src/garbageCollection.ts +156 -31
  89. package/src/index.ts +52 -6
  90. package/src/packageVersion.ts +1 -1
  91. package/src/runningSummarizer.ts +2 -7
  92. package/src/summaryFormat.ts +4 -4
  93. package/src/summaryGenerator.ts +0 -5
  94. package/src/summaryManager.ts +9 -3
package/src/dataStores.ts CHANGED
@@ -5,6 +5,8 @@
5
5
 
6
6
  import { ITelemetryLogger, ITelemetryBaseLogger, IDisposable } from "@fluidframework/common-definitions";
7
7
  import { DataCorruptionError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
8
+ import { IFluidHandle } from "@fluidframework/core-interfaces";
9
+ import { FluidObjectHandle } from "@fluidframework/datastore";
8
10
  import {
9
11
  ISequencedDocumentMessage,
10
12
  ISnapshotTree,
@@ -19,7 +21,7 @@ import {
19
21
  IFluidDataStoreChannel,
20
22
  IFluidDataStoreContextDetached,
21
23
  IGarbageCollectionData,
22
- IGarbageCollectionSummaryDetails,
24
+ IGarbageCollectionDetailsBase,
23
25
  IInboundSignalMessage,
24
26
  InboundAttachMessage,
25
27
  ISummarizeResult,
@@ -42,7 +44,7 @@ import { DataStoreContexts } from "./dataStoreContexts";
42
44
  import { ContainerRuntime } from "./containerRuntime";
43
45
  import {
44
46
  FluidDataStoreContext,
45
- RemotedFluidDataStoreContext,
47
+ RemoteFluidDataStoreContext,
46
48
  LocalFluidDataStoreContext,
47
49
  createAttributesBlob,
48
50
  LocalDetachedFluidDataStoreContext,
@@ -97,6 +99,13 @@ export class DataStores implements IDisposable {
97
99
  readonly referencedDataStoreCount: number;
98
100
  };
99
101
 
102
+ // Stores the ids of new data stores between two GC runs. This is used to notify the garbage collector of new
103
+ // root data stores that are added.
104
+ private dataStoresSinceLastGC: string[] = [];
105
+ // The handle to the container runtime. This is used mainly for GC purposes to represent outbound reference from
106
+ // the container runtime to other nodes.
107
+ private readonly containerRuntimeHandle: IFluidHandle;
108
+
100
109
  constructor(
101
110
  private readonly baseSnapshot: ISnapshotTree | undefined,
102
111
  private readonly runtime: ContainerRuntime,
@@ -105,19 +114,21 @@ export class DataStores implements IDisposable {
105
114
  (id: string, createParam: CreateChildSummarizerNodeParam) => CreateChildSummarizerNodeFn,
106
115
  private readonly deleteChildSummarizerNodeFn: (id: string) => void,
107
116
  baseLogger: ITelemetryBaseLogger,
108
- getDataStoreBaseGCDetails: () => Promise<Map<string, IGarbageCollectionSummaryDetails>>,
117
+ getBaseGCDetails: () => Promise<Map<string, IGarbageCollectionDetailsBase>>,
109
118
  private readonly dataStoreChanged: (id: string) => void,
110
119
  private readonly aliasMap: Map<string, string>,
120
+ private readonly writeGCDataAtRoot: boolean,
111
121
  private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
112
122
  ) {
113
123
  this.logger = ChildLogger.create(baseLogger);
124
+ this.containerRuntimeHandle = new FluidObjectHandle(this.runtime, "/", this.runtime.IFluidHandleContext);
114
125
 
115
- const baseDataStoresGCDetailsP = new LazyPromise(async () => {
116
- return getDataStoreBaseGCDetails();
126
+ const baseGCDetailsP = new LazyPromise(async () => {
127
+ return getBaseGCDetails();
117
128
  });
118
- // Returns the base summary GC details for the data store with the given id.
129
+ // Returns the base GC details for the data store with the given id.
119
130
  const dataStoreBaseGCDetails = async (dataStoreId: string) => {
120
- const baseGCDetails = await baseDataStoresGCDetailsP;
131
+ const baseGCDetails = await baseGCDetailsP;
121
132
  return baseGCDetails.get(dataStoreId);
122
133
  };
123
134
 
@@ -140,30 +151,41 @@ export class DataStores implements IDisposable {
140
151
  }
141
152
  // If we have a detached container, then create local data store contexts.
142
153
  if (this.runtime.attachState !== AttachState.Detached) {
143
- dataStoreContext = new RemotedFluidDataStoreContext(
144
- key,
145
- value,
146
- async () => dataStoreBaseGCDetails(key),
147
- this.runtime,
148
- this.runtime.storage,
149
- this.runtime.scope,
150
- this.getCreateChildSummarizerNodeFn(key, { type: CreateSummarizerNodeSource.FromSummary }));
154
+ dataStoreContext = new RemoteFluidDataStoreContext({
155
+ id: key,
156
+ snapshotTree: value,
157
+ getBaseGCDetails: async () => dataStoreBaseGCDetails(key),
158
+ runtime: this.runtime,
159
+ storage: this.runtime.storage,
160
+ scope: this.runtime.scope,
161
+ createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
162
+ key,
163
+ { type: CreateSummarizerNodeSource.FromSummary },
164
+ ),
165
+ writeGCDataAtRoot: this.writeGCDataAtRoot,
166
+ disableIsolatedChannels: this.runtime.disableIsolatedChannels,
167
+ });
151
168
  } else {
152
169
  if (typeof value !== "object") {
153
170
  throw new Error("Snapshot should be there to load from!!");
154
171
  }
155
172
  const snapshotTree = value;
156
- dataStoreContext = new LocalFluidDataStoreContext(
157
- key,
158
- undefined,
159
- this.runtime,
160
- this.runtime.storage,
161
- this.runtime.scope,
162
- this.getCreateChildSummarizerNodeFn(key, { type: CreateSummarizerNodeSource.FromSummary }),
163
- (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
173
+ dataStoreContext = new LocalFluidDataStoreContext({
174
+ id: key,
175
+ pkg: undefined,
176
+ runtime: this.runtime,
177
+ storage: this.runtime.storage,
178
+ scope: this.runtime.scope,
179
+ createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
180
+ key,
181
+ { type: CreateSummarizerNodeSource.FromSummary },
182
+ ),
183
+ bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
164
184
  snapshotTree,
165
- undefined,
166
- );
185
+ isRootDataStore: undefined,
186
+ writeGCDataAtRoot: this.writeGCDataAtRoot,
187
+ disableIsolatedChannels: this.runtime.disableIsolatedChannels,
188
+ });
167
189
  }
168
190
  this.contexts.addBoundOrRemoted(dataStoreContext);
169
191
  }
@@ -179,6 +201,9 @@ export class DataStores implements IDisposable {
179
201
 
180
202
  public processAttachMessage(message: ISequencedDocumentMessage, local: boolean) {
181
203
  const attachMessage = message.contents as InboundAttachMessage;
204
+
205
+ this.dataStoresSinceLastGC.push(attachMessage.id);
206
+
182
207
  // The local object has already been attached
183
208
  if (local) {
184
209
  assert(this.pendingAttach.has(attachMessage.id),
@@ -189,7 +214,7 @@ export class DataStores implements IDisposable {
189
214
  }
190
215
 
191
216
  // If a non-local operation then go and create the object, otherwise mark it as officially attached.
192
- if (this.contexts.has(attachMessage.id)) {
217
+ if (this.alreadyProcessed(attachMessage.id)) {
193
218
  // TODO: dataStoreId may require a different tag from PackageData #7488
194
219
  const error = new DataCorruptionError(
195
220
  "duplicateDataStoreCreatedWithExistingId",
@@ -211,17 +236,17 @@ export class DataStores implements IDisposable {
211
236
  }
212
237
 
213
238
  // Include the type of attach message which is the pkg of the store to be
214
- // used by RemotedFluidDataStoreContext in case it is not in the snapshot.
239
+ // used by RemoteFluidDataStoreContext in case it is not in the snapshot.
215
240
  const pkg = [attachMessage.type];
216
- const remotedFluidDataStoreContext = new RemotedFluidDataStoreContext(
217
- attachMessage.id,
241
+ const remoteFluidDataStoreContext = new RemoteFluidDataStoreContext({
242
+ id: attachMessage.id,
218
243
  snapshotTree,
219
244
  // New data stores begin with empty GC details since GC hasn't run on them yet.
220
- async () => { return {}; },
221
- this.runtime,
222
- new BlobCacheStorageService(this.runtime.storage, flatBlobs),
223
- this.runtime.scope,
224
- this.getCreateChildSummarizerNodeFn(
245
+ getBaseGCDetails: async () => { return {}; },
246
+ runtime: this.runtime,
247
+ storage: new BlobCacheStorageService(this.runtime.storage, flatBlobs),
248
+ scope: this.runtime.scope,
249
+ createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
225
250
  attachMessage.id,
226
251
  {
227
252
  type: CreateSummarizerNodeSource.FromAttach,
@@ -233,14 +258,14 @@ export class DataStores implements IDisposable {
233
258
  this.runtime.disableIsolatedChannels,
234
259
  )],
235
260
  },
236
- }),
237
- pkg);
238
-
239
- this.contexts.addBoundOrRemoted(remotedFluidDataStoreContext);
261
+ },
262
+ ),
263
+ writeGCDataAtRoot: this.writeGCDataAtRoot,
264
+ disableIsolatedChannels: this.runtime.disableIsolatedChannels,
265
+ pkg,
266
+ });
240
267
 
241
- // Equivalent of nextTick() - Prefetch once all current ops have completed
242
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
243
- Promise.resolve().then(async () => remotedFluidDataStoreContext.realize());
268
+ this.contexts.addBoundOrRemoted(remoteFluidDataStoreContext);
244
269
  }
245
270
 
246
271
  public processAliasMessage(
@@ -266,15 +291,7 @@ export class DataStores implements IDisposable {
266
291
  }
267
292
 
268
293
  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) {
294
+ if (this.alreadyProcessed(aliasMessage.alias)) {
278
295
  return false;
279
296
  }
280
297
 
@@ -292,6 +309,10 @@ export class DataStores implements IDisposable {
292
309
  return true;
293
310
  }
294
311
 
312
+ private alreadyProcessed(id: string): boolean {
313
+ return this.aliasMap.get(id) !== undefined || this.contexts.get(id) !== undefined;
314
+ }
315
+
295
316
  public bindFluidDataStore(fluidDataStoreRuntime: IFluidDataStoreChannel): void {
296
317
  const id = fluidDataStoreRuntime.id;
297
318
  const localContext = this.contexts.getUnbound(id);
@@ -317,33 +338,44 @@ export class DataStores implements IDisposable {
317
338
  isRoot: boolean,
318
339
  id = uuid()): IFluidDataStoreContextDetached
319
340
  {
320
- const context = new LocalDetachedFluidDataStoreContext(
341
+ const context = new LocalDetachedFluidDataStoreContext({
321
342
  id,
322
343
  pkg,
323
- this.runtime,
324
- this.runtime.storage,
325
- this.runtime.scope,
326
- this.getCreateChildSummarizerNodeFn(id, { type: CreateSummarizerNodeSource.Local }),
327
- (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
328
- isRoot,
329
- );
344
+ runtime: this.runtime,
345
+ storage: this.runtime.storage,
346
+ scope: this.runtime.scope,
347
+ createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
348
+ id,
349
+ { type: CreateSummarizerNodeSource.Local },
350
+ ),
351
+ bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
352
+ snapshotTree: undefined,
353
+ isRootDataStore: isRoot,
354
+ writeGCDataAtRoot: this.writeGCDataAtRoot,
355
+ disableIsolatedChannels: this.runtime.disableIsolatedChannels,
356
+ });
330
357
  this.contexts.addUnbound(context);
331
358
  return context;
332
359
  }
333
360
 
334
361
  public _createFluidDataStoreContext(pkg: string[], id: string, isRoot: boolean, props?: any) {
335
- const context = new LocalFluidDataStoreContext(
362
+ const context = new LocalFluidDataStoreContext({
336
363
  id,
337
364
  pkg,
338
- this.runtime,
339
- this.runtime.storage,
340
- this.runtime.scope,
341
- this.getCreateChildSummarizerNodeFn(id, { type: CreateSummarizerNodeSource.Local }),
342
- (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
343
- undefined,
344
- isRoot,
345
- props,
346
- );
365
+ runtime: this.runtime,
366
+ storage: this.runtime.storage,
367
+ scope: this.runtime.scope,
368
+ createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
369
+ id,
370
+ { type: CreateSummarizerNodeSource.Local },
371
+ ),
372
+ bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
373
+ snapshotTree: undefined,
374
+ isRootDataStore: isRoot,
375
+ writeGCDataAtRoot: this.writeGCDataAtRoot,
376
+ disableIsolatedChannels: this.runtime.disableIsolatedChannels,
377
+ createProps: props,
378
+ });
347
379
  this.contexts.addUnbound(context);
348
380
  return context;
349
381
  }
@@ -498,6 +530,24 @@ export class DataStores implements IDisposable {
498
530
  return builder.getSummaryTree();
499
531
  }
500
532
 
533
+ /**
534
+ * Before GC runs, called by the garbage collector to update any pending GC state.
535
+ * The garbage collector needs to know all outbound references that are added. Since root data stores are not
536
+ * explicitly marked as referenced, notify GC of new root data stores that were added since the last GC run.
537
+ */
538
+ public async updateStateBeforeGC(): Promise<void> {
539
+ for (const id of this.dataStoresSinceLastGC) {
540
+ const context = this.contexts.get(id);
541
+ assert(context !== undefined, 0x2b6 /* `Missing data store context with id ${id}` */);
542
+ if (await context.isRoot()) {
543
+ // A root data store is basically a reference from the container runtime to the data store.
544
+ const handle = new FluidObjectHandle(context, id, this.runtime.IFluidHandleContext);
545
+ this.runtime.addedGCOutboundReference(this.containerRuntimeHandle, handle);
546
+ }
547
+ }
548
+ this.dataStoresSinceLastGC = [];
549
+ }
550
+
501
551
  /**
502
552
  * Generates data used for garbage collection. It does the following:
503
553
  * 1. Calls into each child data store context to get its GC data.