@fluidframework/container-runtime 0.57.1 → 0.58.0-55561

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 (90) hide show
  1. package/dist/batchTracker.d.ts +26 -0
  2. package/dist/batchTracker.d.ts.map +1 -0
  3. package/dist/batchTracker.js +59 -0
  4. package/dist/batchTracker.js.map +1 -0
  5. package/dist/containerRuntime.d.ts +2 -1
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +63 -27
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStoreContext.js +1 -1
  10. package/dist/dataStoreContext.js.map +1 -1
  11. package/dist/dataStores.js +1 -1
  12. package/dist/dataStores.js.map +1 -1
  13. package/dist/garbageCollection.d.ts +1 -0
  14. package/dist/garbageCollection.d.ts.map +1 -1
  15. package/dist/garbageCollection.js +6 -4
  16. package/dist/garbageCollection.js.map +1 -1
  17. package/dist/packageVersion.d.ts +1 -1
  18. package/dist/packageVersion.d.ts.map +1 -1
  19. package/dist/packageVersion.js +1 -1
  20. package/dist/packageVersion.js.map +1 -1
  21. package/dist/pendingStateManager.d.ts.map +1 -1
  22. package/dist/pendingStateManager.js +1 -6
  23. package/dist/pendingStateManager.js.map +1 -1
  24. package/dist/runningSummarizer.d.ts +1 -1
  25. package/dist/runningSummarizer.d.ts.map +1 -1
  26. package/dist/runningSummarizer.js +1 -1
  27. package/dist/runningSummarizer.js.map +1 -1
  28. package/dist/summarizer.d.ts +3 -4
  29. package/dist/summarizer.d.ts.map +1 -1
  30. package/dist/summarizer.js +8 -9
  31. package/dist/summarizer.js.map +1 -1
  32. package/dist/summaryGenerator.d.ts +1 -1
  33. package/dist/summaryGenerator.d.ts.map +1 -1
  34. package/dist/summaryGenerator.js +1 -1
  35. package/dist/summaryGenerator.js.map +1 -1
  36. package/dist/summaryManager.d.ts +2 -6
  37. package/dist/summaryManager.d.ts.map +1 -1
  38. package/dist/summaryManager.js +4 -10
  39. package/dist/summaryManager.js.map +1 -1
  40. package/lib/batchTracker.d.ts +26 -0
  41. package/lib/batchTracker.d.ts.map +1 -0
  42. package/lib/batchTracker.js +54 -0
  43. package/lib/batchTracker.js.map +1 -0
  44. package/lib/containerRuntime.d.ts +2 -1
  45. package/lib/containerRuntime.d.ts.map +1 -1
  46. package/lib/containerRuntime.js +63 -27
  47. package/lib/containerRuntime.js.map +1 -1
  48. package/lib/dataStoreContext.js +2 -2
  49. package/lib/dataStoreContext.js.map +1 -1
  50. package/lib/dataStores.js +1 -1
  51. package/lib/dataStores.js.map +1 -1
  52. package/lib/garbageCollection.d.ts +1 -0
  53. package/lib/garbageCollection.d.ts.map +1 -1
  54. package/lib/garbageCollection.js +4 -2
  55. package/lib/garbageCollection.js.map +1 -1
  56. package/lib/packageVersion.d.ts +1 -1
  57. package/lib/packageVersion.d.ts.map +1 -1
  58. package/lib/packageVersion.js +1 -1
  59. package/lib/packageVersion.js.map +1 -1
  60. package/lib/pendingStateManager.d.ts.map +1 -1
  61. package/lib/pendingStateManager.js +1 -6
  62. package/lib/pendingStateManager.js.map +1 -1
  63. package/lib/runningSummarizer.d.ts +1 -1
  64. package/lib/runningSummarizer.d.ts.map +1 -1
  65. package/lib/runningSummarizer.js +1 -1
  66. package/lib/runningSummarizer.js.map +1 -1
  67. package/lib/summarizer.d.ts +3 -4
  68. package/lib/summarizer.d.ts.map +1 -1
  69. package/lib/summarizer.js +8 -9
  70. package/lib/summarizer.js.map +1 -1
  71. package/lib/summaryGenerator.d.ts +1 -1
  72. package/lib/summaryGenerator.d.ts.map +1 -1
  73. package/lib/summaryGenerator.js +1 -1
  74. package/lib/summaryGenerator.js.map +1 -1
  75. package/lib/summaryManager.d.ts +2 -6
  76. package/lib/summaryManager.d.ts.map +1 -1
  77. package/lib/summaryManager.js +5 -11
  78. package/lib/summaryManager.js.map +1 -1
  79. package/package.json +14 -14
  80. package/src/batchTracker.ts +80 -0
  81. package/src/containerRuntime.ts +84 -31
  82. package/src/dataStoreContext.ts +2 -2
  83. package/src/dataStores.ts +1 -1
  84. package/src/garbageCollection.ts +11 -10
  85. package/src/packageVersion.ts +1 -1
  86. package/src/pendingStateManager.ts +4 -8
  87. package/src/runningSummarizer.ts +3 -3
  88. package/src/summarizer.ts +8 -8
  89. package/src/summaryGenerator.ts +2 -2
  90. package/src/summaryManager.ts +5 -20
