@fluidframework/container-runtime 0.48.5 → 0.49.1

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 (65) hide show
  1. package/dist/containerRuntime.d.ts +1 -7
  2. package/dist/containerRuntime.d.ts.map +1 -1
  3. package/dist/containerRuntime.js +3 -13
  4. package/dist/containerRuntime.js.map +1 -1
  5. package/dist/packageVersion.d.ts +1 -1
  6. package/dist/packageVersion.js +1 -1
  7. package/dist/packageVersion.js.map +1 -1
  8. package/dist/runningSummarizer.d.ts.map +1 -1
  9. package/dist/runningSummarizer.js +8 -3
  10. package/dist/runningSummarizer.js.map +1 -1
  11. package/dist/summarizer.d.ts.map +1 -1
  12. package/dist/summarizer.js +1 -1
  13. package/dist/summarizer.js.map +1 -1
  14. package/dist/summarizerTypes.d.ts +11 -7
  15. package/dist/summarizerTypes.d.ts.map +1 -1
  16. package/dist/summarizerTypes.js.map +1 -1
  17. package/dist/summaryCollection.d.ts +2 -0
  18. package/dist/summaryCollection.d.ts.map +1 -1
  19. package/dist/summaryCollection.js +6 -0
  20. package/dist/summaryCollection.js.map +1 -1
  21. package/dist/summaryGenerator.d.ts +5 -5
  22. package/dist/summaryGenerator.d.ts.map +1 -1
  23. package/dist/summaryGenerator.js +5 -6
  24. package/dist/summaryGenerator.js.map +1 -1
  25. package/dist/summaryManager.d.ts +1 -1
  26. package/dist/summaryManager.d.ts.map +1 -1
  27. package/dist/summaryManager.js +23 -12
  28. package/dist/summaryManager.js.map +1 -1
  29. package/lib/containerRuntime.d.ts +1 -7
  30. package/lib/containerRuntime.d.ts.map +1 -1
  31. package/lib/containerRuntime.js +3 -13
  32. package/lib/containerRuntime.js.map +1 -1
  33. package/lib/packageVersion.d.ts +1 -1
  34. package/lib/packageVersion.js +1 -1
  35. package/lib/packageVersion.js.map +1 -1
  36. package/lib/runningSummarizer.d.ts.map +1 -1
  37. package/lib/runningSummarizer.js +8 -3
  38. package/lib/runningSummarizer.js.map +1 -1
  39. package/lib/summarizer.d.ts.map +1 -1
  40. package/lib/summarizer.js +1 -1
  41. package/lib/summarizer.js.map +1 -1
  42. package/lib/summarizerTypes.d.ts +11 -7
  43. package/lib/summarizerTypes.d.ts.map +1 -1
  44. package/lib/summarizerTypes.js.map +1 -1
  45. package/lib/summaryCollection.d.ts +2 -0
  46. package/lib/summaryCollection.d.ts.map +1 -1
  47. package/lib/summaryCollection.js +6 -0
  48. package/lib/summaryCollection.js.map +1 -1
  49. package/lib/summaryGenerator.d.ts +5 -5
  50. package/lib/summaryGenerator.d.ts.map +1 -1
  51. package/lib/summaryGenerator.js +6 -7
  52. package/lib/summaryGenerator.js.map +1 -1
  53. package/lib/summaryManager.d.ts +1 -1
  54. package/lib/summaryManager.d.ts.map +1 -1
  55. package/lib/summaryManager.js +24 -13
  56. package/lib/summaryManager.js.map +1 -1
  57. package/package.json +15 -15
  58. package/src/containerRuntime.ts +3 -12
  59. package/src/packageVersion.ts +1 -1
  60. package/src/runningSummarizer.ts +11 -3
  61. package/src/summarizer.ts +1 -0
  62. package/src/summarizerTypes.ts +12 -6
  63. package/src/summaryCollection.ts +8 -0
  64. package/src/summaryGenerator.ts +18 -11
  65. package/src/summaryManager.ts +25 -13
