@fluidframework/container-runtime 2.4.0-297027 → 2.4.0-297385

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 (50) hide show
  1. package/container-runtime.test-files.tar +0 -0
  2. package/dist/containerRuntime.d.ts.map +1 -1
  3. package/dist/containerRuntime.js +7 -6
  4. package/dist/containerRuntime.js.map +1 -1
  5. package/dist/dataStoreContext.d.ts.map +1 -1
  6. package/dist/dataStoreContext.js +0 -3
  7. package/dist/dataStoreContext.js.map +1 -1
  8. package/dist/packageVersion.d.ts +1 -1
  9. package/dist/packageVersion.js +1 -1
  10. package/dist/packageVersion.js.map +1 -1
  11. package/dist/summary/summarizerNode/summarizerNode.d.ts +30 -13
  12. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  13. package/dist/summary/summarizerNode/summarizerNode.js +84 -102
  14. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  15. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
  16. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  17. package/dist/summary/summarizerNode/summarizerNodeUtils.js +10 -88
  18. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  19. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
  20. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  21. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +28 -38
  22. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  23. package/lib/containerRuntime.d.ts.map +1 -1
  24. package/lib/containerRuntime.js +7 -6
  25. package/lib/containerRuntime.js.map +1 -1
  26. package/lib/dataStoreContext.d.ts.map +1 -1
  27. package/lib/dataStoreContext.js +0 -3
  28. package/lib/dataStoreContext.js.map +1 -1
  29. package/lib/packageVersion.d.ts +1 -1
  30. package/lib/packageVersion.js +1 -1
  31. package/lib/packageVersion.js.map +1 -1
  32. package/lib/summary/summarizerNode/summarizerNode.d.ts +30 -13
  33. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  34. package/lib/summary/summarizerNode/summarizerNode.js +85 -103
  35. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  36. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
  37. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  38. package/lib/summary/summarizerNode/summarizerNodeUtils.js +8 -84
  39. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  40. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
  41. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  42. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +29 -39
  43. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  44. package/package.json +19 -19
  45. package/src/containerRuntime.ts +8 -6
  46. package/src/dataStoreContext.ts +0 -3
  47. package/src/packageVersion.ts +1 -1
  48. package/src/summary/summarizerNode/summarizerNode.ts +90 -123
  49. package/src/summary/summarizerNode/summarizerNodeUtils.ts +19 -99
  50. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +37 -53
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-runtime",
3
- "version": "2.4.0-297027",
3
+ "version": "2.4.0-297385",
4
4
  "description": "Fluid container runtime",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -127,18 +127,18 @@
127
127
  "temp-directory": "nyc/.nyc_output"
128
128
  },
