@fluidframework/container-runtime 2.4.0-294316 → 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 (70) hide show
  1. package/container-runtime.test-files.tar +0 -0
  2. package/dist/containerRuntime.d.ts +1 -3
  3. package/dist/containerRuntime.d.ts.map +1 -1
  4. package/dist/containerRuntime.js +11 -12
  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/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/packageVersion.d.ts +1 -1
  14. package/dist/packageVersion.js +1 -1
  15. package/dist/packageVersion.js.map +1 -1
  16. package/dist/summary/orderedClientElection.d.ts +0 -7
  17. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  18. package/dist/summary/orderedClientElection.js +0 -16
  19. package/dist/summary/orderedClientElection.js.map +1 -1
  20. package/dist/summary/summarizerNode/summarizerNode.d.ts +30 -13
  21. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  22. package/dist/summary/summarizerNode/summarizerNode.js +84 -102
  23. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  24. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
  25. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  26. package/dist/summary/summarizerNode/summarizerNodeUtils.js +10 -88
  27. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  28. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
  29. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  30. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +28 -38
  31. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  32. package/lib/containerRuntime.d.ts +1 -3
  33. package/lib/containerRuntime.d.ts.map +1 -1
  34. package/lib/containerRuntime.js +9 -10
  35. package/lib/containerRuntime.js.map +1 -1
  36. package/lib/dataStoreContext.d.ts.map +1 -1
  37. package/lib/dataStoreContext.js +0 -3
  38. package/lib/dataStoreContext.js.map +1 -1
  39. package/lib/index.d.ts +1 -1
  40. package/lib/index.d.ts.map +1 -1
  41. package/lib/index.js +1 -1
  42. package/lib/index.js.map +1 -1
  43. package/lib/packageVersion.d.ts +1 -1
  44. package/lib/packageVersion.js +1 -1
  45. package/lib/packageVersion.js.map +1 -1
  46. package/lib/summary/orderedClientElection.d.ts +0 -7
  47. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  48. package/lib/summary/orderedClientElection.js +0 -16
  49. package/lib/summary/orderedClientElection.js.map +1 -1
  50. package/lib/summary/summarizerNode/summarizerNode.d.ts +30 -13
  51. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  52. package/lib/summary/summarizerNode/summarizerNode.js +85 -103
  53. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  54. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +15 -42
  55. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  56. package/lib/summary/summarizerNode/summarizerNodeUtils.js +8 -84
  57. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  58. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +5 -6
  59. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  60. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +29 -39
  61. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  62. package/package.json +24 -20
  63. package/src/containerRuntime.ts +10 -10
  64. package/src/dataStoreContext.ts +0 -3
  65. package/src/index.ts +0 -1
  66. package/src/packageVersion.ts +1 -1
  67. package/src/summary/orderedClientElection.ts +0 -19
  68. package/src/summary/summarizerNode/summarizerNode.ts +90 -123
  69. package/src/summary/summarizerNode/summarizerNodeUtils.ts +19 -99
  70. 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-294316",
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-294316",
131
- "@fluidframework/container-definitions": "2.4.0-294316",
132
- "@fluidframework/container-runtime-definitions": "2.4.0-294316",
133
- "@fluidframework/core-interfaces": "2.4.0-294316",
134
- "@fluidframework/core-utils": "2.4.0-294316",
135
- "@fluidframework/datastore": "2.4.0-294316",
136
- "@fluidframework/driver-definitions": "2.4.0-294316",
137
- "@fluidframework/driver-utils": "2.4.0-294316",
138
- "@fluidframework/id-compressor": "2.4.0-294316",
139
- "@fluidframework/runtime-definitions": "2.4.0-294316",
140
- "@fluidframework/runtime-utils": "2.4.0-294316",
141
- "@fluidframework/telemetry-utils": "2.4.0-294316",
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-294316",
151
- "@fluid-private/stochastic-test-utils": "2.4.0-294316",
152
- "@fluid-private/test-pairwise-generator": "2.4.0-294316",
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-294316",
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",
@@ -178,8 +178,12 @@
178
178
  "typescript": "~5.4.5"
179
179
  },
180
180
  "typeValidation": {
181
- "broken": {},
182
- "entrypoint": "internal"
181
+ "broken": {
182
+ "Function_isRuntimeMessage": {
183
+ "backCompat": false
184
+ }
185
+ },
186
+ "entrypoint": "legacy"
183
187
  },