@@ -1571,15 +1571,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1571
1571
  this.context.raiseContainerWarning(warning);
1572
1572
  };
1573
1573
 
1574
- /**
1575
- * @deprecated - // back-compat: marked deprecated in 0.35
1576
- * Returns true of document is dirty, i.e. there are some pending local changes that
1577
- * either were not sent out to delta stream or were not yet acknowledged.
1578
- */
1579
- public isDocumentDirty(): boolean {
1580
- return this.dirtyContainer;
1581
- }
1582
-
1583
1574
  /**
1584
1575
  * Returns true of container is dirty, i.e. there are some pending local changes that
1585
1576
  * either were not sent out to delta stream or were not yet acknowledged.
@@ -1998,9 +1989,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1998
1989
 
1999
1990
  this.dirtyContainer = dirty;
2000
1991
  if (this.emitDirtyDocumentEvent) {
2001
- // back-compat: dirtyDocument & savedDocument deprecated in 0.35.
2002
- this.emit(dirty ? "dirtyDocument" : "savedDocument");
2003
-
2004
1992
  this.emit(dirty ? "dirty" : "saved");
2005
1993
  // back-compat: Loader API added in 0.35 only
2006
1994
  if (this.context.updateDirtyContainerState !== undefined) {
@@ -2194,11 +2182,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2194
2182
  public async refreshLatestSummaryAck(
2195
2183
  proposalHandle: string | undefined,
2196
2184
  ackHandle: string,
2185
+ summaryRefSeq: number,
2197
2186
  summaryLogger: ITelemetryLogger,
2198
2187
  ) {
2199
2188
  const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
2200
2189
  const result = await this.summarizerNode.refreshLatestSummary(
2201
2190
  proposalHandle,
2191
+ summaryRefSeq,
2202
2192
  async () => this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
2203
2193
  eventName: "RefreshLatestSummaryGetSnapshot",
2204
2194
  fetchLatest: false,
@@ -2238,6 +2228,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2238
2228
 
2239
2229
  const result = await this.summarizerNode.refreshLatestSummary(
2240
2230
  undefined,
2231
+ snapshotRefSeq,
2241
2232
  async () => snapshot,
2242
2233
  readAndParseBlob,
2243
2234
  summaryLogger,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "0.48.5";
9
+ export const pkgVersion = "0.49.1";
@@ -346,6 +346,8 @@ export class RunningSummarizer implements IDisposable {
346
346
  let totalAttempts = 0;
347
347
  let attemptPerPhase = 0;
348
348
 
349
+ let lastResult: { message: string; error: any; } | undefined;
350
+
349
351
  for (let attemptPhase = 0; attemptPhase < attempts.length;) {
350
352
  if (this.cancellationToken.cancelled) {
351
353
  return;
@@ -378,7 +380,6 @@ export class RunningSummarizer implements IDisposable {
378
380
  const result = await resultSummarize.receivedSummaryAckOrNack;
379
381
 
380
382
  if (result.success) {
381
- assert(result.data.summaryAckNackOp.type === MessageType.SummaryAck, 0x25c /* "not nack" */);
382
383
  return;
383
384
  }
384
385
  // Check for retryDelay that can come from summaryNack or upload summary flow.
@@ -388,9 +389,16 @@ export class RunningSummarizer implements IDisposable {
388
389
  attemptPhase++;
389
390
  attemptPerPhase = 0;
390
391
  }
392
+ lastResult = result;
391
393
  }
392
- // If all attempts failed, close the summarizer container
393
- this.logger.sendErrorEvent({ eventName: "FailToSummarize", summarizeReason });
394
+
395
+ // If all attempts failed, log error (with last attempt info) and close the summarizer container
396
+ this.logger.sendErrorEvent({
397
+ eventName: "FailToSummarize",
398
+ summarizeReason,
399
+ message: lastResult?.message,
400
+ }, lastResult?.error);
401
+
394
402
  this.stopSummarizerCallback("failToSummarize");
