@fluidframework/runtime-utils 2.0.0-internal.3.3.2 → 2.0.0-internal.3.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/runtime-utils",
3
- "version": "2.0.0-internal.3.3.2",
3
+ "version": "2.0.0-internal.3.4.1",
4
4
  "description": "Collection of utility functions for Fluid Runtime",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -37,35 +37,34 @@
37
37
  "dependencies": {
38
38
  "@fluidframework/common-definitions": "^0.20.1",
39
39
  "@fluidframework/common-utils": "^1.1.1",
40
- "@fluidframework/container-definitions": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0",
41
- "@fluidframework/container-runtime-definitions": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0",
42
- "@fluidframework/core-interfaces": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0",
43
- "@fluidframework/datastore-definitions": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0",
44
- "@fluidframework/garbage-collector": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0",
45
- "@fluidframework/protocol-base": "^0.1038.3000",
40
+ "@fluidframework/container-definitions": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0",
41
+ "@fluidframework/container-runtime-definitions": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0",
42
+ "@fluidframework/core-interfaces": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0",
43
+ "@fluidframework/datastore-definitions": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0",
44
+ "@fluidframework/garbage-collector": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0",
45
+ "@fluidframework/protocol-base": "^0.1038.4000",
46
46
  "@fluidframework/protocol-definitions": "^1.1.0",
47
- "@fluidframework/runtime-definitions": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0",
48
- "@fluidframework/telemetry-utils": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0"
47
+ "@fluidframework/runtime-definitions": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0",
48
+ "@fluidframework/telemetry-utils": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@fluid-tools/build-cli": "^0.12.0",
51
+ "@fluid-tools/build-cli": "^0.13.0",
52
52
  "@fluidframework/build-common": "^1.1.0",
53
- "@fluidframework/build-tools": "^0.12.0",
53
+ "@fluidframework/build-tools": "^0.13.0",
54
54
  "@fluidframework/eslint-config-fluid": "^2.0.0",
55
- "@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.3.2 <2.0.0-internal.4.0.0",
55
+ "@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.4.1 <2.0.0-internal.4.0.0",
56
56
  "@fluidframework/runtime-utils-previous": "npm:@fluidframework/runtime-utils@2.0.0-internal.3.2.0",
57
- "@microsoft/api-extractor": "^7.22.2",
58
- "@rushstack/eslint-config": "^2.5.1",
57
+ "@microsoft/api-extractor": "^7.34.4",
59
58
  "@types/mocha": "^9.1.1",
60
- "@types/node": "^14.18.36",
61
- "concurrently": "^6.2.0",
59
+ "@types/node": "^14.18.38",
60
+ "concurrently": "^7.6.0",
62
61
  "copyfiles": "^2.4.1",
63
- "cross-env": "^7.0.2",
62
+ "cross-env": "^7.0.3",
64
63
  "eslint": "~8.6.0",
65
- "mocha": "^10.0.0",
66
- "nyc": "^15.0.0",
64
+ "mocha": "^10.2.0",
65
+ "nyc": "^15.1.0",
67
66
  "prettier": "~2.6.2",
68
- "rimraf": "^2.6.2",
67
+ "rimraf": "^4.4.0",
69
68
  "sinon": "^7.4.2",
70
69
  "ts-node": "^7.0.1",
71
70
  "typescript": "~4.5.5"
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/runtime-utils";
9
- export const pkgVersion = "2.0.0-internal.3.3.2";
9
+ export const pkgVersion = "2.0.0-internal.3.4.1";
@@ -547,7 +547,7 @@ export class SummarizerNode implements IRootSummarizerNode {
547
547
  // There may be additional state that has to be updated in this child. For example, if a summary is being
548
548
  // tracked, the child's summary tracking state needs to be updated too. Same goes for pendingSummaries we might
549
549
  // have outstanding on the parent in case we realize nodes in between Summary Op and Summary Ack.
550
- this.maybeUpdateChildState(child);
550
+ this.maybeUpdateChildState(child, id);
551
551
 
552
552
  this.children.set(id, child);
553
553
  return child;
@@ -663,8 +663,10 @@ export class SummarizerNode implements IRootSummarizerNode {
663
663
  * Also, in case a child node gets realized in between Summary Op and Summary Ack, let's initialize the child's
664
664
  * pending summary as well.
665
665
  * @param child - The child node whose state is to be updated.
666
+ * @param id - Initial id or path part of this node
667
+ *
666
668
  */
667
- protected maybeUpdateChildState(child: SummarizerNode) {
669
+ protected maybeUpdateChildState(child: SummarizerNode, id: string) {
668
670
  // If a summary is in progress, this child was created after the summary started. So, we need to update the
669
671
  // child's summary state as well.
670
672
  if (this.isSummaryInProgress()) {
@@ -10,6 +10,7 @@ import {
10
10
  getGCDataFromSnapshot,
11
11
  runGarbageCollection,
12
12
  unpackChildNodesGCDetails,
13
+ unpackChildNodesUsedRoutes,
13
14
  } from "@fluidframework/garbage-collector";
14
15
  import { ISnapshotTree } from "@fluidframework/protocol-definitions";
15
16
  import {
@@ -24,6 +25,7 @@ import {
24
25
  SummarizeInternalFn,
25
26
  ITelemetryContext,
26
27
  } from "@fluidframework/runtime-definitions";
28
+ import { LoggingError, TelemetryDataTag } from "@fluidframework/telemetry-utils";
27
29
  import { ReadAndParseBlob } from "../utils";
28
30
  import { SummarizerNode } from "./summarizerNode";
29
31
  import {
@@ -81,9 +83,14 @@ class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNode
81
83
  // The base GC details of this node used to initialize the GC state.
82
84
  private readonly baseGCDetailsP: LazyPromise<IGarbageCollectionDetailsBase>;
83
85
 
84
- // Keeps track of whether we have loaded the base details to ensure that we on;y do it once.
86
+ // Keeps track of whether we have loaded the base details to ensure that we only do it once.
85
87
  private baseGCDetailsLoaded: boolean = false;
86
88
 
89
+ // The base GC details for the child nodes. This is passed to child nodes when creating them.
90
+ private readonly childNodesBaseGCDetailsP: LazyPromise<
91
+ Map<string, IGarbageCollectionDetailsBase>
92
+ >;
93
+
87
94
  private gcData: IGarbageCollectionData | undefined;
88
95
 
89
96
  // Set used routes to have self route by default. This makes the node referenced by default. This is done to ensure
@@ -133,6 +140,11 @@ class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNode
133
140
  this.baseGCDetailsP = new LazyPromise(async () => {
134
141
  return (await getBaseGCDetailsFn?.()) ?? { usedRoutes: [] };
135
142
  });
143
+
144
+ this.childNodesBaseGCDetailsP = new LazyPromise(async () => {
145
+ await this.loadBaseGCDetails();
146
+ return unpackChildNodesGCDetails({ gcData: this.gcData, usedRoutes: this.usedRoutes });
147
+ });
136
148
  }
137
149
 
138
150
  /**
@@ -297,9 +309,28 @@ class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNode
297
309
  ): void {
298
310
  // If GC is disabled, skip setting referenced used routes since we are not tracking GC state.
299
311
  if (!this.gcDisabled) {
300
- const summaryNode = this.pendingSummaries.get(proposalHandle) as SummaryNodeWithGC;
301
- if (summaryNode?.serializedUsedRoutes !== undefined) {
302
- this.referenceUsedRoutes = JSON.parse(summaryNode.serializedUsedRoutes);
312
+ const summaryNode = this.pendingSummaries.get(proposalHandle);
313
+ if (summaryNode !== undefined) {
314
+ // If a pending summary exists, it must have used routes since GC is enabled.
315
+ const summaryNodeWithGC = summaryNode as SummaryNodeWithGC;
316
+ if (summaryNodeWithGC.serializedUsedRoutes === undefined) {
317
+ const error = new LoggingError("MissingGCStateInPendingSummary", {
318
+ proposalHandle,
319
+ referenceSequenceNumber,
320
+ id: {
321
+ tag: TelemetryDataTag.CodeArtifact,
322
+ value: this.telemetryNodeId,
323
+ },
324
+ });
325
+ this.logger.sendErrorEvent(
326
+ {
327
+ eventName: error.message,
328
+ },
329
+ error,
330
+ );
331
+ throw error;
332
+ }
333
+ this.referenceUsedRoutes = JSON.parse(summaryNodeWithGC.serializedUsedRoutes);
303
334
  }
304
335
  }
305
336
 
@@ -434,23 +465,16 @@ class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNode
434
465
  getBaseGCDetailsFn?: () => Promise<IGarbageCollectionDetailsBase>,
435
466
  ): ISummarizerNodeWithGC {
436
467
  assert(!this.children.has(id), 0x1b6 /* "Create SummarizerNode child already exists" */);
437
-
438
468
  /**
439
469
  * Update the child node's base GC details from this node's current GC details instead of updating from the base
440
470
  * GC details of this node. This will handle scenarios where the GC details was updated during refresh from
441
471
  * snapshot and the child node wasn't created then. If a child is created after that, its GC details should be
442
472
  * the one from the downloaded snapshot and not the base GC details.
443
473
  */
444
- const getChildBaseGCDetailsP = new LazyPromise<IGarbageCollectionDetailsBase>(async () => {
445
- // Ensure that the base GC details is loaded because a child can be created before GC runs which is when
446
- // base GC details is usually loaded.
447
- await this.loadBaseGCDetails();
448
- const childBaseGCDetails = unpackChildNodesGCDetails({
449
- gcData: this.gcData,
450
- usedRoutes: this.usedRoutes,
451
- });
452
- return childBaseGCDetails.get(id) ?? {};
453
- });
474
+ const getChildBaseGCDetailsFn = async () => {
475
+ const childNodesBaseGCDetails = await this.childNodesBaseGCDetailsP;
476
+ return childNodesBaseGCDetails.get(id) ?? {};
477
+ };
454
478
 
455
479
  const createDetails: ICreateChildDetails = this.getCreateDetailsForChild(id, createParam);
456
480
  const child = new SummarizerNodeWithGC(
@@ -466,18 +490,53 @@ class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNode
466
490
  createDetails.initialSummary,
467
491
  this.wipSummaryLogger,
468
492
  getGCDataFn,
469
- async () => getChildBaseGCDetailsP,
493
+ getChildBaseGCDetailsFn,
470
494
  createDetails.telemetryNodeId,
471
495
  );
472
496
 
473
497
  // There may be additional state that has to be updated in this child. For example, if a summary is being
474
498
  // tracked, the child's summary tracking state needs to be updated too.
475
- this.maybeUpdateChildState(child);
499
+ this.maybeUpdateChildState(child, id);
476
500
 
477
501
  this.children.set(id, child);
478
502
  return child;
479
503
  }
480
504
 
505
+ /**
506
+ * Updates the state of the child if required. For example, if a summary is currently being tracked, the child's
507
+ * summary tracking state needs to be updated too.
508
+ * Also, in case a child node gets realized in between Summary Op and Summary Ack, let's initialize the child's
509
+ * pending summary as well. Finally, if the pendingSummaries entries have serializedRoutes, replicate them to the
510
+ * pendingSummaries from the child nodes.
511
+ * @param child - The child node whose state is to be updated.
512
+ * @param id - Initial id or path part of this node
513
+ */
514
+ protected maybeUpdateChildState(child: SummarizerNodeWithGC, id: string) {
515
+ super.maybeUpdateChildState(child, id);
516
+
517
+ // In case we have pending summaries on the parent, let's initialize it on the child.
518
+ if (child.latestSummary !== undefined) {
519
+ for (const [key, value] of this.pendingSummaries.entries()) {
520
+ const summaryNodeWithGC = value as SummaryNodeWithGC;
521
+ if (summaryNodeWithGC.serializedUsedRoutes !== undefined) {
522
+ const childNodeUsedRoutes = unpackChildNodesUsedRoutes(
523
+ JSON.parse(summaryNodeWithGC.serializedUsedRoutes),
524
+ );
525
+ const newSerializedRoutes = childNodeUsedRoutes.get(id) ?? [""];
526
+ const newLatestSummaryNode = new SummaryNodeWithGC(
527
+ JSON.stringify(newSerializedRoutes),
528
+ {
529
+ referenceSequenceNumber: value.referenceSequenceNumber,
530
+ basePath: value.basePath,
531
+ localPath: value.localPath,
532
+ },
533
+ );
534
+ child.addPendingSummary(key, newLatestSummaryNode);
535
+ }
536
+ }
537
+ }
538
+ }
539
+
481
540
  /**
482
541
  * Deletes the child node with the given id.
483
542
  */