@fluidframework/container-runtime 2.74.0 → 2.81.0

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 (126) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/container-runtime.test-files.tar +0 -0
  3. package/dist/blobManager/blobManager.d.ts +0 -2
  4. package/dist/blobManager/blobManager.d.ts.map +1 -1
  5. package/dist/blobManager/blobManager.js +4 -5
  6. package/dist/blobManager/blobManager.js.map +1 -1
  7. package/dist/channelCollection.d.ts +3 -3
  8. package/dist/channelCollection.d.ts.map +1 -1
  9. package/dist/channelCollection.js.map +1 -1
  10. package/dist/containerRuntime.d.ts +5 -0
  11. package/dist/containerRuntime.d.ts.map +1 -1
  12. package/dist/containerRuntime.js +13 -7
  13. package/dist/containerRuntime.js.map +1 -1
  14. package/dist/dataStoreContext.d.ts +1 -1
  15. package/dist/dataStoreContext.d.ts.map +1 -1
  16. package/dist/dataStoreContext.js +2 -1
  17. package/dist/dataStoreContext.js.map +1 -1
  18. package/dist/deltaScheduler.d.ts.map +1 -1
  19. package/dist/deltaScheduler.js +1 -0
  20. package/dist/deltaScheduler.js.map +1 -1
  21. package/dist/gc/gcHelpers.d.ts.map +1 -1
  22. package/dist/gc/gcHelpers.js +1 -0
  23. package/dist/gc/gcHelpers.js.map +1 -1
  24. package/dist/packageVersion.d.ts +1 -1
  25. package/dist/packageVersion.js +1 -1
  26. package/dist/packageVersion.js.map +1 -1
  27. package/dist/runtimeLayerCompatState.d.ts +10 -6
  28. package/dist/runtimeLayerCompatState.d.ts.map +1 -1
  29. package/dist/runtimeLayerCompatState.js +16 -6
  30. package/dist/runtimeLayerCompatState.js.map +1 -1
  31. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  32. package/dist/summary/summarizerClientElection.js +1 -0
  33. package/dist/summary/summarizerClientElection.js.map +1 -1
  34. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  35. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +1 -0
  36. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  37. package/dist/summary/summarizerTypes.d.ts +5 -0
  38. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  39. package/dist/summary/summarizerTypes.js.map +1 -1
  40. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +3 -1
  41. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  42. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js +39 -18
  43. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  44. package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts +1 -1
  45. package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
  46. package/dist/summary/summaryDelayLoadedModule/summarizer.js +13 -11
  47. package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  48. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +4 -1
  49. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
  50. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js +37 -16
  51. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  52. package/dist/summary/summaryManager.d.ts +1 -1
  53. package/dist/summary/summaryManager.d.ts.map +1 -1
  54. package/dist/summary/summaryManager.js +10 -9
  55. package/dist/summary/summaryManager.js.map +1 -1
  56. package/eslint.config.mts +4 -4
  57. package/lib/blobManager/blobManager.d.ts +0 -2
  58. package/lib/blobManager/blobManager.d.ts.map +1 -1
  59. package/lib/blobManager/blobManager.js +4 -5
  60. package/lib/blobManager/blobManager.js.map +1 -1
  61. package/lib/channelCollection.d.ts +3 -3
  62. package/lib/channelCollection.d.ts.map +1 -1
  63. package/lib/channelCollection.js.map +1 -1
  64. package/lib/containerRuntime.d.ts +5 -0
  65. package/lib/containerRuntime.d.ts.map +1 -1
  66. package/lib/containerRuntime.js +13 -7
  67. package/lib/containerRuntime.js.map +1 -1
  68. package/lib/dataStoreContext.d.ts +1 -1
  69. package/lib/dataStoreContext.d.ts.map +1 -1
  70. package/lib/dataStoreContext.js +2 -1
  71. package/lib/dataStoreContext.js.map +1 -1
  72. package/lib/deltaScheduler.d.ts.map +1 -1
  73. package/lib/deltaScheduler.js +1 -0
  74. package/lib/deltaScheduler.js.map +1 -1
  75. package/lib/gc/gcHelpers.d.ts.map +1 -1
  76. package/lib/gc/gcHelpers.js +1 -0
  77. package/lib/gc/gcHelpers.js.map +1 -1
  78. package/lib/packageVersion.d.ts +1 -1
  79. package/lib/packageVersion.js +1 -1
  80. package/lib/packageVersion.js.map +1 -1
  81. package/lib/runtimeLayerCompatState.d.ts +10 -6
  82. package/lib/runtimeLayerCompatState.d.ts.map +1 -1
  83. package/lib/runtimeLayerCompatState.js +15 -5
  84. package/lib/runtimeLayerCompatState.js.map +1 -1
  85. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  86. package/lib/summary/summarizerClientElection.js +1 -0
  87. package/lib/summary/summarizerClientElection.js.map +1 -1
  88. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  89. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +1 -0
  90. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  91. package/lib/summary/summarizerTypes.d.ts +5 -0
  92. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  93. package/lib/summary/summarizerTypes.js.map +1 -1
  94. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +3 -1
  95. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  96. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js +39 -18
  97. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  98. package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts +1 -1
  99. package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
  100. package/lib/summary/summaryDelayLoadedModule/summarizer.js +13 -11
  101. package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  102. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +4 -1
  103. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
  104. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js +34 -13
  105. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  106. package/lib/summary/summaryManager.d.ts +1 -1
  107. package/lib/summary/summaryManager.d.ts.map +1 -1
  108. package/lib/summary/summaryManager.js +10 -9
  109. package/lib/summary/summaryManager.js.map +1 -1
  110. package/package.json +27 -27
  111. package/src/blobManager/blobManager.ts +3 -7
  112. package/src/channelCollection.ts +3 -3
  113. package/src/containerRuntime.ts +19 -6
  114. package/src/dataStoreContext.ts +3 -2
  115. package/src/deltaScheduler.ts +1 -0
  116. package/src/gc/gcHelpers.ts +1 -0
  117. package/src/packageVersion.ts +1 -1
  118. package/src/runtimeLayerCompatState.ts +25 -9
  119. package/src/summary/summarizerClientElection.ts +1 -0
  120. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -0
  121. package/src/summary/summarizerTypes.ts +5 -0
  122. package/src/summary/summaryDelayLoadedModule/runningSummarizer.ts +54 -26
  123. package/src/summary/summaryDelayLoadedModule/summarizer.ts +13 -11
  124. package/src/summary/summaryDelayLoadedModule/summaryGenerator.ts +43 -13
  125. package/src/summary/summaryManager.ts +11 -10
  126. package/.eslintrc.cjs +0 -30
