@fluidframework/container-runtime 2.0.0-dev-rc.1.0.0.228517 → 2.0.0-dev-rc.1.0.0.232845

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 (149) hide show
  1. package/README.md +1 -1
  2. package/api-report/container-runtime.api.md +13 -3
  3. package/dist/container-runtime-alpha.d.ts +11 -3
  4. package/dist/container-runtime-beta.d.ts +7 -0
  5. package/dist/container-runtime-public.d.ts +7 -0
  6. package/dist/container-runtime-untrimmed.d.ts +28 -3
  7. package/dist/containerRuntime.d.ts +5 -2
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +55 -67
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/dataStoreContext.d.ts +4 -1
  12. package/dist/dataStoreContext.d.ts.map +1 -1
  13. package/dist/dataStoreContext.js +2 -3
  14. package/dist/dataStoreContext.js.map +1 -1
  15. package/dist/dataStores.d.ts +4 -2
  16. package/dist/dataStores.d.ts.map +1 -1
  17. package/dist/dataStores.js +8 -2
  18. package/dist/dataStores.js.map +1 -1
  19. package/dist/gc/garbageCollection.d.ts +17 -1
  20. package/dist/gc/garbageCollection.d.ts.map +1 -1
  21. package/dist/gc/garbageCollection.js +72 -29
  22. package/dist/gc/garbageCollection.js.map +1 -1
  23. package/dist/gc/gcDefinitions.d.ts +18 -2
  24. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  25. package/dist/gc/gcDefinitions.js +5 -1
  26. package/dist/gc/gcDefinitions.js.map +1 -1
  27. package/dist/gc/gcTelemetry.d.ts +1 -0
  28. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  29. package/dist/gc/gcTelemetry.js +0 -2
  30. package/dist/gc/gcTelemetry.js.map +1 -1
  31. package/dist/gc/gcUnreferencedStateTracker.d.ts +5 -0
  32. package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -1
  33. package/dist/gc/gcUnreferencedStateTracker.js +12 -1
  34. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
  35. package/dist/gc/index.d.ts +1 -1
  36. package/dist/gc/index.d.ts.map +1 -1
  37. package/dist/gc/index.js +2 -1
  38. package/dist/gc/index.js.map +1 -1
  39. package/dist/index.d.ts +2 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +3 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/messageTypes.d.ts +1 -1
  44. package/dist/messageTypes.js.map +1 -1
  45. package/dist/packageVersion.d.ts +1 -1
  46. package/dist/packageVersion.js +1 -1
  47. package/dist/packageVersion.js.map +1 -1
  48. package/dist/summary/runningSummarizer.d.ts +6 -6
  49. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  50. package/dist/summary/runningSummarizer.js +90 -72
  51. package/dist/summary/runningSummarizer.js.map +1 -1
  52. package/dist/summary/summarizerNode/summarizerNode.d.ts +2 -3
  53. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  54. package/dist/summary/summarizerNode/summarizerNode.js +6 -48
  55. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  56. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -18
  57. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  58. package/dist/summary/summarizerNode/summarizerNodeUtils.js +1 -21
  59. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  60. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +3 -3
  61. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  62. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +5 -5
  63. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  64. package/dist/summary/summarizerTypes.d.ts +1 -1
  65. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  66. package/dist/summary/summarizerTypes.js.map +1 -1
  67. package/dist/tsdoc-metadata.json +1 -1
  68. package/lib/container-runtime-alpha.d.mts +11 -3
  69. package/lib/container-runtime-beta.d.mts +7 -0
  70. package/lib/container-runtime-public.d.mts +7 -0
  71. package/lib/container-runtime-untrimmed.d.mts +28 -3
  72. package/lib/containerRuntime.d.mts +5 -2
  73. package/lib/containerRuntime.d.mts.map +1 -1
  74. package/lib/containerRuntime.mjs +56 -68
  75. package/lib/containerRuntime.mjs.map +1 -1
  76. package/lib/dataStoreContext.d.mts +4 -1
  77. package/lib/dataStoreContext.d.mts.map +1 -1
  78. package/lib/dataStoreContext.mjs +2 -3
  79. package/lib/dataStoreContext.mjs.map +1 -1
  80. package/lib/dataStores.d.mts +4 -2
  81. package/lib/dataStores.d.mts.map +1 -1
  82. package/lib/dataStores.mjs +8 -2
  83. package/lib/dataStores.mjs.map +1 -1
  84. package/lib/gc/garbageCollection.d.mts +17 -1
  85. package/lib/gc/garbageCollection.d.mts.map +1 -1
  86. package/lib/gc/garbageCollection.mjs +74 -31
  87. package/lib/gc/garbageCollection.mjs.map +1 -1
  88. package/lib/gc/gcDefinitions.d.mts +18 -2
  89. package/lib/gc/gcDefinitions.d.mts.map +1 -1
  90. package/lib/gc/gcDefinitions.mjs +4 -0
  91. package/lib/gc/gcDefinitions.mjs.map +1 -1
  92. package/lib/gc/gcTelemetry.d.mts +1 -0
  93. package/lib/gc/gcTelemetry.d.mts.map +1 -1
  94. package/lib/gc/gcTelemetry.mjs +0 -2
  95. package/lib/gc/gcTelemetry.mjs.map +1 -1
  96. package/lib/gc/gcUnreferencedStateTracker.d.mts +5 -0
  97. package/lib/gc/gcUnreferencedStateTracker.d.mts.map +1 -1
  98. package/lib/gc/gcUnreferencedStateTracker.mjs +10 -0
  99. package/lib/gc/gcUnreferencedStateTracker.mjs.map +1 -1
  100. package/lib/gc/index.d.mts +1 -1
  101. package/lib/gc/index.d.mts.map +1 -1
  102. package/lib/gc/index.mjs +1 -1
  103. package/lib/gc/index.mjs.map +1 -1
  104. package/lib/index.d.mts +2 -1
  105. package/lib/index.d.mts.map +1 -1
  106. package/lib/index.mjs +1 -0
  107. package/lib/index.mjs.map +1 -1
  108. package/lib/messageTypes.d.mts +1 -1
  109. package/lib/messageTypes.mjs.map +1 -1
  110. package/lib/packageVersion.d.mts +1 -1
  111. package/lib/packageVersion.mjs +1 -1
  112. package/lib/packageVersion.mjs.map +1 -1
  113. package/lib/summary/runningSummarizer.d.mts +6 -6
  114. package/lib/summary/runningSummarizer.d.mts.map +1 -1
  115. package/lib/summary/runningSummarizer.mjs +90 -72
  116. package/lib/summary/runningSummarizer.mjs.map +1 -1
  117. package/lib/summary/summarizerNode/summarizerNode.d.mts +2 -3
  118. package/lib/summary/summarizerNode/summarizerNode.d.mts.map +1 -1
  119. package/lib/summary/summarizerNode/summarizerNode.mjs +8 -50
  120. package/lib/summary/summarizerNode/summarizerNode.mjs.map +1 -1
  121. package/lib/summary/summarizerNode/summarizerNodeUtils.d.mts +1 -18
  122. package/lib/summary/summarizerNode/summarizerNodeUtils.d.mts.map +1 -1
  123. package/lib/summary/summarizerNode/summarizerNodeUtils.mjs +0 -19
  124. package/lib/summary/summarizerNode/summarizerNodeUtils.mjs.map +1 -1
  125. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.mts +3 -3
  126. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.mts.map +1 -1
  127. package/lib/summary/summarizerNode/summarizerNodeWithGc.mjs +5 -5
  128. package/lib/summary/summarizerNode/summarizerNodeWithGc.mjs.map +1 -1
  129. package/lib/summary/summarizerTypes.d.mts +1 -1
  130. package/lib/summary/summarizerTypes.d.mts.map +1 -1
  131. package/lib/summary/summarizerTypes.mjs.map +1 -1
  132. package/package.json +27 -21
  133. package/src/containerRuntime.ts +96 -85
  134. package/src/dataStoreContext.ts +6 -4
  135. package/src/dataStores.ts +8 -1
  136. package/src/gc/garbageCollection.ts +86 -30
  137. package/src/gc/gcDefinitions.ts +19 -3
  138. package/src/gc/gcTelemetry.ts +1 -2
  139. package/src/gc/gcUnreferencedStateTracker.ts +11 -0
  140. package/src/gc/index.ts +1 -0
  141. package/src/index.ts +2 -0
  142. package/src/messageTypes.ts +1 -1
  143. package/src/packageVersion.ts +1 -1
  144. package/src/summary/runningSummarizer.ts +116 -88
  145. package/src/summary/summarizerNode/summarizerNode.ts +4 -64
  146. package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -33
  147. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +0 -6
  148. package/src/summary/summarizerTypes.ts +1 -1
  149. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