129
129
  "dependencies": {
130
- "@fluid-internal/client-utils": "2.4.0-297027",
131
- "@fluidframework/container-definitions": "2.4.0-297027",
132
- "@fluidframework/container-runtime-definitions": "2.4.0-297027",
133
- "@fluidframework/core-interfaces": "2.4.0-297027",
134
- "@fluidframework/core-utils": "2.4.0-297027",
135
- "@fluidframework/datastore": "2.4.0-297027",
136
- "@fluidframework/driver-definitions": "2.4.0-297027",
137
- "@fluidframework/driver-utils": "2.4.0-297027",
138
- "@fluidframework/id-compressor": "2.4.0-297027",
139
- "@fluidframework/runtime-definitions": "2.4.0-297027",
140
- "@fluidframework/runtime-utils": "2.4.0-297027",
141
- "@fluidframework/telemetry-utils": "2.4.0-297027",
130
+ "@fluid-internal/client-utils": "2.4.0-297385",
131
+ "@fluidframework/container-definitions": "2.4.0-297385",
132
+ "@fluidframework/container-runtime-definitions": "2.4.0-297385",
133
+ "@fluidframework/core-interfaces": "2.4.0-297385",
134
+ "@fluidframework/core-utils": "2.4.0-297385",
135
+ "@fluidframework/datastore": "2.4.0-297385",
136
+ "@fluidframework/driver-definitions": "2.4.0-297385",
137
+ "@fluidframework/driver-utils": "2.4.0-297385",
138
+ "@fluidframework/id-compressor": "2.4.0-297385",
139
+ "@fluidframework/runtime-definitions": "2.4.0-297385",
140
+ "@fluidframework/runtime-utils": "2.4.0-297385",
141
+ "@fluidframework/telemetry-utils": "2.4.0-297385",
142
142
  "@tylerbu/sorted-btree-es6": "^1.8.0",
143
143
  "double-ended-queue": "^2.1.0-0",
144
144
  "lz4js": "^0.2.0",
@@ -147,16 +147,16 @@
147
147
  "devDependencies": {
148
148
  "@arethetypeswrong/cli": "^0.15.2",
149
149
  "@biomejs/biome": "~1.8.3",
150
- "@fluid-internal/mocha-test-setup": "2.4.0-297027",
151
- "@fluid-private/stochastic-test-utils": "2.4.0-297027",
152
- "@fluid-private/test-pairwise-generator": "2.4.0-297027",
150
+ "@fluid-internal/mocha-test-setup": "2.4.0-297385",
151
+ "@fluid-private/stochastic-test-utils": "2.4.0-297385",
152
+ "@fluid-private/test-pairwise-generator": "2.4.0-297385",
153
153
  "@fluid-tools/benchmark": "^0.50.0",
154
154
  "@fluid-tools/build-cli": "^0.46.0",
155
155
  "@fluidframework/build-common": "^2.0.3",
156
156
  "@fluidframework/build-tools": "^0.46.0",
157
- "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.3.0",
157
+ "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@~2.3.0",
158
158
  "@fluidframework/eslint-config-fluid": "^5.4.0",
159
- "@fluidframework/test-runtime-utils": "2.4.0-297027",
159
+ "@fluidframework/test-runtime-utils": "2.4.0-297385",
160
160
  "@microsoft/api-extractor": "7.47.8",
161
161
  "@types/double-ended-queue": "^2.1.0",
162
162
  "@types/mocha": "^9.1.1",
@@ -183,7 +183,7 @@
183
183
  "backCompat": false
184
184
  }
185
185
  },
186
- "entrypoint": "internal"
186
+ "entrypoint": "legacy"
187
187
  },
188
188
  "scripts": {
189
189
  "api": "fluid-build . --task api",
@@ -1711,6 +1711,13 @@ export class ContainerRuntime
1711
1711
  });
1712
1712
 
1713
1713
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
1714
+ // If the base snapshot was generated when isolated channels were disabled, set the summary reference
1715
+ // sequence to undefined so that this snapshot will not be used for incremental summaries. This is for
1716
+ // back-compat and will rarely happen so its okay to re-summarize everything in the first summary.
1717
+ const summaryReferenceSequenceNumber =
1718
+ baseSnapshot === undefined || metadata?.disableIsolatedChannels === true
1719
+ ? undefined
1720
+ : loadedFromSequenceNumber;
1714
1721
  this.summarizerNode = createRootSummarizerNodeWithGC(
1715
1722
  createChildLogger({ logger: this.logger, namespace: "SummarizerNode" }),
1716
1723
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
@@ -1718,8 +1725,7 @@ export class ContainerRuntime
1718
1725
  this.summarizeInternal(fullTree, trackState, telemetryContext),
1719
1726
  // Latest change sequence number, no changes since summary applied yet
1720
1727
  loadedFromSequenceNumber,
1721
- // Summary reference sequence number, undefined if no summary yet
1722
- baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined,
1728
+ summaryReferenceSequenceNumber,
1723
1729
  {
1724
1730
  // Must set to false to prevent sending summary handle which would be pointing to
1725
1731
  // a summary with an older protocol state.
@@ -1733,10 +1739,6 @@ export class ContainerRuntime
1733
1739
  async () => this.garbageCollector.getBaseGCDetails(),
1734
1740
  );
1735
1741
 
1736
- if (baseSnapshot) {
1737
- this.summarizerNode.updateBaseSummaryState(baseSnapshot);
1738
- }
1739
-
1740
1742
  const parentContext = wrapContext(this);
1741
1743
 
1742
1744
  if (snapshotWithContents !== undefined) {
@@ -1034,9 +1034,6 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
1034
1034
  this._baseSnapshot = props.snapshot;
1035
1035
  this.isSnapshotInISnapshotFormat = false;
1036
1036
  }
1037
- if (this._baseSnapshot !== undefined) {
1038
- this.summarizerNode.updateBaseSummaryState(this._baseSnapshot);
1039
- }
1040
1037
  }
1041
1038
 
1042
1039
  /*
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.4.0-297027";
9
+ export const pkgVersion = "2.4.0-297385";
@@ -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
  );