@@ -177,11 +177,6 @@ export class RunningSummarizer
177
177
  private totalSuccessfulAttempts = 0;
178
178
  private initialized = false;
179
179
 
180
- private readonly runtimeListener: (
181
- op: ISequencedDocumentMessage,
182
- runtimeMessage?: boolean,
183
- ) => void;
184
-
185
180
  /**
186
181
  * The maximum number of summary attempts to do when submit summary fails.
187
182
  */
@@ -295,12 +290,6 @@ export class RunningSummarizer
295
290
  this.mc.logger,
296
291
  );
297
292
 
298
- // Listen to runtime for ops
299
- this.runtimeListener = (op: ISequencedDocumentMessage, runtimeMessage?: boolean) => {
300
- this.handleOp(op, runtimeMessage === true);
301
- };
302
- this.runtime.on("op", this.runtimeListener);
303
-
304
293
  // The max attempts for submit failures can be overridden via a feature flag. This allows us to
305
294
  // tweak this as per telemetry data until we arrive at a stable number.
306
295
  // If its set to a number higher than `defaultMaxAttemptsForSubmitFailures`, it will be ignored.
@@ -312,6 +301,8 @@ export class RunningSummarizer
312
301
  overrideMaxAttempts < defaultMaxAttemptsForSubmitFailures
