@fluidframework/runtime-utils 2.0.0-internal.2.2.0 → 2.0.0-internal.2.3.0
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/.eslintrc.js +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summarizerNode/summarizerNode.d.ts +1 -1
- package/dist/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts +6 -2
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.js +98 -22
- package/dist/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summarizerNode/summarizerNode.d.ts +1 -1
- package/lib/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts +6 -2
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.js +101 -25
- package/lib/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/package.json +14 -14
- package/src/packageVersion.ts +1 -1
- package/src/summarizerNode/summarizerNode.ts +1 -1
- package/src/summarizerNode/summarizerNodeWithGc.ts +114 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/runtime-utils",
|
|
3
|
-
"version": "2.0.0-internal.2.
|
|
3
|
+
"version": "2.0.0-internal.2.3.0",
|
|
4
4
|
"description": "Collection of utility functions for Fluid Runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -64,23 +64,23 @@
|
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
66
66
|
"@fluidframework/common-utils": "^1.0.0",
|
|
67
|
-
"@fluidframework/container-definitions": ">=2.0.0-internal.2.
|
|
68
|
-
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.2.
|
|
69
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.2.
|
|
70
|
-
"@fluidframework/datastore-definitions": ">=2.0.0-internal.2.
|
|
71
|
-
"@fluidframework/garbage-collector": ">=2.0.0-internal.2.
|
|
67
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0",
|
|
68
|
+
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0",
|
|
69
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0",
|
|
70
|
+
"@fluidframework/datastore-definitions": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0",
|
|
71
|
+
"@fluidframework/garbage-collector": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0",
|
|
72
72
|
"@fluidframework/protocol-base": "^0.1038.2000",
|
|
73
73
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
74
|
-
"@fluidframework/runtime-definitions": ">=2.0.0-internal.2.
|
|
75
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-internal.2.
|
|
74
|
+
"@fluidframework/runtime-definitions": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0",
|
|
75
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@fluid-tools/build-cli": "^0.7.0",
|
|
79
79
|
"@fluidframework/build-common": "^1.1.0",
|
|
80
80
|
"@fluidframework/build-tools": "^0.7.0",
|
|
81
|
-
"@fluidframework/eslint-config-fluid": "^
|
|
82
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.2.
|
|
83
|
-
"@fluidframework/runtime-utils-previous": "npm:@fluidframework/runtime-utils@2.0.0-internal.2.
|
|
81
|
+
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
82
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.2.3.0 <2.0.0-internal.3.0.0",
|
|
83
|
+
"@fluidframework/runtime-utils-previous": "npm:@fluidframework/runtime-utils@2.0.0-internal.2.2.0",
|
|
84
84
|
"@microsoft/api-extractor": "^7.22.2",
|
|
85
85
|
"@rushstack/eslint-config": "^2.5.1",
|
|
86
86
|
"@types/mocha": "^9.1.1",
|
|
@@ -98,9 +98,9 @@
|
|
|
98
98
|
"typescript": "~4.5.5"
|
|
99
99
|
},
|
|
100
100
|
"typeValidation": {
|
|
101
|
-
"version": "2.0.0-internal.2.
|
|
102
|
-
"baselineRange": ">=2.0.0-internal.2.
|
|
103
|
-
"baselineVersion": "2.0.0-internal.2.
|
|
101
|
+
"version": "2.0.0-internal.2.3.0",
|
|
102
|
+
"baselineRange": ">=2.0.0-internal.2.2.0 <2.0.0-internal.2.3.0",
|
|
103
|
+
"baselineVersion": "2.0.0-internal.2.2.0",
|
|
104
104
|
"broken": {}
|
|
105
105
|
}
|
|
106
106
|
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { assert, LazyPromise } from "@fluidframework/common-utils";
|
|
8
|
-
import { cloneGCData } from "@fluidframework/garbage-collector";
|
|
8
|
+
import { cloneGCData, getGCDataFromSnapshot, runGarbageCollection, unpackChildNodesGCDetails } from "@fluidframework/garbage-collector";
|
|
9
9
|
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
10
10
|
import {
|
|
11
11
|
CreateChildSummarizerNodeParam,
|
|
12
|
-
|
|
12
|
+
gcTreeKey,
|
|
13
13
|
IGarbageCollectionData,
|
|
14
14
|
IGarbageCollectionDetailsBase,
|
|
15
15
|
ISummarizeInternalResult,
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
ICreateChildDetails,
|
|
27
27
|
IInitialSummary,
|
|
28
28
|
ISummarizerNodeRootContract,
|
|
29
|
+
parseSummaryForSubtrees,
|
|
29
30
|
SummaryNode,
|
|
30
31
|
} from "./summarizerNodeUtils";
|
|
31
32
|
|
|
@@ -127,22 +128,28 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
127
128
|
* - gcData: The garbage collection data of this node that is required for running GC.
|
|
128
129
|
*/
|
|
129
130
|
private async loadBaseGCDetails() {
|
|
131
|
+
if (this.baseGCDetailsLoaded) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
130
134
|
const baseGCDetails = await this.baseGCDetailsP;
|
|
131
135
|
|
|
132
|
-
// Possible race - If there were parallel calls to loadBaseGCDetails, we want to make sure that we
|
|
136
|
+
// Possible race - If there were parallel calls to loadBaseGCDetails, we want to make sure that we update
|
|
133
137
|
// the state from the base details only once.
|
|
134
138
|
if (this.baseGCDetailsLoaded) {
|
|
135
139
|
return;
|
|
136
140
|
}
|
|
137
141
|
this.baseGCDetailsLoaded = true;
|
|
138
142
|
|
|
143
|
+
// Update GC data, used routes and reference used routes. The used routes are sorted because they are compared
|
|
144
|
+
// across GC runs to check if they changed. Sorting ensures that the elements are in the same order.
|
|
139
145
|
// If the GC details has GC data, initialize our GC data from it.
|
|
140
146
|
if (baseGCDetails.gcData !== undefined) {
|
|
141
147
|
this.gcData = cloneGCData(baseGCDetails.gcData);
|
|
142
148
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
if (baseGCDetails.usedRoutes !== undefined) {
|
|
150
|
+
this.usedRoutes = Array.from(baseGCDetails.usedRoutes).sort();
|
|
151
|
+
this.referenceUsedRoutes = Array.from(baseGCDetails.usedRoutes).sort()
|
|
152
|
+
}
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
public async summarize(
|
|
@@ -177,10 +184,11 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
177
184
|
// called and the node's data has not changed since last summary, the GC data in initial details is returned.
|
|
178
185
|
await this.loadBaseGCDetails();
|
|
179
186
|
|
|
180
|
-
// If there is no new data since last summary and we have GC data from the previous run, return it.
|
|
181
|
-
//
|
|
182
|
-
// GC
|
|
183
|
-
|
|
187
|
+
// If there is no new data since last summary and we have GC data from the previous run, return it. The previous
|
|
188
|
+
// GC data may not be available if loaded from a snapshot with either GC disabled or before GC was added.
|
|
189
|
+
// Note - canReuseHandle is checked to be consistent with summarize - generate GC data for nodes for which
|
|
190
|
+
// summary must be generated.
|
|
191
|
+
if (this.canReuseHandle && !fullGC && !this.hasDataChanged() && this.gcData !== undefined) {
|
|
184
192
|
return cloneGCData(this.gcData);
|
|
185
193
|
}
|
|
186
194
|
|
|
@@ -259,8 +267,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
259
267
|
}
|
|
260
268
|
|
|
261
269
|
/**
|
|
262
|
-
* Called when we need to upload the reference state from the given summary.
|
|
263
|
-
* to upload from it.
|
|
270
|
+
* Called when we need to upload the reference state from the given summary.
|
|
264
271
|
*/
|
|
265
272
|
protected async refreshLatestSummaryFromSnapshot(
|
|
266
273
|
referenceSequenceNumber: number,
|
|
@@ -270,21 +277,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
270
277
|
correlatedSummaryLogger: ITelemetryLogger,
|
|
271
278
|
readAndParseBlob: ReadAndParseBlob,
|
|
272
279
|
): Promise<void> {
|
|
273
|
-
|
|
274
|
-
if (!this.gcDisabled) {
|
|
275
|
-
const gcDetailsBlob = snapshotTree.blobs[gcBlobKey];
|
|
276
|
-
if (gcDetailsBlob !== undefined) {
|
|
277
|
-
const gcDetails = await readAndParseBlob<IGarbageCollectionDetailsBase>(gcDetailsBlob);
|
|
278
|
-
|
|
279
|
-
// Possible re-entrancy. If we have already seen a summary later than this one, ignore it.
|
|
280
|
-
if (this.referenceSequenceNumber >= referenceSequenceNumber) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
this.referenceUsedRoutes = gcDetails.usedRoutes;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
280
|
+
await this.refreshGCStateFromSnapshot(referenceSequenceNumber, snapshotTree, readAndParseBlob);
|
|
288
281
|
return super.refreshLatestSummaryFromSnapshot(
|
|
289
282
|
referenceSequenceNumber,
|
|
290
283
|
snapshotTree,
|
|
@@ -295,6 +288,85 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
295
288
|
);
|
|
296
289
|
}
|
|
297
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Updates GC state from the given snapshot if GC is enabled and the snapshot is newer than the one this node
|
|
293
|
+
* is tracking.
|
|
294
|
+
*/
|
|
295
|
+
private async refreshGCStateFromSnapshot(
|
|
296
|
+
referenceSequenceNumber: number,
|
|
297
|
+
snapshotTree: ISnapshotTree,
|
|
298
|
+
readAndParseBlob: ReadAndParseBlob,
|
|
299
|
+
): Promise<void> {
|
|
300
|
+
// If GC is disabled or we have seen a newer summary, skip updating GC state.
|
|
301
|
+
if (this.gcDisabled || this.referenceSequenceNumber >= referenceSequenceNumber) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Load the base GC details before proceeding because if that happens later it can overwrite the GC details
|
|
306
|
+
// written by the following code.
|
|
307
|
+
await this.loadBaseGCDetails();
|
|
308
|
+
|
|
309
|
+
// Possible re-entrancy. We may already have processed this while loading base GC details.
|
|
310
|
+
if (this.referenceSequenceNumber >= referenceSequenceNumber) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* GC data is written at root of the snapshot tree under "gc" sub-tree. This data needs to be propagated to
|
|
316
|
+
* all the nodes in the container.
|
|
317
|
+
* The root summarizer node reads the GC data from the "gc" sub-tree, runs GC on it to get used routes in
|
|
318
|
+
* the container and updates its GC data and referenced used routes. It then gets the GC data and used
|
|
319
|
+
* routes of all its children and adds it to their snapshot tree.
|
|
320
|
+
* All the other nodes gets the GC data and used routes from their snapshot tree and updates their state.
|
|
321
|
+
* They get the GC data and used routes of their children and add it to their snapshot tree and so on.
|
|
322
|
+
*
|
|
323
|
+
* Note that if the snapshot does not have GC tree, GC data will be set to undefined and used routes will be
|
|
324
|
+
* set to self-route (meaning referenced) for all nodes. This is important because the GC data needs to be
|
|
325
|
+
* regenerated in the next summary.
|
|
326
|
+
*/
|
|
327
|
+
let gcDetails: IGarbageCollectionDetailsBase | undefined;
|
|
328
|
+
const gcSnapshotTree = snapshotTree.trees[gcTreeKey];
|
|
329
|
+
if (gcSnapshotTree !== undefined) {
|
|
330
|
+
// If there is a GC tree in the snapshot, this is the root summarizer node. Read GC data from the tree
|
|
331
|
+
// process it as explained above.
|
|
332
|
+
const gcSnapshotData = await getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);
|
|
333
|
+
|
|
334
|
+
const gcNodes: { [id: string]: string[]; } = {};
|
|
335
|
+
for (const [nodeId, nodeData] of Object.entries(gcSnapshotData.gcState.gcNodes)) {
|
|
336
|
+
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
337
|
+
}
|
|
338
|
+
// Run GC on the nodes in the snapshot to get the used routes for each node in the container.
|
|
339
|
+
const usedRoutes = runGarbageCollection(gcNodes, ["/"]).referencedNodeIds;
|
|
340
|
+
gcDetails = { gcData: { gcNodes }, usedRoutes };
|
|
341
|
+
} else {
|
|
342
|
+
// If there is a GC blob in the snapshot, it's a non-root summarizer nodes - The root summarizer node
|
|
343
|
+
// writes GC blob in the snapshot of child nodes. Get GC data and used routes from the blob.
|
|
344
|
+
const gcDetailsBlob = snapshotTree.blobs[gcTreeKey];
|
|
345
|
+
if (gcDetailsBlob !== undefined) {
|
|
346
|
+
gcDetails = JSON.parse(gcDetailsBlob) as IGarbageCollectionDetailsBase;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Update this node to the same GC state it was when the ack corresponding to this summary was processed.
|
|
351
|
+
this.gcData = gcDetails?.gcData !== undefined ? cloneGCData(gcDetails.gcData) : undefined;
|
|
352
|
+
this.referenceUsedRoutes = gcDetails?.usedRoutes !== undefined ? Array.from(gcDetails.usedRoutes) : undefined;
|
|
353
|
+
// If there are no used routes in the GC details, set it to have self route which will make the node
|
|
354
|
+
// referenced. This scenario can only happen if the snapshot is from a client where GC was not run or
|
|
355
|
+
// disabled. In both the cases, the node should be referenced.
|
|
356
|
+
this.usedRoutes = gcDetails?.usedRoutes !== undefined ? Array.from(gcDetails.usedRoutes) : [""];
|
|
357
|
+
|
|
358
|
+
if (gcDetails === undefined) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Generate the GC data and used routes of children GC nodes and add it to their snapshot tree.
|
|
363
|
+
const gcDetailsMap = unpackChildNodesGCDetails(gcDetails);
|
|
364
|
+
const { childrenTree } = parseSummaryForSubtrees(snapshotTree);
|
|
365
|
+
gcDetailsMap.forEach((childGCDetails: IGarbageCollectionDetailsBase, childId: string) => {
|
|
366
|
+
childrenTree.trees[childId].blobs[gcTreeKey] = JSON.stringify(childGCDetails);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
298
370
|
/**
|
|
299
371
|
* Override the createChild method to return an instance of SummarizerNodeWithGC.
|
|
300
372
|
*/
|
|
@@ -315,6 +387,20 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
315
387
|
): ISummarizerNodeWithGC {
|
|
316
388
|
assert(!this.children.has(id), 0x1b6 /* "Create SummarizerNode child already exists" */);
|
|
317
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Update the child node's base GC details from this node's current GC details instead of updating from the base
|
|
392
|
+
* GC details of this node. This will handle scenarios where the GC details was updated during refresh from
|
|
393
|
+
* snapshot and the child node wasn't created then. If a child is created after that, its GC details should be
|
|
394
|
+
* the one from the downloaded snapshot and not the base GC details.
|
|
395
|
+
*/
|
|
396
|
+
const getChildBaseGCDetailsP = new LazyPromise<IGarbageCollectionDetailsBase>(async () => {
|
|
397
|
+
// Ensure that the base GC details is loaded because a child can be created before GC runs which is when
|
|
398
|
+
// base GC details is usually loaded.
|
|
399
|
+
await this.loadBaseGCDetails();
|
|
400
|
+
const childBaseGCDetails = unpackChildNodesGCDetails({ gcData: this.gcData, usedRoutes: this.usedRoutes });
|
|
401
|
+
return childBaseGCDetails.get(id) ?? {};
|
|
402
|
+
});
|
|
403
|
+
|
|
318
404
|
const createDetails: ICreateChildDetails = this.getCreateDetailsForChild(id, createParam);
|
|
319
405
|
const child = new SummarizerNodeWithGC(
|
|
320
406
|
this.defaultLogger,
|
|
@@ -329,7 +415,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
329
415
|
createDetails.initialSummary,
|
|
330
416
|
this.wipSummaryLogger,
|
|
331
417
|
getGCDataFn,
|
|
332
|
-
|
|
418
|
+
async () => getChildBaseGCDetailsP,
|
|
333
419
|
);
|
|
334
420
|
|
|
335
421
|
// There may be additional state that has to be updated in this child. For example, if a summary is being
|