184
188
  "scripts": {
185
189
  "api": "fluid-build . --task api",
@@ -633,10 +633,8 @@ const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
633
633
 
634
634
  /**
635
635
  * Checks whether a message.type is one of the values in ContainerMessageType
636
- * @deprecated please use version in driver-utils
637
- * @internal
638
636
  */
639
- export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
637
+ export function isUnpackedRuntimeMessage(message: ISequencedDocumentMessage): boolean {
640
638
  return (Object.values(ContainerMessageType) as string[]).includes(message.type);
641
639
  }
642
640
 
@@ -1713,6 +1711,13 @@ export class ContainerRuntime
1713
1711
  });
1714
1712
 
1715
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;
1716
1721
  this.summarizerNode = createRootSummarizerNodeWithGC(
1717
1722
  createChildLogger({ logger: this.logger, namespace: "SummarizerNode" }),
1718
1723
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
@@ -1720,8 +1725,7 @@ export class ContainerRuntime
1720
1725
  this.summarizeInternal(fullTree, trackState, telemetryContext),
1721
1726
  // Latest change sequence number, no changes since summary applied yet
1722
1727
  loadedFromSequenceNumber,
1723
- // Summary reference sequence number, undefined if no summary yet
1724
- baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined,
1728
+ summaryReferenceSequenceNumber,
1725
1729
  {
1726
1730
  // Must set to false to prevent sending summary handle which would be pointing to
1727
1731
  // a summary with an older protocol state.
@@ -1735,10 +1739,6 @@ export class ContainerRuntime
1735
1739
  async () => this.garbageCollector.getBaseGCDetails(),
1736
1740
  );
1737
1741
 
1738
- if (baseSnapshot) {
1739
- this.summarizerNode.updateBaseSummaryState(baseSnapshot);
1740
- }
1741
-
1742
1742
  const parentContext = wrapContext(this);
1743
1743
 
1744
1744
  if (snapshotWithContents !== undefined) {
@@ -2808,7 +2808,7 @@ export class ContainerRuntime
2808
2808
  { batchStart: true, batchEnd: true }, // Single message
2809
2809
  local,
2810
2810
  savedOp,
2811
- isRuntimeMessage(messageCopy) /* runtimeBatch */,
2811
+ isUnpackedRuntimeMessage(messageCopy) /* runtimeBatch */,
2812
2812
  );
2813
2813
  }
2814
2814
 
@@ -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
  /*
package/src/index.ts CHANGED
@@ -12,7 +12,6 @@ export {
12
12
  IContainerRuntimeOptions,
13
13
  loadContainerRuntime,
14
14
  LoadContainerRuntimeParams,
15
- isRuntimeMessage,
16
15
  agentSchedulerId,
17
16
  ContainerRuntime,
18
17
  DeletedResponseHeaderKey,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.4.0-294316";
9
+ export const pkgVersion = "2.4.0-297385";
@@ -270,8 +270,6 @@ export interface IOrderedClientElection extends IEventProvider<IOrderedClientEle
270
270
  readonly electedParent: ITrackedClient | undefined;
271
271
  /** Sequence number of most recent election. */
272
272
  readonly electionSequenceNumber: number;
273
- /** Marks the currently elected client as invalid, and elects the next eligible client. */
274
- incrementElectedClient(sequenceNumber: number): void;
275
273
  /** Resets the currently elected client back to the oldest eligible client. */
276
274
  resetElectedClient(sequenceNumber: number): void;
277
275
  /** Peeks at what the next elected client would be if incrementElectedClient were called. */
@@ -581,23 +579,6 @@ export class OrderedClientElection
581
579
  return this.orderedClientCollection.getAllClients().filter(this.isEligibleFn);
582
580
  }
583
581
 
584
- /**
585
- * Advance election to the next-oldest client. This is called if the current parent is leaving the quorum,
586
- * or if the current summarizer is not responsive and we want to stop it and spawn a new one.
587
- */
588
- public incrementElectedClient(sequenceNumber: number): void {
589
- const nextClient =
590
- this.findFirstEligibleParent(this._electedParent?.youngerClient) ??
591
- this.findFirstEligibleParent(this.orderedClientCollection.oldestClient);
592
- if (this._electedClient === undefined || this._electedClient === this._electedParent) {
593
- this.tryElectingClient(nextClient, sequenceNumber, "IncrementElectedClient");
594
- } else {
595
- // The _electedClient is a summarizer and should not be replaced until it leaves the quorum.
596
- // Changing the _electedParent will stop the summarizer.
597
- this.tryElectingParent(nextClient, sequenceNumber, "IncrementElectedClient");
598
- }
599
- }
600
-
601
582
  /**
602
583
  * (Re-)start election with the oldest client in the quorum. This is called if we need to summarize
603
584
  * and no client has been elected.
@@ -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
  );