@fluidframework/runtime-utils 2.0.0-internal.3.1.0 → 2.0.0-internal.3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.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/summarizerNode/summarizerNode.d.ts +26 -11
- package/dist/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNode.js +116 -54
- package/dist/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summarizerNode/summarizerNodeUtils.d.ts +12 -14
- package/dist/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts +8 -114
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.js +30 -12
- package/dist/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summaryUtils.d.ts +4 -0
- package/dist/summaryUtils.d.ts.map +1 -1
- package/dist/summaryUtils.js +9 -0
- package/dist/summaryUtils.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summarizerNode/summarizerNode.d.ts +26 -11
- package/lib/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNode.js +116 -54
- package/lib/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summarizerNode/summarizerNodeUtils.d.ts +12 -14
- package/lib/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts +8 -114
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.js +30 -11
- package/lib/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summaryUtils.d.ts +4 -0
- package/lib/summaryUtils.d.ts.map +1 -1
- package/lib/summaryUtils.js +9 -0
- package/lib/summaryUtils.js.map +1 -1
- package/package.json +110 -111
- package/src/index.ts +1 -0
- package/src/packageVersion.ts +1 -1
- package/src/summarizerNode/summarizerNode.ts +178 -79
- package/src/summarizerNode/summarizerNodeUtils.ts +12 -17
- package/src/summarizerNode/summarizerNodeWithGc.ts +33 -10
- package/src/summaryUtils.ts +14 -0
|
@@ -19,8 +19,14 @@ import {
|
|
|
19
19
|
ISnapshotTree,
|
|
20
20
|
SummaryObject,
|
|
21
21
|
} from "@fluidframework/protocol-definitions";
|
|
22
|
-
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
22
|
+
import { ITelemetryErrorEvent, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
23
23
|
import { assert, unreachableCase } from "@fluidframework/common-utils";
|
|
24
|
+
import {
|
|
25
|
+
ChildLogger,
|
|
26
|
+
LoggingError,
|
|
27
|
+
PerformanceEvent,
|
|
28
|
+
TelemetryDataTag,
|
|
29
|
+
} from "@fluidframework/telemetry-utils";
|
|
24
30
|
import { mergeStats, convertToSummaryTree, calculateStats } from "../summaryUtils";
|
|
25
31
|
import { ReadAndParseBlob } from "../utils";
|
|
26
32
|
import {
|
|
@@ -35,6 +41,10 @@ import {
|
|
|
35
41
|
SummaryNode,
|
|
36
42
|
} from "./summarizerNodeUtils";
|
|
37
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
46
|
+
* upcoming release.
|
|
47
|
+
*/
|
|
38
48
|
export interface IRootSummarizerNode extends ISummarizerNode, ISummarizerNodeRootContract {}
|
|
39
49
|
|
|
40
50
|
/**
|
|
@@ -61,10 +71,40 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
61
71
|
|
|
62
72
|
protected readonly children = new Map<string, SummarizerNode>();
|
|
63
73
|
protected readonly pendingSummaries = new Map<string, SummaryNode>();
|
|
64
|
-
|
|
74
|
+
protected wipReferenceSequenceNumber: number | undefined;
|
|
65
75
|
private wipLocalPaths: { localPath: EscapedPath; additionalPath?: EscapedPath } | undefined;
|
|
66
76
|
private wipSkipRecursion = false;
|
|
67
77
|
|
|
78
|
+
protected readonly logger: ITelemetryLogger;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Do not call constructor directly.
|
|
82
|
+
* Use createRootSummarizerNode to create root node, or createChild to create child nodes.
|
|
83
|
+
*/
|
|
84
|
+
public constructor(
|
|
85
|
+
baseLogger: ITelemetryLogger,
|
|
86
|
+
private readonly summarizeInternalFn: SummarizeInternalFn,
|
|
87
|
+
config: ISummarizerNodeConfig,
|
|
88
|
+
private _changeSequenceNumber: number,
|
|
89
|
+
/** Undefined means created without summary */
|
|
90
|
+
private _latestSummary?: SummaryNode,
|
|
91
|
+
private readonly initialSummary?: IInitialSummary,
|
|
92
|
+
protected wipSummaryLogger?: ITelemetryLogger,
|
|
93
|
+
/** A unique id of this node to be logged when sending telemetry. */
|
|
94
|
+
protected telemetryNodeId?: string,
|
|
95
|
+
) {
|
|
96
|
+
this.canReuseHandle = config.canReuseHandle ?? true;
|
|
97
|
+
// All logs posted by the summarizer node should include the telemetryNodeId.
|
|
98
|
+
this.logger = ChildLogger.create(baseLogger, undefined /* namespace */, {
|
|
99
|
+
all: {
|
|
100
|
+
id: {
|
|
101
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
102
|
+
value: this.telemetryNodeId,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
68
108
|
public startSummary(referenceSequenceNumber: number, summaryLogger: ITelemetryLogger) {
|
|
69
109
|
assert(
|
|
70
110
|
this.wipSummaryLogger === undefined,
|
|
@@ -89,7 +129,7 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
89
129
|
telemetryContext?: ITelemetryContext,
|
|
90
130
|
): Promise<ISummarizeResult> {
|
|
91
131
|
assert(
|
|
92
|
-
this.
|
|
132
|
+
this.isSummaryInProgress(),
|
|
93
133
|
0x1a1 /* "summarize should not be called when not tracking the summary" */,
|
|
94
134
|
);
|
|
95
135
|
assert(
|
|
@@ -176,10 +216,21 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
176
216
|
}
|
|
177
217
|
}
|
|
178
218
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
219
|
+
/**
|
|
220
|
+
* The absence of wip local path indicates that summarize was not called for this node. This can happen if:
|
|
221
|
+
* 1. A child node was created after summarize was already called on the parent. For example, a data store
|
|
222
|
+
* is realized (loaded) after summarize was called on it creating summarizer nodes for its DDSes. In this case,
|
|
223
|
+
* parentSkipRecursion will be true and the if block above would handle it.
|
|
224
|
+
* 2. A new node was created but summarize was never called on it. This can mean that the summary that is
|
|
225
|
+
* generated may not have the data from this node. We should not continue, log and throw an error. This
|
|
226
|
+
* will help us identify these cases and take appropriate action.
|
|
227
|
+
*/
|
|
228
|
+
if (localPathsToUse === undefined) {
|
|
229
|
+
this.throwUnexpectedError({
|
|
230
|
+
eventName: "NodeNotSummarized",
|
|
231
|
+
proposalHandle,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
183
234
|
|
|
184
235
|
const summary = new SummaryNode({
|
|
185
236
|
...localPathsToUse,
|
|
@@ -235,66 +286,104 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
235
286
|
readAndParseBlob: ReadAndParseBlob,
|
|
236
287
|
correlatedSummaryLogger: ITelemetryLogger,
|
|
237
288
|
): Promise<RefreshSummaryResult> {
|
|
238
|
-
|
|
239
|
-
|
|
289
|
+
const eventProps: {
|
|
290
|
+
proposalHandle: string | undefined;
|
|
291
|
+
summaryRefSeq: number;
|
|
292
|
+
referenceSequenceNumber: number;
|
|
293
|
+
latestSummaryUpdated?: boolean;
|
|
294
|
+
wasSummaryTracked?: boolean;
|
|
295
|
+
} = {
|
|
240
296
|
proposalHandle,
|
|
241
|
-
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
242
297
|
summaryRefSeq,
|
|
243
|
-
|
|
298
|
+
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
299
|
+
};
|
|
300
|
+
return PerformanceEvent.timedExecAsync(
|
|
301
|
+
this.logger,
|
|
302
|
+
{
|
|
303
|
+
eventName: "refreshLatestSummary",
|
|
304
|
+
...eventProps,
|
|
305
|
+
},
|
|
306
|
+
async (event) => {
|
|
307
|
+
// Refresh latest summary should not happen while a summary is in progress. If it does, it can result
|
|
308
|
+
// in inconsistent state, so, we should not continue;
|
|
309
|
+
if (this.isSummaryInProgress()) {
|
|
310
|
+
throw new LoggingError("UnexpectedRefreshDuringSummarize", {
|
|
311
|
+
inProgressSummaryRefSeq: this.wipReferenceSequenceNumber,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
244
314
|
|
|
245
|
-
|
|
246
|
-
|
|
315
|
+
if (proposalHandle !== undefined) {
|
|
316
|
+
const maybeSummaryNode = this.pendingSummaries.get(proposalHandle);
|
|
247
317
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
318
|
+
if (maybeSummaryNode !== undefined) {
|
|
319
|
+
this.refreshLatestSummaryFromPending(
|
|
320
|
+
proposalHandle,
|
|
321
|
+
maybeSummaryNode.referenceSequenceNumber,
|
|
322
|
+
);
|
|
323
|
+
eventProps.wasSummaryTracked = true;
|
|
324
|
+
eventProps.latestSummaryUpdated = true;
|
|
325
|
+
event.end(eventProps);
|
|
326
|
+
return {
|
|
327
|
+
latestSummaryUpdated: true,
|
|
328
|
+
wasSummaryTracked: true,
|
|
329
|
+
summaryRefSeq,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
255
332
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
333
|
+
const props = {
|
|
334
|
+
summaryRefSeq,
|
|
335
|
+
pendingSize: this.pendingSummaries.size ?? undefined,
|
|
336
|
+
};
|
|
337
|
+
this.logger.sendTelemetryEvent({
|
|
338
|
+
eventName: "PendingSummaryNotFound",
|
|
339
|
+
proposalHandle,
|
|
340
|
+
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
341
|
+
details: JSON.stringify(props),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
267
344
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
345
|
+
// If the summary for which refresh is called is older than the latest tracked summary, ignore it.
|
|
346
|
+
if (this.referenceSequenceNumber >= summaryRefSeq) {
|
|
347
|
+
eventProps.latestSummaryUpdated = false;
|
|
348
|
+
event.end(eventProps);
|
|
349
|
+
return { latestSummaryUpdated: false };
|
|
350
|
+
}
|
|
272
351
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
352
|
+
// Fetch the latest snapshot and refresh state from it. Note that we need to use the reference sequence number
|
|
353
|
+
// of the fetched snapshot and not the "summaryRefSeq" that was passed in.
|
|
354
|
+
const { snapshotTree, snapshotRefSeq: fetchedSnapshotRefSeq } =
|
|
355
|
+
await fetchLatestSnapshot();
|
|
356
|
+
|
|
357
|
+
// Possible re-entrancy. We may have updated latest summary state while fetching the snapshot. If the fetched
|
|
358
|
+
// snapshot is older than the latest tracked summary, ignore it.
|
|
359
|
+
if (this.referenceSequenceNumber >= fetchedSnapshotRefSeq) {
|
|
360
|
+
eventProps.latestSummaryUpdated = false;
|
|
361
|
+
event.end(eventProps);
|
|
362
|
+
return { latestSummaryUpdated: false };
|
|
363
|
+
}
|
|
276
364
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
365
|
+
await this.refreshLatestSummaryFromSnapshot(
|
|
366
|
+
fetchedSnapshotRefSeq,
|
|
367
|
+
snapshotTree,
|
|
368
|
+
undefined,
|
|
369
|
+
EscapedPath.create(""),
|
|
370
|
+
correlatedSummaryLogger,
|
|
371
|
+
readAndParseBlob,
|
|
372
|
+
);
|
|
282
373
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
374
|
+
eventProps.latestSummaryUpdated = true;
|
|
375
|
+
eventProps.wasSummaryTracked = false;
|
|
376
|
+
eventProps.summaryRefSeq = fetchedSnapshotRefSeq;
|
|
377
|
+
event.end(eventProps);
|
|
378
|
+
return {
|
|
379
|
+
latestSummaryUpdated: true,
|
|
380
|
+
wasSummaryTracked: false,
|
|
381
|
+
snapshotTree,
|
|
382
|
+
summaryRefSeq: fetchedSnapshotRefSeq,
|
|
383
|
+
};
|
|
384
|
+
},
|
|
385
|
+
{ start: true, end: true, cancel: "error" },
|
|
290
386
|
);
|
|
291
|
-
|
|
292
|
-
return {
|
|
293
|
-
latestSummaryUpdated: true,
|
|
294
|
-
wasSummaryTracked: false,
|
|
295
|
-
snapshotTree,
|
|
296
|
-
summaryRefSeq: fetchedSnapshotRefSeq,
|
|
297
|
-
};
|
|
298
387
|
}
|
|
299
388
|
/**
|
|
300
389
|
* Called when we get an ack from the server for a summary we've just sent. Updates the reference state of this node
|
|
@@ -428,23 +517,6 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
428
517
|
|
|
429
518
|
protected readonly canReuseHandle: boolean;
|
|
430
519
|
|
|
431
|
-
/**
|
|
432
|
-
* Do not call constructor directly.
|
|
433
|
-
* Use createRootSummarizerNode to create root node, or createChild to create child nodes.
|
|
434
|
-
*/
|
|
435
|
-
public constructor(
|
|
436
|
-
protected readonly defaultLogger: ITelemetryLogger,
|
|
437
|
-
private readonly summarizeInternalFn: SummarizeInternalFn,
|
|
438
|
-
config: ISummarizerNodeConfig,
|
|
439
|
-
private _changeSequenceNumber: number,
|
|
440
|
-
/** Undefined means created without summary */
|
|
441
|
-
private _latestSummary?: SummaryNode,
|
|
442
|
-
private readonly initialSummary?: IInitialSummary,
|
|
443
|
-
protected wipSummaryLogger?: ITelemetryLogger,
|
|
444
|
-
) {
|
|
445
|
-
this.canReuseHandle = config.canReuseHandle ?? true;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
520
|
public createChild(
|
|
449
521
|
/** Summarize function */
|
|
450
522
|
summarizeInternalFn: SummarizeInternalFn,
|
|
@@ -462,13 +534,14 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
462
534
|
|
|
463
535
|
const createDetails: ICreateChildDetails = this.getCreateDetailsForChild(id, createParam);
|
|
464
536
|
const child = new SummarizerNode(
|
|
465
|
-
this.
|
|
537
|
+
this.logger,
|
|
466
538
|
summarizeInternalFn,
|
|
467
539
|
config,
|
|
468
540
|
createDetails.changeSequenceNumber,
|
|
469
541
|
createDetails.latestSummary,
|
|
470
542
|
createDetails.initialSummary,
|
|
471
543
|
this.wipSummaryLogger,
|
|
544
|
+
createDetails.telemetryNodeId,
|
|
472
545
|
);
|
|
473
546
|
|
|
474
547
|
// There may be additional state that has to be updated in this child. For example, if a summary is being
|
|
@@ -574,10 +647,13 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
574
647
|
}
|
|
575
648
|
}
|
|
576
649
|
|
|
650
|
+
const childtelemetryNodeId = `${this.telemetryNodeId ?? ""}/${id}`;
|
|
651
|
+
|
|
577
652
|
return {
|
|
578
653
|
initialSummary,
|
|
579
654
|
latestSummary,
|
|
580
655
|
changeSequenceNumber,
|
|
656
|
+
telemetryNodeId: childtelemetryNodeId,
|
|
581
657
|
};
|
|
582
658
|
}
|
|
583
659
|
|
|
@@ -589,9 +665,9 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
589
665
|
* @param child - The child node whose state is to be updated.
|
|
590
666
|
*/
|
|
591
667
|
protected maybeUpdateChildState(child: SummarizerNode) {
|
|
592
|
-
// If
|
|
593
|
-
// child's
|
|
594
|
-
if (this.
|
|
668
|
+
// If a summary is in progress, this child was created after the summary started. So, we need to update the
|
|
669
|
+
// child's summary state as well.
|
|
670
|
+
if (this.isSummaryInProgress()) {
|
|
595
671
|
child.wipReferenceSequenceNumber = this.wipReferenceSequenceNumber;
|
|
596
672
|
}
|
|
597
673
|
// In case we have pending summaries on the parent, let's initialize it on the child.
|
|
@@ -611,12 +687,29 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
611
687
|
protected addPendingSummary(key: string, summary: SummaryNode) {
|
|
612
688
|
this.pendingSummaries.set(key, summary);
|
|
613
689
|
}
|
|
690
|
+
|
|
614
691
|
/**
|
|
615
692
|
* Tells whether summary tracking is in progress. True if "startSummary" API is called before summarize.
|
|
616
693
|
*/
|
|
617
|
-
|
|
694
|
+
public isSummaryInProgress(): boolean {
|
|
618
695
|
return this.wipReferenceSequenceNumber !== undefined;
|
|
619
696
|
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Creates and throws an error due to unexpected conditions.
|
|
700
|
+
*/
|
|
701
|
+
protected throwUnexpectedError(eventProps: ITelemetryErrorEvent): never {
|
|
702
|
+
const error = new LoggingError(eventProps.eventName, {
|
|
703
|
+
...eventProps,
|
|
704
|
+
referenceSequenceNumber: this.wipReferenceSequenceNumber,
|
|
705
|
+
id: {
|
|
706
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
707
|
+
value: this.telemetryNodeId,
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
this.logger.sendErrorEvent(eventProps, error);
|
|
711
|
+
throw error;
|
|
712
|
+
}
|
|
620
713
|
}
|
|
621
714
|
|
|
622
715
|
/**
|
|
@@ -627,6 +720,9 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
627
720
|
* @param referenceSequenceNumber - Reference sequence number of last acked summary,
|
|
628
721
|
* or undefined if not loaded from summary
|
|
629
722
|
* @param config - Configure behavior of summarizer node
|
|
723
|
+
*
|
|
724
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
725
|
+
* upcoming release.
|
|
630
726
|
*/
|
|
631
727
|
export const createRootSummarizerNode = (
|
|
632
728
|
logger: ITelemetryLogger,
|
|
@@ -643,4 +739,7 @@ export const createRootSummarizerNode = (
|
|
|
643
739
|
referenceSequenceNumber === undefined
|
|
644
740
|
? undefined
|
|
645
741
|
: SummaryNode.createForRoot(referenceSequenceNumber),
|
|
742
|
+
undefined /* initialSummary */,
|
|
743
|
+
undefined /* wipSummaryLogger */,
|
|
744
|
+
"" /* telemetryNodeId */,
|
|
646
745
|
);
|
|
@@ -17,6 +17,9 @@ import { ReadAndParseBlob } from "../utils";
|
|
|
17
17
|
*
|
|
18
18
|
* 3. The latest summary was updated but the summary corresponding to the params was not tracked. In this case, the
|
|
19
19
|
* latest snapshot is fetched and the latest summary state is updated based on it.
|
|
20
|
+
*
|
|
21
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
22
|
+
* upcoming release.
|
|
20
23
|
*/
|
|
21
24
|
export type RefreshSummaryResult =
|
|
22
25
|
| {
|
|
@@ -36,12 +39,19 @@ export type RefreshSummaryResult =
|
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* Result of snapshot fetch during refreshing latest summary state.
|
|
42
|
+
*
|
|
43
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
44
|
+
* upcoming release.
|
|
39
45
|
*/
|
|
40
46
|
export interface IFetchSnapshotResult {
|
|
41
47
|
snapshotTree: ISnapshotTree;
|
|
42
48
|
snapshotRefSeq: number;
|
|
43
49
|
}
|
|
44
50
|
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
53
|
+
* upcoming release.
|
|
54
|
+
*/
|
|
45
55
|
export interface ISummarizerNodeRootContract {
|
|
46
56
|
startSummary(referenceSequenceNumber: number, summaryLogger: ITelemetryLogger): void;
|
|
47
57
|
completeSummary(proposalHandle: string): void;
|
|
@@ -143,23 +153,6 @@ export class SummaryNode {
|
|
|
143
153
|
}
|
|
144
154
|
}
|
|
145
155
|
|
|
146
|
-
/**
|
|
147
|
-
* Parameter to help encode summary with conditional behavior.
|
|
148
|
-
* When fromSummary is true, it will contain the SummaryNode of
|
|
149
|
-
* its previous summary, which it can use to point to with a handle.
|
|
150
|
-
* When fromSummary is false, it will use an actual summary tree
|
|
151
|
-
* as its base summary in case the first summary is a differential summary.
|
|
152
|
-
*/
|
|
153
|
-
export type EncodeSummaryParam =
|
|
154
|
-
| {
|
|
155
|
-
fromSummary: true;
|
|
156
|
-
summaryNode: SummaryNode;
|
|
157
|
-
}
|
|
158
|
-
| {
|
|
159
|
-
fromSummary: false;
|
|
160
|
-
initialSummary: ISummaryTreeWithStats;
|
|
161
|
-
};
|
|
162
|
-
|
|
163
156
|
/**
|
|
164
157
|
* Information about the initial summary tree found from an attach op.
|
|
165
158
|
*/
|
|
@@ -179,6 +172,8 @@ export interface ICreateChildDetails {
|
|
|
179
172
|
latestSummary: SummaryNode | undefined;
|
|
180
173
|
/** Sequence number of latest known change to the node */
|
|
181
174
|
changeSequenceNumber: number;
|
|
175
|
+
/** A unique id of this child to be logged when sending telemetry. */
|
|
176
|
+
telemetryNodeId: string;
|
|
182
177
|
}
|
|
183
178
|
|
|
184
179
|
export interface ISubtreeInfo<T extends ISnapshotTree | SummaryObject> {
|
|
@@ -35,6 +35,10 @@ import {
|
|
|
35
35
|
SummaryNode,
|
|
36
36
|
} from "./summarizerNodeUtils";
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
40
|
+
* upcoming release.
|
|
41
|
+
*/
|
|
38
42
|
export interface IRootSummarizerNodeWithGC
|
|
39
43
|
extends ISummarizerNodeWithGC,
|
|
40
44
|
ISummarizerNodeRootContract {}
|
|
@@ -67,7 +71,7 @@ class SummaryNodeWithGC extends SummaryNode {
|
|
|
67
71
|
* - Adds trackState param to summarize. If trackState is false, it bypasses the SummarizerNode and calls
|
|
68
72
|
* directly into summarizeInternal method.
|
|
69
73
|
*/
|
|
70
|
-
|
|
74
|
+
class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNodeWithGC {
|
|
71
75
|
// Tracks the work-in-progress used routes during summary.
|
|
72
76
|
private wipSerializedUsedRoutes: string | undefined;
|
|
73
77
|
|
|
@@ -109,6 +113,8 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
109
113
|
wipSummaryLogger?: ITelemetryLogger,
|
|
110
114
|
private readonly getGCDataFn?: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
|
|
111
115
|
getBaseGCDetailsFn?: () => Promise<IGarbageCollectionDetailsBase>,
|
|
116
|
+
/** A unique id of this node to be logged when sending telemetry. */
|
|
117
|
+
telemetryId?: string,
|
|
112
118
|
) {
|
|
113
119
|
super(
|
|
114
120
|
logger,
|
|
@@ -119,6 +125,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
119
125
|
latestSummary,
|
|
120
126
|
initialSummary,
|
|
121
127
|
wipSummaryLogger,
|
|
128
|
+
telemetryId,
|
|
122
129
|
);
|
|
123
130
|
|
|
124
131
|
this.gcDisabled = config.gcDisabled === true;
|
|
@@ -164,9 +171,9 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
164
171
|
trackState: boolean = true,
|
|
165
172
|
telemetryContext?: ITelemetryContext,
|
|
166
173
|
): Promise<ISummarizeResult> {
|
|
167
|
-
// If GC is not disabled and
|
|
174
|
+
// If GC is not disabled and a summary is in progress, GC should have run and updated the used routes for this
|
|
168
175
|
// summary by calling updateUsedRoutes which sets wipSerializedUsedRoutes.
|
|
169
|
-
if (!this.gcDisabled && this.
|
|
176
|
+
if (!this.gcDisabled && this.isSummaryInProgress()) {
|
|
170
177
|
assert(
|
|
171
178
|
this.wipSerializedUsedRoutes !== undefined,
|
|
172
179
|
0x1b1 /* "wip used routes should be set if tracking a summary" */,
|
|
@@ -239,10 +246,21 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
239
246
|
// If GC is disabled, don't set wip used routes.
|
|
240
247
|
if (!this.gcDisabled) {
|
|
241
248
|
wipSerializedUsedRoutes = this.wipSerializedUsedRoutes;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
249
|
+
/**
|
|
250
|
+
* The absence of wip used routes indicates that GC was not run on this node. This can happen if:
|
|
251
|
+
* 1. A child node was created after GC was already run on the parent. For example, a data store
|
|
252
|
+
* is realized (loaded) after GC was run on it creating summarizer nodes for its DDSes. In this
|
|
253
|
+
* case, the used routes of the parent should be passed on the child nodes and it should be fine.
|
|
254
|
+
* 2. A new node was created but GC was never run on it. This can mean that the GC data generated
|
|
255
|
+
* during summarize is complete . We should not continue, log and throw an error. This will help us
|
|
256
|
+
* identify these cases and take appropriate action.
|
|
257
|
+
*/
|
|
258
|
+
if (wipSerializedUsedRoutes === undefined) {
|
|
259
|
+
this.throwUnexpectedError({
|
|
260
|
+
eventName: "NodeDidNotRunGC",
|
|
261
|
+
proposalHandle,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
246
264
|
}
|
|
247
265
|
|
|
248
266
|
super.completeSummaryCore(proposalHandle, parentPath, parentSkipRecursion);
|
|
@@ -436,7 +454,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
436
454
|
|
|
437
455
|
const createDetails: ICreateChildDetails = this.getCreateDetailsForChild(id, createParam);
|
|
438
456
|
const child = new SummarizerNodeWithGC(
|
|
439
|
-
this.
|
|
457
|
+
this.logger,
|
|
440
458
|
summarizeInternalFn,
|
|
441
459
|
{
|
|
442
460
|
...config,
|
|
@@ -449,6 +467,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
449
467
|
this.wipSummaryLogger,
|
|
450
468
|
getGCDataFn,
|
|
451
469
|
async () => getChildBaseGCDetailsP,
|
|
470
|
+
createDetails.telemetryNodeId,
|
|
452
471
|
);
|
|
453
472
|
|
|
454
473
|
// There may be additional state that has to be updated in this child. For example, if a summary is being
|
|
@@ -482,9 +501,9 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
482
501
|
// are in the same order.
|
|
483
502
|
this.usedRoutes = usedRoutes.sort();
|
|
484
503
|
|
|
485
|
-
// If GC is not disabled and
|
|
504
|
+
// If GC is not disabled and a summary is in progress, update the work-in-progress used routes so that it can
|
|
486
505
|
// be tracked for this summary.
|
|
487
|
-
if (!this.gcDisabled && this.
|
|
506
|
+
if (!this.gcDisabled && this.isSummaryInProgress()) {
|
|
488
507
|
this.wipSerializedUsedRoutes = JSON.stringify(this.usedRoutes);
|
|
489
508
|
}
|
|
490
509
|
}
|
|
@@ -531,6 +550,9 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
531
550
|
* @param config - Configure behavior of summarizer node
|
|
532
551
|
* @param getGCDataFn - Function to get the GC data of this node
|
|
533
552
|
* @param baseGCDetailsP - Function to get the initial GC details of this node
|
|
553
|
+
*
|
|
554
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
555
|
+
* upcoming release.
|
|
534
556
|
*/
|
|
535
557
|
export const createRootSummarizerNodeWithGC = (
|
|
536
558
|
logger: ITelemetryLogger,
|
|
@@ -553,4 +575,5 @@ export const createRootSummarizerNodeWithGC = (
|
|
|
553
575
|
undefined /* wipSummaryLogger */,
|
|
554
576
|
getGCDataFn,
|
|
555
577
|
getBaseGCDetailsFn,
|
|
578
|
+
"" /* telemetryId */,
|
|
556
579
|
);
|
package/src/summaryUtils.ts
CHANGED
|
@@ -356,6 +356,20 @@ export class TelemetryContext implements ITelemetryContext {
|
|
|
356
356
|
this.telemetry.set(`${prefix}${property}`, value);
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
/**
|
|
360
|
+
* {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.setMultiple}
|
|
361
|
+
*/
|
|
362
|
+
setMultiple(
|
|
363
|
+
prefix: string,
|
|
364
|
+
property: string,
|
|
365
|
+
values: Record<string, TelemetryEventPropertyType>,
|
|
366
|
+
): void {
|
|
367
|
+
// Set the values individually so that they are logged as a flat list along with other properties.
|
|
368
|
+
for (const key of Object.keys(values)) {
|
|
369
|
+
this.set(prefix, `${property}_${key}`, values[key]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
359
373
|
/**
|
|
360
374
|
* {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.get}
|
|
361
375
|
*/
|