@@ -22,7 +22,6 @@ import {
22
22
  IDeltaManager,
23
23
  IDeltaSender,
24
24
  IRuntime,
25
- ContainerWarning,
26
25
  ICriticalContainerError,
27
26
  AttachState,
28
27
  ILoaderOptions,
@@ -152,6 +151,7 @@ import {
152
151
  IDataStoreAliasMessage,
153
152
  isDataStoreAliasMessage,
154
153
  } from "./dataStore";
154
+ import { BindBatchTracker } from "./batchTracker";
155
155
 
156
156
  export enum ContainerMessageType {
157
157
  // An op to be delivered to store
@@ -329,15 +329,24 @@ export enum RuntimeHeaders {
329
329
  * have the untagged logger, so to accommodate that scenario the below interface is used. It can be removed once
330
330
  * its usage is removed from TaggedLoggerAdapter fallback.
331
331
  */
332
- interface OldContainerContextWithLogger extends IContainerContext {
332
+ interface OldContainerContextWithLogger extends Omit<IContainerContext, "taggedLogger"> {
333
333
  logger: ITelemetryBaseLogger;
334
+ taggedLogger: undefined;
334
335
  }
335
336
 
336
- // Local storage key to set the default flush mode to TurnBased
337
- const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
338
337
  const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
339
338
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
340
339
 
340
+ // Feature gate for the max op size. If the value is negative, chunking is enabled
341
+ // and all ops over 16k would be chunked. If the value is positive, all ops with
342
+ // a size strictly larger will be rejected and the container closed with an error.
343
+ const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
344
+
345
+ // By default, we should reject any op larger than 768KB,
346
+ // in order to account for some extra overhead from serialization
347
+ // to not reach the 1MB limits in socket.io and Kafka.
348
+ const defaultMaxOpSizeInBytes = 768000;
349
+
341
350
  export enum RuntimeMessage {
342
351
  FluidDataStoreOp = "component",
343
352
  Attach = "attach",
@@ -687,8 +696,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
687
696
  ): Promise<ContainerRuntime> {
688
697
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
689
698
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
690
- const passLogger = context.taggedLogger ?? new TaggedLoggerAdapter((context as
691
- OldContainerContextWithLogger).logger);
699
+ const backCompatContext: IContainerContext | OldContainerContextWithLogger = context;
700
+ const passLogger = backCompatContext.taggedLogger ??
701
+ new TaggedLoggerAdapter((backCompatContext as OldContainerContextWithLogger).logger);
692
702
  const logger = ChildLogger.create(passLogger, undefined, {
693
703
  all: {
694
704
  runtimeVersion: pkgVersion,
@@ -770,7 +780,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
770
780
  if (loadSequenceNumberVerification !== "bypass" && runtimeSequenceNumber !== protocolSequenceNumber) {
771
781
  // "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
772
782
  const error = new DataCorruptionError(
773
- "SummaryMetadataMismatch",
783
+ "Summary metadata mismatch",
774
784
  { runtimeSequenceNumber, protocolSequenceNumber },
775
785
  );
776
786
 
@@ -892,12 +902,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
892
902
 
893
903
  private readonly summarizerNode: IRootSummarizerNodeWithGC;
894
904
  private readonly _aliasingEnabled: boolean;
905
+ private readonly _maxOpSizeInBytes: number;
895
906
 
896
907
  private readonly maxConsecutiveReconnects: number;
897
908
  private readonly defaultMaxConsecutiveReconnects = 15;
898
909
 
899
910
  private _orderSequentiallyCalls: number = 0;
900
- private _flushMode: FlushMode;
911
+ private _flushMode: FlushMode = FlushMode.TurnBased;
901
912
  private needsFlush = false;
902
913
  private flushTrigger = false;
903
914
 
@@ -933,8 +944,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
933
944
  private dirtyContainer: boolean;
934
945
  private emitDirtyDocumentEvent = true;
935
946
 
936
- private readonly summarizerWarning = (warning: ContainerWarning) =>
937
- this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
938
947
  /**
939
948
  * Summarizer is responsible for coordinating when to send generate and send summaries.
940
949
  * It is the main entry point for summary work.
@@ -1019,14 +1028,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1019
1028
  this.mc = loggerToMonitoringContext(
1020
1029
  ChildLogger.create(this.logger, "ContainerRuntime"));
1021
1030
 
1022
- this._flushMode =
1023
- this.mc.config.getBoolean(turnBasedFlushModeKey) ?? false
1024
- ? FlushMode.TurnBased : FlushMode.Immediate;
1025
-
1026
1031
  this._aliasingEnabled =
1027
1032
  (this.mc.config.getBoolean(useDataStoreAliasingKey) ?? false) ||
1028
1033
  (runtimeOptions.useDataStoreAliasing ?? false);
1029
1034
 
1035
+ this._maxOpSizeInBytes = (this.mc.config.getNumber(maxOpSizeInBytesKey) ?? defaultMaxOpSizeInBytes);
1030
1036
  this.maxConsecutiveReconnects =
1031
1037
  this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? this.defaultMaxConsecutiveReconnects;
1032
1038
 
@@ -1219,7 +1225,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1219
1225
  },
1220
1226
  this.runtimeOptions.summaryOptions.summarizerOptions,
1221
1227
  );
1222
- this.summaryManager.on("summarizerWarning", this.summarizerWarning);
1223
1228
  this.summaryManager.start();
1224
1229
  }
1225
1230
  }
@@ -1269,6 +1274,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1269
1274
  });
1270
1275
 
1271
1276
  ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
1277
+ BindBatchTracker(this, this.logger);
1272
1278
  }
1273
1279
 
1274
1280
  public dispose(error?: Error): void {
@@ -1285,7 +1291,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1285
1291
  }, error);
1286
1292
 
1287
1293
  if (this.summaryManager !== undefined) {
1288
- this.summaryManager.off("summarizerWarning", this.summarizerWarning);
1289
1294
  this.summaryManager.dispose();
1290
1295
  }
1291
1296
  this.garbageCollector.dispose();
@@ -1477,7 +1482,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1477
1482
  if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
1478
1483
  // If we're halfway through the max reconnects, send an event in order
1479
1484
  // to better identify false positives, if any. If the rate of this event
1480
- // matches `MaxReconnectsWithNoProgress`, we can safely cut down
1485
+ // matches Container Close count below, we can safely cut down
1481
1486
  // maxConsecutiveReconnects to half.
1482
1487
  this.mc.logger.sendTelemetryEvent({
1483
1488
  eventName: "ReconnectsWithNoProgress",
@@ -1574,7 +1579,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1574
1579
  this.context.pendingLocalState = undefined;
1575
1580
  if (!this.shouldContinueReconnecting()) {
1576
1581
  this.closeFn(new GenericError(
1577
- "MaxReconnectsWithNoProgress",
1582
+ "Runtime detected too many reconnects with no progress syncing local ops",
1578
1583
  undefined, // error
1579
1584
  { attempts: this.consecutiveReconnects }));
1580
1585
  return;
@@ -1757,7 +1762,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1757
1762
  this._orderSequentiallyCalls++;
1758
1763
  callback();
1759
1764
  } catch (error) {
1760
- this.closeFn(new GenericError("orderSequentiallyCallbackException", error));
1765
+ this.closeFn(new GenericError("orderSequentially callback exception", error));
1761
1766
  throw error; // throw the original error for the consumer of the runtime
1762
1767
  } finally {
1763
1768
  this._orderSequentiallyCalls--;
@@ -2137,6 +2142,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2137
2142
  const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
2138
2143
  const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
2139
2144
 
2145
+ // We should be here is we haven't processed be here. If we are of if the last message's sequence number
2146
+ // doesn't match the last processed sequence number, log an error.
2147
+ if (summaryRefSeqNum !== this.deltaManager.lastMessage?.sequenceNumber) {
2148
+ summaryLogger.sendErrorEvent({
2149
+ eventName: "LastSequenceMismatch",
2150
+ message,
2151
+ });
2152
+ }
2153
+
2140
2154
  this.summarizerNode.startSummary(summaryRefSeqNum, summaryLogger);
2141
2155
 
2142
2156
  // Helper function to check whether we should still continue between each async step.
@@ -2406,18 +2420,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2406
2420
  }
2407
2421
  }
2408
2422
 
2409
- // Note: Chunking will increase content beyond maxOpSize because we JSON'ing JSON payload -
2410
- // there will be a lot of escape characters that can make it up to 2x bigger!
2411
- // This is Ok, because DeltaManager.shouldSplit() will have 2 * maxMessageSize limit
2412
- if (!serializedContent || serializedContent.length <= maxOpSize) {
2413
- clientSequenceNumber = this.submitRuntimeMessage(
2414
- type,
2415
- content,
2416
- /* batch: */ this._flushMode === FlushMode.TurnBased,
2417
- opMetadataInternal);
2418
- } else {
2419
- clientSequenceNumber = this.submitChunkedMessage(type, serializedContent, maxOpSize);
2420
- }
2423
+ clientSequenceNumber = this.submitMaybeChunkedMessages(
2424
+ type,
2425
+ content,
2426
+ serializedContent,
2427
+ maxOpSize,
2428
+ this._flushMode === FlushMode.TurnBased,
2429
+ opMetadataInternal);
2421
2430
  }
2422
2431
 
2423
2432
  // Let the PendingStateManager know that a message was submitted.
@@ -2434,6 +2443,48 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2434
2443
  }
2435
2444
  }
2436
2445
 
2446
+ private submitMaybeChunkedMessages(
2447
+ type: ContainerMessageType,
2448
+ content: any,
2449
+ serializedContent: string,
2450
+ serverMaxOpSize: number,
2451
+ batch: boolean,
2452
+ opMetadataInternal: unknown = undefined,
2453
+ ): number {
2454
+ if (this._maxOpSizeInBytes >= 0) {
2455
+ // Chunking disabled
2456
+ if (!serializedContent || serializedContent.length <= this._maxOpSizeInBytes) {
2457
+ return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
2458
+ }
2459
+
2460
+ // When chunking is disabled, we ignore the server max message size
2461
+ // and if the content length is larger than the client configured message size
2462
+ // instead of splitting the content, we will fail by explicitly close the container
2463
+ this.closeFn(new GenericError(
2464
+ "OpTooLarge",
2465
+ /* error */ undefined,
2466
+ {
2467
+ length: {
2468
+ value: serializedContent.length,
2469
+ tag: TelemetryDataTag.PackageData,
2470
+ },
2471
+ limit: {
2472
+ value: this._maxOpSizeInBytes,
2473
+ tag: TelemetryDataTag.PackageData,
2474
+ },
2475
+ }));
2476
+ return -1;
2477
+ }
2478
+
2479
+ // Chunking enabled, fallback on the server's max message size
2480
+ // and split the content accordingly
2481
+ if (!serializedContent || serializedContent.length <= serverMaxOpSize) {
2482
+ return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
2483
+ }
2484
+
2485
+ return this.submitChunkedMessage(type, serializedContent, serverMaxOpSize);
2486
+ }
2487
+
2437
2488
  private submitChunkedMessage(type: ContainerMessageType, content: string, maxOpSize: number): number {
2438
2489
  const contentLength = content.length;
2439
2490
  const chunkN = Math.floor((contentLength - 1) / maxOpSize) + 1;
@@ -2549,6 +2600,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2549
2600
  summaryRefSeq,
2550
2601
  async () => this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
2551
2602
  eventName: "RefreshLatestSummaryGetSnapshot",
2603
+ ackHandle,
2604
+ summaryRefSeq,
2552
2605
  fetchLatest: false,
2553
2606
  }),
2554
2607
  readAndParseBlob,
@@ -66,7 +66,7 @@ import {
66
66
  TelemetryDataTag,
67
67
  ThresholdCounter,
68
68
  } from "@fluidframework/telemetry-utils";
69
- import { CreateProcessingError } from "@fluidframework/container-utils";
69
+ import { DataProcessingError } from "@fluidframework/container-utils";
70
70
 
71
71
  import { ContainerRuntime } from "./containerRuntime";
72
72
  import {
@@ -310,7 +310,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
310
310
  if (!this.channelDeferred) {
311
311
  this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
312
312
  this.realizeCore(this.existing).catch((error) => {
313
- const errorWrapped = CreateProcessingError(error, "realizeFluidDataStoreContext");
313
+ const errorWrapped = DataProcessingError.wrapIfUnrecognized(error, "realizeFluidDataStoreContext");
314
314
  errorWrapped.addTelemetryProperties({ fluidDataStoreId: { value: this.id, tag: "PackageData"} });
315
315
  this.channelDeferred?.reject(errorWrapped);
316
316
  this.logger.sendErrorEvent({ eventName: "RealizeError"}, errorWrapped);
package/src/dataStores.ts CHANGED
@@ -194,7 +194,7 @@ export class DataStores implements IDisposable {
194
194
  if (this.alreadyProcessed(attachMessage.id)) {
195
195
  // TODO: dataStoreId may require a different tag from PackageData #7488
196
196
  const error = new DataCorruptionError(
197
- "duplicateDataStoreCreatedWithExistingId",
197
+ "Duplicate DataStore created with existing id",
198
198
  {
199
199
  ...extractSafePropertiesFromMessage(message),
200
200
  dataStoreId: {
@@ -69,7 +69,7 @@ const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
69
69
  const runSessionExpiry = "Fluid.GarbageCollection.RunSessionExpiry";
70
70
 
71
71
  const defaultDeleteTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days
72
- const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days
72
+ export const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days
73
73
 
74
74
  /** The statistics of the system state after a garbage collection run. */
75
75
  export interface IGCStats {
@@ -545,17 +545,18 @@ export class GarbageCollector implements IGarbageCollector {
545
545
  // used in the container.
546
546
  if (this.shouldRunGC) {
547
547
  this.initializeBaseStateP.catch((error) => {
548
- throw new DataProcessingError(
549
- error?.message,
548
+ const dpe = DataProcessingError.wrapIfUnrecognized(
549
+ error,
550
550
  "FailedToInitializeGC",
551
- {
552
- gcEnabled: this.gcEnabled,
553
- runSweep: this.shouldRunSweep,
554
- writeAtRoot: this._writeDataAtRoot,
555
- testMode: this.testMode,
556
- sessionExpiry: this.sessionExpiryTimeoutMs,
557
- },
558
551
  );
552
+ dpe.addTelemetryProperties({
553
+ gcEnabled: this.gcEnabled,
554
+ runSweep: this.shouldRunSweep,
555
+ writeAtRoot: this._writeDataAtRoot,
556
+ testMode: this.testMode,
557
+ sessionExpiry: this.sessionExpiryTimeoutMs,
558
+ });
559
+ throw dpe;
559
560
  });
560
561
  }
561
562
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "0.57.1";
9
+ export const pkgVersion = "0.58.0-55561";
@@ -339,15 +339,11 @@ export class PendingStateManager implements IDisposable {
339
339
  // The clientSequenceNumber of the incoming message must match that of the pending message.
340
340
  if (pendingState.clientSequenceNumber !== message.clientSequenceNumber) {
341
341
  // Close the container because this could indicate data corruption.
342
- const error = new DataProcessingError(
342
+ const error = DataProcessingError.create(
343
+ "pending local message clientSequenceNumber mismatch",
343
344
  "unexpectedAckReceived",
344
- "unexpectedAckReceived",
345
- {
346
- clientId: message.clientId,
347
- sequenceNumber: message.sequenceNumber,
348
- clientSequenceNumber: message.clientSequenceNumber,
349
- expectedClientSequenceNumber: pendingState.clientSequenceNumber,
350
- },
345
+ message,
346
+ { expectedClientSequenceNumber: pendingState.clientSequenceNumber },
351
347
  );
352
348
 
353
349
  this.containerRuntime.closeFn(error);
@@ -51,7 +51,7 @@ export class RunningSummarizer implements IDisposable {
51
51
  configuration: ISummaryConfiguration,
52
52
  submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
53
53
  heuristicData: ISummarizeHeuristicData,
54
- raiseSummarizingError: (errorCode: string) => void,
54
+ raiseSummarizingError: (errorMessage: string) => void,
55
55
  summaryCollection: SummaryCollection,
56
56
  cancellationToken: ISummaryCancellationToken,
57
57
  stopSummarizerCallback: (reason: SummarizerStopReason) => void,
@@ -101,7 +101,7 @@ export class RunningSummarizer implements IDisposable {
101
101
  private readonly configuration: ISummaryConfiguration,
102
102
  private readonly submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
103
103
  private readonly heuristicData: ISummarizeHeuristicData,
104
- private readonly raiseSummarizingError: (errorCode: string) => void,
104
+ private readonly raiseSummarizingError: (errorMessage: string) => void,
105
105
  private readonly summaryCollection: SummaryCollection,
106
106
  private readonly cancellationToken: ISummaryCancellationToken,
107
107
  private readonly stopSummarizerCallback: (reason: SummarizerStopReason) => void,
@@ -131,7 +131,7 @@ export class RunningSummarizer implements IDisposable {
131
131
  this.pendingAckTimer = new PromiseTimer(
132
132
  maxAckWaitTime,
133
133
  () => {
134
- this.raiseSummarizingError("summaryAckWaitTimeout");
134
+ this.raiseSummarizingError("Pending summary ack not received in time");
135
135
  // Note: summarizeCount (from ChildLogger definition) may be 0,
136
136
  // since this code path is hit when RunningSummarizer first starts up,
137
137
  // before this instance has kicked off a new summarize run.
package/src/summarizer.ts CHANGED
@@ -46,20 +46,19 @@ export class SummarizingWarning extends LoggingError implements ISummarizingWarn
46
46
 
47
47
  constructor(
48
48
  errorMessage: string,
49
- readonly fluidErrorCode: string,
50
49
  readonly logged: boolean = false,
51
50
  ) {
52
51
  super(errorMessage);
53
52
  }
54
53
 
55
- static wrap(error: any, errorCode: string, logged: boolean = false, logger: ITelemetryLogger) {
56
- const newErrorFn = (errMsg: string) => new SummarizingWarning(errMsg, errorCode, logged);
54
+ static wrap(error: any, logged: boolean = false, logger: ITelemetryLogger) {
55
+ const newErrorFn = (errMsg: string) => new SummarizingWarning(errMsg, logged);
57
56
  return wrapErrorAndLog<SummarizingWarning>(error, newErrorFn, logger);
58
57
  }
59
58
  }
60
59
 
61
60
  export const createSummarizingWarning =
62
- (errorCode: string, logged: boolean) => new SummarizingWarning(errorCode, errorCode, logged);
61
+ (errorMessage: string, logged: boolean) => new SummarizingWarning(errorMessage, logged);
63
62
 
64
63
  /**
65
64
  * Summarizer is responsible for coordinating when to generate and send summaries.
@@ -144,7 +143,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
144
143
  return await this.runCore(onBehalfOf, options);
145
144
  } catch (error) {
146
145
  this.stop("summarizerException");
147
- throw SummarizingWarning.wrap(error, "summarizerRun", false /* logged */, this.logger);
146
+ throw SummarizingWarning.wrap(error, false /* logged */, this.logger);
148
147
  } finally {
149
148
  this.dispose();
150
149
  this.runtime.closeFn();
@@ -257,9 +256,10 @@ export class Summarizer extends EventEmitter implements ISummarizer {
257
256
  summaryTime: Date.now(),
258
257
  } as const,
259
258
  ),
260
- (errorCode: string) => {
259
+ (errorMessage: string) => {
261
260
  if (!this._disposed) {
262
- this.emit("summarizingError", createSummarizingWarning(errorCode, true));
261
+ this.logger.sendErrorEvent({ eventName: "summarizingError" },
262
+ createSummarizingWarning(errorMessage, true));
263
263
  }
264
264
  },
265
265
  this.summaryCollection,
@@ -356,7 +356,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
356
356
  return builder.build();
357
357
  }
358
358
  catch (error) {
359
- throw SummarizingWarning.wrap(error, "summarizerRun", false /* logged */, this.logger);
359
+ throw SummarizingWarning.wrap(error, false /* logged */, this.logger);
360
360
  }
361
361
  };
362
362
 
@@ -152,7 +152,7 @@ export class SummaryGenerator {
152
152
  private readonly pendingAckTimer: IPromiseTimer,
153
153
  private readonly heuristicData: ISummarizeHeuristicData,
154
154
  private readonly submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
155
- private readonly raiseSummarizingError: (errorCode: string) => void,
155
+ private readonly raiseSummarizingError: (errorMessage: string) => void,
156
156
  private readonly successfulSummaryCallback: () => void,
157
157
  private readonly summaryWatcher: Pick<IClientSummaryWatcher, "watchSummary">,
158
158
  private readonly logger: ITelemetryLogger,
@@ -394,7 +394,7 @@ export class SummaryGenerator {
394
394
  const message = summaryNack?.message;
395
395
  const retryAfterSeconds = summaryNack?.retryAfter;
396
396
 
397
- const error = new LoggingError(`summaryNack: ${message}`, { retryAfterSeconds });
397
+ const error = new LoggingError(`Received summaryNack: ${message}`, { retryAfterSeconds });
398
398
  logger.sendErrorEvent(
399
399
  { eventName: "SummaryNack", ...summarizeTelemetryProps, retryAfterSeconds }, error);
400
400
 
@@ -4,16 +4,14 @@
4
4
  */
5
5
 
6
6
  import { IDisposable, IEvent, IEventProvider, ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { TypedEventEmitter, assert } from "@fluidframework/common-utils";
7
+ import { assert } from "@fluidframework/common-utils";
8
8
  import { ChildLogger, PerformanceEvent } from "@fluidframework/telemetry-utils";
9
9
  import { DriverErrorType } from "@fluidframework/driver-definitions";
10
- import { createSummarizingWarning } from "./summarizer";
11
10
  import { ISummarizerClientElection } from "./summarizerClientElection";
12
11
  import { IThrottler } from "./throttler";
13
12
  import {
14
13
  ISummarizer,
15
14
  ISummarizerOptions,
16
- ISummarizingWarning,
17
15
  SummarizerStopReason,
18
16
  } from "./summarizerTypes";
19
17
  import { SummaryCollection } from "./summaryCollection";
@@ -60,10 +58,6 @@ export interface IConnectedState extends IEventProvider<IConnectedEvents> {
60
58
  readonly clientId: string | undefined;
61
59
  }
62
60
 
63
- export interface ISummaryManagerEvents extends IEvent {
64
- (event: "summarizerWarning", listener: (warning: ISummarizingWarning) => void);
65
- }
66
-
67
61
  export interface ISummaryManagerConfig {
68
62
  initialDelayMs: number;
69
63
  opsToBypassInitialDelay: number;
@@ -74,7 +68,7 @@ export interface ISummaryManagerConfig {
74
68
  * It observes changes in calculated summarizer and reacts to changes by either creating summarizer client or
75
69
  * stopping existing summarizer client.
76
70
  */
77
- export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> implements IDisposable {
71
+ export class SummaryManager implements IDisposable {
78
72
  private readonly logger: ITelemetryLogger;
79
73
  private readonly opsToBypassInitialDelay: number;
80
74
  private readonly initialDelayMs: number;
@@ -105,7 +99,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
105
99
  }: Readonly<Partial<ISummaryManagerConfig>> = {},
106
100
  private readonly summarizerOptions?: Readonly<Partial<ISummarizerOptions>>,
107
101
  ) {
108
- super();
109
102
 
110
103
  this.logger = ChildLogger.create(
111
104
  parentLogger,
@@ -219,8 +212,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
219
212
  assert(this.state === SummaryManagerState.Starting, 0x263 /* "Expected: starting" */);
220
213
  this.state = SummaryManagerState.Running;
221
214
 
222
- summarizer.on("summarizingError",
223
- (warning: ISummarizingWarning) => this.emit("summarizerWarning", warning));
224
215
  this.summarizer = summarizer;
225
216
 
226
217
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -238,8 +229,8 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
238
229
  // we ignore blindly, so try to narrow signature we are looking for - skip logging
239
230
  // error only if this client should no longer be a summarizer (which in practice
240
231
  // means it also lost connection), and error happened on load (we do not have summarizer).
241
- // We could add error.fluidErrorCode !== "containerClosedWithoutErrorDuringLoad" check to narrow it down,
242
- // but that does not seem to be necessary.
232
+ // We could annotate the error raised in Container.load where the container closed during load with no error
233
+ // and check for that case here, but that does not seem to be necessary.
243
234
  if (this.getShouldSummarizeState().shouldSummarize || this.summarizer !== undefined) {
244
235
  // Report any failure as an error unless it was due to cancellation (like "disconnected" error)
245
236
  // If failure happened on container load, we may not yet realized that socket disconnected, so check
@@ -251,7 +242,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
251
242
  category,
252
243
  },
253
244
  error);
254
- this.emit("summarizerWarning", error);
255
245
 
256
246
  // Note that summarizer may keep going (like doing last summary).
257
247
  // Ideally we await stopping process, but this code path is due to a bug
@@ -296,12 +286,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
296
286
  private async delayBeforeCreatingSummarizer(): Promise<boolean> {
297
287
  // throttle creation of new summarizer containers to prevent spamming the server with websocket connections
298
288
  let delayMs = this.startThrottler.getDelay();
299
- if (delayMs > 0 && delayMs > this.startThrottler.maxDelayMs) {
300
- this.emit(
301
- "summarizerWarning",
302
- createSummarizingWarning("summaryManagerCreateSummarizerMaxThrottleDelay", false),
303
- );
304
- }
305
289
 
306
290
  // We have been elected the summarizer. Some day we may be able to summarize with a live document but for
307
291
  // now we play it safe and launch a second copy.
@@ -309,6 +293,7 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
309
293
  eventName: "CreatingSummarizer",
310
294
  throttlerDelay: delayMs,
311
295
  initialDelay: this.initialDelayMs,
296
+ startThrottlerMaxDelayMs: this.startThrottler.maxDelayMs,
312
297
  opsSinceLastAck: this.summaryCollection.opsSinceLastAck,
313
298
  opsToBypassInitialDelay: this.opsToBypassInitialDelay,
314
299
  });