@fluidframework/container-runtime 2.11.0 → 2.12.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 (68) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/api-report/container-runtime.legacy.alpha.api.md +17 -15
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/containerRuntime.d.ts +38 -5
  5. package/dist/containerRuntime.d.ts.map +1 -1
  6. package/dist/containerRuntime.js +28 -7
  7. package/dist/containerRuntime.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/packageVersion.d.ts +1 -1
  12. package/dist/packageVersion.js +1 -1
  13. package/dist/packageVersion.js.map +1 -1
  14. package/dist/summary/runWhileConnectedCoordinator.d.ts +2 -1
  15. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  16. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
  17. package/dist/summary/runningSummarizer.d.ts +8 -1
  18. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  19. package/dist/summary/runningSummarizer.js +70 -8
  20. package/dist/summary/runningSummarizer.js.map +1 -1
  21. package/dist/summary/summarizer.d.ts +5 -2
  22. package/dist/summary/summarizer.d.ts.map +1 -1
  23. package/dist/summary/summarizer.js +39 -6
  24. package/dist/summary/summarizer.js.map +1 -1
  25. package/dist/summary/summarizerTypes.d.ts +8 -4
  26. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  27. package/dist/summary/summarizerTypes.js.map +1 -1
  28. package/dist/summary/summaryManager.d.ts +5 -2
  29. package/dist/summary/summaryManager.d.ts.map +1 -1
  30. package/dist/summary/summaryManager.js +23 -6
  31. package/dist/summary/summaryManager.js.map +1 -1
  32. package/lib/containerRuntime.d.ts +38 -5
  33. package/lib/containerRuntime.d.ts.map +1 -1
  34. package/lib/containerRuntime.js +28 -7
  35. package/lib/containerRuntime.js.map +1 -1
  36. package/lib/index.d.ts +1 -1
  37. package/lib/index.d.ts.map +1 -1
  38. package/lib/index.js.map +1 -1
  39. package/lib/packageVersion.d.ts +1 -1
  40. package/lib/packageVersion.js +1 -1
  41. package/lib/packageVersion.js.map +1 -1
  42. package/lib/summary/runWhileConnectedCoordinator.d.ts +2 -1
  43. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  44. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
  45. package/lib/summary/runningSummarizer.d.ts +8 -1
  46. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  47. package/lib/summary/runningSummarizer.js +70 -8
  48. package/lib/summary/runningSummarizer.js.map +1 -1
  49. package/lib/summary/summarizer.d.ts +5 -2
  50. package/lib/summary/summarizer.d.ts.map +1 -1
  51. package/lib/summary/summarizer.js +39 -6
  52. package/lib/summary/summarizer.js.map +1 -1
  53. package/lib/summary/summarizerTypes.d.ts +8 -4
  54. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  55. package/lib/summary/summarizerTypes.js.map +1 -1
  56. package/lib/summary/summaryManager.d.ts +5 -2
  57. package/lib/summary/summaryManager.d.ts.map +1 -1
  58. package/lib/summary/summaryManager.js +23 -6
  59. package/lib/summary/summaryManager.js.map +1 -1
  60. package/package.json +19 -19
  61. package/src/containerRuntime.ts +82 -23
  62. package/src/index.ts +1 -0
  63. package/src/packageVersion.ts +1 -1
  64. package/src/summary/runWhileConnectedCoordinator.ts +2 -5
  65. package/src/summary/runningSummarizer.ts +82 -11
  66. package/src/summary/summarizer.ts +49 -10
  67. package/src/summary/summarizerTypes.ts +11 -4
  68. package/src/summary/summaryManager.ts +30 -10
