@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.
Files changed (52) hide show
  1. package/container-runtime.test-files.tar +0 -0
  2. package/dist/containerRuntime.d.ts +8 -0
  3. package/dist/containerRuntime.d.ts.map +1 -1
  4. package/dist/containerRuntime.js +79 -58
  5. package/dist/containerRuntime.js.map +1 -1
  6. package/dist/dataStoreContext.d.ts.map +1 -1
  7. package/dist/dataStoreContext.js +0 -3
  8. package/dist/dataStoreContext.js.map +1 -1
  9. package/dist/packageVersion.d.ts +1 -1
  10. package/dist/packageVersion.js +1 -1
  11. package/dist/packageVersion.js.map +1 -1
  12. package/dist/summary/summarizerNode/summarizerNode.d.ts +30 -13
  13. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  14. package/dist/summary/summarizerNode/summarizerNode.js +84 -102
  15. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  16. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
  17. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  18. package/dist/summary/summarizerNode/summarizerNodeUtils.js +10 -88
  19. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  20. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
  21. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  22. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +28 -38
  23. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  24. package/lib/containerRuntime.d.ts +8 -0
  25. package/lib/containerRuntime.d.ts.map +1 -1
  26. package/lib/containerRuntime.js +79 -58
  27. package/lib/containerRuntime.js.map +1 -1
  28. package/lib/dataStoreContext.d.ts.map +1 -1
  29. package/lib/dataStoreContext.js +0 -3
  30. package/lib/dataStoreContext.js.map +1 -1
  31. package/lib/packageVersion.d.ts +1 -1
  32. package/lib/packageVersion.js +1 -1
  33. package/lib/packageVersion.js.map +1 -1
  34. package/lib/summary/summarizerNode/summarizerNode.d.ts +30 -13
  35. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  36. package/lib/summary/summarizerNode/summarizerNode.js +85 -103
  37. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  38. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
  39. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  40. package/lib/summary/summarizerNode/summarizerNodeUtils.js +8 -84
  41. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  42. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
  43. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  44. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +29 -39
  45. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  46. package/package.json +21 -22
  47. package/src/containerRuntime.ts +98 -76
  48. package/src/dataStoreContext.ts +0 -3
  49. package/src/packageVersion.ts +1 -1
  50. package/src/summary/summarizerNode/summarizerNode.ts +90 -123
  51. package/src/summary/summarizerNode/summarizerNodeUtils.ts +19 -99
  52. 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
- parseSummaryForSubtrees,
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._latestSummary?.referenceSequenceNumber ?? 0;
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
- protected readonly pendingSummaries = new Map<string, SummaryNode>();
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
- private wipLocalPaths: { localPath: EscapedPath; additionalPath?: EscapedPath } | undefined;
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
- /** Undefined means created without summary */
87
- private _latestSummary?: SummaryNode,
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._latestSummary?.referenceSequenceNumber;
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
- const latestSummary = this._latestSummary;
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: latestSummary.fullPath.path,
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._latestSummary !== undefined
218
+ this._lastSummaryReferenceSequenceNumber !== undefined
203
219
  ? {
204
220
  summarySequenceNumber: this.wipReferenceSequenceNumber,
205
- latestSummarySequenceNumber: this._latestSummary.referenceSequenceNumber,
206
- // TODO: remove summaryPath
207
- summaryPath: this._latestSummary.fullPath.path,
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
- this.wipLocalPaths = { localPath: EscapedPath.create(result.id) };
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 wipLocalPaths must be set.
284
- if (parentSkipRecursion || this.wipLocalPaths !== undefined) {
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
- const latestSummary = this._latestSummary;
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, summary);
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.wipLocalPaths = undefined;
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
- const maybeSummaryNode = this.pendingSummaries.get(proposalHandle);
441
- if (maybeSummaryNode !== undefined) {
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
- maybeSummaryNode.referenceSequenceNumber,
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 summaryNode = this.pendingSummaries.get(proposalHandle);
465
- if (summaryNode === undefined) {
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._latestSummary === undefined,
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 === summaryNode.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
- this.refreshLatestSummaryCore(referenceSequenceNumber);
483
-
484
- this._latestSummary = summaryNode;
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
- // Check base summary to see if it has any additional path parts
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.latestSummary,
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 latestSummary: SummaryNode | undefined;
554
+ let childLastSummaryReferenceSequenceNumber: number | undefined;
583
555
  let changeSequenceNumber: number;
584
556
 
585
- const parentLatestSummary = this._latestSummary;
557
+ const parentLastSummaryReferenceSequenceNumber = this._lastSummaryReferenceSequenceNumber;
586
558
  switch (createParam.type) {
587
559
  case CreateSummarizerNodeSource.FromAttach: {
588
560
  if (
589
- parentLatestSummary !== undefined &&
590
- createParam.sequenceNumber <= parentLatestSummary.referenceSequenceNumber
561
+ parentLastSummaryReferenceSequenceNumber !== undefined &&
562
+ createParam.sequenceNumber <= parentLastSummaryReferenceSequenceNumber
591
563
  ) {
592
564
  // Prioritize latest summary if it was after this node was attached.
593
- latestSummary = parentLatestSummary.createForChild(id);
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
- latestSummary = parentLatestSummary?.createForChild(id);
601
- changeSequenceNumber = parentLatestSummary?.referenceSequenceNumber ?? -1;
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._latestSummary !== undefined) {
636
- for (const [key, value] of this.pendingSummaries.entries()) {
637
- const newLatestSummaryNode = new SummaryNode({
638
- referenceSequenceNumber: value.referenceSequenceNumber,
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, summary: SummaryNode) {
649
- this.pendingSummaries.set(key, summary);
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 === undefined
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
- /** Path for nodes in a tree with escaped special characters */
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 node within the same summary for a child of this node.
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 createForChild(id: string): SummaryNode {
147
- return new SummaryNode({
148
- referenceSequenceNumber: this.referenceSequenceNumber,
149
- basePath: this.fullPathForChildren,
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
- }