@@ -76,6 +76,8 @@ export const disableDatastoreSweepKey = "Fluid.GarbageCollection.DisableDataStor
76
76
  export const disableAttachmentBlobSweepKey = "Fluid.GarbageCollection.DisableAttachmentBlobSweep";
77
77
  /** Config key to revert new paradigm of detecting outbound routes in ContainerRuntime layer (use true) */
78
78
  export const detectOutboundRoutesViaDDSKey = "Fluid.GarbageCollection.DetectOutboundRoutesViaDDS";
79
+ /** Config key to disable auto-recovery mechanism that protects Tombstones that are loaded from being swept (use true) */
80
+ export const disableAutoRecoveryKey = "Fluid.GarbageCollection.DisableAutoRecovery";
79
81
 
80
82
  // One day in milliseconds.
81
83
  export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
@@ -253,6 +255,8 @@ export type GCNodeType = (typeof GCNodeType)[keyof typeof GCNodeType];
253
255
  export const GarbageCollectionMessageType = {
254
256
  /** Message sent directing GC to delete the given nodes */
255
257
  Sweep: "Sweep",
258
+ /** Message sent notifying GC that a Tombstoned object was Loaded */
259
+ TombstoneLoaded: "TombstoneLoaded",
256
260
  } as const;
257
261
 