@@ -218,7 +218,6 @@ import {
218
218
  ISubmitSummaryOptions,
219
219
  ISummarizeResults,
220
220
  ISummarizer,
221
- ISummarizerEvents,
222
221
  ISummarizerInternalsProvider,
223
222
  ISummarizerRuntime,
224
223
  ISummaryMetadataMessage,
@@ -475,6 +474,8 @@ export interface IContainerRuntimeOptions {
475
474
  * send all operations to the driver layer, while in TurnBased the operations will be buffered
476
475
  * and then sent them as a single batch at the end of the turn.
477
476
  * By default, flush mode is TurnBased.
477
+ *
478
+ * @deprecated Only the default value TurnBased is supported. This option will be removed in the future.
478
479
  */
479
480
  readonly flushMode?: FlushMode;
480
481
  /**
@@ -516,9 +517,10 @@ export interface IContainerRuntimeOptions {
516
517
  /**
517
518
  * If enabled, the runtime will group messages within a batch into a single
518
519
  * message to be sent to the service.
519
- * The grouping an ungrouping of such messages is handled by the "OpGroupingManager".
520
+ * The grouping and ungrouping of such messages is handled by the "OpGroupingManager".
520
521
  *
521
522
  * By default, the feature is enabled.
523
+ * @deprecated The ability to disable Grouped Batching is deprecated and will be removed in v2.20.0. This feature is required for the proper functioning of the Fluid Framework.
522
524
  */
523
525
  readonly enableGroupedBatching?: boolean;
524
526
 
@@ -532,6 +534,31 @@ export interface IContainerRuntimeOptions {
532
534
  readonly explicitSchemaControl?: boolean;
533
535
  }
534
536
 
537
+ /**
538
+ * Internal extension of @see IContainerRuntimeOptions
539
+ *
540
+ * These options are not available to consumers when creating a new container runtime,
541
+ * but we do need to expose them for internal use, e.g. when configuring the container runtime
542
+ * to ensure compability with older versions.
543
+ *
544
+ * @internal
545
+ */
546
+ export interface IContainerRuntimeOptionsInternal extends IContainerRuntimeOptions {
547
+ /**
548
+ * Sets the flush mode for the runtime. In Immediate flush mode the runtime will immediately
549
+ * send all operations to the driver layer, while in TurnBased the operations will be buffered
550
+ * and then sent them as a single batch at the end of the turn.
551
+ * By default, flush mode is TurnBased.
552
+ */
553
+ readonly flushMode?: FlushMode;
554
+
555
+ /**
556
+ * Allows Grouped Batching to be disabled by setting to false (default is true).
557
+ * In that case, batched messages will be sent individually (but still all at the same time).
558
+ */
559
+ readonly enableGroupedBatching?: boolean;
560
+ }
561
+
535
562
  /**
536
563
  * Error responses when requesting a deleted object will have this header set to true
537
564
  * @legacy
@@ -836,11 +863,15 @@ export async function loadContainerRuntime(
836
863
  /**
837
864
  * Represents the runtime of the container. Contains helper functions/state of the container.
838
865
  * It will define the store level mappings.
866
+ *
867
+ * @deprecated To be removed from the Legacy-Alpha API in version 2.20.0.
868
+ * Use the loadContainerRuntime function and interfaces IContainerRuntime / IRuntime instead.
869
+ *
839
870
  * @legacy
840
871
  * @alpha
841
872
  */
842
873
  export class ContainerRuntime
843
- extends TypedEventEmitter<IContainerRuntimeEvents & ISummarizerEvents>
874
+ extends TypedEventEmitter<IContainerRuntimeEvents>
844
875
  implements
845
876
  IContainerRuntime,
846
877
  IRuntime,
@@ -867,7 +898,7 @@ export class ContainerRuntime
867
898
  context: IContainerContext;
868
899
  registryEntries: NamedFluidDataStoreRegistryEntries;
869
900
  existing: boolean;
870
- runtimeOptions?: IContainerRuntimeOptions;
901
+ runtimeOptions?: IContainerRuntimeOptions; // May also include options from IContainerRuntimeOptionsInternal
871
902
  containerScope?: FluidObject;
872
903
  containerRuntimeCtor?: typeof ContainerRuntime;
873
904
  /** @deprecated Will be removed once Loader LTS version is "2.0.0-internal.7.0.0". Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md */
@@ -914,7 +945,7 @@ export class ContainerRuntime
914
945
  chunkSizeInBytes = defaultChunkSizeInBytes,
915
946
  enableGroupedBatching = true,
916
947
  explicitSchemaControl = false,
917
- } = runtimeOptions;
948
+ }: IContainerRuntimeOptionsInternal = runtimeOptions;
918
949
 