313
302
  ? overrideMaxAttempts
314
303
  : defaultMaxAttemptsForSubmitFailures;
304
+
305
+ this.setupEventListeners();
315
306
  }
316
307
 
317
308
  private async handleSummaryAck(ack: IAckedSummary): Promise<void> {
@@ -397,7 +388,7 @@ export class RunningSummarizer
397
388
  }
398
389
 
399
390
  public dispose(): void {
400
- this.runtime.off("op", this.runtimeListener);
391
+ this.cleanupEventListeners();
401
392
  this.summaryWatcher.dispose();
402
393
  this.heuristicRunner?.dispose();
403
394
  this.heuristicRunner = undefined;
@@ -408,6 +399,33 @@ export class RunningSummarizer
408
399
  this.stopping = true;
409
400
  }
410
401
 
402
+ private readonly eventsCleanup: (() => void)[] = [];
403
+
404
+ private setupEventListeners(): void {
405
+ const runtimeListener: (op: ISequencedDocumentMessage, runtimeMessage?: boolean) => void =
406
+ (op: ISequencedDocumentMessage, runtimeMessage?: boolean) => {
407
+ this.handleOp(op, runtimeMessage === true);
408
+ };
409
+ this.runtime.on("op", runtimeListener);
410
+ this.eventsCleanup.push(() => this.runtime.off("op", runtimeListener));
411
+
412
+ // Forward events
413
+ for (const event of ["summarizeTimeout"] as const) {
414
+ const listener = (...args: unknown[]): void => {
415
+ this.emit(event, ...args);
416
+ };
417
+ this.generator.on(event, listener);
418
+ this.eventsCleanup.push(() => this.generator.off(event, listener));
419
+ }
420
+ }
421
+
422
+ private cleanupEventListeners(): void {
423
+ for (const cleanup of this.eventsCleanup) {
424
+ cleanup();
425
+ }
426
+ this.eventsCleanup.length = 0;
427
+ }
428
+
411
429
  /**
412
430
  * RunningSummarizer's logger includes the sequenced index of the current summary on each event.
413
431
  * If some other Summarizer code wants that event on their logs they can get it here,
@@ -655,11 +673,12 @@ export class RunningSummarizer
655
673
  numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
656
674
  isLastSummary,
657
675
  });
658
- this.mc.logger.sendErrorEvent(
676
+ summaryLogger.sendErrorEvent(
659
677
  {
660
678
  eventName: "SummarizeFailed",
661
679
  maxAttempts: 1,
662
680
  summaryAttempts: 1,
681
+ isLastSummary,
663
682
  },
664
683
  result.error,
665
684
  );
@@ -708,7 +727,10 @@ export class RunningSummarizer
708
727
  this.afterSummaryAction();
709
728
  },
710
729
  ).catch((error) => {
711
- this.mc.logger.sendErrorEvent({ eventName: "UnexpectedSummarizeError" }, error);
730
+ this.mc.logger.sendErrorEvent(
731
+ { eventName: "UnexpectedSummarizeError", summarizeReason: reason },
732
+ error,
733
+ );
712
734
  });
713
735
  }
714
736
 
@@ -718,6 +740,7 @@ export class RunningSummarizer
718
740
  */