395
403
  }).catch((error) => {
396
404
  this.logger.sendErrorEvent({ eventName: "UnexpectedSummarizeError" }, error);
package/src/summarizer.ts CHANGED
@@ -277,6 +277,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
277
277
  await this.internalsProvider.refreshLatestSummaryAck(
278
278
  ack.summaryOp.contents.handle,
279
279
  ack.summaryAck.contents.handle,
280
+ refSequenceNumber,
280
281
  summaryLogger,
281
282
  );
282
283
  } catch (error) {
@@ -57,6 +57,7 @@ export interface ISummarizerInternalsProvider {
57
57
  refreshLatestSummaryAck(
58
58
  proposalHandle: string,
59
59
  ackHandle: string,
60
+ summaryRefSeq: number,
60
61
  summaryLogger: ITelemetryLogger,
61
62
  ): Promise<void>;
62
63
  }
@@ -196,17 +197,22 @@ export interface IBroadcastSummaryResult {
196
197
  readonly broadcastDuration: number;
197
198
  }
198
199
 
199
- export interface IAckNackSummaryResult {
200
- readonly summaryAckNackOp: ISummaryAckMessage | ISummaryNackMessage;
200
+ export interface IAckSummaryResult {
201
+ readonly summaryAckOp: ISummaryAckMessage;
201
202
  readonly ackNackDuration: number;
202
203
  }
203
204
 
204
- export type SummarizeResultPart<T> = {
205
+ export interface INackSummaryResult {
206
+ readonly summaryNackOp: ISummaryNackMessage;
207
+ readonly ackNackDuration: number;
208
+ }
209
+
210
+ export type SummarizeResultPart<TSuccess, TFailure = undefined> = {
205
211
  success: true;
206
- data: T;
212
+ data: TSuccess;
207
213
  } | {
208
214
  success: false;
209
- data: T | undefined;
215
+ data: TFailure | undefined;
210
216
  message: string;
211
217
  error: any;
212
218
  retryAfterSeconds?: number;
@@ -218,7 +224,7 @@ export interface ISummarizeResults {
218
224
  /** Resolves when we observe our summarize op broadcast. */
219
225
  readonly summaryOpBroadcasted: Promise<SummarizeResultPart<IBroadcastSummaryResult>>;
220
226
  /** Resolves when we receive a summaryAck or summaryNack. */
221
- readonly receivedSummaryAckOrNack: Promise<SummarizeResultPart<IAckNackSummaryResult>>;
227
+ readonly receivedSummaryAckOrNack: Promise<SummarizeResultPart<IAckSummaryResult, INackSummaryResult>>;
222
228
  }
223
229
 
224
230
  export type OnDemandSummarizeResult = (ISummarizeResults & {
@@ -226,6 +226,14 @@ export class SummaryCollection extends TypedEventEmitter<ISummaryCollectionOpEve
226
226
  (this.lastAck?.summaryAck.sequenceNumber ?? this.deltaManager.initialSequenceNumber);
227
227
  }
228
228
 
229
+ public addOpListener(listener: () => void) {
230
+ this.deltaManager.on("op", listener);
231
+ }
232
+
233
+ public removeOpListener(listener: () => void) {
234
+ this.deltaManager.off("op", listener);
235
+ }
236
+
229
237
  public constructor(
230
238
  private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
231
239
  private readonly logger: ITelemetryLogger,
@@ -4,12 +4,19 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
7
- import { assert, Deferred, IPromiseTimer, IPromiseTimerResult, Timer } from "@fluidframework/common-utils";
7
+ import {
8
+ assert,
9
+ Deferred,
10
+ IPromiseTimer,
11
+ IPromiseTimerResult,
12
+ Timer,
13
+ } from "@fluidframework/common-utils";
8
14
  import { MessageType } from "@fluidframework/protocol-definitions";
9
15
  import { PerformanceEvent, LoggingError, ChildLogger } from "@fluidframework/telemetry-utils";
10
16
  import { getRetryDelaySecondsFromError } from "@fluidframework/driver-utils";
11
17
  import {
12
- IAckNackSummaryResult,
18
+ IAckSummaryResult,
19
+ INackSummaryResult,
13
20
  ISummarizeOptions,
14
21
  IBroadcastSummaryResult,
15
22
  ISummarizeResults,
@@ -118,9 +125,10 @@ const summarizeErrors = {
118
125
  export class SummarizeResultBuilder {
119
126
  public readonly summarySubmitted = new Deferred<SummarizeResultPart<SubmitSummaryResult>>();
120
127
  public readonly summaryOpBroadcasted = new Deferred<SummarizeResultPart<IBroadcastSummaryResult>>();
121
- public readonly receivedSummaryAckOrNack = new Deferred<SummarizeResultPart<IAckNackSummaryResult>>();
128
+ public readonly receivedSummaryAckOrNack =
129
+ new Deferred<SummarizeResultPart<IAckSummaryResult, INackSummaryResult>>();
122
130
 
123
- public fail(message: string, error: any, nackSummaryResult?: IAckNackSummaryResult, retryAfterSeconds?: number) {
131
+ public fail(message: string, error: any, nackSummaryResult?: INackSummaryResult, retryAfterSeconds?: number) {
124
132
  assert(!this.receivedSummaryAckOrNack.isCompleted,
125
133
  0x25e /* "no reason to call fail if all promises have been completed" */);
126
134
 
@@ -203,7 +211,7 @@ export class SummaryGenerator {
203
211
  errorCode: keyof typeof summarizeErrors,
204
212
  error?: any,
205
213
  properties?: ITelemetryProperties,
206
- nackSummaryResult?: IAckNackSummaryResult,
214
+ nackSummaryResult?: INackSummaryResult,
207
215
  ) => {
208
216
  this.raiseSummarizingError(summarizeErrors[errorCode]);
209
217
  // UploadSummary may fail with 429 and retryAfter - respect that
@@ -327,16 +335,15 @@ export class SummaryGenerator {
327
335
  this.heuristicData.markLastAttemptAsSuccessful();
328
336
  summarizeEvent.end({ ...telemetryProps, handle: ackNackOp.contents.handle, message: "summaryAck" });
329
337
  resultsBuilder.receivedSummaryAckOrNack.resolve({ success: true, data: {
330
- summaryAckNackOp: ackNackOp,
338
+ summaryAckOp: ackNackOp,
331
339
  ackNackDuration,
332
340
  }});
333
341
  } else {
334
342
  // Check for retryDelay in summaryNack response.
335
343
  // back-compat: cast needed until dep on protocol-definitions version bump
336
- const summaryNack = ackNackOp.type === MessageType.SummaryNack
337
- ? ackNackOp.contents as { message?: string; retryAfter?: number; }
338
- : undefined;
339
- const message = (ackNackOp.contents as { message?: string }).message ?? ackNackOp.contents.errorMessage;
344
+ assert(ackNackOp.type === MessageType.SummaryNack, 0x274 /* "type check" */);
345
+ const summaryNack = ackNackOp.contents as { message?: string; retryAfter?: number; };
346
+ const message = summaryNack.message ?? ackNackOp.contents.errorMessage;
340
347
  const retryAfterSeconds = summaryNack?.retryAfter;
341
348
 
342
349
  const error = new LoggingError(`summaryNack: ${message}`, { retryAfterSeconds });
@@ -348,7 +355,7 @@ export class SummaryGenerator {
348
355
  "summaryNack",
349
356
  error,
350
357
  { ...telemetryProps, nackRetryAfter: retryAfterSeconds },
351
- { summaryAckNackOp: ackNackOp, ackNackDuration },
358
+ { summaryNackOp: ackNackOp, ackNackDuration },
352
359
  );
353
360
  }
354
361
  } finally {
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { IDisposable, IEvent, IEventProvider, ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { delay, TypedEventEmitter, assert } from "@fluidframework/common-utils";
7
+ import { TypedEventEmitter, assert } from "@fluidframework/common-utils";
8
8
  import { ChildLogger, PerformanceEvent } from "@fluidframework/telemetry-utils";
9
9
  import { IFluidRouter, IRequest } from "@fluidframework/core-interfaces";
10
10
  import { IDeltaManager, LoaderHeader } from "@fluidframework/container-definitions";
@@ -90,7 +90,8 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
90
90
  constructor(
91
91
  private readonly clientElection: ISummarizerClientElection,
92
92
  private readonly connectedState: IConnectedState,
93
- private readonly summaryCollection: Pick<SummaryCollection, "opsSinceLastAck">,
93
+ private readonly summaryCollection:
94
+ Pick<SummaryCollection, "opsSinceLastAck" | "addOpListener" | "removeOpListener">,
94
95
  parentLogger: ITelemetryLogger,
95
96
  /** Creates summarizer by asking interactive container to spawn summarizing container and
96
97
  * get back its Summarizer instance. */
@@ -265,15 +266,9 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
265
266
  0x265 /* "Expected: Starting or Running" */);
266
267
  this.state = SummaryManagerState.Stopping;
267
268
 
268
- if (this.summarizer !== undefined) {
269
- // Stopping the running summarizer client should trigger a change
270
- // in states when the running summarizer closes
271
- this.summarizer.stop(reason);
272
- } else {
273
- // Should not be possible to hit this case
274
- this.logger.sendErrorEvent({ eventName: "StopCalledWithoutRunningSummarizer", reason });
275
- this.state = SummaryManagerState.Off;
276
- }
269
+ // Stopping the running summarizer client should trigger a change
270
+ // in states when the running summarizer closes
271
+ this.summarizer?.stop(reason);
277
272
  }
278
273
 
279
274
  /**
@@ -284,7 +279,7 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
284
279
  private async delayBeforeCreatingSummarizer(): Promise<boolean> {
285
280
  // throttle creation of new summarizer containers to prevent spamming the server with websocket connections
286
281
  let delayMs = this.startThrottler.getDelay();
287
- if (delayMs > 0 && delayMs >= this.startThrottler.maxDelayMs) {
282
+ if (delayMs > 0 && delayMs > this.startThrottler.maxDelayMs) {
288
283
  this.emit(
289
284
  "summarizerWarning",
290
285
  createSummarizingWarning("summaryManagerCreateSummarizerMaxThrottleDelay", false),
@@ -317,7 +312,24 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
317
312
  }
318
313
 
319
314
  if (delayMs > 0) {
320
- await delay(delayMs);
315
+ let timer;
316
+ let resolveOpPromiseFn;
317
+ // Create a listener that will break the delay if we've exceeded the initial delay ops count.
318
+ const opsListenerFn = () => {
319
+ if (this.summaryCollection.opsSinceLastAck >= this.opsToBypassInitialDelay) {
320
+ clearTimeout(timer);
321
+ resolveOpPromiseFn();
322
+ }
323
+ };
324
+ // Create a Promise that will resolve when the delay expires.
325
+ const delayPromise = new Promise<void>((resolve) => {
326
+ timer = setTimeout(() => resolve(), delayMs);
327
+ });
328
+ // Create a Promise that will resolve if the ops count passes the threshold.
329
+ const opPromise = new Promise<void>((resolve) => { resolveOpPromiseFn = resolve; });
330
+ this.summaryCollection.addOpListener(opsListenerFn);
331
+ await Promise.race([ delayPromise, opPromise ]);
332
+ this.summaryCollection.removeOpListener(opsListenerFn);
321
333
  }
322
334
  return startWithInitialDelay;
323
335
  }