919
950
  const registry = new FluidDataStoreRegistry(registryEntries);
920
951
 
@@ -1093,6 +1124,21 @@ export class ContainerRuntime
1093
1124
 
1094
1125
  const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {};
1095
1126
 
1127
+ // Make sure we've got all the options including internal ones
1128
+ const internalRuntimeOptions: Readonly<Required<IContainerRuntimeOptionsInternal>> = {
1129
+ summaryOptions,
1130
+ gcOptions,
1131
+ loadSequenceNumberVerification,
1132
+ flushMode,
1133
+ compressionOptions,
1134
+ maxBatchSizeInBytes,
1135
+ chunkSizeInBytes,
1136
+ // Requires<> drops undefined from IdCompressorType
1137
+ enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
1138
+ enableGroupedBatching,
1139
+ explicitSchemaControl,
1140
+ };
1141
+
1096
1142
  const runtime = new containerRuntimeCtor(
1097
1143
  context,
1098
1144
  registry,
@@ -1100,19 +1146,7 @@ export class ContainerRuntime
1100
1146
  electedSummarizerData,
1101
1147
  chunks ?? [],
1102
1148
  aliases ?? [],
1103
- {
1104
- summaryOptions,
1105
- gcOptions,
1106
- loadSequenceNumberVerification,
1107
- flushMode,
1108
- compressionOptions,
1109
- maxBatchSizeInBytes,
1110
- chunkSizeInBytes,
1111
- // Requires<> drops undefined from IdCompressorType
1112
- enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
1113
- enableGroupedBatching,
1114
- explicitSchemaControl,
1115
- },
1149
+ internalRuntimeOptions,
1116
1150
  containerScope,
1117
1151
  logger,
1118
1152
  existing,
@@ -1474,6 +1508,11 @@ export class ContainerRuntime
1474
1508
  expiry: { policy: "absolute", durationMs: 60000 },
1475
1509
  });
1476
1510
 
1511
+ /**
1512
+ * The options to apply to this ContainerRuntime instance (including internal options hidden from the public API)
1513
+ */
1514
+ private readonly runtimeOptions: Readonly<Required<IContainerRuntimeOptionsInternal>>;
1515
+
1477
1516
  /***/
1478
1517
  protected constructor(
1479
1518
  context: IContainerContext,
@@ -1482,7 +1521,10 @@ export class ContainerRuntime
1482
1521
  electedSummarizerData: ISerializedElection | undefined,
1483
1522
  chunks: [string, string[]][],
1484
1523
  dataStoreAliasMap: [string, string][],
1485
- private readonly runtimeOptions: Readonly<Required<IContainerRuntimeOptions>>,
1524
+ runtimeOptions: Readonly<
1525
+ Required<Omit<IContainerRuntimeOptions, "flushMode" | "enableGroupedBatching">> &
1526
+ IContainerRuntimeOptions // Let flushMode and enabledGroupedBatching be optional now since they're soon to be removed
1527
+ >,
1486
1528
  private readonly containerScope: FluidObject,
1487
1529
  // Create a custom ITelemetryBaseLogger to output telemetry events.
1488
1530
  public readonly baseLogger: ITelemetryBaseLogger,
@@ -1527,6 +1569,12 @@ export class ContainerRuntime
1527
1569
  snapshotWithContents,
1528
1570
  } = context;
1529
1571
 
1572
+ // Backfill in defaults for the internal runtimeOptions, since they may not be present on the provided runtimeOptions object
1573
+ this.runtimeOptions = {
1574
+ flushMode: defaultFlushMode,
1575
+ enableGroupedBatching: true,
1576
+ ...runtimeOptions,
1577
+ };
1530
1578
  this.logger = createChildLogger({ logger: this.baseLogger });