258
262
  /**
@@ -266,16 +270,28 @@ export type GarbageCollectionMessageType =
266
270
  * @internal
267
271
  */
268
272
  export interface ISweepMessage {
269
- type: "Sweep";
270
- // The ids of nodes that are deleted.
273
+ /** @see GarbageCollectionMessageType.Sweep */
274
+ type: typeof GarbageCollectionMessageType.Sweep;
275
+ /** The ids of nodes that are deleted. */
271
276
  deletedNodeIds: string[];
272
277
  }
273
278
 
279
+ /**
280
+ * The GC TombstoneLoaded message.
281
+ * @internal
282
+ */
283
+ export interface ITombstoneLoadedMessage {
284
+ /** @see GarbageCollectionMessageType.TombstoneLoaded */
285
+ type: typeof GarbageCollectionMessageType.TombstoneLoaded;
286
+ /** The id of Tombstoned node that was loaded. */
287
+ nodePath: string;
288
+ }
289
+
274
290
  /**
275
291
  * Type for a message to be used for sending / received garbage collection messages.
276
292
  * @internal
277
293
  */
278
- export type GarbageCollectionMessage = ISweepMessage;
294
+ export type GarbageCollectionMessage = ISweepMessage | ITombstoneLoadedMessage;
279
295
 
280
296
  /**
281
297
  * Defines the APIs for the runtime object to be passed to the garbage collector.
@@ -63,6 +63,7 @@ interface INodeUsageProps extends ICommonProps {
63
63
  currentReferenceTimestampMs: number | undefined;
64
64
  packagePath: readonly string[] | undefined;
65
65
  fromId?: string;
66
+ autorecovery?: true;
66
67
  }
67
68
 
68
69
  /**
@@ -189,8 +190,6 @@ export class GCTelemetryTracker {
189
190
  };
190
191
 
191
192
  // If the node that is used is tombstoned, log a tombstone telemetry.
192
- // Note that this is done before checking if "nodeStateTracker" is undefined below because unreferenced
193
- // tracking may not have yet been enabled. That happens only after the client transitions to write mode.
194
193
  if (nodeUsageProps.isTombstoned) {
195
194
  this.logTombstoneUsageTelemetry(nodeUsageProps, unrefEventProps, nodeType, usageType);
196
195
  }
@@ -25,6 +25,17 @@ class TimerWithNoDefaultTimeout extends Timer {
25
25
  }
26
26
  }
27
27
 
28
+ /** The collection of UnreferencedStateTrackers for all unreferenced nodes. Ensures stopTracking is called when deleting */
29
+ export class UnreferencedStateTrackerMap extends Map<string, UnreferencedStateTracker> {
30
+ /** Delete the given key, and stop tracking if that node was actually unreferenced */
31
+ delete(key: string): boolean {
32
+ // Stop tracking so as to clear out any running timers.
33
+ this.get(key)?.stopTracking();
34
+ // Delete the node as we don't need to track it any more.
35
+ return super.delete(key);
36
+ }
37
+ }
38
+
28
39
  /**
29
40
  * Helper class that tracks the state of an unreferenced node such as the time it was unreferenced and if it can
30
41
  * be tombstoned or deleted by the sweep phase.
package/src/gc/index.ts CHANGED
@@ -33,6 +33,7 @@ export {
33
33
  runSweepKey,
34
34
  stableGCVersion,
35
35
  disableAttachmentBlobSweepKey,
36
+ disableAutoRecoveryKey,
36
37
  disableDatastoreSweepKey,
37
38
  detectOutboundRoutesViaDDSKey,
38
39
  UnreferencedState,
package/src/index.ts CHANGED
@@ -30,9 +30,11 @@ export {
30
30
  IContainerRuntimeMessageCompatDetails,
31
31
  CompatModeBehavior,
32
32
  RecentlyAddedContainerRuntimeMessageDetails,
33
+ UnknownContainerRuntimeMessage,
33
34
  } from "./messageTypes";
34
35
  export { IBlobManagerLoadInfo } from "./blobManager";
35
36
  export { FluidDataStoreRegistry } from "./dataStoreRegistry";
37
+ export { detectOutboundReferences } from "./dataStores";
36
38
  export {
37
39
  GCNodeType,
38
40
  IGCMetadata,
@@ -133,7 +133,7 @@ export type ContainerRuntimeGCMessage = TypedContainerRuntimeMessage<
133
133
  >;
134
134
 
135
135
  /**
136
- * Represents an unrecognized {@link TypedContainerRuntimeMessage}, e.g. a message from a future version of the container runtime.
136
+ * Represents an unrecognized TypedContainerRuntimeMessage, e.g. a message from a future version of the container runtime.
137
137
  * @internal
138
138
  */
139
139
  export interface UnknownContainerRuntimeMessage
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.0.0-dev-rc.1.0.0.228517";
9
+ export const pkgVersion = "2.0.0-dev-rc.1.0.0.232845";
@@ -91,15 +91,26 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
91
91
  runtime,
92
92
  );
93
93
 
