@fluidframework/container-runtime 0.59.1001-62246 → 0.59.2000
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/dist/connectionTelemetry.js +1 -1
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +6 -6
- package/dist/containerRuntime.js.map +1 -1
- package/dist/garbageCollection.d.ts +26 -8
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +81 -57
- package/dist/garbageCollection.js.map +1 -1
- package/dist/orderedClientElection.d.ts +57 -6
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +141 -26
- package/dist/orderedClientElection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +11 -10
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts +1 -0
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +8 -4
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.d.ts +2 -0
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js +15 -2
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerTypes.d.ts +47 -1
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryGenerator.d.ts +0 -2
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +2 -3
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +29 -18
- package/dist/summaryManager.js.map +1 -1
- package/lib/connectionTelemetry.js +1 -1
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +6 -6
- package/lib/containerRuntime.js.map +1 -1
- package/lib/garbageCollection.d.ts +26 -8
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +81 -57
- package/lib/garbageCollection.js.map +1 -1
- package/lib/orderedClientElection.d.ts +57 -6
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +141 -26
- package/lib/orderedClientElection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +11 -10
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts +1 -0
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +8 -4
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.d.ts +2 -0
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js +15 -2
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerTypes.d.ts +47 -1
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryGenerator.d.ts +0 -2
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +2 -3
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +29 -18
- package/lib/summaryManager.js.map +1 -1
- package/package.json +29 -53
- package/src/connectionTelemetry.ts +2 -2
- package/src/containerRuntime.ts +4 -6
- package/src/garbageCollection.ts +96 -61
- package/src/orderedClientElection.ts +155 -25
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +13 -10
- package/src/summarizer.ts +9 -4
- package/src/summarizerClientElection.ts +15 -2
- package/src/summarizerTypes.ts +60 -1
- package/src/summaryGenerator.ts +3 -51
- package/src/summaryManager.ts +32 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.2000",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"types": "dist/index.d.ts",
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "npm run build:genver && concurrently npm:build:compile npm:lint && npm run build:docs",
|
|
19
|
-
"build:commonjs": "npm run tsc && npm run build:test",
|
|
19
|
+
"build:commonjs": "npm run tsc && npm run typetests:gen && npm run build:test",
|
|
20
20
|
"build:compile": "concurrently npm:build:commonjs npm:build:esnext",
|
|
21
21
|
"build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
|
|
22
22
|
"build:esnext": "tsc --project ./tsconfig.esnext.json",
|
|
@@ -38,8 +38,7 @@
|
|
|
38
38
|
"tsc:watch": "tsc --watch",
|
|
39
39
|
"tsfmt": "tsfmt --verify",
|
|
40
40
|
"tsfmt:fix": "tsfmt --replace",
|
|
41
|
-
"typetests:gen": "fluid-type-validator -d ."
|
|
42
|
-
"typetests:prepare": "fluid-type-validator -d . -p"
|
|
41
|
+
"typetests:gen": "fluid-type-validator -g -d ."
|
|
43
42
|
},
|
|
44
43
|
"nyc": {
|
|
45
44
|
"all": true,
|
|
@@ -65,29 +64,29 @@
|
|
|
65
64
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
66
65
|
"@fluidframework/common-utils": "^0.32.1",
|
|
67
66
|
"@fluidframework/container-definitions": "^0.48.1000",
|
|
68
|
-
"@fluidframework/container-runtime-definitions": "0.59.
|
|
69
|
-
"@fluidframework/container-utils": "0.59.
|
|
67
|
+
"@fluidframework/container-runtime-definitions": "^0.59.2000",
|
|
68
|
+
"@fluidframework/container-utils": "^0.59.2000",
|
|
70
69
|
"@fluidframework/core-interfaces": "^0.43.1000",
|
|
71
|
-
"@fluidframework/datastore": "0.59.
|
|
70
|
+
"@fluidframework/datastore": "^0.59.2000",
|
|
72
71
|
"@fluidframework/driver-definitions": "^0.46.1000",
|
|
73
|
-
"@fluidframework/driver-utils": "0.59.
|
|
74
|
-
"@fluidframework/garbage-collector": "0.59.
|
|
72
|
+
"@fluidframework/driver-utils": "^0.59.2000",
|
|
73
|
+
"@fluidframework/garbage-collector": "^0.59.2000",
|
|
75
74
|
"@fluidframework/protocol-base": "^0.1036.1000",
|
|
76
75
|
"@fluidframework/protocol-definitions": "^0.1028.1000",
|
|
77
|
-
"@fluidframework/runtime-definitions": "0.59.
|
|
78
|
-
"@fluidframework/runtime-utils": "0.59.
|
|
79
|
-
"@fluidframework/telemetry-utils": "0.59.
|
|
76
|
+
"@fluidframework/runtime-definitions": "^0.59.2000",
|
|
77
|
+
"@fluidframework/runtime-utils": "^0.59.2000",
|
|
78
|
+
"@fluidframework/telemetry-utils": "^0.59.2000",
|
|
80
79
|
"double-ended-queue": "^2.1.0-0",
|
|
81
80
|
"uuid": "^8.3.1"
|
|
82
81
|
},
|
|
83
82
|
"devDependencies": {
|
|
84
83
|
"@fluidframework/build-common": "^0.23.0",
|
|
85
|
-
"@fluidframework/build-tools": "^0.2.
|
|
86
|
-
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime
|
|
84
|
+
"@fluidframework/build-tools": "^0.2.61804",
|
|
85
|
+
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@0.59.1000",
|
|
87
86
|
"@fluidframework/eslint-config-fluid": "^0.28.1000",
|
|
88
|
-
"@fluidframework/mocha-test-setup": "0.59.
|
|
89
|
-
"@fluidframework/test-runtime-utils": "0.59.
|
|
90
|
-
"@microsoft/api-extractor": "^7.
|
|
87
|
+
"@fluidframework/mocha-test-setup": "^0.59.2000",
|
|
88
|
+
"@fluidframework/test-runtime-utils": "^0.59.2000",
|
|
89
|
+
"@microsoft/api-extractor": "^7.22.2",
|
|
91
90
|
"@rushstack/eslint-config": "^2.5.1",
|
|
92
91
|
"@types/double-ended-queue": "^2.1.0",
|
|
93
92
|
"@types/mocha": "^8.2.2",
|
|
@@ -103,8 +102,11 @@
|
|
|
103
102
|
"eslint-plugin-editorconfig": "~3.2.0",
|
|
104
103
|
"eslint-plugin-eslint-comments": "~3.2.0",
|
|
105
104
|
"eslint-plugin-import": "~2.25.4",
|
|
106
|
-
"eslint-plugin-
|
|
105
|
+
"eslint-plugin-jest": "~26.1.3",
|
|
106
|
+
"eslint-plugin-mocha": "~10.0.3",
|
|
107
|
+
"eslint-plugin-promise": "~6.0.0",
|
|
107
108
|
"eslint-plugin-react": "~7.28.0",
|
|
109
|
+
"eslint-plugin-tsdoc": "~0.2.14",
|
|
108
110
|
"eslint-plugin-unicorn": "~40.0.0",
|
|
109
111
|
"mocha": "^8.4.0",
|
|
110
112
|
"nyc": "^15.0.0",
|
|
@@ -114,42 +116,16 @@
|
|
|
114
116
|
"typescript-formatter": "7.1.0"
|
|
115
117
|
},
|
|
116
118
|
"typeValidation": {
|
|
117
|
-
"version": "0.59.
|
|
119
|
+
"version": "0.59.2000",
|
|
118
120
|
"broken": {
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"
|
|
127
|
-
"forwardCompat": false
|
|
128
|
-
},
|
|
129
|
-
"InterfaceDeclaration_IUploadSummaryResult": {
|
|
130
|
-
"forwardCompat": false
|
|
131
|
-
},
|
|
132
|
-
"InterfaceDeclaration_SubmitSummaryResult": {
|
|
133
|
-
"forwardCompat": false
|
|
134
|
-
},
|
|
135
|
-
"TypeAliasDeclaration_SubmitSummaryResult": {
|
|
136
|
-
"forwardCompat": false
|
|
137
|
-
},
|
|
138
|
-
"InterfaceDeclaration_IGeneratedSummaryStats": {
|
|
139
|
-
"forwardCompat": false
|
|
140
|
-
},
|
|
141
|
-
"ClassDeclaration_ContainerRuntime": {
|
|
142
|
-
"forwardCompat": false
|
|
143
|
-
},
|
|
144
|
-
"InterfaceDeclaration_IGarbageCollectionRuntime": {
|
|
145
|
-
"forwardCompat": false
|
|
146
|
-
},
|
|
147
|
-
"InterfaceDeclaration_IGCStats": {
|
|
148
|
-
"forwardCompat": false
|
|
149
|
-
},
|
|
150
|
-
"InterfaceDeclaration_IRootSummaryTreeWithStats": {
|
|
151
|
-
"forwardCompat": false
|
|
152
|
-
}
|
|
121
|
+
"ClassDeclaration_Summarizer": {
|
|
122
|
+
"forwardCompat": false
|
|
123
|
+
},
|
|
124
|
+
"InterfaceDeclaration_IProvideSummarizer": {
|
|
125
|
+
"forwardCompat": false
|
|
126
|
+
},
|
|
127
|
+
"InterfaceDeclaration_ISummarizer": {
|
|
128
|
+
"forwardCompat": false
|
|
153
129
|
}
|
|
154
130
|
}
|
|
155
131
|
}
|
|
@@ -33,7 +33,7 @@ interface IOpPerfTelemetryProperties {
|
|
|
33
33
|
/** Measure time between (3) and (4) - Time between DM's inbound "push" event until DM's "op" event */
|
|
34
34
|
durationInboundToProcessing: number;
|
|
35
35
|
/** Length of the DeltaManager's inbound queue at the time of the DM's inbound "push" event (3) */
|
|
36
|
-
|
|
36
|
+
lengthInboundQueue: number;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -135,7 +135,7 @@ class OpPerfTelemetry {
|
|
|
135
135
|
this.opPerfData.durationInboundQueue = this.opProcessingTimes.opStartTimeInboundPushEvent
|
|
136
136
|
- this.opProcessingTimes.opStartTimeSittingInboundQueue;
|
|
137
137
|
this.opProcessingTimes.opStartTimeSittingInboundQueue = undefined;
|
|
138
|
-
this.opPerfData.
|
|
138
|
+
this.opPerfData.lengthInboundQueue = this.deltaManager.inbound.length;
|
|
139
139
|
}
|
|
140
140
|
});
|
|
141
141
|
|
package/src/containerRuntime.ts
CHANGED
|
@@ -194,17 +194,17 @@ export interface ContainerRuntimeMessage {
|
|
|
194
194
|
const IdleDetectionTime = 5000;
|
|
195
195
|
|
|
196
196
|
const DefaultSummaryConfiguration: ISummaryConfiguration = {
|
|
197
|
-
idleTime: IdleDetectionTime,
|
|
197
|
+
idleTime: IdleDetectionTime * 3,
|
|
198
198
|
|
|
199
199
|
maxTime: IdleDetectionTime * 12,
|
|
200
200
|
|
|
201
|
-
//
|
|
201
|
+
// Summarize if 1000 ops received since last snapshot.
|
|
202
202
|
maxOps: 1000,
|
|
203
203
|
|
|
204
|
-
// Wait
|
|
204
|
+
// Wait 10 minutes for summary ack
|
|
205
205
|
// this is less than maxSummarizeAckWaitTime
|
|
206
206
|
// the min of the two will be chosen
|
|
207
|
-
maxAckWaitTime:
|
|
207
|
+
maxAckWaitTime: 600000,
|
|
208
208
|
};
|
|
209
209
|
|
|
210
210
|
export interface IGCRuntimeOptions {
|
|
@@ -967,8 +967,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
967
967
|
return {
|
|
968
968
|
// the defaults
|
|
969
969
|
... DefaultSummaryConfiguration,
|
|
970
|
-
// the server provided values
|
|
971
|
-
... this.context?.serviceConfiguration?.summary,
|
|
972
970
|
// the runtime configuration overrides
|
|
973
971
|
... this.runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
974
972
|
};
|
package/src/garbageCollection.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
|
+
import { ITelemetryLogger, ITelemetryPerformanceEvent } from "@fluidframework/common-definitions";
|
|
7
7
|
import { assert, LazyPromise, Timer } from "@fluidframework/common-utils";
|
|
8
8
|
import { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
9
9
|
import { ClientSessionExpiredError, DataProcessingError } from "@fluidframework/container-utils";
|
|
@@ -66,6 +66,8 @@ const runSweepKey = "Fluid.GarbageCollection.RunSweep";
|
|
|
66
66
|
const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
|
|
67
67
|
// Feature gate key to expire a session after a set period of time.
|
|
68
68
|
const runSessionExpiry = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
69
|
+
// Feature gate key to log error messages if GC reference validation fails.
|
|
70
|
+
const logUnknownOutboundReferencesKey = "Fluid.GarbageCollection.LogUnknownOutboundReferences";
|
|
69
71
|
|
|
70
72
|
const defaultDeleteTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
71
73
|
export const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
@@ -227,6 +229,19 @@ class UnreferencedStateTracker {
|
|
|
227
229
|
/**
|
|
228
230
|
* The garbage collector for the container runtime. It consolidates the garbage collection functionality and maintains
|
|
229
231
|
* its state across summaries.
|
|
232
|
+
*
|
|
233
|
+
* Node - represented as nodeId, it's a node on the GC graph
|
|
234
|
+
* Outbound Route - a path from one node to another node, think `nodeA` -> `nodeB`
|
|
235
|
+
* Graph - all nodes with their respective routes
|
|
236
|
+
* GC Graph
|
|
237
|
+
*
|
|
238
|
+
* Node
|
|
239
|
+
* NodeId = "datastore1"
|
|
240
|
+
* / \
|
|
241
|
+
* OutboundRoute OutboundRoute
|
|
242
|
+
* / \
|
|
243
|
+
* Node Node
|
|
244
|
+
* NodeId = "dds1" NodeId = "dds2"
|
|
230
245
|
*/
|
|
231
246
|
export class GarbageCollector implements IGarbageCollector {
|
|
232
247
|
public static create(
|
|
@@ -320,10 +335,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
320
335
|
private latestSummaryGCVersion: GCVersion;
|
|
321
336
|
|
|
322
337
|
// Keeps track of the GC state from the last run.
|
|
323
|
-
private
|
|
338
|
+
private previousGCDataFromLastRun: IGarbageCollectionData | undefined;
|
|
324
339
|
// Keeps a list of references (edges in the GC graph) between GC runs. Each entry has a node id and a list of
|
|
325
340
|
// outbound routes from that node.
|
|
326
|
-
private readonly
|
|
341
|
+
private readonly newReferencesSinceLastRun: Map<string, string[]> = new Map();
|
|
327
342
|
|
|
328
343
|
// Promise when resolved initializes the base state of the nodes from the base summary state.
|
|
329
344
|
private readonly initializeBaseStateP: Promise<void>;
|
|
@@ -513,7 +528,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
513
528
|
}
|
|
514
529
|
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
515
530
|
}
|
|
516
|
-
this.
|
|
531
|
+
this.previousGCDataFromLastRun = { gcNodes };
|
|
517
532
|
});
|
|
518
533
|
|
|
519
534
|
// Get the GC details for each node from the GC state in the base summary. This is returned in getBaseGCDetails
|
|
@@ -637,12 +652,12 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
637
652
|
* blobs. All the blob keys should start with `gcBlobPrefix`.
|
|
638
653
|
*/
|
|
639
654
|
public summarize(): ISummaryTreeWithStats | undefined {
|
|
640
|
-
if (!this.shouldRunGC || this.
|
|
655
|
+
if (!this.shouldRunGC || this.previousGCDataFromLastRun === undefined) {
|
|
641
656
|
return;
|
|
642
657
|
}
|
|
643
658
|
|
|
644
659
|
const gcState: IGarbageCollectionState = { gcNodes: {} };
|
|
645
|
-
for (const [nodeId, outboundRoutes] of Object.entries(this.
|
|
660
|
+
for (const [nodeId, outboundRoutes] of Object.entries(this.previousGCDataFromLastRun.gcNodes)) {
|
|
646
661
|
gcState.gcNodes[nodeId] = {
|
|
647
662
|
outboundRoutes,
|
|
648
663
|
unreferencedTimestampMs: this.unreferencedNodesState.get(nodeId)?.unreferencedTimestampMs,
|
|
@@ -729,9 +744,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
729
744
|
return;
|
|
730
745
|
}
|
|
731
746
|
|
|
732
|
-
const outboundRoutes = this.
|
|
747
|
+
const outboundRoutes = this.newReferencesSinceLastRun.get(fromNodePath) ?? [];
|
|
733
748
|
outboundRoutes.push(toNodePath);
|
|
734
|
-
this.
|
|
749
|
+
this.newReferencesSinceLastRun.set(fromNodePath, outboundRoutes);
|
|
735
750
|
|
|
736
751
|
// If the node that got referenced is inactive, log an event as that may indicate use-after-delete.
|
|
737
752
|
this.logIfInactive(
|
|
@@ -772,8 +787,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
772
787
|
gcResult: IGCResult,
|
|
773
788
|
currentReferenceTimestampMs?: number,
|
|
774
789
|
) {
|
|
775
|
-
this.
|
|
776
|
-
this.
|
|
790
|
+
this.previousGCDataFromLastRun = cloneGCData(gcData);
|
|
791
|
+
this.newReferencesSinceLastRun.clear();
|
|
777
792
|
|
|
778
793
|
// Iterate through the referenced nodes and stop tracking if they were unreferenced before.
|
|
779
794
|
for (const nodeId of gcResult.referencedNodeIds) {
|
|
@@ -826,13 +841,37 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
826
841
|
* If these nodes are currently unreferenced, they will be assigned new unreferenced state by the current run.
|
|
827
842
|
*/
|
|
828
843
|
private updateStateSinceLastRun(currentGCData: IGarbageCollectionData) {
|
|
829
|
-
// If we haven't run GC before
|
|
830
|
-
if (this.
|
|
844
|
+
// If we haven't run GC before there is nothing to do.
|
|
845
|
+
if (this.previousGCDataFromLastRun === undefined) {
|
|
831
846
|
return;
|
|
832
847
|
}
|
|
833
848
|
|
|
834
|
-
//
|
|
835
|
-
this.
|
|
849
|
+
// Find any references that haven't been identified correctly.
|
|
850
|
+
const missingExplicitReferences = this.findMissingExplicitReferences(
|
|
851
|
+
currentGCData,
|
|
852
|
+
this.previousGCDataFromLastRun,
|
|
853
|
+
this.newReferencesSinceLastRun,
|
|
854
|
+
);
|
|
855
|
+
|
|
856
|
+
// The following log will be enabled once this issue is resolved:
|
|
857
|
+
// https://github.com/microsoft/FluidFramework/issues/8878.
|
|
858
|
+
if(this.mc.config.getBoolean(logUnknownOutboundReferencesKey) === true
|
|
859
|
+
&& missingExplicitReferences.length > 0) {
|
|
860
|
+
missingExplicitReferences.forEach((missingExplicitReference) => {
|
|
861
|
+
const event: ITelemetryPerformanceEvent = {
|
|
862
|
+
eventName: "gcUnknownOutboundReferences",
|
|
863
|
+
gcNodeId: missingExplicitReference[0],
|
|
864
|
+
gcRoutes: JSON.stringify(missingExplicitReference[1]),
|
|
865
|
+
};
|
|
866
|
+
this.mc.logger.sendPerformanceEvent(event);
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// No references were added since the last run so we don't have to update reference states of any unreferenced
|
|
871
|
+
// nodes
|
|
872
|
+
if (this.newReferencesSinceLastRun.size === 0) {
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
836
875
|
|
|
837
876
|
/**
|
|
838
877
|
* Generate a super set of the GC data that contains the nodes and edges from last run, plus any new node and
|
|
@@ -849,8 +888,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
849
888
|
* which is tracked by https://github.com/microsoft/FluidFramework/issues/8470.
|
|
850
889
|
* - A new data store may have "root" DDSs already created and we don't detect them today.
|
|
851
890
|
*/
|
|
852
|
-
const gcDataSuperSet = concatGarbageCollectionData(this.
|
|
853
|
-
this.
|
|
891
|
+
const gcDataSuperSet = concatGarbageCollectionData(this.previousGCDataFromLastRun, currentGCData);
|
|
892
|
+
this.newReferencesSinceLastRun.forEach((outboundRoutes: string[], sourceNodeId: string) => {
|
|
854
893
|
if (gcDataSuperSet.gcNodes[sourceNodeId] === undefined) {
|
|
855
894
|
gcDataSuperSet.gcNodes[sourceNodeId] = outboundRoutes;
|
|
856
895
|
} else {
|
|
@@ -876,59 +915,55 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
876
915
|
}
|
|
877
916
|
|
|
878
917
|
/**
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
918
|
+
* Finds all new references or outbound routes in the current graph that haven't been explicitly notified to GC.
|
|
919
|
+
* The principle is that every new reference or outbound route must be notified to GC via the
|
|
920
|
+
* addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.
|
|
921
|
+
*
|
|
922
|
+
* In more simple terms:
|
|
923
|
+
* Missing Explicit References = Current References - Previous References - Explicitly Added References;
|
|
924
|
+
*
|
|
884
925
|
* @param currentGCData - The GC data (reference graph) from the current GC run.
|
|
926
|
+
* @param previousGCData - The GC data (reference graph) from the previous GC run.
|
|
927
|
+
* @param explicitReferences - New references added explicity between the previous and the current run.
|
|
928
|
+
* @returns - a list of missing explicit references
|
|
885
929
|
*/
|
|
886
|
-
private
|
|
930
|
+
private findMissingExplicitReferences(
|
|
931
|
+
currentGCData: IGarbageCollectionData,
|
|
932
|
+
previousGCData: IGarbageCollectionData,
|
|
933
|
+
explicitReferences: Map<string, string[]>,
|
|
934
|
+
): [string, string[]][] {
|
|
887
935
|
assert(
|
|
888
|
-
|
|
936
|
+
previousGCData !== undefined,
|
|
889
937
|
0x2b7, /* "Can't validate correctness without GC data from last run" */
|
|
890
938
|
);
|
|
891
939
|
|
|
892
|
-
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
// Validate that the current reference graph doesn't have references that we are not already aware of. If this
|
|
915
|
-
// happens, it might indicate data corruption since we may delete objects prematurely.
|
|
916
|
-
currentReferences.forEach((route: string) => {
|
|
917
|
-
// Validate references for data stores only. Currently, layers below data stores don't have GC implemented
|
|
918
|
-
// so there is no guarantee their references will be notified.
|
|
919
|
-
if (this.runtime.getNodeType(route) === GCNodeType.DataStore && !explicitReferences.includes(route)) {
|
|
920
|
-
/**
|
|
921
|
-
* The following log will be enabled once this issue is resolved:
|
|
922
|
-
* https://github.com/microsoft/FluidFramework/issues/8878.
|
|
923
|
-
*/
|
|
924
|
-
// We should ideally throw a data corruption error here. However, send an error for now until we have
|
|
925
|
-
// implemented sweep and have reasonable confidence in the sweep process.
|
|
926
|
-
// this.mc.logger.sendErrorEvent({
|
|
927
|
-
// eventName: "gcUnknownOutboundRoute",
|
|
928
|
-
// route,
|
|
929
|
-
// });
|
|
940
|
+
const currentGraph = Object.entries(currentGCData.gcNodes);
|
|
941
|
+
const missingExplicitReferences: [string, string[]][] = [];
|
|
942
|
+
currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {
|
|
943
|
+
const previousRoutes = previousGCData.gcNodes[nodeId] ?? [];
|
|
944
|
+
const explicitRoutes = explicitReferences.get(nodeId) ?? [];
|
|
945
|
+
const missingExplicitRoutes: string[] = [];
|
|
946
|
+
currentOutboundRoutes.forEach((route) => {
|
|
947
|
+
const isBlobOrDataStoreRoute =
|
|
948
|
+
this.runtime.getNodeType(route) === GCNodeType.Blob ||
|
|
949
|
+
this.runtime.getNodeType(route) === GCNodeType.DataStore;
|
|
950
|
+
// Ignore implicitly added DDS routes to their parent datastores
|
|
951
|
+
const notRouteFromDDSToParentDataStore = !nodeId.startsWith(route);
|
|
952
|
+
if (
|
|
953
|
+
isBlobOrDataStoreRoute &&
|
|
954
|
+
notRouteFromDDSToParentDataStore &&
|
|
955
|
+
(!previousRoutes.includes(route) && !explicitRoutes.includes(route))
|
|
956
|
+
) {
|
|
957
|
+
missingExplicitRoutes.push(route);
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
if (missingExplicitRoutes.length > 0) {
|
|
961
|
+
missingExplicitReferences.push([nodeId, missingExplicitRoutes]);
|
|
930
962
|
}
|
|
931
963
|
});
|
|
964
|
+
|
|
965
|
+
// Ideally missingExplicitReferences should always have a size 0
|
|
966
|
+
return missingExplicitReferences;
|
|
932
967
|
}
|
|
933
968
|
|
|
934
969
|
/**
|