1531
1579
  this.mc = createChildMonitoringContext({
1532
1580
  logger: this.logger,
@@ -1697,14 +1745,15 @@ export class ContainerRuntime
1697
1745
  this.defaultMaxConsecutiveReconnects;
1698
1746
 
1699
1747
  if (
1700
- runtimeOptions.flushMode === (FlushModeExperimental.Async as unknown as FlushMode) &&
1748
+ this.runtimeOptions.flushMode ===
1749
+ (FlushModeExperimental.Async as unknown as FlushMode) &&
1701
1750
  supportedFeatures?.get("referenceSequenceNumbers") !== true
1702
1751
  ) {
1703
1752
  // The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
1704
1753
  this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
1705
1754
  this._flushMode = FlushMode.TurnBased;
1706
1755
  } else {
1707
- this._flushMode = runtimeOptions.flushMode;
1756
+ this._flushMode = this.runtimeOptions.flushMode;
1708
1757
  }
1709
1758
  this.offlineEnabled =
1710
1759
  this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
@@ -2015,9 +2064,19 @@ export class ContainerRuntime
2015
2064
  initialDelayMs: this.initialSummarizerDelayMs,
2016
2065
  },
2017
2066
  );
2018
- this.summaryManager.on("summarize", (eventProps) => {
2019
- this.emit("summarize", eventProps);
2067
+ // Forward events from SummaryManager
2068
+ [
2069
+ "summarize",
2070
+ "summarizeAllAttemptsFailed",
2071
+ "summarizerStop",
2072
+ "summarizerStart",
2073
+ "summarizerStartupFailed",
2074
+ ].forEach((eventName) => {
2075
+ this.summaryManager?.on(eventName, (...args: any[]) => {
2076
+ this.emit(eventName, ...args);
2077
+ });
2020
2078
  });
2079
+
2021
2080
  this.summaryManager.start();
2022
2081
  }
2023
2082
  }
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export {
10
10
  ISummaryConfigurationDisableSummarizer,
11
11
  ISummaryConfigurationDisableHeuristics,
12
12
  IContainerRuntimeOptions,
13
+ IContainerRuntimeOptionsInternal,
13
14
  loadContainerRuntime,
14
15
  LoadContainerRuntimeParams,
15
16
  agentSchedulerId,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.11.0";
9
+ export const pkgVersion = "2.12.0";
@@ -3,13 +3,10 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import type { SummarizerStopReason } from "@fluidframework/container-runtime-definitions/internal";
6
7
  import { assert, Deferred } from "@fluidframework/core-utils/internal";
7
8
 
8
- import {
9
- IConnectableRuntime,
10
- ISummaryCancellationToken,
11
- SummarizerStopReason,
12
- } from "./summarizerTypes.js";
9
+ import { IConnectableRuntime, ISummaryCancellationToken } from "./summarizerTypes.js";
13
10
 
14
11
  /**
15
12
  * Similar to AbortController, but using promise instead of events
@@ -4,6 +4,12 @@
4
4
  */
5
5
 
6
6
  import { TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import type {
8
+ ISummarizeEventProps,
9
+ ISummarizerEvents,
10
+ ISummarizerObservabilityProps,
11
+ SummarizerStopReason,
12
+ } from "@fluidframework/container-runtime-definitions/internal";
7
13
  import { IDisposable, ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
8
14
  import { assert, Deferred, PromiseTimer, delay } from "@fluidframework/core-utils/internal";
9
15
  import {
@@ -29,18 +35,15 @@ import {
29
35
  IOnDemandSummarizeOptions,
30
36
  IRefreshSummaryAckOptions,
31
37
  ISubmitSummaryOptions,
32
- ISummarizeEventProps,
33
38
  ISummarizeHeuristicData,
34
39
  ISummarizeHeuristicRunner,
35
40
  ISummarizeOptions,
36
41
  ISummarizeResults,
37
42
  ISummarizeRunnerTelemetry,
38
43
  ISummarizeTelemetryProperties,
39
- ISummarizerEvents,
40
44
  ISummarizerRuntime,
41
45
  ISummaryCancellationToken,
42
46
  SubmitSummaryResult,
43
- SummarizerStopReason,
44
47
  type IRetriableFailureError,
45
48
  } from "./summarizerTypes.js";
46
49
  import {
@@ -189,6 +192,13 @@ export class RunningSummarizer
189
192
  /** The maximum number of summary attempts to do when submit summary fails. */
190
193
  private readonly maxAttemptsForSubmitFailures: number;
191
194
 
195
+ /**
196
+ * These are necessary to store outside of methods because of the logic around runnning a lastSummary.
197
+ * We want the lastSummary to also be captured as "all attempts failed".
198
+ */
199
+ private lastSummarizeFailureEventProps: Omit<ISummarizeEventProps, "result"> | undefined =
200
+ undefined;
201
+
192
202
  private constructor(
193
203
  baseLogger: ITelemetryBaseLogger,
194
204
  private readonly summaryWatcher: IClientSummaryWatcher,
@@ -492,6 +502,8 @@ export class RunningSummarizer
492
502
  // summarizeProps
493
503
  { summarizeReason: "lastSummary" },
494
504
  {},
505
+ undefined,
506
+ true /* isLastSummary */,
495
507
  );
496
508
  }
497
509
  }
@@ -502,6 +514,15 @@ export class RunningSummarizer
502
514
  // submit summary. We should reconsider this flow and make summarizer move to exit faster.
503
515
  // This resolves when the current pending summary gets an ack or fails.
504
516
  await this.summarizingLock;
517
+
518
+ if (this.lastSummarizeFailureEventProps !== undefined) {
519
+ this.emit("summarizeAllAttemptsFailed", {
520
+ ...this.lastSummarizeFailureEventProps,
521
+ numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
522
+ numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
523
+ });
524
+ }
525
+ this.lastSummarizeFailureEventProps = undefined;
505
526
  }
506
527
 
507
528
  private async waitStart() {
@@ -579,12 +600,14 @@ export class RunningSummarizer
579
600
  * @param options - summary options
580
601
  * @param cancellationToken - cancellation token to use to be able to cancel this summary, if needed
581
602
  * @param resultsBuilder - optional, result builder to use.
603
+ * @param isLastSummary - optional, is the call to this method for a last summary when shutting down the summarizer?
582
604
  * @returns ISummarizeResult - result of running a summary.
583
605
  */
584
606
  private trySummarizeOnce(
585
607
  summarizeProps: ISummarizeTelemetryProperties,
586
608
  options: ISummarizeOptions,
587
609
  resultsBuilder = new SummarizeResultBuilder(),
610
+ isLastSummary = false,
588
611
  ): ISummarizeResults {
589
612
  this.lockedSummaryAction(
590
613
  () => {
@@ -604,7 +627,28 @@ export class RunningSummarizer
604
627
  const summarizeResult = this.generator.summarize(summaryOptions, resultsBuilder);
605
628
  // ensure we wait till the end of the process
606
629
  const result = await summarizeResult.receivedSummaryAckOrNack;
607
- if (!result.success) {
630
+
631
+ if (result.success) {
632
+ this.emit("summarize", {
633
+ result: "success",
634
+ currentAttempt: 1,
635
+ maxAttempts: 1,
636
+ numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
637
+ numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
638
+ isLastSummary,
639
+ });
640
+ this.lastSummarizeFailureEventProps = undefined;
641
+ } else {
642
+ this.emit("summarize", {
643
+ result: "failure",
644
+ currentAttempt: 1,
645
+ maxAttempts: 1,
646
+ error: result.error,
647
+ failureMessage: result.message,
648
+ numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
649
+ numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
650
+ isLastSummary,
651
+ });
608
652
  this.mc.logger.sendErrorEvent(
609
653
  {
610
654
  eventName: "SummarizeFailed",
@@ -613,6 +657,14 @@ export class RunningSummarizer
613
657
  },
614
658
  result.error,
615
659
  );
660
+ if (isLastSummary) {
661
+ this.lastSummarizeFailureEventProps = {
662
+ currentAttempt: (this.lastSummarizeFailureEventProps?.currentAttempt ?? 0) + 1,
663
+ maxAttempts: (this.lastSummarizeFailureEventProps?.currentAttempt ?? 0) + 1,
664
+ error: result.error,
665
+ failureMessage: result.message,
666
+ };
667
+ }
616
668
  }
617
669
  },
618
670
  () => {
@@ -701,6 +753,7 @@ export class RunningSummarizer
701
753
  let status: "success" | "failure" | "canceled" = "success";
702
754
  let results: ISummarizeResults | undefined;
703
755
  let error: IRetriableFailureError | undefined;
756
+ let failureMessage: string | undefined;
704
757
  do {
705
758
  currentAttempt++;
706
759
  if (this.cancellationToken.cancelled) {
@@ -731,12 +784,16 @@ export class RunningSummarizer
731
784
  // Emit "summarize" event for this failed attempt.
732
785
  status = "failure";
733
786
  error = ackNackResult.error;
787
+ failureMessage = ackNackResult.message;
734
788
  retryAfterSeconds = error.retryAfterSeconds;
735
- const eventProps: ISummarizeEventProps = {
789
+ const eventProps: ISummarizeEventProps & ISummarizerObservabilityProps = {
736
790
  result: status,
737
791
  currentAttempt,
738
792
  maxAttempts,
739
793
  error,
794
+ failureMessage,
795
+ numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
796
+ numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
740
797
  };
741
798
  this.emit("summarize", eventProps);
742
799
 
@@ -761,7 +818,13 @@ export class RunningSummarizer
761
818
 
762
819
  // If the attempt was successful, emit "summarize" event and return. A failed attempt may be retried below.
763
820
  if (status !== "failure") {
764
- this.emit("summarize", { result: status, currentAttempt, maxAttempts });
821
+ this.emit("summarize", {
822
+ result: status,
823
+ currentAttempt,
824
+ maxAttempts,
825
+ numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
826
+ numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
827
+ });
765
828
  return results;
766
829
  }
767
830
 
@@ -772,14 +835,16 @@ export class RunningSummarizer
772
835
  // Ack / nack is the final step, so if it succeeds we're done.
773
836
  const ackNackResult = await summarizeResult.receivedSummaryAckOrNack;
774
837
  status = ackNackResult.success ? "success" : "failure";
775
- if (!ackNackResult.success) {
776
- error = ackNackResult.error;
777
- }
778
- const eventProps: ISummarizeEventProps = {
838
+ error = ackNackResult.success ? undefined : ackNackResult.error;
839
+ failureMessage = ackNackResult.success ? undefined : ackNackResult.message;
840
+ const eventProps: ISummarizeEventProps & ISummarizerObservabilityProps = {
779
841
  result: status,
780
842
  currentAttempt,
781
843
  maxAttempts,
782
- error: ackNackResult.success ? undefined : ackNackResult.error,
844
+ error,
845
+ failureMessage,
846
+ numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
847
+ numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
783
848
  };
784
849
  this.emit("summarize", eventProps);
785
850
  results = summarizeResult;
@@ -795,6 +860,12 @@ export class RunningSummarizer
795
860
  },
796
861
  error,
797
862
  );
863
+ this.lastSummarizeFailureEventProps = {
864
+ currentAttempt,
865
+ maxAttempts,
866
+ error,
867
+ failureMessage,
868
+ };
798
869
  this.stopSummarizerCallback("failToSummarize");
799
870
  }
800
871
  return results;
@@ -4,6 +4,10 @@
4
4
  */
5
5
 
6
6
  import { TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import type {
8
+ ISummarizerEvents,
9
+ SummarizerStopReason,
10
+ } from "@fluidframework/container-runtime-definitions/internal";
7
11
  import { IFluidHandleContext } from "@fluidframework/core-interfaces/internal";
8
12
  import { Deferred } from "@fluidframework/core-utils/internal";
9
13
  import {
@@ -25,15 +29,12 @@ import {
25
29
  IConnectableRuntime,
26
30
  IEnqueueSummarizeOptions,
27
31
  IOnDemandSummarizeOptions,
28
- ISummarizeEventProps,
29
32
  ISummarizeHeuristicData,
30
33
  ISummarizeResults,
31
34
  ISummarizer,
32
- ISummarizerEvents,
33
35
  ISummarizerInternalsProvider,
34
36
  ISummarizerRuntime,
35
37
  ISummarizingWarning,
36
- SummarizerStopReason,
37
38
  } from "./summarizerTypes.js";
38
39
  import { SummaryCollection } from "./summaryCollection.js";
39
40
  import { SummarizeResultBuilder } from "./summaryGenerator.js";
@@ -107,9 +108,21 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
107
108
 
108
109
  public async run(onBehalfOf: string): Promise<SummarizerStopReason> {
109
110
  try {
110
- return await this.runCore(onBehalfOf);
111
+ const stopReason = await this.runCore(onBehalfOf);
112
+ this.emit("summarizerStop", {
113
+ stopReason,
114
+ numUnsummarizedRuntimeOps: this._heuristicData?.numRuntimeOps,
115
+ numUnsummarizedNonRuntimeOps: this._heuristicData?.numNonRuntimeOps,
116
+ });
117
+ return stopReason;
111
118
  } catch (error) {
112
119
  this.stop("summarizerException");
120
+ this.emit("summarizerStop", {
121
+ stopReason: "summarizerException",
122
+ error,
123
+ numUnsummarizedRuntimeOps: this._heuristicData?.numRuntimeOps,
124
+ numUnsummarizedNonRuntimeOps: this._heuristicData?.numNonRuntimeOps,
125
+ });
113
126
  throw SummarizingWarning.wrap(error, false /* logged */, this.logger);
114
127
  } finally {
115
128
  this.close();
@@ -148,9 +161,20 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
148
161
  });
149
162
 
150
163
  if (runCoordinator.cancelled) {
164
+ this.emit("summarizerStartupFailed", {
165
+ reason: await runCoordinator.waitCancelled,
166
+ numUnsummarizedRuntimeOps: this._heuristicData?.numRuntimeOps,
167
+ numUnsummarizedNonRuntimeOps: this._heuristicData?.numNonRuntimeOps,
168
+ });
151
169
  return runCoordinator.waitCancelled;
152
170
  }
153
171
 
172
+ this.emit("summarizerStart", {
173
+ onBehalfOf,
174
+ numUnsummarizedRuntimeOps: this._heuristicData?.numRuntimeOps,
175
+ numUnsummarizedNonRuntimeOps: this._heuristicData?.numNonRuntimeOps,
176
+ });
177
+
154
178
  const runningSummarizer = await this.start(onBehalfOf, runCoordinator);
155
179
 
156
180
  // Wait for either external signal to cancel, or loss of connectivity.
@@ -254,15 +278,11 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
254
278
  this.runtime,
255
279
  );
256
280
  this.runningSummarizer = runningSummarizer;
257
- this.runningSummarizer.on("summarize", this.handleSummarizeEvent);
281
+ this.setupForwardedEvents();
258
282
  this.starting = false;
259
283
  return runningSummarizer;
260
284
  }
261
285
 
262
- private readonly handleSummarizeEvent = (eventProps: ISummarizeEventProps) => {
263
- this.emit("summarize", eventProps);
264
- };
265
-
266
286
  /**
267
287
  * Disposes of resources after running. This cleanup will
268
288
  * clear any outstanding timers and reset some of the state
@@ -275,7 +295,7 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
275
295
 
276
296
  this._disposed = true;
277
297
  if (this.runningSummarizer) {
278
- this.runningSummarizer.off("summarize", this.handleSummarizeEvent);
298
+ this.cleanupForwardedEvents();
279
299
  this.runningSummarizer.dispose();
280
300
  this.runningSummarizer = undefined;
281
301
  }
@@ -355,4 +375,23 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
355
375
  public recordSummaryAttempt?(summaryRefSeqNum?: number) {
356
376
  this._heuristicData?.recordAttempt(summaryRefSeqNum);
357
377
  }
378
+
379
+ private readonly forwardedEvents = new Map<any, () => void>();
380
+
381
+ private setupForwardedEvents() {
382
+ ["summarize", "summarizeAllAttemptsFailed"].forEach((event) => {
383
+ const listener = (...args: any[]) => {
384
+ this.emit(event, ...args);
385
+ };
386
+ this.runningSummarizer?.on(event as any, listener);
387
+ this.forwardedEvents.set(event, listener);
388
+ });
389
+ }
390
+
391
+ private cleanupForwardedEvents() {
392
+ this.forwardedEvents.forEach((listener, event) =>
393
+ this.runningSummarizer?.off(event, listener),
394
+ );
395
+ this.forwardedEvents.clear();
396
+ }
358
397
  }
@@ -7,6 +7,10 @@ import {
7
7
  IDeltaManager,
8
8
  ContainerWarning,
9
9
  } from "@fluidframework/container-definitions/internal";
10
+ import type {
11
+ ISummarizerEvents as NewISummarizerEvents,
12
+ SummarizerStopReason as NewSummarizerStopReason,
13
+ } from "@fluidframework/container-runtime-definitions/internal";
10
14
  import {
11
15
  IEvent,
12
16
  IEventProvider,
@@ -54,7 +58,7 @@ export interface ICancellationToken<T> {
54
58
  * @legacy
55
59
  * @alpha
56
60
  */
57
- export type ISummaryCancellationToken = ICancellationToken<SummarizerStopReason>;
61
+ export type ISummaryCancellationToken = ICancellationToken<NewSummarizerStopReason>;
58
62
 
59
63
  /**
60
64
  * Data required to update internal tracking state after receiving a Summary Ack.
@@ -397,6 +401,7 @@ export type EnqueueSummarizeResult =
397
401
  /**
398
402
  * @legacy
399
403
  * @alpha
404
+ * @deprecated Use SummarizerStopReason from the "\@fluidframework/container-runtime-definitions" package
400
405
  */
401
406
  export type SummarizerStopReason =
402
407
  /** Summarizer client failed to summarize in all attempts. */
@@ -427,6 +432,7 @@ export type SummarizerStopReason =
427
432
  /**
428
433
  * @legacy
429
434
  * @alpha
435
+ * @deprecated Use ISummarizeEventProps from the "\@fluidframework/container-runtime-definitions" package
430
436
  */
431
437
  export interface ISummarizeEventProps {
432
438
  result: "success" | "failure" | "canceled";
@@ -438,6 +444,7 @@ export interface ISummarizeEventProps {
438
444
  /**
439
445
  * @legacy
440
446
  * @alpha
447
+ * @deprecated Use ISummarizerEvents from the "\@fluidframework/container-runtime-definitions" package
441
448
  */
442
449
  export interface ISummarizerEvents extends IEvent {
443
450
  (event: "summarize", listener: (props: ISummarizeEventProps) => void);
@@ -447,7 +454,7 @@ export interface ISummarizerEvents extends IEvent {
447
454
  * @legacy
448
455
  * @alpha
449
456
  */
450
- export interface ISummarizer extends IEventProvider<ISummarizerEvents> {
457
+ export interface ISummarizer extends IEventProvider<NewISummarizerEvents> {
451
458
  /**
452
459
  * Allows {@link ISummarizer} to be used with our {@link @fluidframework/core-interfaces#FluidObject} pattern.
453
460
  */
@@ -458,12 +465,12 @@ export interface ISummarizer extends IEventProvider<ISummarizerEvents> {
458
465
  * Summarizer will finish current processes, which may take a while.
459
466
  * For example, summarizer may complete last summary before exiting.
460
467
  */
461
- stop(reason: SummarizerStopReason): void;
468
+ stop(reason: NewSummarizerStopReason): void;
462
469
 
463
470
  /* Closes summarizer. Any pending processes (summary in flight) are abandoned. */
464
471
  close(): void;
465
472
 
466
- run(onBehalfOf: string): Promise<SummarizerStopReason>;
473
+ run(onBehalfOf: string): Promise<NewSummarizerStopReason>;
467
474
 
468
475
  /**
469
476
  * Attempts to generate a summary on demand. If already running, takes no action.