94
- // Before doing any heuristics or proceeding with its refreshing, if there is a summary ack received while
95
- // this summarizer catches up, let's refresh state before proceeding with the summarization.
96
- const lastAckRefSeq = await summarizer.handleSummaryAck();
94
+ // If there have been any acks newer that the one this client loaded from until now, process them before
95
+ // starting the running summarizer which will trigger summary heuristics.
96
+ // This is done primarily to handle scenarios where the summarizer loads from a cached snapshot and there
97
+ // is newer one available. The ack for the newer summary is processed before summarizing because otherwise
98
+ // that summary would fail as it has an older parent.
99
+ let nextReferenceSequenceNumber = runtime.deltaManager.initialSequenceNumber + 1;
100
+ const latestAck = summaryCollection.latestAck;
101
+ if (
102
+ latestAck !== undefined &&
103
+ latestAck.summaryOp.referenceSequenceNumber >= nextReferenceSequenceNumber
104
+ ) {
105
+ await summarizer.handleSummaryAck(latestAck);
106
+ nextReferenceSequenceNumber = latestAck.summaryOp.referenceSequenceNumber + 1;
107
+ }
97
108
 
98
109
  await summarizer.waitStart();
99
110
 
100
- // Handle summary acks asynchronously
111
+ // Process summary acks asynchronously
101
112
  // Note: no exceptions are thrown from processIncomingSummaryAcks handler as it handles all exceptions
