@fluidframework/container-runtime 2.4.0-297027 → 2.4.0-299374
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/container-runtime.test-files.tar +0 -0
- package/dist/containerRuntime.d.ts +8 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +79 -58
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +0 -3
- package/dist/dataStoreContext.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/summary/summarizerNode/summarizerNode.d.ts +30 -13
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +84 -102
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +10 -88
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +28 -38
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/containerRuntime.d.ts +8 -0
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +79 -58
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +0 -3
- package/lib/dataStoreContext.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/summary/summarizerNode/summarizerNode.d.ts +30 -13
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +85 -103
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +8 -84
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +29 -39
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/package.json +21 -22
- package/src/containerRuntime.ts +98 -76
- package/src/dataStoreContext.ts +0 -3
- package/src/packageVersion.ts +1 -1
- package/src/summary/summarizerNode/summarizerNode.ts +90 -123
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +19 -99
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +37 -53
|
@@ -37,9 +37,8 @@ import {
|
|
|
37
37
|
IRefreshSummaryResult,
|
|
38
38
|
IStartSummaryResult,
|
|
39
39
|
ISummarizerNodeRootContract,
|
|
40
|
-
SummaryNode,
|
|
41
40
|
ValidateSummaryResult,
|
|
42
|
-
|
|
41
|
+
PendingSummaryInfo,
|
|
43
42
|
} from "./summarizerNodeUtils.js";
|
|
44
43
|
|
|
45
44
|
export interface IRootSummarizerNode extends ISummarizerNode, ISummarizerNodeRootContract {}
|
|
@@ -63,13 +62,30 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
63
62
|
* Returns 0 if there is not yet an acked summary.
|
|
64
63
|
*/
|
|
65
64
|
public get referenceSequenceNumber() {
|
|
66
|
-
return this.
|
|
65
|
+
return this._lastSummaryReferenceSequenceNumber ?? 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* returns the handle of the last successful summary of this summarizerNode in string format
|
|
70
|
+
* (this getter is primarily only used in the test code)
|
|
71
|
+
*/
|
|
72
|
+
public get summaryHandleId(): string {
|
|
73
|
+
return this._summaryHandleId.toString();
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
protected readonly children = new Map<string, SummarizerNode>();
|
|
70
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Key value pair of summaries submitted by this client which are not yet acked.
|
|
79
|
+
* Key is the proposalHandle and value is the summary op's referece sequence number.
|
|
80
|
+
*/
|
|
81
|
+
protected readonly pendingSummaries = new Map<string, PendingSummaryInfo>();
|
|
71
82
|
protected wipReferenceSequenceNumber: number | undefined;
|
|
72
|
-
|
|
83
|
+
/**
|
|
84
|
+
* True if the current node was summarized during the current summary process
|
|
85
|
+
* This flag is used to identify scenarios where summarize was not called on a node.
|
|
86
|
+
* For example, this node was created after its parent was already summarized due to out-of-order realization via application code.
|
|
87
|
+
*/
|
|
88
|
+
private wipSummarizeCalled: boolean = false;
|
|
73
89
|
private wipSkipRecursion = false;
|
|
74
90
|
|
|
75
91
|
protected readonly logger: ITelemetryLoggerExt;
|
|
@@ -82,9 +98,11 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
82
98
|
baseLogger: ITelemetryBaseLogger,
|
|
83
99
|
private readonly summarizeInternalFn: SummarizeInternalFn,
|
|
84
100
|
config: ISummarizerNodeConfig,
|
|
101
|
+
/** Encoded handle or path to the node */
|
|
102
|
+
private readonly _summaryHandleId: EscapedPath,
|
|
85
103
|
private _changeSequenceNumber: number,
|
|
86
|
-
/**
|
|
87
|
-
private
|
|
104
|
+
/** Summary reference sequence number, i.e. last sequence number seen when last successful summary was created */
|
|
105
|
+
private _lastSummaryReferenceSequenceNumber?: number,
|
|
88
106
|
protected wipSummaryLogger?: ITelemetryBaseLogger,
|
|
89
107
|
/** A unique id of this node to be logged when sending telemetry. */
|
|
90
108
|
protected telemetryNodeId?: string,
|
|
@@ -124,10 +142,10 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
124
142
|
0x1a0 /* "Already tracking a summary" */,
|
|
125
143
|
);
|
|
126
144
|
|
|
127
|
-
let nodes = 1;
|
|
145
|
+
let nodes = 1; // number of summarizerNodes at the start of the summary
|
|
128
146
|
let invalidNodes = 0;
|
|
129
147
|
const sequenceNumberMismatchKeySet = new Set<string>();
|
|
130
|
-
const nodeLatestSummaryRefSeqNum = this.
|
|
148
|
+
const nodeLatestSummaryRefSeqNum = this._lastSummaryReferenceSequenceNumber;
|
|
131
149
|
if (
|
|
132
150
|
nodeLatestSummaryRefSeqNum !== undefined &&
|
|
133
151
|
latestSummaryRefSeqNum !== nodeLatestSummaryRefSeqNum
|
|
@@ -170,21 +188,19 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
170
188
|
return this.summarizeInternalFn(fullTree, trackState, telemetryContext);
|
|
171
189
|
}
|
|
172
190
|
|
|
191
|
+
// Set to wipSummarizeCalled true to represent that current node was included in the summary process.
|
|
192
|
+
this.wipSummarizeCalled = true;
|
|
193
|
+
|
|
173
194
|
// Try to reuse the tree if unchanged
|
|
174
195
|
if (this.canReuseHandle && !fullTree && !this.hasChanged()) {
|
|
175
|
-
|
|
176
|
-
if (latestSummary !== undefined) {
|
|
177
|
-
this.wipLocalPaths = {
|
|
178
|
-
localPath: latestSummary.localPath,
|
|
179
|
-
additionalPath: latestSummary.additionalPath,
|
|
180
|
-
};
|
|
196
|
+
if (this._lastSummaryReferenceSequenceNumber !== undefined) {
|
|
181
197
|
this.wipSkipRecursion = true;
|
|
182
198
|
const stats = mergeStats();
|
|
183
199
|
stats.handleNodeCount++;
|
|
184
200
|
return {
|
|
185
201
|
summary: {
|
|
186
202
|
type: SummaryType.Handle,
|
|
187
|
-
handle:
|
|
203
|
+
handle: this.summaryHandleId,
|
|
188
204
|
handleType: SummaryType.Tree,
|
|
189
205
|
},
|
|
190
206
|
stats,
|
|
@@ -199,12 +215,12 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
199
215
|
0x5df /* Summarize should not be called when not tracking the summary */,
|
|
200
216
|
);
|
|
201
217
|
incrementalSummaryContext =
|
|
202
|
-
this.
|
|
218
|
+
this._lastSummaryReferenceSequenceNumber !== undefined
|
|
203
219
|
? {
|
|
204
220
|
summarySequenceNumber: this.wipReferenceSequenceNumber,
|
|
205
|
-
latestSummarySequenceNumber: this.
|
|
206
|
-
// TODO: remove summaryPath
|
|
207
|
-
summaryPath: this.
|
|
221
|
+
latestSummarySequenceNumber: this._lastSummaryReferenceSequenceNumber,
|
|
222
|
+
// TODO: remove summaryPath.
|
|
223
|
+
summaryPath: this.summaryHandleId,
|
|
208
224
|
}
|
|
209
225
|
: undefined;
|
|
210
226
|
}
|
|
@@ -215,12 +231,7 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
215
231
|
telemetryContext,
|
|
216
232
|
incrementalSummaryContext,
|
|
217
233
|
);
|
|
218
|
-
|
|
219
|
-
if (result.pathPartsForChildren !== undefined) {
|
|
220
|
-
this.wipLocalPaths.additionalPath = EscapedPath.createAndConcat(
|
|
221
|
-
result.pathPartsForChildren,
|
|
222
|
-
);
|
|
223
|
-
}
|
|
234
|
+
|
|
224
235
|
return { summary: result.summary, stats: result.stats };
|
|
225
236
|
}
|
|
226
237
|
|
|
@@ -280,8 +291,8 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
280
291
|
|
|
281
292
|
// If the parent node skipped recursion, it did not call summarize on this node. So, summarize was not missed
|
|
282
293
|
// but was intentionally not called.
|
|
283
|
-
// Otherwise, summarize should have been called on this node and
|
|
284
|
-
if (parentSkipRecursion || this.
|
|
294
|
+
// Otherwise, summarize should have been called on this node and wipSummarizeCalled must be set.
|
|
295
|
+
if (parentSkipRecursion || this.wipSummarizeCalled) {
|
|
285
296
|
return false;
|
|
286
297
|
}
|
|
287
298
|
|
|
@@ -307,11 +318,7 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
307
318
|
* @param proposalHandle - The handle of the summary that was uploaded to the server.
|
|
308
319
|
*/
|
|
309
320
|
public completeSummary(proposalHandle: string) {
|
|
310
|
-
this.completeSummaryCore(
|
|
311
|
-
proposalHandle,
|
|
312
|
-
undefined /* parentPath */,
|
|
313
|
-
false /* parentSkipRecursion */,
|
|
314
|
-
);
|
|
321
|
+
this.completeSummaryCore(proposalHandle, false /* parentSkipRecursion */);
|
|
315
322
|
}
|
|
316
323
|
|
|
317
324
|
/**
|
|
@@ -322,31 +329,13 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
322
329
|
* In that case, the children will not have work-in-progress state.
|
|
323
330
|
* @param validate - true to validate that the in-progress summary is correct for all nodes.
|
|
324
331
|
*/
|
|
325
|
-
protected completeSummaryCore(
|
|
326
|
-
proposalHandle: string,
|
|
327
|
-
parentPath: EscapedPath | undefined,
|
|
328
|
-
parentSkipRecursion: boolean,
|
|
329
|
-
) {
|
|
332
|
+
protected completeSummaryCore(proposalHandle: string, parentSkipRecursion: boolean) {
|
|
330
333
|
assert(
|
|
331
334
|
this.wipReferenceSequenceNumber !== undefined,
|
|
332
335
|
0x1a4 /* "Not tracking a summary" */,
|
|
333
336
|
);
|
|
334
|
-
let localPathsToUse = this.wipLocalPaths;
|
|
335
|
-
|
|
336
337
|
if (parentSkipRecursion) {
|
|
337
|
-
|
|
338
|
-
if (latestSummary !== undefined) {
|
|
339
|
-
// This case the parent node created a failure summary or was reused.
|
|
340
|
-
// This node and all children should only try to reference their path
|
|
341
|
-
// by its last known good state in the actual summary tree.
|
|
342
|
-
// If parent fails or is reused, the child summarize is not called so
|
|
343
|
-
// it did not get a chance to change its paths.
|
|
344
|
-
// In this case, essentially only propagate the new summary ref seq num.
|
|
345
|
-
localPathsToUse = {
|
|
346
|
-
localPath: latestSummary.localPath,
|
|
347
|
-
additionalPath: latestSummary.additionalPath,
|
|
348
|
-
};
|
|
349
|
-
} else {
|
|
338
|
+
if (this._lastSummaryReferenceSequenceNumber === undefined) {
|
|
350
339
|
// This case the child is added after the latest non-failure summary.
|
|
351
340
|
// This node and all children should consider themselves as still not
|
|
352
341
|
// having a successful summary yet.
|
|
@@ -358,21 +347,8 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
358
347
|
}
|
|
359
348
|
}
|
|
360
349
|
|
|
361
|
-
// If localPathsToUse is undefined, it means summarize didn't run for this node and in that case the validate
|
|
362
|
-
// step should have failed.
|
|
363
|
-
assert(localPathsToUse !== undefined, 0x6fe /* summarize didn't run for node */);
|
|
364
|
-
const summary = new SummaryNode({
|
|
365
|
-
...localPathsToUse,
|
|
366
|
-
referenceSequenceNumber: this.wipReferenceSequenceNumber,
|
|
367
|
-
basePath: parentPath,
|
|
368
|
-
});
|
|
369
|
-
const fullPathForChildren = summary.fullPathForChildren;
|
|
370
350
|
for (const child of this.children.values()) {
|
|
371
|
-
child.completeSummaryCore(
|
|
372
|
-
proposalHandle,
|
|
373
|
-
fullPathForChildren,
|
|
374
|
-
this.wipSkipRecursion || parentSkipRecursion,
|
|
375
|
-
);
|
|
351
|
+
child.completeSummaryCore(proposalHandle, this.wipSkipRecursion || parentSkipRecursion);
|
|
376
352
|
}
|
|
377
353
|
// Note that this overwrites existing pending summary with
|
|
378
354
|
// the same proposalHandle. If proposalHandle is something like
|
|
@@ -380,13 +356,15 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
380
356
|
// can return the same proposalHandle for a different summary,
|
|
381
357
|
// this should still be okay, because we should be proposing the
|
|
382
358
|
// newer one later which would have to overwrite the previous one.
|
|
383
|
-
this.pendingSummaries.set(proposalHandle,
|
|
359
|
+
this.pendingSummaries.set(proposalHandle, {
|
|
360
|
+
referenceSequenceNumber: this.wipReferenceSequenceNumber,
|
|
361
|
+
});
|
|
384
362
|
this.clearSummary();
|
|
385
363
|
}
|
|
386
364
|
|
|
387
365
|
public clearSummary() {
|
|
388
366
|
this.wipReferenceSequenceNumber = undefined;
|
|
389
|
-
this.
|
|
367
|
+
this.wipSummarizeCalled = false;
|
|
390
368
|
this.wipSkipRecursion = false;
|
|
391
369
|
this.wipSummaryLogger = undefined;
|
|
392
370
|
for (const child of this.children.values()) {
|
|
@@ -398,7 +376,8 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
398
376
|
* Refreshes the latest summary tracked by this node. If we have a pending summary for the given proposal handle,
|
|
399
377
|
* it becomes the latest summary. If the current summary is already ahead, we skip the update.
|
|
400
378
|
* If the current summary is behind, then we do not refresh.
|
|
401
|
-
*
|
|
379
|
+
* @param proposalHandle - Handle of the generated / uploaded summary.
|
|
380
|
+
* @param summaryRefSeq - Reference sequence of the acked summary
|
|
402
381
|
* @returns true if the summary is tracked by this node, false otherwise.
|
|
403
382
|
*/
|
|
404
383
|
public async refreshLatestSummary(
|
|
@@ -437,13 +416,17 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
437
416
|
if (summaryRefSeq > this.referenceSequenceNumber) {
|
|
438
417
|
isSummaryNewer = true;
|
|
439
418
|
}
|
|
440
|
-
|
|
441
|
-
|
|
419
|
+
|
|
420
|
+
// If the acked summary is found in the pendingSummaries, it means the summary was created and tracked by the current client
|
|
421
|
+
// so set the isSummaryTracked to true.
|
|
422
|
+
const pendingSummary = this.pendingSummaries.get(proposalHandle);
|
|
423
|
+
if (pendingSummary?.referenceSequenceNumber !== undefined) {
|
|
424
|
+
isSummaryTracked = true;
|
|
425
|
+
// update the pendingSummariesMap for the root and all child summarizerNodes
|
|
442
426
|
this.refreshLatestSummaryFromPending(
|
|
443
427
|
proposalHandle,
|
|
444
|
-
|
|
428
|
+
pendingSummary.referenceSequenceNumber,
|
|
445
429
|
);
|
|
446
|
-
isSummaryTracked = true;
|
|
447
430
|
}
|
|
448
431
|
event.end({ ...eventProps, isSummaryNewer, pendingSummaryFound: isSummaryTracked });
|
|
449
432
|
return { isSummaryTracked, isSummaryNewer };
|
|
@@ -461,17 +444,17 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
461
444
|
proposalHandle: string,
|
|
462
445
|
referenceSequenceNumber: number,
|
|
463
446
|
): void {
|
|
464
|
-
const
|
|
465
|
-
if (
|
|
447
|
+
const pendingSummary = this.pendingSummaries.get(proposalHandle);
|
|
448
|
+
if (pendingSummary === undefined) {
|
|
466
449
|
// This should only happen if parent skipped recursion AND no prior summary existed.
|
|
467
450
|
assert(
|
|
468
|
-
this.
|
|
451
|
+
this._lastSummaryReferenceSequenceNumber === undefined,
|
|
469
452
|
0x1a6 /* "Not found pending summary, but this node has previously completed a summary" */,
|
|
470
453
|
);
|
|
471
454
|
return;
|
|
472
455
|
} else {
|
|
473
456
|
assert(
|
|
474
|
-
referenceSequenceNumber ===
|
|
457
|
+
referenceSequenceNumber === pendingSummary.referenceSequenceNumber,
|
|
475
458
|
0x1a7 /* Pending summary reference sequence number should be consistent */,
|
|
476
459
|
);
|
|
477
460
|
|
|
@@ -479,30 +462,22 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
479
462
|
this.pendingSummaries.delete(proposalHandle);
|
|
480
463
|
}
|
|
481
464
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
465
|
+
// Delete all summaries whose reference sequence number is smaller than the one just acked.
|
|
466
|
+
for (const [key, summary] of this.pendingSummaries) {
|
|
467
|
+
if (summary.referenceSequenceNumber < referenceSequenceNumber) {
|
|
468
|
+
this.pendingSummaries.delete(key);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Update the latest successful summary reference number
|
|
472
|
+
this._lastSummaryReferenceSequenceNumber = pendingSummary.referenceSequenceNumber;
|
|
485
473
|
// Propagate update to all child nodes
|
|
486
474
|
for (const child of this.children.values()) {
|
|
487
475
|
child.refreshLatestSummaryFromPending(proposalHandle, referenceSequenceNumber);
|
|
488
476
|
}
|
|
489
477
|
}
|
|
490
478
|
|
|
491
|
-
private refreshLatestSummaryCore(referenceSequenceNumber: number): void {
|
|
492
|
-
for (const [key, value] of this.pendingSummaries) {
|
|
493
|
-
if (value.referenceSequenceNumber < referenceSequenceNumber) {
|
|
494
|
-
this.pendingSummaries.delete(key);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
479
|
public updateBaseSummaryState(snapshot: ISnapshotTree) {
|
|
500
|
-
//
|
|
501
|
-
// separating child SummarizerNodes. Checks for .channels subtrees.
|
|
502
|
-
const { childrenPathPart } = parseSummaryForSubtrees(snapshot);
|
|
503
|
-
if (childrenPathPart !== undefined && this._latestSummary !== undefined) {
|
|
504
|
-
this._latestSummary.additionalPath = EscapedPath.create(childrenPathPart);
|
|
505
|
-
}
|
|
480
|
+
// Function deprecated. Empty declaration is kept around to compat failures.
|
|
506
481
|
}
|
|
507
482
|
|
|
508
483
|
public recordChange(op: ISequencedDocumentMessage): void {
|
|
@@ -524,10 +499,6 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
524
499
|
return this._changeSequenceNumber > this.referenceSequenceNumber;
|
|
525
500
|
}
|
|
526
501
|
|
|
527
|
-
public get latestSummary(): Readonly<SummaryNode> | undefined {
|
|
528
|
-
return this._latestSummary;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
502
|
protected readonly canReuseHandle: boolean;
|
|
532
503
|
|
|
533
504
|
public createChild(
|
|
@@ -550,8 +521,9 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
550
521
|
this.logger,
|
|
551
522
|
summarizeInternalFn,
|
|
552
523
|
config,
|
|
524
|
+
createDetails.summaryHandleId,
|
|
553
525
|
createDetails.changeSequenceNumber,
|
|
554
|
-
createDetails.
|
|
526
|
+
createDetails.lastSummaryReferenceSequenceNumber,
|
|
555
527
|
this.wipSummaryLogger,
|
|
556
528
|
createDetails.telemetryNodeId,
|
|
557
529
|
);
|
|
@@ -579,26 +551,26 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
579
551
|
id: string,
|
|
580
552
|
createParam: CreateChildSummarizerNodeParam,
|
|
581
553
|
): ICreateChildDetails {
|
|
582
|
-
let
|
|
554
|
+
let childLastSummaryReferenceSequenceNumber: number | undefined;
|
|
583
555
|
let changeSequenceNumber: number;
|
|
584
556
|
|
|
585
|
-
const
|
|
557
|
+
const parentLastSummaryReferenceSequenceNumber = this._lastSummaryReferenceSequenceNumber;
|
|
586
558
|
switch (createParam.type) {
|
|
587
559
|
case CreateSummarizerNodeSource.FromAttach: {
|
|
588
560
|
if (
|
|
589
|
-
|
|
590
|
-
createParam.sequenceNumber <=
|
|
561
|
+
parentLastSummaryReferenceSequenceNumber !== undefined &&
|
|
562
|
+
createParam.sequenceNumber <= parentLastSummaryReferenceSequenceNumber
|
|
591
563
|
) {
|
|
592
564
|
// Prioritize latest summary if it was after this node was attached.
|
|
593
|
-
|
|
565
|
+
childLastSummaryReferenceSequenceNumber = parentLastSummaryReferenceSequenceNumber;
|
|
594
566
|
}
|
|
595
567
|
changeSequenceNumber = createParam.sequenceNumber;
|
|
596
568
|
break;
|
|
597
569
|
}
|
|
598
570
|
case CreateSummarizerNodeSource.FromSummary:
|
|
599
571
|
case CreateSummarizerNodeSource.Local: {
|
|
600
|
-
|
|
601
|
-
changeSequenceNumber =
|
|
572
|
+
childLastSummaryReferenceSequenceNumber = parentLastSummaryReferenceSequenceNumber;
|
|
573
|
+
changeSequenceNumber = parentLastSummaryReferenceSequenceNumber ?? -1;
|
|
602
574
|
break;
|
|
603
575
|
}
|
|
604
576
|
default: {
|
|
@@ -608,11 +580,13 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
608
580
|
}
|
|
609
581
|
|
|
610
582
|
const childTelemetryNodeId = `${this.telemetryNodeId ?? ""}/${id}`;
|
|
583
|
+
const childSummaryHandleId = this._summaryHandleId.createChildPath(EscapedPath.create(id));
|
|
611
584
|
|
|
612
585
|
return {
|
|
613
|
-
latestSummary,
|
|
614
586
|
changeSequenceNumber,
|
|
615
587
|
telemetryNodeId: childTelemetryNodeId,
|
|
588
|
+
summaryHandleId: childSummaryHandleId,
|
|
589
|
+
lastSummaryReferenceSequenceNumber: childLastSummaryReferenceSequenceNumber,
|
|
616
590
|
};
|
|
617
591
|
}
|
|
618
592
|
|
|
@@ -632,21 +606,15 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
632
606
|
child.wipReferenceSequenceNumber = this.wipReferenceSequenceNumber;
|
|
633
607
|
}
|
|
634
608
|
// In case we have pending summaries on the parent, let's initialize it on the child.
|
|
635
|
-
if (child.
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
basePath: child._latestSummary.basePath,
|
|
640
|
-
localPath: child._latestSummary.localPath,
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
child.addPendingSummary(key, newLatestSummaryNode);
|
|
644
|
-
}
|
|
609
|
+
if (child._lastSummaryReferenceSequenceNumber !== undefined) {
|
|
610
|
+
this.pendingSummaries.forEach((pendingSummaryInfo, proposedHandle) => {
|
|
611
|
+
child.addPendingSummary(proposedHandle, pendingSummaryInfo);
|
|
612
|
+
});
|
|
645
613
|
}
|
|
646
614
|
}
|
|
647
615
|
|
|
648
|
-
protected addPendingSummary(key: string,
|
|
649
|
-
this.pendingSummaries.set(key,
|
|
616
|
+
protected addPendingSummary(key: string, pendingSummaryInfo: PendingSummaryInfo) {
|
|
617
|
+
this.pendingSummaries.set(key, pendingSummaryInfo);
|
|
650
618
|
}
|
|
651
619
|
|
|
652
620
|
/**
|
|
@@ -692,10 +660,9 @@ export const createRootSummarizerNode = (
|
|
|
692
660
|
logger,
|
|
693
661
|
summarizeInternalFn,
|
|
694
662
|
config,
|
|
663
|
+
EscapedPath.create("") /* summaryHandleId */,
|
|
695
664
|
changeSequenceNumber,
|
|
696
|
-
referenceSequenceNumber
|
|
697
|
-
? undefined
|
|
698
|
-
: SummaryNode.createForRoot(referenceSequenceNumber),
|
|
665
|
+
referenceSequenceNumber,
|
|
699
666
|
undefined /* wipSummaryLogger */,
|
|
700
667
|
"" /* telemetryNodeId */,
|
|
701
668
|
);
|
|
@@ -64,104 +64,46 @@ export interface ISummarizerNodeRootContract {
|
|
|
64
64
|
): Promise<IRefreshSummaryResult>;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/**
|
|
67
|
+
/** Class to build paths for nodes in a tree with escaped special characters */
|
|
68
68
|
export class EscapedPath {
|
|
69
69
|
private constructor(public readonly path: string) {}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Creates and returns a new instance of this class.
|
|
73
|
+
* @param path - Id or path part of a node
|
|
74
|
+
*/
|
|
70
75
|
public static create(path: string): EscapedPath {
|
|
71
76
|
return new EscapedPath(encodeURIComponent(path));
|
|
72
77
|
}
|
|
73
|
-
public static createAndConcat(pathParts: string[]): EscapedPath {
|
|
74
|
-
let ret = EscapedPath.create(pathParts[0] ?? "");
|
|
75
|
-
for (let i = 1; i < pathParts.length; i++) {
|
|
76
|
-
ret = ret.concat(EscapedPath.create(pathParts[i]));
|
|
77
|
-
}
|
|
78
|
-
return ret;
|
|
79
|
-
}
|
|
80
78
|
public toString(): string {
|
|
81
79
|
return this.path;
|
|
82
80
|
}
|
|
83
|
-
public concat(path: EscapedPath): EscapedPath {
|
|
84
|
-
return new EscapedPath(`${this.path}/${path.path}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Information about a summary relevant to a specific node in the tree */
|
|
89
|
-
export class SummaryNode {
|
|
90
|
-
/** Creates an instance that is valid for the root with specific basePath and localPath */
|
|
91
|
-
public static createForRoot(referenceSequenceNumber: number): SummaryNode {
|
|
92
|
-
return new SummaryNode({
|
|
93
|
-
referenceSequenceNumber,
|
|
94
|
-
basePath: undefined,
|
|
95
|
-
localPath: EscapedPath.create(""), // root hard-coded to ""
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** Summary reference sequence number, i.e. last sequence number seen when it was created */
|
|
100
|
-
public get referenceSequenceNumber(): number {
|
|
101
|
-
return this.summary.referenceSequenceNumber;
|
|
102
|
-
}
|
|
103
|
-
/** Full path to parent node, or undefined if this is the root */
|
|
104
|
-
public get basePath(): EscapedPath | undefined {
|
|
105
|
-
return this.summary.basePath;
|
|
106
|
-
}
|
|
107
|
-
/** Relative path to this node from its parent node */
|
|
108
|
-
public get localPath(): EscapedPath {
|
|
109
|
-
return this.summary.localPath;
|
|
110
|
-
}
|
|
111
|
-
/** Relative path from this node to its node innermost base summary */
|
|
112
|
-
public get additionalPath(): EscapedPath | undefined {
|
|
113
|
-
return this.summary.additionalPath;
|
|
114
|
-
}
|
|
115
|
-
public set additionalPath(additionalPath: EscapedPath | undefined) {
|
|
116
|
-
this.summary.additionalPath = additionalPath;
|
|
117
|
-
}
|
|
118
|
-
constructor(
|
|
119
|
-
private readonly summary: {
|
|
120
|
-
readonly referenceSequenceNumber: number;
|
|
121
|
-
readonly basePath: EscapedPath | undefined;
|
|
122
|
-
readonly localPath: EscapedPath;
|
|
123
|
-
additionalPath?: EscapedPath;
|
|
124
|
-
},
|
|
125
|
-
) {}
|
|
126
|
-
|
|
127
|
-
/** Gets the full path to this node, to be used when sending a handle */
|
|
128
|
-
public get fullPath(): EscapedPath {
|
|
129
|
-
return this.basePath?.concat(this.localPath) ?? this.localPath;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Gets the full path to this node's innermost base summary.
|
|
134
|
-
* The children nodes can use this as their basePath to determine their path.
|
|
135
|
-
*/
|
|
136
|
-
public get fullPathForChildren(): EscapedPath {
|
|
137
|
-
return this.additionalPath !== undefined
|
|
138
|
-
? this.fullPath.concat(this.additionalPath)
|
|
139
|
-
: this.fullPath;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
81
|
/**
|
|
143
|
-
* Creates a new
|
|
144
|
-
* @param id - id of the child node
|
|
82
|
+
* Creates and returns a new instance of this class for child of the current node.
|
|
145
83
|
*/
|
|
146
|
-
public
|
|
147
|
-
return new
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
localPath: EscapedPath.create(id),
|
|
151
|
-
});
|
|
84
|
+
public createChildPath(childNodePath: EscapedPath): EscapedPath {
|
|
85
|
+
return new EscapedPath(
|
|
86
|
+
`${this.path}/${encodeURIComponent(channelsTreeName)}/${childNodePath.path}`,
|
|
87
|
+
);
|
|
152
88
|
}
|
|
153
89
|
}
|
|
90
|
+
export interface PendingSummaryInfo {
|
|
91
|
+
/** The sequence number at which the summary was created. */
|
|
92
|
+
referenceSequenceNumber: number;
|
|
93
|
+
}
|
|
154
94
|
|
|
155
95
|
/**
|
|
156
96
|
* Represents the details needed to create a child summarizer node.
|
|
157
97
|
*/
|
|
158
98
|
export interface ICreateChildDetails {
|
|
159
|
-
/** Latest summary from server node data */
|
|
160
|
-
latestSummary: SummaryNode | undefined;
|
|
161
99
|
/** Sequence number of latest known change to the node */
|
|
162
100
|
changeSequenceNumber: number;
|
|
163
101
|
/** A unique id of this child to be logged when sending telemetry. */
|
|
164
102
|
telemetryNodeId: string;
|
|
103
|
+
/** Summary handle for child node */
|
|
104
|
+
summaryHandleId: EscapedPath;
|
|
105
|
+
/** the reference sequence number of the last successful summary. */
|
|
106
|
+
lastSummaryReferenceSequenceNumber: number | undefined;
|
|
165
107
|
}
|
|
166
108
|
|
|
167
109
|
export interface ISubtreeInfo<T extends ISnapshotTree | SummaryObject> {
|
|
@@ -170,25 +112,3 @@ export interface ISubtreeInfo<T extends ISnapshotTree | SummaryObject> {
|
|
|
170
112
|
/** Additional path part where children are isolated */
|
|
171
113
|
childrenPathPart: string | undefined;
|
|
172
114
|
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Checks if the summary contains .channels subtree where the children subtrees
|
|
176
|
-
* would be located if exists.
|
|
177
|
-
* @param baseSummary - summary to check
|
|
178
|
-
*/
|
|
179
|
-
export function parseSummaryForSubtrees(
|
|
180
|
-
baseSummary: ISnapshotTree,
|
|
181
|
-
): ISubtreeInfo<ISnapshotTree> {
|
|
182
|
-
// New versions of snapshots have child nodes isolated in .channels subtree
|
|
183
|
-
const channelsSubtree = baseSummary.trees[channelsTreeName];
|
|
184
|
-
if (channelsSubtree !== undefined) {
|
|
185
|
-
return {
|
|
186
|
-
childrenTree: channelsSubtree,
|
|
187
|
-
childrenPathPart: channelsTreeName,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
return {
|
|
191
|
-
childrenTree: baseSummary,
|
|
192
|
-
childrenPathPart: undefined,
|
|
193
|
-
};
|
|
194
|
-
}
|