@fluidframework/container-runtime 2.0.0-internal.5.1.0 → 2.0.0-internal.5.2.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/CHANGELOG.md +16 -0
- package/dist/containerRuntime.d.ts +22 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +54 -10
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -2
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +2 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +2 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +0 -1
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +2 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +5 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +11 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +29 -2
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +87 -19
- 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/pendingStateManager.d.ts +8 -3
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +23 -16
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summary/index.d.ts +2 -2
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +2 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +1 -1
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizerNode/index.d.ts +1 -1
- package/dist/summary/summarizerNode/index.d.ts.map +1 -1
- package/dist/summary/summarizerNode/index.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +32 -6
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +90 -22
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +22 -2
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +17 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +56 -20
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +7 -1
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +1 -0
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +2 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +13 -4
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +22 -8
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +2 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +22 -0
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +57 -13
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -2
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +2 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +2 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +0 -1
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +2 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +5 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +11 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +29 -2
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +85 -18
- 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/pendingStateManager.d.ts +8 -3
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +23 -16
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summary/index.d.ts +2 -2
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +1 -1
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizerNode/index.d.ts +1 -1
- package/lib/summary/summarizerNode/index.d.ts.map +1 -1
- package/lib/summary/summarizerNode/index.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +32 -6
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +90 -22
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +22 -2
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +17 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +56 -20
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +7 -1
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +1 -0
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +2 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +13 -4
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +20 -7
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +2 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +17 -17
- package/src/containerRuntime.ts +82 -14
- package/src/dataStoreContext.ts +8 -2
- package/src/dataStoreContexts.ts +2 -1
- package/src/dataStores.ts +2 -2
- package/src/gc/gcDefinitions.ts +0 -1
- package/src/index.ts +2 -0
- package/src/opLifecycle/batchManager.ts +9 -1
- package/src/opLifecycle/definitions.ts +11 -0
- package/src/opLifecycle/index.ts +1 -1
- package/src/opLifecycle/outbox.ts +107 -16
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +38 -34
- package/src/summary/index.ts +3 -1
- package/src/summary/runningSummarizer.ts +1 -1
- package/src/summary/summarizerNode/index.ts +1 -0
- package/src/summary/summarizerNode/summarizerNode.ts +107 -25
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +25 -2
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +61 -19
- package/src/summary/summarizerTypes.ts +10 -1
- package/src/summary/summaryCollection.ts +2 -1
- package/src/summary/summaryFormat.ts +5 -1
- package/src/summary/summaryGenerator.ts +31 -8
- package/src/summary/summaryManager.ts +2 -1
package/src/summary/index.ts
CHANGED
|
@@ -62,6 +62,8 @@ export {
|
|
|
62
62
|
ISummarizingWarning,
|
|
63
63
|
IUploadSummaryResult,
|
|
64
64
|
SummarizeResultPart,
|
|
65
|
+
SubmitSummaryFailureData,
|
|
66
|
+
SummaryStage,
|
|
65
67
|
} from "./summarizerTypes";
|
|
66
68
|
export {
|
|
67
69
|
IAckedSummary,
|
|
@@ -96,7 +98,7 @@ export {
|
|
|
96
98
|
wrapSummaryInChannelsTree,
|
|
97
99
|
idCompressorBlobName,
|
|
98
100
|
} from "./summaryFormat";
|
|
99
|
-
export { getFailMessage, SummarizeReason } from "./summaryGenerator";
|
|
101
|
+
export { getFailMessage, RetriableSummaryError, SummarizeReason } from "./summaryGenerator";
|
|
100
102
|
export {
|
|
101
103
|
IConnectedEvents,
|
|
102
104
|
IConnectedState,
|
|
@@ -7,6 +7,7 @@ export {
|
|
|
7
7
|
IFetchSnapshotResult,
|
|
8
8
|
ISummarizerNodeRootContract,
|
|
9
9
|
RefreshSummaryResult,
|
|
10
|
+
ValidateSummaryResult,
|
|
10
11
|
} from "./summarizerNodeUtils";
|
|
11
12
|
export { IRootSummarizerNode, createRootSummarizerNode } from "./summarizerNode";
|
|
12
13
|
export { IRootSummarizerNodeWithGC, createRootSummarizerNodeWithGC } from "./summarizerNodeWithGc";
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { ITelemetryErrorEvent } from "@fluidframework/common-definitions";
|
|
6
7
|
import {
|
|
7
8
|
ISummarizerNode,
|
|
8
9
|
ISummarizerNodeConfig,
|
|
@@ -20,7 +21,6 @@ import {
|
|
|
20
21
|
ISnapshotTree,
|
|
21
22
|
SummaryObject,
|
|
22
23
|
} from "@fluidframework/protocol-definitions";
|
|
23
|
-
import { ITelemetryErrorEvent } from "@fluidframework/common-definitions";
|
|
24
24
|
import {
|
|
25
25
|
ITelemetryLoggerExt,
|
|
26
26
|
ChildLogger,
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
parseSummaryTreeForSubtrees,
|
|
46
46
|
RefreshSummaryResult,
|
|
47
47
|
SummaryNode,
|
|
48
|
+
ValidateSummaryResult,
|
|
48
49
|
} from "./summarizerNodeUtils";
|
|
49
50
|
|
|
50
51
|
export interface IRootSummarizerNode extends ISummarizerNode, ISummarizerNodeRootContract {}
|
|
@@ -193,24 +194,117 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
/**
|
|
196
|
-
*
|
|
197
|
+
* Validates that the in-progress summary is correct, i.e., summarize should have run for all non-skipped
|
|
198
|
+
* nodes. This will only be called for the root summarizer node and is called by it recursively on all child nodes.
|
|
199
|
+
*
|
|
200
|
+
* @returns ValidateSummaryResult which contains a boolean success indicating whether the validation was successful.
|
|
201
|
+
* In case of failure, additional information is returned indicating type of failure and where it was.
|
|
202
|
+
*/
|
|
203
|
+
public validateSummary(): ValidateSummaryResult {
|
|
204
|
+
return this.validateSummaryCore(false /* parentSkipRecursion */);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Validates that the in-progress summary is correct for all nodes, i.e., summarize should have run for all
|
|
209
|
+
* non-skipped nodes.
|
|
210
|
+
* @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when summarizing.
|
|
211
|
+
* In that case, the children will not have work-in-progress state.
|
|
212
|
+
*
|
|
213
|
+
* @returns ValidateSummaryResult which contains a boolean success indicating whether the validation was successful.
|
|
214
|
+
* In case of failure, additional information is returned indicating type of failure and where it was.
|
|
215
|
+
*/
|
|
216
|
+
protected validateSummaryCore(parentSkipRecursion: boolean): ValidateSummaryResult {
|
|
217
|
+
if (this.wasSummarizeMissed(parentSkipRecursion)) {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
reason: "NodeDidNotSummarize",
|
|
221
|
+
id: {
|
|
222
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
223
|
+
value: this.telemetryNodeId,
|
|
224
|
+
},
|
|
225
|
+
// These errors are usually transient and should go away when summarize is retried.
|
|
226
|
+
retryAfterSeconds: 1,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
if (parentSkipRecursion) {
|
|
230
|
+
return { success: true };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
for (const child of this.children.values()) {
|
|
234
|
+
const result = child.validateSummaryCore(this.wipSkipRecursion || parentSkipRecursion);
|
|
235
|
+
// If any child fails, return the failure.
|
|
236
|
+
if (!result.success) {
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return { success: true };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private wasSummarizeMissed(parentSkipRecursion: boolean): boolean {
|
|
244
|
+
assert(
|
|
245
|
+
this.wipSummaryLogger !== undefined,
|
|
246
|
+
0x6fc /* wipSummaryLogger should have been set in startSummary or ctor */,
|
|
247
|
+
);
|
|
248
|
+
assert(this.wipReferenceSequenceNumber !== undefined, 0x6fd /* Not tracking a summary */);
|
|
249
|
+
|
|
250
|
+
// If the parent node skipped recursion, it did not call summarize on this node. So, summarize was not missed
|
|
251
|
+
// but was intentionally not called.
|
|
252
|
+
// Otherwise, summarize should have been called on this node and wipLocalPaths must be set.
|
|
253
|
+
if (parentSkipRecursion || this.wipLocalPaths !== undefined) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The absence of wip local path indicates that summarize was not called for this node. Return failure.
|
|
259
|
+
* This can happen if:
|
|
260
|
+
* 1. A child node was created after summarize was already called on the parent. For example, a data store
|
|
261
|
+
* is realized (loaded) after summarize was called on it creating summarizer nodes for its DDSes. In this case,
|
|
262
|
+
* parentSkipRecursion will be true and the if block above would handle it.
|
|
263
|
+
* 2. A new node was created but summarize was never called on it. This can mean that the summary that is
|
|
264
|
+
* generated may not have the data from this node. We should not continue, log and throw an error. This
|
|
265
|
+
* will help us identify these cases and take appropriate action.
|
|
266
|
+
*
|
|
267
|
+
* This happens due to scenarios such as data store created during summarize. Such errors should go away when
|
|
268
|
+
* summarize is attempted again.
|
|
269
|
+
*/
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Called after summary has been uploaded to the server. Add the work-in-progress state to the pending summary
|
|
275
|
+
* queue. We track this until we get an ack from the server for this summary.
|
|
276
|
+
* @param proposalHandle - The handle of the summary that was uploaded to the server.
|
|
197
277
|
*/
|
|
198
|
-
public completeSummary(proposalHandle: string) {
|
|
199
|
-
this.completeSummaryCore(
|
|
278
|
+
public completeSummary(proposalHandle: string, validate: boolean) {
|
|
279
|
+
this.completeSummaryCore(
|
|
280
|
+
proposalHandle,
|
|
281
|
+
undefined /* parentPath */,
|
|
282
|
+
false /* parentSkipRecursion */,
|
|
283
|
+
validate,
|
|
284
|
+
);
|
|
200
285
|
}
|
|
201
286
|
|
|
202
287
|
/**
|
|
203
|
-
* Recursive implementation for completeSummary, with additional internal-only parameters
|
|
288
|
+
* Recursive implementation for completeSummary, with additional internal-only parameters.
|
|
289
|
+
* @param proposalHandle - The handle of the summary that was uploaded to the server.
|
|
290
|
+
* @param parentPath - The path of the parent node which is used to build the path of this node.
|
|
291
|
+
* @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when summarizing.
|
|
292
|
+
* In that case, the children will not have work-in-progress state.
|
|
293
|
+
* @param validate - true to validate that the in-progress summary is correct for all nodes.
|
|
204
294
|
*/
|
|
205
295
|
protected completeSummaryCore(
|
|
206
296
|
proposalHandle: string,
|
|
207
297
|
parentPath: EscapedPath | undefined,
|
|
208
298
|
parentSkipRecursion: boolean,
|
|
299
|
+
validate: boolean,
|
|
209
300
|
) {
|
|
210
|
-
|
|
211
|
-
this.
|
|
212
|
-
|
|
213
|
-
|
|
301
|
+
if (validate && this.wasSummarizeMissed(parentSkipRecursion)) {
|
|
302
|
+
this.throwUnexpectedError({
|
|
303
|
+
eventName: "NodeDidNotSummarize",
|
|
304
|
+
proposalHandle,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
214
308
|
assert(this.wipReferenceSequenceNumber !== undefined, 0x1a4 /* "Not tracking a summary" */);
|
|
215
309
|
let localPathsToUse = this.wipLocalPaths;
|
|
216
310
|
|
|
@@ -239,22 +333,9 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
239
333
|
}
|
|
240
334
|
}
|
|
241
335
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
* is realized (loaded) after summarize was called on it creating summarizer nodes for its DDSes. In this case,
|
|
246
|
-
* parentSkipRecursion will be true and the if block above would handle it.
|
|
247
|
-
* 2. A new node was created but summarize was never called on it. This can mean that the summary that is
|
|
248
|
-
* generated may not have the data from this node. We should not continue, log and throw an error. This
|
|
249
|
-
* will help us identify these cases and take appropriate action.
|
|
250
|
-
*/
|
|
251
|
-
if (localPathsToUse === undefined) {
|
|
252
|
-
this.throwUnexpectedError({
|
|
253
|
-
eventName: "NodeNotSummarized",
|
|
254
|
-
proposalHandle,
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
336
|
+
// If localPathsToUse is undefined, it means summarize didn't run for this node and in that case the validate
|
|
337
|
+
// step should have failed.
|
|
338
|
+
assert(localPathsToUse !== undefined, 0x6fe /* summarize didn't run for node */);
|
|
258
339
|
const summary = new SummaryNode({
|
|
259
340
|
...localPathsToUse,
|
|
260
341
|
referenceSequenceNumber: this.wipReferenceSequenceNumber,
|
|
@@ -266,6 +347,7 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
266
347
|
proposalHandle,
|
|
267
348
|
fullPathForChildren,
|
|
268
349
|
this.wipSkipRecursion || parentSkipRecursion,
|
|
350
|
+
validate,
|
|
269
351
|
);
|
|
270
352
|
}
|
|
271
353
|
// Note that this overwrites existing pending summary with
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
6
|
+
import { ITelemetryLoggerExt, TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
7
7
|
import { ISnapshotTree, ISummaryTree, SummaryObject } from "@fluidframework/protocol-definitions";
|
|
8
8
|
import { channelsTreeName, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
9
9
|
import { ReadAndParseBlob } from "@fluidframework/runtime-utils";
|
|
@@ -42,9 +42,32 @@ export interface IFetchSnapshotResult {
|
|
|
42
42
|
snapshotRefSeq: number;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Return type of validateSummary function. In case of success, the object returned should have success: true.
|
|
47
|
+
* In case of failure, the object returned should have success: false and additional properties to indicate what
|
|
48
|
+
* the failure was, where it was, can it be retried, etc.
|
|
49
|
+
*/
|
|
50
|
+
export type ValidateSummaryResult =
|
|
51
|
+
| {
|
|
52
|
+
success: true;
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
success: false;
|
|
56
|
+
/** The failure reason */
|
|
57
|
+
reason: string;
|
|
58
|
+
/** id of the node that failed during validation */
|
|
59
|
+
id: {
|
|
60
|
+
tag: TelemetryDataTag.CodeArtifact;
|
|
61
|
+
value: string | undefined;
|
|
62
|
+
};
|
|
63
|
+
/** If the error can be retried, time to wait before retrying */
|
|
64
|
+
retryAfterSeconds?: number;
|
|
65
|
+
};
|
|
66
|
+
|
|
45
67
|
export interface ISummarizerNodeRootContract {
|
|
46
68
|
startSummary(referenceSequenceNumber: number, summaryLogger: ITelemetryLoggerExt): void;
|
|
47
|
-
|
|
69
|
+
validateSummary(): ValidateSummaryResult;
|
|
70
|
+
completeSummary(proposalHandle: string, validate: boolean): void;
|
|
48
71
|
clearSummary(): void;
|
|
49
72
|
refreshLatestSummary(
|
|
50
73
|
proposalHandle: string | undefined,
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
ISummarizerNodeRootContract,
|
|
39
39
|
parseSummaryForSubtrees,
|
|
40
40
|
SummaryNode,
|
|
41
|
+
ValidateSummaryResult,
|
|
41
42
|
} from "./summarizerNodeUtils";
|
|
42
43
|
|
|
43
44
|
export interface IRootSummarizerNodeWithGC
|
|
@@ -47,7 +48,7 @@ export interface IRootSummarizerNodeWithGC
|
|
|
47
48
|
// Extend SummaryNode to add used routes tracking to it.
|
|
48
49
|
class SummaryNodeWithGC extends SummaryNode {
|
|
49
50
|
constructor(
|
|
50
|
-
public readonly serializedUsedRoutes: string,
|
|
51
|
+
public readonly serializedUsedRoutes: string | undefined,
|
|
51
52
|
summary: {
|
|
52
53
|
readonly referenceSequenceNumber: number;
|
|
53
54
|
readonly basePath: EscapedPath | undefined;
|
|
@@ -255,45 +256,86 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
255
256
|
super.startSummary(referenceSequenceNumber, summaryLogger);
|
|
256
257
|
}
|
|
257
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Validates that the in-progress summary is correct for all nodes, i.e., GC should have run for non-skipped nodes.
|
|
261
|
+
* @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when running GC.
|
|
262
|
+
* In that case, the children will not have work-in-progress state.
|
|
263
|
+
*
|
|
264
|
+
* @returns ValidateSummaryResult which contains a boolean success indicating whether the validation was successful.
|
|
265
|
+
* In case of failure, additional information is returned indicating type of failure and where it was.
|
|
266
|
+
*/
|
|
267
|
+
protected validateSummaryCore(parentSkipRecursion: boolean): ValidateSummaryResult {
|
|
268
|
+
if (this.wasGCMissed()) {
|
|
269
|
+
return {
|
|
270
|
+
success: false,
|
|
271
|
+
reason: "NodeDidNotRunGC",
|
|
272
|
+
id: {
|
|
273
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
274
|
+
value: this.telemetryNodeId,
|
|
275
|
+
},
|
|
276
|
+
// These errors are usually transient and should go away when summarize / GC is retried.
|
|
277
|
+
retryAfterSeconds: 1,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return super.validateSummaryCore(parentSkipRecursion);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private wasGCMissed(): boolean {
|
|
284
|
+
// If GC is disabled, it should not have run so it was not missed.
|
|
285
|
+
// Otherwise, GC should have been called on this node and wipSerializedUsedRoutes must be set.
|
|
286
|
+
if (this.gcDisabled || this.wipSerializedUsedRoutes !== undefined) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* The absence of wip used routes indicates that GC was not run on this node. This can happen if:
|
|
291
|
+
* 1. A child node was created after GC was already run on the parent. For example, a data store
|
|
292
|
+
* is realized (loaded) after GC was run on it creating summarizer nodes for its DDSes. In this
|
|
293
|
+
* case, the parent will pass on used routes to the child nodes and it will have wip used routes.
|
|
294
|
+
* 2. A new node was created but GC was never run on it. This can mean that the GC data generated
|
|
295
|
+
* during summarize is incomplete.
|
|
296
|
+
*
|
|
297
|
+
* This happens due to scenarios such as data store created during summarize. Such errors should go away when
|
|
298
|
+
* summarize is attempted again.
|
|
299
|
+
*/
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
|
|
258
303
|
/**
|
|
259
304
|
* Called after summary has been uploaded to the server. Add the work-in-progress state to the pending
|
|
260
305
|
* summary queue. We track this until we get an ack from the server for this summary.
|
|
306
|
+
* @param proposalHandle - The handle of the summary that was uploaded to the server.
|
|
307
|
+
* @param parentPath - The path of the parent node which is used to build the path of this node.
|
|
308
|
+
* @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when summarizing.
|
|
309
|
+
* In that case, the children will not have work-in-progress state.
|
|
310
|
+
* @param validate - true to validate that the in-progress summary is correct for all nodes.
|
|
261
311
|
*/
|
|
262
312
|
protected completeSummaryCore(
|
|
263
313
|
proposalHandle: string,
|
|
264
314
|
parentPath: EscapedPath | undefined,
|
|
265
315
|
parentSkipRecursion: boolean,
|
|
316
|
+
validate: boolean,
|
|
266
317
|
) {
|
|
318
|
+
if (validate && this.wasGCMissed()) {
|
|
319
|
+
this.throwUnexpectedError({
|
|
320
|
+
eventName: "NodeDidNotRunGC",
|
|
321
|
+
proposalHandle,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
267
325
|
let wipSerializedUsedRoutes: string | undefined;
|
|
268
326
|
// If GC is disabled, don't set wip used routes.
|
|
269
327
|
if (!this.gcDisabled) {
|
|
270
328
|
wipSerializedUsedRoutes = this.wipSerializedUsedRoutes;
|
|
271
|
-
/**
|
|
272
|
-
* The absence of wip used routes indicates that GC was not run on this node. This can happen if:
|
|
273
|
-
* 1. A child node was created after GC was already run on the parent. For example, a data store
|
|
274
|
-
* is realized (loaded) after GC was run on it creating summarizer nodes for its DDSes. In this
|
|
275
|
-
* case, the used routes of the parent should be passed on the child nodes and it should be fine.
|
|
276
|
-
* 2. A new node was created but GC was never run on it. This can mean that the GC data generated
|
|
277
|
-
* during summarize is complete . We should not continue, log and throw an error. This will help us
|
|
278
|
-
* identify these cases and take appropriate action.
|
|
279
|
-
*/
|
|
280
|
-
if (wipSerializedUsedRoutes === undefined) {
|
|
281
|
-
this.throwUnexpectedError({
|
|
282
|
-
eventName: "NodeDidNotRunGC",
|
|
283
|
-
proposalHandle,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
329
|
}
|
|
287
330
|
|
|
288
|
-
super.completeSummaryCore(proposalHandle, parentPath, parentSkipRecursion);
|
|
331
|
+
super.completeSummaryCore(proposalHandle, parentPath, parentSkipRecursion, validate);
|
|
289
332
|
|
|
290
333
|
// If GC is disabled, skip setting pending summary with GC state.
|
|
291
334
|
if (!this.gcDisabled) {
|
|
292
335
|
const summaryNode = this.pendingSummaries.get(proposalHandle);
|
|
293
336
|
if (summaryNode !== undefined) {
|
|
294
337
|
const summaryNodeWithGC = new SummaryNodeWithGC(
|
|
295
|
-
|
|
296
|
-
wipSerializedUsedRoutes!,
|
|
338
|
+
wipSerializedUsedRoutes,
|
|
297
339
|
summaryNode,
|
|
298
340
|
);
|
|
299
341
|
this.pendingSummaries.set(proposalHandle, summaryNodeWithGC);
|
|
@@ -213,6 +213,13 @@ export type SubmitSummaryResult =
|
|
|
213
213
|
| IUploadSummaryResult
|
|
214
214
|
| ISubmitSummaryOpResult;
|
|
215
215
|
|
|
216
|
+
/** The stages of Summarize, used to describe how far progress succeeded in case of a failure at a later stage. */
|
|
217
|
+
export type SummaryStage = SubmitSummaryResult["stage"] | "unknown";
|
|
218
|
+
/** The data in summarizer result when submit summary stage fails. */
|
|
219
|
+
export interface SubmitSummaryFailureData {
|
|
220
|
+
stage: SummaryStage;
|
|
221
|
+
}
|
|
222
|
+
|
|
216
223
|
export interface IBroadcastSummaryResult {
|
|
217
224
|
readonly summarizeOp: ISummaryOpMessage;
|
|
218
225
|
readonly broadcastDuration: number;
|
|
@@ -243,7 +250,9 @@ export type SummarizeResultPart<TSuccess, TFailure = undefined> =
|
|
|
243
250
|
|
|
244
251
|
export interface ISummarizeResults {
|
|
245
252
|
/** Resolves when we generate, upload, and submit the summary. */
|
|
246
|
-
readonly summarySubmitted: Promise<
|
|
253
|
+
readonly summarySubmitted: Promise<
|
|
254
|
+
SummarizeResultPart<SubmitSummaryResult, SubmitSummaryFailureData>
|
|
255
|
+
>;
|
|
247
256
|
/** Resolves when we observe our summarize op broadcast. */
|
|
248
257
|
readonly summaryOpBroadcasted: Promise<SummarizeResultPart<IBroadcastSummaryResult>>;
|
|
249
258
|
/** Resolves when we receive a summaryAck or summaryNack. */
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { IEvent } from "@fluidframework/common-definitions";
|
|
7
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
7
8
|
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
8
9
|
import { Deferred, assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
9
10
|
import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/common-utils";
|
|
7
7
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
readAndParse,
|
|
10
|
+
blobHeadersBlobName as blobNameForBlobHeaders,
|
|
11
|
+
} from "@fluidframework/driver-utils";
|
|
9
12
|
import {
|
|
10
13
|
ISequencedDocumentMessage,
|
|
11
14
|
ISnapshotTree,
|
|
@@ -153,6 +156,7 @@ export const chunksBlobName = ".chunks";
|
|
|
153
156
|
export const electedSummarizerBlobName = ".electedSummarizer";
|
|
154
157
|
export const blobsTreeName = ".blobs";
|
|
155
158
|
export const idCompressorBlobName = ".idCompressor";
|
|
159
|
+
export const blobHeadersBlobName = blobNameForBlobHeaders;
|
|
156
160
|
|
|
157
161
|
export function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {
|
|
158
162
|
return !!metadata && !metadata.disableIsolatedChannels;
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
LoggingError,
|
|
10
10
|
ChildLogger,
|
|
11
11
|
} from "@fluidframework/telemetry-utils";
|
|
12
|
+
import { ITelemetryProperties } from "@fluidframework/common-definitions";
|
|
13
|
+
|
|
12
14
|
import {
|
|
13
15
|
assert,
|
|
14
16
|
Deferred,
|
|
@@ -32,6 +34,8 @@ import {
|
|
|
32
34
|
ISummaryCancellationToken,
|
|
33
35
|
ISummarizeTelemetryProperties,
|
|
34
36
|
SummaryGeneratorTelemetry,
|
|
37
|
+
SummaryStage,
|
|
38
|
+
SubmitSummaryFailureData,
|
|
35
39
|
} from "./summarizerTypes";
|
|
36
40
|
import { IClientSummaryWatcher } from "./summaryCollection";
|
|
37
41
|
|
|
@@ -129,7 +133,9 @@ export const getFailMessage = (errorCode: keyof typeof summarizeErrors) =>
|
|
|
129
133
|
`${errorCode}: ${summarizeErrors[errorCode]}`;
|
|
130
134
|
|
|
131
135
|
export class SummarizeResultBuilder {
|
|
132
|
-
public readonly summarySubmitted = new Deferred<
|
|
136
|
+
public readonly summarySubmitted = new Deferred<
|
|
137
|
+
SummarizeResultPart<SubmitSummaryResult, SubmitSummaryFailureData>
|
|
138
|
+
>();
|
|
133
139
|
public readonly summaryOpBroadcasted = new Deferred<
|
|
134
140
|
SummarizeResultPart<IBroadcastSummaryResult>
|
|
135
141
|
>();
|
|
@@ -140,6 +146,7 @@ export class SummarizeResultBuilder {
|
|
|
140
146
|
public fail(
|
|
141
147
|
message: string,
|
|
142
148
|
error: any,
|
|
149
|
+
stage: SummaryStage = "unknown",
|
|
143
150
|
nackSummaryResult?: INackSummaryResult,
|
|
144
151
|
retryAfterSeconds?: number,
|
|
145
152
|
) {
|
|
@@ -155,7 +162,7 @@ export class SummarizeResultBuilder {
|
|
|
155
162
|
error,
|
|
156
163
|
retryAfterSeconds,
|
|
157
164
|
} as const;
|
|
158
|
-
this.summarySubmitted.resolve(result);
|
|
165
|
+
this.summarySubmitted.resolve({ ...result, data: { stage } });
|
|
159
166
|
this.summaryOpBroadcasted.resolve(result);
|
|
160
167
|
this.receivedSummaryAckOrNack.resolve({ ...result, data: nackSummaryResult });
|
|
161
168
|
}
|
|
@@ -168,6 +175,20 @@ export class SummarizeResultBuilder {
|
|
|
168
175
|
}
|
|
169
176
|
}
|
|
170
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Errors type for errors hit during summary that may be retriable.
|
|
180
|
+
*/
|
|
181
|
+
export class RetriableSummaryError extends LoggingError {
|
|
182
|
+
public readonly canRetry = this.retryAfterSeconds !== undefined;
|
|
183
|
+
constructor(
|
|
184
|
+
message: string,
|
|
185
|
+
public readonly retryAfterSeconds?: number,
|
|
186
|
+
props?: ITelemetryProperties,
|
|
187
|
+
) {
|
|
188
|
+
super(message, props);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
171
192
|
/**
|
|
172
193
|
* This class generates and tracks a summary attempt.
|
|
173
194
|
*/
|
|
@@ -242,6 +263,7 @@ export class SummaryGenerator {
|
|
|
242
263
|
{ start: true, end: true, cancel: "generic" },
|
|
243
264
|
);
|
|
244
265
|
|
|
266
|
+
let summaryData: SubmitSummaryResult | undefined;
|
|
245
267
|
const fail = (
|
|
246
268
|
errorCode: keyof typeof summarizeErrors,
|
|
247
269
|
error?: any,
|
|
@@ -270,14 +292,15 @@ export class SummaryGenerator {
|
|
|
270
292
|
},
|
|
271
293
|
error ?? reason,
|
|
272
294
|
); // disconnect & summaryAckTimeout do not have proper error.
|
|
273
|
-
|
|
295
|
+
|
|
296
|
+
// If summarize did not hit an unexpected error, summaryData would be available. Otherwise, the state is
|
|
297
|
+
// unknown.
|
|
298
|
+
const stage = summaryData?.stage ?? "unknown";
|
|
299
|
+
resultsBuilder.fail(reason, error, stage, nackSummaryResult, retryAfterSeconds);
|
|
274
300
|
};
|
|
275
301
|
|
|
276
302
|
// Wait to generate and send summary
|
|
277
303
|
this.summarizeTimer.start();
|
|
278
|
-
|
|
279
|
-
// Use record type to prevent unexpected value types
|
|
280
|
-
let summaryData: SubmitSummaryResult | undefined;
|
|
281
304
|
try {
|
|
282
305
|
// Need to save refSeqNum before we record new attempt (happens as part of submitSummaryCallback)
|
|
283
306
|
const lastAttemptRefSeqNum = this.heuristicData.lastAttempt.refSequenceNumber;
|
|
@@ -359,10 +382,10 @@ export class SummaryGenerator {
|
|
|
359
382
|
cancellationToken,
|
|
360
383
|
);
|
|
361
384
|
if (waitBroadcastResult.result === "cancelled") {
|
|
362
|
-
return fail("disconnect");
|
|
385
|
+
return fail("disconnect", summaryData.stage);
|
|
363
386
|
}
|
|
364
387
|
if (waitBroadcastResult.result !== "done") {
|
|
365
|
-
return fail("summaryOpWaitTimeout");
|
|
388
|
+
return fail("summaryOpWaitTimeout", summaryData.stage);
|
|
366
389
|
}
|
|
367
390
|
const summarizeOp = waitBroadcastResult.value;
|
|
368
391
|
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { IEvent, IEventProvider } from "@fluidframework/common-definitions";
|
|
7
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
7
8
|
import { assert } from "@fluidframework/common-utils";
|
|
8
9
|
import {
|
|
9
10
|
ChildLogger,
|