102
- summarizer.processIncomingSummaryAcks(lastAckRefSeq).catch((error) => {
113
+ summarizer.processIncomingSummaryAcks(nextReferenceSequenceNumber).catch((error) => {
103
114
  createChildLogger({ logger }).sendErrorEvent(
104
115
  { eventName: "HandleSummaryAckFatalError" },
105
116
  error,
@@ -272,94 +283,77 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
272
283
  : defaultMaxAttemptsForSubmitFailures;
273
284
  }
274
285
 
275
- private async handleSummaryAck(): Promise<number> {
276
- const lastAck: IAckedSummary | undefined = this.summaryCollection.latestAck;
277
- let refSequenceNumber = -1;
278
- // In case we haven't received the lastestAck yet, just return.
279
- if (lastAck !== undefined) {
280
- refSequenceNumber = lastAck.summaryOp.referenceSequenceNumber;
281
- const summaryLogger = this.tryGetCorrelatedLogger(refSequenceNumber) ?? this.mc.logger;
282
- const summaryOpHandle = lastAck.summaryOp.contents.handle;
283
- const summaryAckHandle = lastAck.summaryAck.contents.handle;
284
- while (this.summarizingLock !== undefined) {
285
- summaryLogger.sendTelemetryEvent({
286
- eventName: "RefreshAttemptWithSummarizerRunning",
287
- referenceSequenceNumber: refSequenceNumber,
286
+ private async handleSummaryAck(ack: IAckedSummary) {
287
+ const refSequenceNumber = ack.summaryOp.referenceSequenceNumber;
288
+ const summaryLogger = this.tryGetCorrelatedLogger(refSequenceNumber) ?? this.mc.logger;
289
+ const summaryOpHandle = ack.summaryOp.contents.handle;
290
+ const summaryAckHandle = ack.summaryAck.contents.handle;
291
+ while (this.summarizingLock !== undefined) {
292
+ summaryLogger.sendTelemetryEvent({
293
+ eventName: "RefreshAttemptWithSummarizerRunning",
294
+ referenceSequenceNumber: refSequenceNumber,
295
+ proposalHandle: summaryOpHandle,
296
+ ackHandle: summaryAckHandle,
297
+ });
298
+ await this.summarizingLock;
299
+ }
300
+
301
+ // Make sure we block any summarizer from being executed/enqueued while
302
+ // executing the refreshLatestSummaryAck.
303
+ // https://dev.azure.com/fluidframework/internal/_workitems/edit/779
304
+ await this.lockedSummaryAction(
305
+ () => {},
306
+ async () =>
307
+ this.refreshLatestSummaryAckCallback({
288
308
  proposalHandle: summaryOpHandle,
289
309
  ackHandle: summaryAckHandle,
290
- });
291
- await this.summarizingLock;
292
- }
293
-
294
- // Make sure we block any summarizer from being executed/enqueued while
295
- // executing the refreshLatestSummaryAck.
296
- // https://dev.azure.com/fluidframework/internal/_workitems/edit/779
297
- await this.lockedSummaryAction(
298
- () => {},
299
- async () =>
300
- this.refreshLatestSummaryAckCallback({
301
- proposalHandle: summaryOpHandle,
302
- ackHandle: summaryAckHandle,
303
- summaryRefSeq: refSequenceNumber,
304
- summaryLogger,
305
- }).catch(async (error) => {
306
- // If the error is 404, so maybe the fetched version no longer exists on server. We just
307
- // ignore this error in that case, as that means we will have another summaryAck for the
308
- // latest version with which we will refresh the state. However in case of single commit
309
- // summary, we might me missing a summary ack, so in that case we are still fine as the
310
- // code in `submitSummary` function in container runtime, will refresh the latest state
311
- // by calling `prefetchLatestSummaryThenClose`. We will load the next summarizer from the
312
- // updated state and be fine.
313
- const isIgnoredError =
314
- isFluidError(error) &&
315
- error.errorType === DriverErrorTypes.fileNotFoundOrAccessDeniedError;
316
-
317
- summaryLogger.sendTelemetryEvent(
318
- {
319
- eventName: isIgnoredError
320
- ? "HandleSummaryAckErrorIgnored"
321
- : "HandleLastSummaryAckError",
322
- referenceSequenceNumber: refSequenceNumber,
323
- proposalHandle: summaryOpHandle,
324
- ackHandle: summaryAckHandle,
325
- },
326
- error,
327
- );
328
- }),
329
- () => {},
330
- );
331
- refSequenceNumber++;
332
- }
333
- return refSequenceNumber;
310
+ summaryRefSeq: refSequenceNumber,
311
+ summaryLogger,
312
+ }).catch(async (error) => {
313
+ // If the error is 404, so maybe the fetched version no longer exists on server. We just
314
+ // ignore this error in that case, as that means we will have another summaryAck for the
315
+ // latest version with which we will refresh the state. However in case of single commit
316
+ // summary, we might me missing a summary ack, so in that case we are still fine as the
317
+ // code in `submitSummary` function in container runtime, will refresh the latest state
318
+ // by calling `prefetchLatestSummaryThenClose`. We will load the next summarizer from the
319
+ // updated state and be fine.
320
+ const isIgnoredError =
321
+ isFluidError(error) &&
322
+ error.errorType === DriverErrorTypes.fileNotFoundOrAccessDeniedError;
323
+
324
+ summaryLogger.sendTelemetryEvent(
325
+ {
326
+ eventName: isIgnoredError
327
+ ? "HandleSummaryAckErrorIgnored"
328
+ : "HandleLastSummaryAckError",
329
+ referenceSequenceNumber: refSequenceNumber,
330
+ proposalHandle: summaryOpHandle,
331
+ ackHandle: summaryAckHandle,
332
+ },
333
+ error,
334
+ );
335
+ }),
336
+ () => {},
337
+ );
334
338
  }
335
339
 
336
340
  /**
337
- * Responsible for receiving and processing all the summaryAcks.
338
- * In case there was a summary ack processed by the running summarizer before processIncomingSummaryAcks is called,
339
- * it will wait for the summary ack that is newer than the one indicated by the lastAckRefSeq.
340
- * @param lastAckRefSeq - Identifies the minimum reference sequence number the summarizer needs to wait for.
341
- * In case of a negative number, the summarizer will wait for ANY summary ack that is greater than the deltaManager's initial sequence number,
342
- * and, in case of a positive one, it will wait for a summary ack that is greater than this current reference sequence number.
341
+ * Responsible for receiving and processing all the summary acks.
342
+ * It starts processing ACKs after the one for the summary this client loaded from (initialSequenceNumber). Any
343
+ * ACK before that is not interesting as it will simply be ignored.
344
+ *
345
+ * @param referenceSequenceNumber - The referenceSequenceNumber of the summary from which to start processing
346
+ * acks.
343
347
  */
344
- private async processIncomingSummaryAcks(lastAckRefSeq: number) {
345
- let refSequenceNumber =
346
- lastAckRefSeq > 0 ? lastAckRefSeq : this.runtime.deltaManager.initialSequenceNumber;
348
+ private async processIncomingSummaryAcks(referenceSequenceNumber: number) {
349
+ // Start waiting for acks that are for summaries newer that the one this client loaded from.
350
+ let nextReferenceSequenceNumber = referenceSequenceNumber;
347
351
  while (!this.disposed) {
348
- const summaryLogger = this.tryGetCorrelatedLogger(refSequenceNumber) ?? this.mc.logger;
349
-
350
- // Initialize ack with undefined if exception happens inside of waitSummaryAck on second iteration,
351
- // we record undefined, not previous handles.
352
- await this.summaryCollection.waitSummaryAck(refSequenceNumber);
353
-
354
- summaryLogger.sendTelemetryEvent({
355
- eventName: "processIncomingSummaryAcks",
356
- referenceSequenceNumber: refSequenceNumber,
357
- lastAckRefSeq,
358
- });
359
-
360
- refSequenceNumber = await this.handleSummaryAck();
361
- // A valid Summary Ack must have been processed.
362
- assert(refSequenceNumber >= 0, 0x58f /* Invalid ref sequence number */);
352
+ const ackedSummary = await this.summaryCollection.waitSummaryAck(
353
+ nextReferenceSequenceNumber,
354
+ );
355
+ await this.handleSummaryAck(ackedSummary);
356
+ nextReferenceSequenceNumber = ackedSummary.summaryOp.referenceSequenceNumber + 1;
363
357
  }
364
358
  }
365
359
 
@@ -574,7 +568,17 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
574
568
  };
575
569
  const summarizeResult = this.generator.summarize(summaryOptions, resultsBuilder);
576
570
  // ensure we wait till the end of the process
577
- return summarizeResult.receivedSummaryAckOrNack;
571
+ const result = await summarizeResult.receivedSummaryAckOrNack;
572
+ if (!result.success) {
573
+ this.mc.logger.sendErrorEvent(
574
+ {
575
+ eventName: "SummarizeFailed",
576
+ maxAttempts: 1,
577
+ summaryAttempts: 1,
578
+ },
579
+ result.error,
580
+ );
581
+ }
578
582
  },
579
583
  () => {
580
584
  this.afterSummaryAction();
@@ -627,6 +631,7 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
627
631
  let summaryAttempts = 0;
628
632
  let summaryAttemptsPerPhase = 0;
629
633
  let summaryAttemptPhase = 0;
634
+ let error: any;
630
635
  while (summaryAttemptPhase < attemptOptions.length) {
631
636
  if (this.cancellationToken.cancelled) {
632
637
  return;
@@ -665,6 +670,8 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
665
670
  return;
666
671
  }
667
672
 
673
+ error = ackNackResult.error;
674
+
668
675
  // Check for retryDelay that can come from summaryNack, upload summary or submit summary flows.
669
676
  // Retry the same step only once per retryAfter response.
670
677
  const submitResult = await resultSummarize.summarySubmitted;
@@ -686,6 +693,14 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
686
693
  await delay(delaySeconds * 1000);
687
694
  }
688
695
  }
696
+ this.mc.logger.sendErrorEvent(
697
+ {
698
+ eventName: "SummarizeFailed",
699
+ maxAttempts: attemptOptions.length,
700
+ summaryAttempts: summaryAttemptPhase,
701
+ },
702
+ error,
703
+ );
689
704
  this.stopSummarizerCallback("failToSummarize");
690
705
  }
691
706
 
@@ -736,6 +751,7 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
736
751
  let done = false;
737
752
  let status: "success" | "failure" | "canceled" = "success";
738
753
  let results: ISummarizeResults | undefined;
754
+ let error: any;
739
755
  do {
740
756
  currentAttempt++;
741
757
  if (this.cancellationToken.cancelled) {
@@ -770,11 +786,12 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
770
786
 
771
787
  // Emit "summarize" event for this failed attempt.
772
788
  status = "failure";
789
+ error = ackNackResult.error;
773
790
  const eventProps: ISummarizeEventProps = {
774
791
  result: status,
775
792
  currentAttempt,
776
793
  maxAttempts,
777
- error: ackNackResult.error,
794
+ error,
778
795
  };
779
796
  this.emit("summarize", eventProps);
780
797
 
@@ -811,6 +828,9 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
811
828
  // Ack / nack is the final step, so if it succeeds we're done.
812
829
  const ackNackResult = await summarizeResult.receivedSummaryAckOrNack;
813
830
  status = ackNackResult.success ? "success" : "failure";
831
+ if (!ackNackResult.success) {
832
+ error = ackNackResult.error;
833
+ }
814
834
  const eventProps: ISummarizeEventProps = {
815
835
  result: status,
816
836
  currentAttempt,
@@ -823,6 +843,14 @@ export class RunningSummarizer extends TypedEventEmitter<ISummarizerEvents> impl
823
843
 
824
844
  // If summarization is still unsuccessful, stop the summarizer.
825
845
  if (status === "failure") {
846
+ this.mc.logger.sendErrorEvent(
847
+ {
848
+ eventName: "SummarizeFailed",
849
+ maxAttempts,
850
+ summaryAttempts: currentAttempt,
851
+ },
852
+ error,
853
+ );
826
854
  this.stopSummarizerCallback("failToSummarize");
827
855
  }
828
856
  return results;
@@ -8,7 +8,6 @@ import {
8
8
  ISummarizerNode,
9
9
  ISummarizerNodeConfig,
10
10
  ISummarizeResult,
11
- ISummaryTreeWithStats,
12
11
  CreateChildSummarizerNodeParam,
13
12
  CreateSummarizerNodeSource,
14
13
  SummarizeInternalFn,
@@ -19,7 +18,6 @@ import {
19
18
  ISequencedDocumentMessage,
20
19
  SummaryType,
21
20
  ISnapshotTree,
22
- SummaryObject,
23
21
  } from "@fluidframework/protocol-definitions";
24
22
  import {
25
23
  ITelemetryLoggerExt,
@@ -30,15 +28,13 @@ import {
30
28
  tagCodeArtifacts,
31
29
  } from "@fluidframework/telemetry-utils";
32
30
  import { assert, unreachableCase } from "@fluidframework/core-utils";
33
- import { convertToSummaryTree, calculateStats, mergeStats } from "@fluidframework/runtime-utils";
31
+ import { mergeStats } from "@fluidframework/runtime-utils";
34
32
  import {
35
33
  EscapedPath,
36
34
  ICreateChildDetails,
37
- IInitialSummary,
38
35
  IRefreshSummaryResult,
39
36
  ISummarizerNodeRootContract,
40
37
  parseSummaryForSubtrees,
41
- parseSummaryTreeForSubtrees,
42
38
  SummaryNode,
43
39
  ValidateSummaryResult,
44
40
  } from "./summarizerNodeUtils";
@@ -86,7 +82,6 @@ export class SummarizerNode implements IRootSummarizerNode {
86
82
  private _changeSequenceNumber: number,
87
83
  /** Undefined means created without summary */
88
84
  private _latestSummary?: SummaryNode,
89
- private readonly initialSummary?: IInitialSummary,
90
85
  protected wipSummaryLogger?: ITelemetryBaseLogger,
91
86
  /** A unique id of this node to be logged when sending telemetry. */
92
87
  protected telemetryNodeId?: string,
@@ -521,7 +516,6 @@ export class SummarizerNode implements IRootSummarizerNode {
521
516
  config,
522
517
  createDetails.changeSequenceNumber,
523
518
  createDetails.latestSummary,
524
- createDetails.initialSummary,
525
519
  this.wipSummaryLogger,
526
520
  createDetails.telemetryNodeId,
527
521
  );
@@ -549,7 +543,6 @@ export class SummarizerNode implements IRootSummarizerNode {
549
543
  id: string,
550
544
  createParam: CreateChildSummarizerNodeParam,
551
545
  ): ICreateChildDetails {
552
- let initialSummary: IInitialSummary | undefined;
553
546
  let latestSummary: SummaryNode | undefined;
554
547
  let changeSequenceNumber: number;
555
548
 
@@ -562,63 +555,12 @@ export class SummarizerNode implements IRootSummarizerNode {
562
555
  ) {
563
556
  // Prioritize latest summary if it was after this node was attached.
564
557
  latestSummary = parentLatestSummary.createForChild(id);
565
- } else {
566
- const summary = convertToSummaryTree(
567
- createParam.snapshot,
568
- ) as ISummaryTreeWithStats;
569
- initialSummary = {
570
- sequenceNumber: createParam.sequenceNumber,
571
- id,
572
- summary,
573
- };
574
558
  }
575
559
  changeSequenceNumber = createParam.sequenceNumber;
576
560
  break;
577
561
  }
578
- case CreateSummarizerNodeSource.FromSummary: {
579
- if (this.initialSummary === undefined) {
580
- assert(
581
- !!parentLatestSummary,
582
- 0x1ac /* "Cannot create child from summary if parent does not have latest summary" */,
583
- );
584
- }
585
- // fallthrough to local
586
- }
562
+ case CreateSummarizerNodeSource.FromSummary:
587
563
  case CreateSummarizerNodeSource.Local: {
588
- const parentInitialSummary = this.initialSummary;
589
- if (parentInitialSummary !== undefined) {
590
- let childSummary: SummaryObject | undefined;
591
- if (parentInitialSummary.summary !== undefined) {
592
- const { childrenTree } = parseSummaryTreeForSubtrees(
593
- parentInitialSummary.summary.summary,
594
- );
595
- assert(
596
- childrenTree.type === SummaryType.Tree,
597
- 0x1d6 /* "Parent summary object is not a tree" */,
598
- );
599
- childSummary = childrenTree.tree[id];
600
- }
601
- if (createParam.type === CreateSummarizerNodeSource.FromSummary) {
602
- // Locally created would not have differential subtree.
603
- assert(!!childSummary, 0x1ad /* "Missing child summary tree" */);
604
- }
605
- let childSummaryWithStats: ISummaryTreeWithStats | undefined;
606
- if (childSummary !== undefined) {
607
- assert(
608
- childSummary.type === SummaryType.Tree,
609
- 0x1ae /* "Child summary object is not a tree" */,
610
- );
611
- childSummaryWithStats = {
612
- summary: childSummary,
613
- stats: calculateStats(childSummary),
614
- };
615
- }
616
- initialSummary = {
617
- sequenceNumber: parentInitialSummary.sequenceNumber,
618
- id,
619
- summary: childSummaryWithStats,
620
- };
621
- }
622
564
  latestSummary = parentLatestSummary?.createForChild(id);
623
565
  changeSequenceNumber = parentLatestSummary?.referenceSequenceNumber ?? -1;
624
566
  break;
@@ -629,13 +571,12 @@ export class SummarizerNode implements IRootSummarizerNode {
629
571
  }
630
572
  }
631
573
 
632
- const childtelemetryNodeId = `${this.telemetryNodeId ?? ""}/${id}`;
574
+ const childTelemetryNodeId = `${this.telemetryNodeId ?? ""}/${id}`;
633
575
 
634
576
  return {
635
- initialSummary,
636
577
  latestSummary,
637
578
  changeSequenceNumber,
638
- telemetryNodeId: childtelemetryNodeId,
579
+ telemetryNodeId: childTelemetryNodeId,
639
580
  };
640
581
  }
641
582
 
@@ -719,7 +660,6 @@ export const createRootSummarizerNode = (
719
660
  referenceSequenceNumber === undefined
720
661
  ? undefined
721
662
  : SummaryNode.createForRoot(referenceSequenceNumber),
722
- undefined /* initialSummary */,
723
663
  undefined /* wipSummaryLogger */,
724
664
  "" /* telemetryNodeId */,
725
665
  );
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLoggerExt, TelemetryDataTag } from "@fluidframework/telemetry-utils";
7
- import { ISnapshotTree, ISummaryTree, SummaryObject } from "@fluidframework/protocol-definitions";
8
- import { channelsTreeName, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
7
+ import { ISnapshotTree, SummaryObject } from "@fluidframework/protocol-definitions";
8
+ import { channelsTreeName } from "@fluidframework/runtime-definitions";
9
9
 
10
10
  export interface IRefreshSummaryResult {
11
11
  /** Tells whether this summary is tracked by this client. */
@@ -135,21 +135,10 @@ export class SummaryNode {
135
135
  }
136
136
  }
137
137
 
138
- /**
139
- * Information about the initial summary tree found from an attach op.
140
- */
141
- export interface IInitialSummary {
142
- sequenceNumber: number;
143
- id: string;
144
- summary: ISummaryTreeWithStats | undefined;
145
- }
146
-
147
138
  /**
148
139
  * Represents the details needed to create a child summarizer node.
149
140
  */
150
141
  export interface ICreateChildDetails {
151
- /** Summary from attach op if known */
152
- initialSummary: IInitialSummary | undefined;
153
142
  /** Latest summary from server node data */
154
143
  latestSummary: SummaryNode | undefined;
155
144
  /** Sequence number of latest known change to the node */
@@ -184,23 +173,3 @@ export function parseSummaryForSubtrees(baseSummary: ISnapshotTree): ISubtreeInf
184
173
  childrenPathPart: undefined,
185
174
  };
186
175
  }
187
-
188
- /**
189
- * Checks if the summary contains .channels subtree where the children subtrees
190
- * would be located if exists.
191
- * @param baseSummary - summary to check
192
- */
193
- export function parseSummaryTreeForSubtrees(summary: ISummaryTree): ISubtreeInfo<SummaryObject> {
194
- // New versions of snapshots have child nodes isolated in .channels subtree
195
- const channelsSubtree = summary.tree[channelsTreeName];
196
- if (channelsSubtree !== undefined) {
197
- return {
198
- childrenTree: channelsSubtree,
199
- childrenPathPart: channelsTreeName,
200
- };
201
- }
202
- return {
203
- childrenTree: summary,
204
- childrenPathPart: undefined,
205
- };
206
- }
@@ -29,7 +29,6 @@ import { SummarizerNode } from "./summarizerNode";
29
29
  import {
30
30
  EscapedPath,
31
31
  ICreateChildDetails,
32
- IInitialSummary,
33
32
  ISummarizerNodeRootContract,
34
33
  SummaryNode,
35
34
  ValidateSummaryResult,
@@ -111,7 +110,6 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
111
110
  changeSequenceNumber: number,
112
111
  /** Undefined means created without summary */
113
112
  latestSummary?: SummaryNode,
114
- initialSummary?: IInitialSummary,
115
113
  wipSummaryLogger?: ITelemetryBaseLogger,
116
114
  private readonly getGCDataFn?: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
117
115
  getBaseGCDetailsFn?: () => Promise<IGarbageCollectionDetailsBase>,
@@ -135,7 +133,6 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
135
133
  config,
136
134
  changeSequenceNumber,
137
135
  latestSummary,
138
- initialSummary,
139
136
  wipSummaryLogger,
140
137
  telemetryId,
141
138
  );
@@ -398,7 +395,6 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
398
395
  createParam: CreateChildSummarizerNodeParam,
399
396
  config: ISummarizerNodeConfigWithGC = {},
400
397
  getGCDataFn?: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
401
- getBaseGCDetailsFn?: () => Promise<IGarbageCollectionDetailsBase>,
402
398
  ): ISummarizerNodeWithGC {
403
399
  assert(!this.children.has(id), 0x1b6 /* "Create SummarizerNode child already exists" */);
404
400
  /**
@@ -423,7 +419,6 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
423
419
  },
424
420
  createDetails.changeSequenceNumber,
425
421
  createDetails.latestSummary,
426
- createDetails.initialSummary,
427
422
  this.wipSummaryLogger,
428
423
  getGCDataFn,
429
424
  getChildBaseGCDetailsFn,
@@ -563,7 +558,6 @@ export const createRootSummarizerNodeWithGC = (
563
558
  referenceSequenceNumber === undefined
564
559
  ? undefined
565
560
  : SummaryNode.createForRoot(referenceSequenceNumber),
566
- undefined /* initialSummary */,
567
561
  undefined /* wipSummaryLogger */,
568
562
  getGCDataFn,
569
563
  getBaseGCDetailsFn,
@@ -377,7 +377,7 @@ export type EnqueueSummarizeResult =
377
377
  * @alpha
378
378
  */
379
379
  export type SummarizerStopReason =
380
- /** Summarizer client failed to summarize in all 3 consecutive attempts. */
380
+ /** Summarizer client failed to summarize in all attempts. */
381
381
  | "failToSummarize"
382
382
  /** Parent client reported that it is no longer connected. */
383
383
  | "parentNotConnected"