719
741
  private async trySummarizeWithRetries(
720
742
  reason: SummarizeReason,
743
+ summarizeOptions: ISummarizeOptions = {},
721
744
  ): Promise<ISummarizeResults | undefined> {
722
745
  // Helper to set summarize options, telemetry properties and call summarize.
723
746
  const attemptSummarize = (
@@ -727,13 +750,13 @@ export class RunningSummarizer
727
750
  summarizeProps: ISummarizeTelemetryProperties;
728
751
  summarizeResult: ISummarizeResults;
729
752
  } => {
730
- const summarizeOptions: ISummarizeOptions = {
731
- fullTree: false,
753
+ const attemptSummarizeOptions: ISummarizeOptions = {
754
+ fullTree: summarizeOptions.fullTree ?? false,
732
755
  };
733
756
  const summarizeProps: ISummarizeTelemetryProperties = {
734
757
  summarizeReason: reason,
735
758
  summaryAttempts: attemptNumber,
736
- ...summarizeOptions,
759
+ ...attemptSummarizeOptions,
737
760
  finalAttempt,
738
761
  };
739
762
  const summaryLogger = createChildLogger({
@@ -742,7 +765,7 @@ export class RunningSummarizer
742
765
  });
743
766
 
744
767
  const summaryOptions: ISubmitSummaryOptions = {
745
- ...summarizeOptions,
768
+ ...attemptSummarizeOptions,
746
769
  summaryLogger,
747
770
  cancellationToken: this.cancellationToken,
748
771
  finalAttempt,
@@ -872,6 +895,8 @@ export class RunningSummarizer
872
895
  eventName: "SummarizeFailed",
873
896
  maxAttempts,
874
897
  summaryAttempts: currentAttempt,
898
+ summarizeReason: reason,
899
+ isLastSummary: reason === "lastSummary",
875
900
  },
876
901
  error,
877
902
  );
@@ -893,8 +918,9 @@ export class RunningSummarizer
893
918
  private async summarizeOnDemandWithRetries(
894
919
  reason: SummarizeReason,
895
920
  resultsBuilder: SummarizeResultBuilder,
921
+ summarizeOptions: ISummarizeOptions = {},
896
922
  ): Promise<ISummarizeResults> {
897
- const results = await this.trySummarizeWithRetries(reason);
923
+ const results = await this.trySummarizeWithRetries(reason, summarizeOptions);
898
924
  if (results === undefined) {
899
925
  resultsBuilder.fail(
900
926
  "Summarization was canceled",
@@ -932,13 +958,15 @@ export class RunningSummarizer
932
958
  throw new UsageError("Attempted to run an already-running summarizer on demand");
933
959
  }
934
960
 
935
- const { reason, ...summarizeOptions } = options;
936
- if (options.retryOnFailure === true) {
937
- this.summarizeOnDemandWithRetries(`onDemand;${reason}`, resultsBuilder).catch(
938
- (error: IRetriableFailureError) => {
939
- resultsBuilder.fail("summarize failed", error);
940
- },
941
- );
961
+ const { reason, retryOnFailure, ...summarizeOptions } = options;
962
+ if (retryOnFailure === true) {
963
+ this.summarizeOnDemandWithRetries(
964
+ `onDemand;${reason}`,
965
+ resultsBuilder,
966
+ summarizeOptions,
967
+ ).catch((error: IRetriableFailureError) => {
968
+ resultsBuilder.fail("summarize failed", error);
969
+ });
942
970
  } else {
943
971
  this.trySummarizeOnce(
944
972
  { summarizeReason: `onDemand/${reason}` },
@@ -301,7 +301,7 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
301
301
  this.runtime,
302
302
  );
303
303
  this.runningSummarizer = runningSummarizer;
304
- this.setupForwardedEvents();
304
+ this.setupForwardedEvents(runningSummarizer);
305
305
  this.starting = false;
306
306
  return runningSummarizer;
307
307
  }
@@ -399,24 +399,26 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
399
399
  this._heuristicData?.recordAttempt(summaryRefSeqNum);
400
400
  }
401
401
 
402
- private readonly forwardedEvents = new Map<string, () => void>();
402
+ private readonly forwardedEventsCleanup: (() => void)[] = [];
403
403
 
404
- private setupForwardedEvents(): void {
405
- for (const event of ["summarize", "summarizeAllAttemptsFailed"]) {
404
+ private setupForwardedEvents(runningSummarizer: RunningSummarizer): void {
405
+ for (const event of [
406
+ "summarize",
407
+ "summarizeAllAttemptsFailed",
408
+ "summarizeTimeout",
409
+ ] as const) {
406
410
  const listener = (...args: unknown[]): void => {
407
411
  this.emit(event, ...args);
408
412
  };
409
- // TODO: better typing here
410
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
411
- this.runningSummarizer?.on(event as any, listener);
412
- this.forwardedEvents.set(event, listener);
413
+ runningSummarizer.on(event, listener);
414
+ this.forwardedEventsCleanup.push(() => runningSummarizer.off(event, listener));
413
415
  }
414
416
  }
415
417
 
416
418
  private cleanupForwardedEvents(): void {
417
- for (const [event, listener] of this.forwardedEvents.entries()) {
418
- this.runningSummarizer?.off(event, listener);
419
+ for (const cleanup of this.forwardedEventsCleanup) {
420
+ cleanup();
419
421
  }
420
- this.forwardedEvents.clear();
422
+ this.forwardedEventsCleanup.length = 0;
421
423
  }
422
424
  }
@@ -3,9 +3,12 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import type { ISummarizerEvents } from "@fluidframework/container-runtime-definitions/internal";
6
8
  import { assert, type IPromiseTimer, Timer } from "@fluidframework/core-utils/internal";
7
9
  import { DriverErrorTypes, MessageType } from "@fluidframework/driver-definitions/internal";
8
10
  import { getRetryDelaySecondsFromError } from "@fluidframework/driver-utils/internal";
11
+ import { TelemetryContext } from "@fluidframework/runtime-utils/internal";
9
12
  import {
10
13
  isFluidError,
11
14
  type ITelemetryLoggerExt,
@@ -40,8 +43,9 @@ const maxSummarizeTimeoutCount = 5; // Double and resend 5 times
40
43
  /**
41
44
  * This class generates and tracks a summary attempt.
42
45
  */
43
- export class SummaryGenerator {
46
+ export class SummaryGenerator extends TypedEventEmitter<ISummarizerEvents> {
44
47
  private readonly summarizeTimer: Timer;
48
+ private activeTelemetryContext?: TelemetryContext;
45
49
  constructor(
46
50
  private readonly pendingAckTimer: IPromiseTimer,
47
51
  private readonly heuristicData: ISummarizeHeuristicData,
@@ -55,6 +59,7 @@ export class SummaryGenerator {
55
59
  private readonly summaryWatcher: Pick<IClientSummaryWatcher, "watchSummary">,
56
60
  private readonly logger: ITelemetryLoggerExt,
57
61
  ) {
62
+ super();
58
63
  this.summarizeTimer = new Timer(maxSummarizeTimeoutTime, () =>
59
64
  this.summarizeTimerHandler(maxSummarizeTimeoutTime, 1),
60
65
  );
@@ -85,7 +90,16 @@ export class SummaryGenerator {
85
90
  submitSummaryOptions: ISubmitSummaryOptions,
86
91
  resultsBuilder: SummarizeResultBuilder,
87
92
  ): Promise<void> {
88
- const { summaryLogger, cancellationToken, ...summarizeOptions } = submitSummaryOptions;
93
+ const {
94
+ summaryLogger,
95
+ cancellationToken,
96
+ telemetryContext = new TelemetryContext(),
97
+ ...summarizeOptions
98
+ } = submitSummaryOptions;
99
+
100
+ telemetryContext.setCurrentSummarizeStep("submitSummary");
101
+ const submitOptions = { ...submitSummaryOptions, telemetryContext };
102
+ this.activeTelemetryContext = telemetryContext;
89
103
 
90
104
  // Note: timeSinceLastAttempt and timeSinceLastSummary for the
91
105
  // first summary are basically the time since the summarizer was loaded.
@@ -97,6 +111,8 @@ export class SummaryGenerator {
97
111
  fullTree: summarizeOptions.fullTree ?? false,
98
112
  timeSinceLastAttempt,
99
113
  timeSinceLastSummary,
114
+ nonRuntimeOpsSinceLastSummary: this.heuristicData.numNonRuntimeOps,
115
+ runtimeOpsSinceLastSummary: this.heuristicData.numRuntimeOps,
100
116
  };
101
117
 
102
118
  const summarizeEvent = PerformanceEvent.start(
@@ -115,7 +131,7 @@ export class SummaryGenerator {
115
131
  * For submit failures, submitFailureResult should be provided. For nack failures, nackSummaryResult should
116
132
  * be provided. For op broadcast failures, only errors / properties should be provided.
117
133
  */
118
- const fail = (
134
+ const summaryFail = (
119
135
  errorCode: SummarizeErrorCode,
120
136
  error: IRetriableFailureError,
121
137
  properties?: SummaryGeneratorTelemetry,
@@ -148,10 +164,12 @@ export class SummaryGenerator {
148
164
  // Wait to generate and send summary
149
165
  this.summarizeTimer.start();
150
166
  try {
167
+ telemetryContext.setCurrentSummarizeStep("generateSummary");
151
168
  // Need to save refSeqNum before we record new attempt (happens as part of submitSummaryCallback)
152
169
  const lastAttemptRefSeqNum = this.heuristicData.lastAttempt.refSequenceNumber;
153
170
 
154
- summaryData = await this.submitSummaryCallback(submitSummaryOptions);
171
+ summaryData = await this.submitSummaryCallback(submitOptions);
172
+ telemetryContext.setCurrentSummarizeStep("submitSummaryOp");
155
173
 
156
174
  // Cumulatively add telemetry properties based on how far generateSummary went.
157
175
  const referenceSequenceNumber = summaryData.referenceSequenceNumber;
@@ -173,7 +191,7 @@ export class SummaryGenerator {
173
191
  const errorCode: SummarizeErrorCode = "submitSummaryFailure";
174
192
  const retriableError =
175
193
  summaryData.error ?? new RetriableSummaryError(getFailMessage(errorCode));
176
- return fail(errorCode, retriableError, summarizeTelemetryProps, {
194
+ return summaryFail(errorCode, retriableError, summarizeTelemetryProps, {
177
195
  stage: summaryData.stage,
178
196
  });
179
197
  }
@@ -189,6 +207,7 @@ export class SummaryGenerator {
189
207
  * exceed the number of ops since last summary + number of data store whose reference state changed.
190
208
  */
191
209
  if (submitSummaryOptions.fullTree !== true) {
210
+ telemetryContext.setCurrentSummarizeStep("watchSummary");
192
211
  const { summarizedDataStoreCount, gcStateUpdatedDataStoreCount = 0 } =
193
212
  summaryData.summaryStats;
194
213
  if (
@@ -208,7 +227,7 @@ export class SummaryGenerator {
208
227
  summarizeEvent.reportEvent("generate", { ...summarizeTelemetryProps });
209
228
  resultsBuilder.summarySubmitted.resolve({ success: true, data: summaryData });
210
229
  } catch (error) {
211
- return fail(
230
+ return summaryFail(
212
231
  "submitSummaryFailure",
213
232
  wrapError(
214
233
  error,
@@ -227,6 +246,8 @@ export class SummaryGenerator {
227
246
  this.summarizeTimer.clear();
228
247
  }
229
248
 
249
+ telemetryContext.setCurrentSummarizeStep("waitForSummaryAck");
250
+
230
251
  try {
231
252
  const pendingTimeoutP = this.pendingAckTimer.start();
232
253
  const summary = this.summaryWatcher.watchSummary(summaryData.clientSequenceNumber);
@@ -239,13 +260,13 @@ export class SummaryGenerator {
239
260
  );
240
261
  if (waitBroadcastResult.result === "cancelled") {
241
262
  const errorCode: SummarizeErrorCode = "disconnect";
242
- return fail(errorCode, new RetriableSummaryError(getFailMessage(errorCode)));
263
+ return summaryFail(errorCode, new RetriableSummaryError(getFailMessage(errorCode)));
243
264
  }
244
265
  if (waitBroadcastResult.result !== "done") {
245
266
  // The summary op may not have been received within the timeout due to a transient error. So,
246
267
  // fail with a retriable error to re-attempt the summary if possible.
247
268
  const errorCode: SummarizeErrorCode = "summaryOpWaitTimeout";
248
- return fail(
269
+ return summaryFail(
249
270
  errorCode,
250
271
  new RetriableSummaryError(getFailMessage(errorCode), 0 /* retryAfterSeconds */),
251
272
  );
@@ -275,13 +296,13 @@ export class SummaryGenerator {
275
296
  );
276
297
  if (waitAckNackResult.result === "cancelled") {
277
298
  const errorCode: SummarizeErrorCode = "disconnect";
278
- return fail(errorCode, new RetriableSummaryError(getFailMessage(errorCode)));
299
+ return summaryFail(errorCode, new RetriableSummaryError(getFailMessage(errorCode)));
279
300
  }
280
301
  if (waitAckNackResult.result !== "done") {
281
302
  const errorCode: SummarizeErrorCode = "summaryAckWaitTimeout";
282
303
  // The summary ack may not have been received within the timeout due to a transient error. So,
283
304
  // fail with a retriable error to re-attempt the summary if possible.
284
- return fail(
305
+ return summaryFail(
285
306
  errorCode,
286
307
  new RetriableSummaryError(getFailMessage(errorCode), 0 /* retryAfterSeconds */),
287
308
  );
@@ -300,6 +321,7 @@ export class SummaryGenerator {
300
321
  ...summarizeTelemetryProps,
301
322
  };
302
323
  if (ackNackOp.type === MessageType.SummaryAck) {
324
+ telemetryContext.setCurrentSummarizeStep("ackReceived");
303
325
  this.heuristicData.markLastAttemptAsSuccessful();
304
326
  this.successfulSummaryCallback();
305
327
  summarizeEvent.end({
@@ -324,6 +346,7 @@ export class SummaryGenerator {
324
346
  } else {
325
347
  // Check for retryDelay in summaryNack response.
326
348
  assert(ackNackOp.type === MessageType.SummaryNack, 0x274 /* "type check" */);
349
+ telemetryContext.setCurrentSummarizeStep("nackReceived");
327
350
  const summaryNack = ackNackOp.contents;
328
351
  const errorMessage = summaryNack?.message;
329
352
  const retryAfterSeconds = summaryNack?.retryAfter;
@@ -340,7 +363,7 @@ export class SummaryGenerator {
340
363
  0x25f /* "retryAfterSeconds" */,
341
364
  );
342
365
  // This will only set resultsBuilder.receivedSummaryAckOrNack, as other promises are already set.
343
- return fail(
366
+ return summaryFail(
344
367
  errorCode,
345
368
  error,
346
369
  { ...summarizeTelemetryProps, nackRetryAfter: retryAfterSeconds },
@@ -350,6 +373,7 @@ export class SummaryGenerator {
350
373
  }
351
374
  } finally {
352
375
  this.pendingAckTimer.clear();
376
+ this.activeTelemetryContext = undefined;
353
377
  }
354
378
  }
355
379
 
@@ -390,8 +414,6 @@ export class SummaryGenerator {
390
414
  clientSequenceNumber: summaryData.clientSequenceNumber,
391
415
  hasMissingOpData: this.heuristicData.hasMissingOpData,
392
416
  opsSizesSinceLastSummary: this.heuristicData.totalOpsSize,
393
- nonRuntimeOpsSinceLastSummary: this.heuristicData.numNonRuntimeOps,
394
- runtimeOpsSinceLastSummary: this.heuristicData.numRuntimeOps,
395
417
  };
396
418
  }
397
419
 
@@ -408,6 +430,13 @@ export class SummaryGenerator {
408
430
  eventName: "SummarizeTimeout",
409
431
  timeoutTime: time,
410
432
  timeoutCount: count,
433
+ currentSummarizeStep: this.activeTelemetryContext?.getCurrentSummarizeStep(),
434
+ });
435
+ this.emit("summarizeTimeout", {
436
+ timeoutCount: count,
437
+ currentSummarizeStep: this.activeTelemetryContext?.getCurrentSummarizeStep(),
438
+ numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
439
+ numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
411
440
  });
412
441
  if (count < maxSummarizeTimeoutCount) {
413
442
  // Double and start a new timer
@@ -420,5 +449,6 @@ export class SummaryGenerator {
420
449
 
421
450
  public dispose(): void {
422
451
  this.summarizeTimer.clear();
452
+ this.activeTelemetryContext = undefined;
423
453
  }
424
454
  }
@@ -274,7 +274,7 @@ export class SummaryManager
274
274
 
275
275
  const summarizer = await this.createSummarizerFn();
276
276
  this.summarizer = summarizer;
277
- this.setupForwardedEvents();
277
+ this.setupForwardedEvents(summarizer);
278
278
 
279
279
  // Re-validate that it need to be running. Due to asynchrony, it may be not the case anymore
280
280
  // If we can't run the LastSummary, simply return as to avoid paying the cost of launching
@@ -456,31 +456,32 @@ export class SummaryManager
456
456
  this._disposed = true;
457
457
  }
458
458
 
459
- private readonly forwardedEvents = new Map<string, () => void>();
459
+ private readonly forwardedEventsCleanup: (() => void)[] = [];
460
460
 
461
- private setupForwardedEvents(): void {
461
+ private setupForwardedEvents(summarizer: ISummarizer): void {
462
462
  for (const event of [
463
463
  "summarize",
464
464
  "summarizeAllAttemptsFailed",
465
465
  "summarizerStop",
466
466
  "summarizerStart",
467
467
  "summarizerStartupFailed",
468
- ]) {
468
+ "summarizeTimeout",
469
+ ] as const) {
469
470
  const listener = (...args: unknown[]): void => {
470
471
  this.emit(event, ...args);
471
472
  };
472
473
  // TODO: better typing here
473
474
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
474
- this.summarizer?.on(event as any, listener);
475
- this.forwardedEvents.set(event, listener);
475
+ summarizer.on(event as any, listener);
476
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
477
+ this.forwardedEventsCleanup.push(() => summarizer.off(event as any, listener));
476
478
  }
477
479
  }
478
480
 
479
481
  private cleanupForwardedEvents(): void {
480
- for (const [event, listener] of this.forwardedEvents.entries()) {
481
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
482
- this.summarizer?.off(event as any, listener);
482
+ for (const cleanup of this.forwardedEventsCleanup) {
483
+ cleanup();
483
484
  }
484
- this.forwardedEvents.clear();
485
+ this.forwardedEventsCleanup.length = 0;
485
486
  }
486
487
  }
package/.eslintrc.cjs DELETED
@@ -1,30 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- module.exports = {
7
- extends: [require.resolve("@fluidframework/eslint-config-fluid/recommended"), "prettier"],
8
- parserOptions: {
9
- project: ["./tsconfig.json", "./src/test/tsconfig.json"],
10
- },
11
- rules: {
12
- // AB#51780: temporarily disabled because of crashes in typescript-eslint on destructuring in promise chains.
13
- // See: channelCollection.ts:1070
14
- "@typescript-eslint/unbound-method": "off",
15
- },
16
- overrides: [
17
- {
18
- // Rules only for test files
19
- files: ["*.spec.ts", "src/test/**"],
20
- rules: {
21
- // TODO: remove these overrides and fix violations
22
- "@typescript-eslint/explicit-function-return-type": "off",
23
- "unicorn/consistent-function-scoping": "off",
24
-
25
- // Test files are run in node only so additional node libraries can be used.
26
- "import-x/no-nodejs-modules": ["error", { allow: ["node:assert", "node:crypto"] }],
27
- },
28
- },
29
- ],
30
- };