@fluidframework/container-runtime 2.0.0-internal.3.0.1 → 2.0.0-internal.3.0.3
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/containerRuntime.d.ts +1 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +58 -34
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +12 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +43 -5
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/garbageCollection.d.ts +5 -4
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +15 -6
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +2 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -1
- package/dist/garbageCollectionConstants.js +3 -1
- package/dist/garbageCollectionConstants.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +4 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +42 -3
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +3 -2
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summaryFormat.d.ts +19 -0
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +1 -1
- package/dist/summaryGenerator.js.map +1 -1
- package/lib/containerRuntime.d.ts +1 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +58 -34
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +12 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +45 -7
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/garbageCollection.d.ts +5 -4
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +16 -7
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +2 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -1
- package/lib/garbageCollectionConstants.js +2 -0
- package/lib/garbageCollectionConstants.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +4 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +42 -3
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +3 -2
- package/lib/opLifecycle/outbox.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/summaryFormat.d.ts +19 -0
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +1 -1
- package/lib/summaryGenerator.js.map +1 -1
- package/package.json +19 -53
- package/src/containerRuntime.ts +99 -51
- package/src/dataStoreContext.ts +64 -5
- package/src/garbageCollection.ts +24 -9
- package/src/garbageCollectionConstants.ts +3 -0
- package/src/opLifecycle/opDecompressor.ts +49 -2
- package/src/opLifecycle/outbox.ts +3 -2
- package/src/packageVersion.ts +1 -1
- package/src/summaryFormat.ts +22 -0
- package/src/summaryGenerator.ts +9 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.0.0-internal.3.0.
|
|
3
|
+
"version": "2.0.0-internal.3.0.3",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -65,19 +65,19 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
67
67
|
"@fluidframework/common-utils": "^1.0.0",
|
|
68
|
-
"@fluidframework/container-definitions": ">=2.0.0-internal.3.0.
|
|
69
|
-
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.3.0.
|
|
70
|
-
"@fluidframework/container-utils": ">=2.0.0-internal.3.0.
|
|
71
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.3.0.
|
|
72
|
-
"@fluidframework/datastore": ">=2.0.0-internal.3.0.
|
|
73
|
-
"@fluidframework/driver-definitions": ">=2.0.0-internal.3.0.
|
|
74
|
-
"@fluidframework/driver-utils": ">=2.0.0-internal.3.0.
|
|
75
|
-
"@fluidframework/garbage-collector": ">=2.0.0-internal.3.0.
|
|
68
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
69
|
+
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
70
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
71
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
72
|
+
"@fluidframework/datastore": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
73
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
74
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
75
|
+
"@fluidframework/garbage-collector": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
76
76
|
"@fluidframework/protocol-base": "^0.1038.2000",
|
|
77
77
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
78
|
-
"@fluidframework/runtime-definitions": ">=2.0.0-internal.3.0.
|
|
79
|
-
"@fluidframework/runtime-utils": ">=2.0.0-internal.3.0.
|
|
80
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-internal.3.0.
|
|
78
|
+
"@fluidframework/runtime-definitions": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
79
|
+
"@fluidframework/runtime-utils": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
80
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
81
81
|
"double-ended-queue": "^2.1.0-0",
|
|
82
82
|
"events": "^3.1.0",
|
|
83
83
|
"lz4js": "^0.2.0",
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
"@fluid-tools/build-cli": "^0.8.0",
|
|
88
88
|
"@fluidframework/build-common": "^1.1.0",
|
|
89
89
|
"@fluidframework/build-tools": "^0.8.0",
|
|
90
|
-
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.0.0-internal.
|
|
90
|
+
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.0.0-internal.3.0.0",
|
|
91
91
|
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
92
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.0.
|
|
93
|
-
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.3.0.
|
|
92
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
93
|
+
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.3.0.3 <2.0.0-internal.4.0.0",
|
|
94
94
|
"@microsoft/api-extractor": "^7.22.2",
|
|
95
95
|
"@rushstack/eslint-config": "^2.5.1",
|
|
96
96
|
"@types/double-ended-queue": "^2.1.0",
|
|
@@ -109,43 +109,9 @@
|
|
|
109
109
|
"typescript": "~4.5.5"
|
|
110
110
|
},
|
|
111
111
|
"typeValidation": {
|
|
112
|
-
"version": "2.0.0-internal.3.0.
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"broken": {
|
|
116
|
-
"RemovedVariableDeclaration_gcBlobPrefix": {
|
|
117
|
-
"forwardCompat": false,
|
|
118
|
-
"backCompat": false
|
|
119
|
-
},
|
|
120
|
-
"RemovedVariableDeclaration_gcTombstoneBlobKey": {
|
|
121
|
-
"forwardCompat": false,
|
|
122
|
-
"backCompat": false
|
|
123
|
-
},
|
|
124
|
-
"RemovedVariableDeclaration_gcTreeKey": {
|
|
125
|
-
"forwardCompat": false,
|
|
126
|
-
"backCompat": false
|
|
127
|
-
},
|
|
128
|
-
"VariableDeclaration_DefaultSummaryConfiguration": {
|
|
129
|
-
"backCompat": false
|
|
130
|
-
},
|
|
131
|
-
"InterfaceDeclaration_ISummaryBaseConfiguration": {
|
|
132
|
-
"backCompat": false
|
|
133
|
-
},
|
|
134
|
-
"TypeAliasDeclaration_ISummaryConfiguration": {
|
|
135
|
-
"backCompat": false
|
|
136
|
-
},
|
|
137
|
-
"InterfaceDeclaration_ISummaryConfigurationDisableHeuristics": {
|
|
138
|
-
"backCompat": false
|
|
139
|
-
},
|
|
140
|
-
"InterfaceDeclaration_ISummaryConfigurationHeuristics": {
|
|
141
|
-
"backCompat": false
|
|
142
|
-
},
|
|
143
|
-
"ClassDeclaration_ContainerRuntime": {
|
|
144
|
-
"forwardCompat": false
|
|
145
|
-
},
|
|
146
|
-
"InterfaceDeclaration_ISummarizerRuntime": {
|
|
147
|
-
"backCompat": false
|
|
148
|
-
}
|
|
149
|
-
}
|
|
112
|
+
"version": "2.0.0-internal.3.0.1",
|
|
113
|
+
"previousVersionStyle": "previousPatch",
|
|
114
|
+
"baselineRange": "2.0.0-internal.3.0.0",
|
|
115
|
+
"broken": {}
|
|
150
116
|
}
|
|
151
117
|
}
|
package/src/containerRuntime.ts
CHANGED
|
@@ -94,6 +94,7 @@ import {
|
|
|
94
94
|
addSummarizeResultToSummary,
|
|
95
95
|
addTreeToSummary,
|
|
96
96
|
createRootSummarizerNodeWithGC,
|
|
97
|
+
IFetchSnapshotResult,
|
|
97
98
|
IRootSummarizerNodeWithGC,
|
|
98
99
|
RequestParser,
|
|
99
100
|
create404Response,
|
|
@@ -103,6 +104,7 @@ import {
|
|
|
103
104
|
seqFromTree,
|
|
104
105
|
calculateStats,
|
|
105
106
|
TelemetryContext,
|
|
107
|
+
ReadAndParseBlob,
|
|
106
108
|
} from "@fluidframework/runtime-utils";
|
|
107
109
|
import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
|
|
108
110
|
import { v4 as uuid } from "uuid";
|
|
@@ -1081,7 +1083,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1081
1083
|
Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
|
|
1082
1084
|
runtimeOptions.maxBatchSizeInBytes,
|
|
1083
1085
|
this.mc.logger);
|
|
1084
|
-
this.remoteMessageProcessor = new RemoteMessageProcessor(
|
|
1086
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(
|
|
1087
|
+
opSplitter,
|
|
1088
|
+
new OpDecompressor(this.mc.logger),
|
|
1089
|
+
);
|
|
1085
1090
|
|
|
1086
1091
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
1087
1092
|
|
|
@@ -1800,7 +1805,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1800
1805
|
case ContainerMessageType.Rejoin:
|
|
1801
1806
|
break;
|
|
1802
1807
|
default:
|
|
1803
|
-
|
|
1808
|
+
if (runtimeMessage) {
|
|
1809
|
+
const error = DataProcessingError.create(
|
|
1810
|
+
// Former assert 0x3ce
|
|
1811
|
+
"Runtime message of unknown type",
|
|
1812
|
+
"OpProcessing",
|
|
1813
|
+
message,
|
|
1814
|
+
{
|
|
1815
|
+
local,
|
|
1816
|
+
type: message.type,
|
|
1817
|
+
contentType: typeof message.contents,
|
|
1818
|
+
batch: message.metadata?.batch,
|
|
1819
|
+
compression: message.compression,
|
|
1820
|
+
},
|
|
1821
|
+
);
|
|
1822
|
+
this.closeFn(error);
|
|
1823
|
+
throw error;
|
|
1824
|
+
}
|
|
1804
1825
|
}
|
|
1805
1826
|
|
|
1806
1827
|
// For back-compat, notify only about runtime messages for now.
|
|
@@ -2175,12 +2196,21 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2175
2196
|
fullGC,
|
|
2176
2197
|
} = options;
|
|
2177
2198
|
|
|
2199
|
+
const telemetryContext = new TelemetryContext();
|
|
2200
|
+
// Add the options that are used to generate this summary to the telemetry context.
|
|
2201
|
+
telemetryContext.setAll("fluid_Summarize", "Options", {
|
|
2202
|
+
fullTree,
|
|
2203
|
+
trackState,
|
|
2204
|
+
runGC,
|
|
2205
|
+
fullGC,
|
|
2206
|
+
runSweep,
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2178
2209
|
let gcStats: IGCStats | undefined;
|
|
2179
2210
|
if (runGC) {
|
|
2180
|
-
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
2211
|
+
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC }, telemetryContext);
|
|
2181
2212
|
}
|
|
2182
2213
|
|
|
2183
|
-
const telemetryContext = new TelemetryContext();
|
|
2184
2214
|
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
2185
2215
|
|
|
2186
2216
|
this.logger.sendTelemetryEvent({ eventName: "SummarizeTelemetry", details: telemetryContext.serialize() });
|
|
@@ -2334,8 +2364,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2334
2364
|
/** True to generate full GC data */
|
|
2335
2365
|
fullGC?: boolean;
|
|
2336
2366
|
},
|
|
2367
|
+
telemetryContext?: ITelemetryContext,
|
|
2337
2368
|
): Promise<IGCStats | undefined> {
|
|
2338
|
-
return this.garbageCollector.collectGarbage(options);
|
|
2369
|
+
return this.garbageCollector.collectGarbage(options, telemetryContext);
|
|
2339
2370
|
}
|
|
2340
2371
|
|
|
2341
2372
|
/**
|
|
@@ -2835,18 +2866,17 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2835
2866
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
2836
2867
|
// It should only be done by the summarizerNode, if required.
|
|
2837
2868
|
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
2838
|
-
const
|
|
2869
|
+
const fetchLatestSnapshot: () => Promise<IFetchSnapshotResult> = async () => {
|
|
2839
2870
|
const fetchResult = await this.fetchLatestSnapshotFromStorage(
|
|
2840
2871
|
summaryLogger,
|
|
2841
2872
|
{
|
|
2842
|
-
eventName: "
|
|
2873
|
+
eventName: "RefreshLatestSummaryAckFetch",
|
|
2843
2874
|
ackHandle,
|
|
2844
|
-
summaryRefSeq,
|
|
2845
|
-
fetchLatest: true,
|
|
2875
|
+
targetSequenceNumber: summaryRefSeq,
|
|
2846
2876
|
},
|
|
2877
|
+
readAndParseBlob,
|
|
2847
2878
|
);
|
|
2848
2879
|
|
|
2849
|
-
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
2850
2880
|
/**
|
|
2851
2881
|
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
2852
2882
|
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
@@ -2857,7 +2887,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2857
2887
|
* such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
|
|
2858
2888
|
* state.
|
|
2859
2889
|
*/
|
|
2860
|
-
if (latestSnapshotRefSeq < summaryRefSeq) {
|
|
2890
|
+
if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
|
|
2861
2891
|
const error = DataProcessingError.create(
|
|
2862
2892
|
"Fetched snapshot is older than the received ack",
|
|
2863
2893
|
"RefreshLatestSummaryAck",
|
|
@@ -2865,44 +2895,36 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2865
2895
|
{
|
|
2866
2896
|
ackHandle,
|
|
2867
2897
|
summaryRefSeq,
|
|
2868
|
-
latestSnapshotRefSeq,
|
|
2898
|
+
latestSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
2869
2899
|
},
|
|
2870
2900
|
);
|
|
2871
2901
|
this.closeFn(error);
|
|
2872
2902
|
throw error;
|
|
2873
2903
|
}
|
|
2874
2904
|
|
|
2875
|
-
summaryLogger.sendTelemetryEvent(
|
|
2876
|
-
{
|
|
2877
|
-
eventName: "LatestSummaryRetrieved",
|
|
2878
|
-
ackHandle,
|
|
2879
|
-
lastSequenceNumber: latestSnapshotRefSeq,
|
|
2880
|
-
targetSequenceNumber: summaryRefSeq,
|
|
2881
|
-
});
|
|
2882
|
-
|
|
2883
2905
|
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
2884
2906
|
// wait for the delta manager to catch up before refreshing the latest Summary.
|
|
2885
|
-
await this.waitForDeltaManagerToCatchup(
|
|
2886
|
-
|
|
2907
|
+
await this.waitForDeltaManagerToCatchup(
|
|
2908
|
+
fetchResult.latestSnapshotRefSeq,
|
|
2909
|
+
summaryLogger,
|
|
2910
|
+
);
|
|
2887
2911
|
|
|
2888
|
-
return
|
|
2912
|
+
return {
|
|
2913
|
+
snapshotTree: fetchResult.snapshotTree,
|
|
2914
|
+
snapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
2915
|
+
};
|
|
2889
2916
|
};
|
|
2890
2917
|
|
|
2891
2918
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
2892
2919
|
proposalHandle,
|
|
2893
2920
|
summaryRefSeq,
|
|
2894
|
-
|
|
2921
|
+
fetchLatestSnapshot,
|
|
2895
2922
|
readAndParseBlob,
|
|
2896
2923
|
summaryLogger,
|
|
2897
2924
|
);
|
|
2898
2925
|
|
|
2899
2926
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2900
|
-
await this.garbageCollector.refreshLatestSummary(
|
|
2901
|
-
result,
|
|
2902
|
-
proposalHandle,
|
|
2903
|
-
summaryRefSeq,
|
|
2904
|
-
readAndParseBlob,
|
|
2905
|
-
);
|
|
2927
|
+
await this.garbageCollector.refreshLatestSummary(proposalHandle, result, readAndParseBlob);
|
|
2906
2928
|
}
|
|
2907
2929
|
|
|
2908
2930
|
/**
|
|
@@ -2914,30 +2936,31 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2914
2936
|
private async refreshLatestSummaryAckFromServer(
|
|
2915
2937
|
summaryLogger: ITelemetryLogger,
|
|
2916
2938
|
): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined; }> {
|
|
2917
|
-
const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(
|
|
2918
|
-
summaryLogger,
|
|
2919
|
-
{
|
|
2920
|
-
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2921
|
-
fetchLatest: true,
|
|
2922
|
-
},
|
|
2923
|
-
);
|
|
2924
|
-
|
|
2925
2939
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
2926
|
-
const
|
|
2927
|
-
|
|
2940
|
+
const { snapshotTree, versionId, latestSnapshotRefSeq } =
|
|
2941
|
+
await this.fetchLatestSnapshotFromStorage(
|
|
2942
|
+
summaryLogger,
|
|
2943
|
+
{
|
|
2944
|
+
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
2945
|
+
},
|
|
2946
|
+
readAndParseBlob,
|
|
2947
|
+
);
|
|
2948
|
+
const fetchLatestSnapshot: IFetchSnapshotResult = {
|
|
2949
|
+
snapshotTree,
|
|
2950
|
+
snapshotRefSeq: latestSnapshotRefSeq,
|
|
2951
|
+
};
|
|
2928
2952
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
2929
|
-
undefined
|
|
2953
|
+
undefined /* proposalHandle */,
|
|
2930
2954
|
latestSnapshotRefSeq,
|
|
2931
|
-
async () =>
|
|
2955
|
+
async () => fetchLatestSnapshot,
|
|
2932
2956
|
readAndParseBlob,
|
|
2933
2957
|
summaryLogger,
|
|
2934
2958
|
);
|
|
2935
2959
|
|
|
2936
2960
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2937
2961
|
await this.garbageCollector.refreshLatestSummary(
|
|
2962
|
+
undefined /* proposalHandle */,
|
|
2938
2963
|
result,
|
|
2939
|
-
undefined,
|
|
2940
|
-
latestSnapshotRefSeq,
|
|
2941
2964
|
readAndParseBlob,
|
|
2942
2965
|
)
|
|
2943
2966
|
|
|
@@ -2947,29 +2970,54 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2947
2970
|
private async fetchLatestSnapshotFromStorage(
|
|
2948
2971
|
logger: ITelemetryLogger,
|
|
2949
2972
|
event: ITelemetryGenericEvent,
|
|
2950
|
-
|
|
2973
|
+
readAndParseBlob: ReadAndParseBlob,
|
|
2974
|
+
): Promise<{ snapshotTree: ISnapshotTree; versionId: string; latestSnapshotRefSeq: number }> {
|
|
2951
2975
|
return PerformanceEvent.timedExecAsync(
|
|
2952
|
-
logger,
|
|
2976
|
+
logger,
|
|
2977
|
+
event,
|
|
2978
|
+
async (perfEvent: {
|
|
2953
2979
|
end: (arg0: {
|
|
2954
2980
|
getVersionDuration?: number | undefined;
|
|
2955
2981
|
getSnapshotDuration?: number | undefined;
|
|
2982
|
+
snapshotRefSeq?: number | undefined;
|
|
2983
|
+
snapshotVersion?: string | undefined;
|
|
2956
2984
|
}) => void;
|
|
2957
2985
|
}) => {
|
|
2958
|
-
const stats: {
|
|
2986
|
+
const stats: {
|
|
2987
|
+
getVersionDuration?: number;
|
|
2988
|
+
getSnapshotDuration?: number;
|
|
2989
|
+
snapshotRefSeq?: number;
|
|
2990
|
+
snapshotVersion?: string;
|
|
2991
|
+
} = {};
|
|
2959
2992
|
const trace = Trace.start();
|
|
2960
2993
|
|
|
2961
2994
|
const versions = await this.storage.getVersions(
|
|
2962
|
-
null,
|
|
2963
|
-
|
|
2995
|
+
null,
|
|
2996
|
+
1,
|
|
2997
|
+
"refreshLatestSummaryAckFromServer",
|
|
2998
|
+
FetchSource.noCache,
|
|
2999
|
+
);
|
|
3000
|
+
assert(
|
|
3001
|
+
!!versions && !!versions[0],
|
|
3002
|
+
0x137 /* "Failed to get version from storage" */,
|
|
3003
|
+
);
|
|
2964
3004
|
stats.getVersionDuration = trace.trace().duration;
|
|
2965
3005
|
|
|
2966
3006
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
2967
3007
|
assert(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
|
|
2968
3008
|
stats.getSnapshotDuration = trace.trace().duration;
|
|
3009
|
+
const latestSnapshotRefSeq = await seqFromTree(maybeSnapshot, readAndParseBlob);
|
|
3010
|
+
stats.snapshotRefSeq = latestSnapshotRefSeq;
|
|
3011
|
+
stats.snapshotVersion = versions[0].id;
|
|
2969
3012
|
|
|
2970
3013
|
perfEvent.end(stats);
|
|
2971
|
-
return {
|
|
2972
|
-
|
|
3014
|
+
return {
|
|
3015
|
+
snapshotTree: maybeSnapshot,
|
|
3016
|
+
versionId: versions[0].id,
|
|
3017
|
+
latestSnapshotRefSeq,
|
|
3018
|
+
};
|
|
3019
|
+
},
|
|
3020
|
+
);
|
|
2973
3021
|
}
|
|
2974
3022
|
|
|
2975
3023
|
public notifyAttaching(snapshot: ISnapshotTreeWithBlobContents) {
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -61,9 +61,11 @@ import {
|
|
|
61
61
|
import {
|
|
62
62
|
addBlobToSummary,
|
|
63
63
|
convertSummaryTreeToITree,
|
|
64
|
+
packagePathToTelemetryProperty,
|
|
64
65
|
} from "@fluidframework/runtime-utils";
|
|
65
66
|
import {
|
|
66
67
|
ChildLogger,
|
|
68
|
+
generateStack,
|
|
67
69
|
loggerToMonitoringContext,
|
|
68
70
|
LoggingError,
|
|
69
71
|
MonitoringContext,
|
|
@@ -261,6 +263,13 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
261
263
|
private readonly thresholdOpsCounter: ThresholdCounter;
|
|
262
264
|
private static readonly pendingOpsCountThreshold = 1000;
|
|
263
265
|
|
|
266
|
+
/**
|
|
267
|
+
* If the summarizer makes local changes, a telemetry event is logged. This has the potential to be very noisy.
|
|
268
|
+
* So, adding a count of how many telemetry events are logged per data store context. This can be
|
|
269
|
+
* controlled via feature flags.
|
|
270
|
+
*/
|
|
271
|
+
private localChangesTelemetryCount: number;
|
|
272
|
+
|
|
264
273
|
// The used routes of this node as per the last GC run. This is used to update the used routes of the channel
|
|
265
274
|
// if it realizes after GC is run.
|
|
266
275
|
private lastUsedRoutes: string[] | undefined;
|
|
@@ -318,6 +327,10 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
318
327
|
this.throwOnTombstoneUsage =
|
|
319
328
|
this.mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
|
|
320
329
|
this.clientDetails.type !== summarizerClientType;
|
|
330
|
+
|
|
331
|
+
// By default, a data store can log maximum 10 local changes telemetry in summarizer.
|
|
332
|
+
this.localChangesTelemetryCount =
|
|
333
|
+
this.mc.config.getNumber("Fluid.Telemetry.LocalChangesTelemetryCount") ?? 10;
|
|
321
334
|
}
|
|
322
335
|
|
|
323
336
|
public dispose(): void {
|
|
@@ -343,8 +356,15 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
343
356
|
this._tombstoned = tombstone;
|
|
344
357
|
}
|
|
345
358
|
|
|
346
|
-
private rejectDeferredRealize(
|
|
347
|
-
|
|
359
|
+
private rejectDeferredRealize(
|
|
360
|
+
reason: string,
|
|
361
|
+
failedPkgPath?: string,
|
|
362
|
+
fullPackageName?: readonly string[],
|
|
363
|
+
): never {
|
|
364
|
+
throw new LoggingError(reason, {
|
|
365
|
+
failedPkgPath: { value: failedPkgPath, tag: TelemetryDataTag.CodeArtifact },
|
|
366
|
+
fullPackageName: packagePathToTelemetryProperty(fullPackageName),
|
|
367
|
+
});
|
|
348
368
|
}
|
|
349
369
|
|
|
350
370
|
public async realize(): Promise<IFluidDataStoreChannel> {
|
|
@@ -358,6 +378,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
358
378
|
value: this.id,
|
|
359
379
|
tag: TelemetryDataTag.CodeArtifact,
|
|
360
380
|
},
|
|
381
|
+
packageName: packagePathToTelemetryProperty(this.pkg),
|
|
361
382
|
});
|
|
362
383
|
this.channelDeferred?.reject(errorWrapped);
|
|
363
384
|
this.logger.sendErrorEvent({ eventName: "RealizeError" }, errorWrapped);
|
|
@@ -377,18 +398,22 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
377
398
|
let lastPkg: string | undefined;
|
|
378
399
|
for (const pkg of packages) {
|
|
379
400
|
if (!registry) {
|
|
380
|
-
this.rejectDeferredRealize("No registry for package", lastPkg);
|
|
401
|
+
this.rejectDeferredRealize("No registry for package", lastPkg, packages);
|
|
381
402
|
}
|
|
382
403
|
lastPkg = pkg;
|
|
383
404
|
entry = await registry.get(pkg);
|
|
384
405
|
if (!entry) {
|
|
385
|
-
this.rejectDeferredRealize(
|
|
406
|
+
this.rejectDeferredRealize(
|
|
407
|
+
"Registry does not contain entry for the package",
|
|
408
|
+
pkg,
|
|
409
|
+
packages,
|
|
410
|
+
);
|
|
386
411
|
}
|
|
387
412
|
registry = entry.IFluidDataStoreRegistry;
|
|
388
413
|
}
|
|
389
414
|
const factory = entry?.IFluidDataStoreFactory;
|
|
390
415
|
if (factory === undefined) {
|
|
391
|
-
this.rejectDeferredRealize("Can't find factory for package", lastPkg);
|
|
416
|
+
this.rejectDeferredRealize("Can't find factory for package", lastPkg, packages);
|
|
392
417
|
}
|
|
393
418
|
|
|
394
419
|
return { factory, registry };
|
|
@@ -627,6 +652,10 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
627
652
|
content,
|
|
628
653
|
type,
|
|
629
654
|
};
|
|
655
|
+
|
|
656
|
+
// Summarizer clients should not submit messages.
|
|
657
|
+
this.identifyLocalChangeInSummarizer("DataStoreMessageSubmittedInSummarizer", type);
|
|
658
|
+
|
|
630
659
|
this._containerRuntime.submitDataStoreOp(
|
|
631
660
|
this.id,
|
|
632
661
|
fluidDataStoreContent,
|
|
@@ -800,6 +829,33 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
800
829
|
}
|
|
801
830
|
}
|
|
802
831
|
|
|
832
|
+
/**
|
|
833
|
+
* Summarizer client should not have local changes. These changes can become part of the summary and can break
|
|
834
|
+
* eventual consistency. For example, the next summary (say at ref seq# 100) may contain these changes whereas
|
|
835
|
+
* other clients that are up-to-date till seq# 100 may not have them yet.
|
|
836
|
+
*/
|
|
837
|
+
protected identifyLocalChangeInSummarizer(eventName: string, type?: string) {
|
|
838
|
+
if (this.clientDetails.type !== summarizerClientType || this.localChangesTelemetryCount <= 0) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Log a telemetry if there are local changes in the summarizer. This will give us data on how often
|
|
843
|
+
// this is happening and which data stores do this. The eventual goal is to disallow local changes
|
|
844
|
+
// in the summarizer and the data will help us plan this.
|
|
845
|
+
this.mc.logger.sendTelemetryEvent({
|
|
846
|
+
eventName,
|
|
847
|
+
type,
|
|
848
|
+
fluidDataStoreId: {
|
|
849
|
+
value: this.id,
|
|
850
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
851
|
+
},
|
|
852
|
+
packageName: packagePathToTelemetryProperty(this.pkg),
|
|
853
|
+
isSummaryInProgress: this.summarizerNode.isSummaryInProgress?.(),
|
|
854
|
+
stack: generateStack(),
|
|
855
|
+
});
|
|
856
|
+
this.localChangesTelemetryCount--;
|
|
857
|
+
}
|
|
858
|
+
|
|
803
859
|
public getCreateChildSummarizerNodeFn(id: string, createParam: CreateChildSummarizerNodeParam) {
|
|
804
860
|
return (
|
|
805
861
|
summarizeInternal: SummarizeInternalFn,
|
|
@@ -922,6 +978,9 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
922
978
|
props.makeLocallyVisibleFn,
|
|
923
979
|
);
|
|
924
980
|
|
|
981
|
+
// Summarizer client should not create local data stores.
|
|
982
|
+
this.identifyLocalChangeInSummarizer("DataStoreCreatedInSummarizer");
|
|
983
|
+
|
|
925
984
|
this.snapshotTree = props.snapshotTree;
|
|
926
985
|
if (props.isRootDataStore === true) {
|
|
927
986
|
this.setInMemoryRoot();
|
package/src/garbageCollection.ts
CHANGED
|
@@ -63,7 +63,8 @@ import {
|
|
|
63
63
|
runSessionExpiryKey,
|
|
64
64
|
runSweepKey,
|
|
65
65
|
stableGCVersion,
|
|
66
|
-
trackGCStateKey
|
|
66
|
+
trackGCStateKey,
|
|
67
|
+
gcTombstoneGenerationOptionName
|
|
67
68
|
} from "./garbageCollectionConstants";
|
|
68
69
|
import { sendGCTombstoneEvent } from "./garbageCollectionTombstoneUtils";
|
|
69
70
|
import { SweepReadyUsageDetectionHandler } from "./gcSweepReadyUsageDetection";
|
|
@@ -76,6 +77,7 @@ import {
|
|
|
76
77
|
dataStoreAttributesBlobName,
|
|
77
78
|
IGCMetadata,
|
|
78
79
|
ICreateContainerMetadata,
|
|
80
|
+
GCFeatureMatrix,
|
|
79
81
|
} from "./summaryFormat";
|
|
80
82
|
|
|
81
83
|
/** The statistics of the system state after a garbage collection run. */
|
|
@@ -145,6 +147,7 @@ export interface IGarbageCollector {
|
|
|
145
147
|
/** Run garbage collection and update the reference / used state of the system. */
|
|
146
148
|
collectGarbage(
|
|
147
149
|
options: { logger?: ITelemetryLogger; runSweep?: boolean; fullGC?: boolean; },
|
|
150
|
+
telemetryContext?: ITelemetryContext,
|
|
148
151
|
): Promise<IGCStats | undefined>;
|
|
149
152
|
/** Summarizes the GC data and returns it as a summary tree. */
|
|
150
153
|
summarize(
|
|
@@ -158,9 +161,8 @@ export interface IGarbageCollector {
|
|
|
158
161
|
getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase>;
|
|
159
162
|
/** Called when the latest summary of the system has been refreshed. */
|
|
160
163
|
refreshLatestSummary(
|
|
161
|
-
result: RefreshSummaryResult,
|
|
162
164
|
proposalHandle: string | undefined,
|
|
163
|
-
|
|
165
|
+
result: RefreshSummaryResult,
|
|
164
166
|
readAndParseBlob: ReadAndParseBlob,
|
|
165
167
|
): Promise<void>;
|
|
166
168
|
/** Called when a node is updated. Used to detect and log when an inactive node is changed or loaded. */
|
|
@@ -418,6 +420,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
418
420
|
// This is the version of GC data in the latest summary being tracked.
|
|
419
421
|
private latestSummaryGCVersion: GCVersion;
|
|
420
422
|
|
|
423
|
+
// Feature Support info persisted to this container's summary
|
|
424
|
+
private readonly persistedGcFeatureMatrix: GCFeatureMatrix | undefined;
|
|
425
|
+
|
|
421
426
|
// Keeps track of the GC state from the last run.
|
|
422
427
|
private gcDataFromLastRun: IGarbageCollectionData | undefined;
|
|
423
428
|
// Keeps a list of references (edges in the GC graph) between GC runs. Each entry has a node id and a list of
|
|
@@ -557,6 +562,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
557
562
|
this.sweepTimeoutMs =
|
|
558
563
|
metadata?.sweepTimeoutMs
|
|
559
564
|
?? computeSweepTimeout(this.sessionExpiryTimeoutMs); // Backfill old documents that didn't persist this
|
|
565
|
+
this.persistedGcFeatureMatrix = metadata?.gcFeatureMatrix;
|
|
560
566
|
} else {
|
|
561
567
|
// Sweep should not be enabled without enabling GC mark phase. We could silently disable sweep in this
|
|
562
568
|
// scenario but explicitly failing makes it clearer and promotes correct usage.
|
|
@@ -581,6 +587,11 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
581
587
|
this.sweepTimeoutMs =
|
|
582
588
|
testOverrideSweepTimeoutMs
|
|
583
589
|
?? computeSweepTimeout(this.sessionExpiryTimeoutMs);
|
|
590
|
+
if (this.gcOptions[gcTombstoneGenerationOptionName] !== undefined) {
|
|
591
|
+
this.persistedGcFeatureMatrix = {
|
|
592
|
+
tombstoneGeneration: this.gcOptions[gcTombstoneGenerationOptionName],
|
|
593
|
+
};
|
|
594
|
+
}
|
|
584
595
|
}
|
|
585
596
|
|
|
586
597
|
// If session expiry is enabled, we need to close the container when the session expiry timeout expires.
|
|
@@ -950,6 +961,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
950
961
|
/** True to generate full GC data */
|
|
951
962
|
fullGC?: boolean;
|
|
952
963
|
},
|
|
964
|
+
telemetryContext?: ITelemetryContext,
|
|
953
965
|
): Promise<IGCStats | undefined> {
|
|
954
966
|
const fullGC = options.fullGC ?? (this.gcOptions.runFullGC === true || this.summaryStateNeedsReset);
|
|
955
967
|
const logger = options.logger
|
|
@@ -974,6 +986,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
974
986
|
return undefined;
|
|
975
987
|
}
|
|
976
988
|
|
|
989
|
+
// Add the options that are used to run GC to the telemetry context.
|
|
990
|
+
telemetryContext?.setAll("fluid_GC", "Options", { fullGC, runSweep: options.runSweep });
|
|
991
|
+
|
|
977
992
|
return PerformanceEvent.timedExecAsync(logger, { eventName: "GarbageCollection" }, async (event) => {
|
|
978
993
|
await this.runPreGCSteps();
|
|
979
994
|
|
|
@@ -1160,6 +1175,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1160
1175
|
* into the metadata blob. If GC is disabled, the gcFeature is 0.
|
|
1161
1176
|
*/
|
|
1162
1177
|
gcFeature: this.gcEnabled ? this.currentGCVersion : 0,
|
|
1178
|
+
gcFeatureMatrix: this.persistedGcFeatureMatrix,
|
|
1163
1179
|
sessionExpiryTimeoutMs: this.sessionExpiryTimeoutMs,
|
|
1164
1180
|
sweepEnabled: this.sweepEnabled,
|
|
1165
1181
|
sweepTimeoutMs: this.sweepTimeoutMs,
|
|
@@ -1179,9 +1195,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1179
1195
|
* is downloaded and should be used to update the state.
|
|
1180
1196
|
*/
|
|
1181
1197
|
public async refreshLatestSummary(
|
|
1182
|
-
result: RefreshSummaryResult,
|
|
1183
1198
|
proposalHandle: string | undefined,
|
|
1184
|
-
|
|
1199
|
+
result: RefreshSummaryResult,
|
|
1185
1200
|
readAndParseBlob: ReadAndParseBlob,
|
|
1186
1201
|
): Promise<void> {
|
|
1187
1202
|
// If the latest summary was updated and the summary was tracked, this client is the one that generated this
|
|
@@ -1208,8 +1223,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1208
1223
|
}
|
|
1209
1224
|
|
|
1210
1225
|
// If the summary was not tracked by this client, the state should be updated from the downloaded snapshot.
|
|
1211
|
-
const
|
|
1212
|
-
const metadataBlobId =
|
|
1226
|
+
const snapshotTree = result.snapshotTree;
|
|
1227
|
+
const metadataBlobId = snapshotTree.blobs[metadataBlobName];
|
|
1213
1228
|
if (metadataBlobId) {
|
|
1214
1229
|
const metadata = await readAndParseBlob<IContainerRuntimeMetadata>(metadataBlobId);
|
|
1215
1230
|
this.latestSummaryGCVersion = getGCVersion(metadata);
|
|
@@ -1223,10 +1238,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1223
1238
|
"No reference timestamp when updating GC state from snapshot",
|
|
1224
1239
|
"refreshLatestSummary",
|
|
1225
1240
|
undefined,
|
|
1226
|
-
{ proposalHandle, summaryRefSeq, details: JSON.stringify(this.configs) },
|
|
1241
|
+
{ proposalHandle, summaryRefSeq: result.summaryRefSeq, details: JSON.stringify(this.configs) },
|
|
1227
1242
|
);
|
|
1228
1243
|
}
|
|
1229
|
-
const gcSnapshotTree =
|
|
1244
|
+
const gcSnapshotTree = snapshotTree.trees[gcTreeKey];
|
|
1230
1245
|
// If GC ran in the container that generated this snapshot, it will have a GC tree.
|
|
1231
1246
|
this.wasGCRunInLatestSummary = gcSnapshotTree !== undefined;
|
|
1232
1247
|
let latestGCData: IGarbageCollectionSnapshotData | undefined;
|
|
@@ -10,6 +10,9 @@ export const stableGCVersion: GCVersion = 1;
|
|
|
10
10
|
/** The current version of garbage collection. */
|
|
11
11
|
export const currentGCVersion: GCVersion = 2;
|
|
12
12
|
|
|
13
|
+
/** This undocumented GC Option (on ContainerRuntime Options) allows an app to disable enforcing GC on old documents by incrementing this value */
|
|
14
|
+
export const gcTombstoneGenerationOptionName = "gcTombstoneGeneration";
|
|
15
|
+
|
|
13
16
|
// Feature gate key to turn GC on / off.
|
|
14
17
|
export const runGCKey = "Fluid.GarbageCollection.RunGC";
|
|
15
18
|
// Feature gate key to turn GC sweep on / off.
|