@fluidframework/container-runtime 0.56.0 → 0.57.0-51086

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 (97) hide show
  1. package/dist/blobManager.d.ts.map +1 -1
  2. package/dist/blobManager.js +9 -1
  3. package/dist/blobManager.js.map +1 -1
  4. package/dist/connectionTelemetry.d.ts.map +1 -1
  5. package/dist/connectionTelemetry.js +6 -6
  6. package/dist/connectionTelemetry.js.map +1 -1
  7. package/dist/containerRuntime.d.ts +65 -25
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +149 -79
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/dataStore.d.ts +62 -0
  12. package/dist/dataStore.d.ts.map +1 -0
  13. package/dist/dataStore.js +135 -0
  14. package/dist/dataStore.js.map +1 -0
  15. package/dist/dataStoreContext.js.map +1 -1
  16. package/dist/dataStores.d.ts +9 -5
  17. package/dist/dataStores.d.ts.map +1 -1
  18. package/dist/dataStores.js +14 -19
  19. package/dist/dataStores.js.map +1 -1
  20. package/dist/garbageCollection.d.ts +47 -21
  21. package/dist/garbageCollection.d.ts.map +1 -1
  22. package/dist/garbageCollection.js +195 -61
  23. package/dist/garbageCollection.js.map +1 -1
  24. package/dist/index.d.ts +3 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +4 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/packageVersion.d.ts +1 -1
  29. package/dist/packageVersion.d.ts.map +1 -1
  30. package/dist/packageVersion.js +1 -1
  31. package/dist/packageVersion.js.map +1 -1
  32. package/dist/runningSummarizer.d.ts +1 -0
  33. package/dist/runningSummarizer.d.ts.map +1 -1
  34. package/dist/runningSummarizer.js +23 -15
  35. package/dist/runningSummarizer.js.map +1 -1
  36. package/dist/summarizerTypes.d.ts +6 -6
  37. package/dist/summarizerTypes.d.ts.map +1 -1
  38. package/dist/summarizerTypes.js.map +1 -1
  39. package/dist/summaryGenerator.d.ts +2 -1
  40. package/dist/summaryGenerator.d.ts.map +1 -1
  41. package/dist/summaryGenerator.js +46 -28
  42. package/dist/summaryGenerator.js.map +1 -1
  43. package/lib/blobManager.d.ts.map +1 -1
  44. package/lib/blobManager.js +9 -1
  45. package/lib/blobManager.js.map +1 -1
  46. package/lib/connectionTelemetry.d.ts.map +1 -1
  47. package/lib/connectionTelemetry.js +6 -6
  48. package/lib/connectionTelemetry.js.map +1 -1
  49. package/lib/containerRuntime.d.ts +65 -25
  50. package/lib/containerRuntime.d.ts.map +1 -1
  51. package/lib/containerRuntime.js +150 -80
  52. package/lib/containerRuntime.js.map +1 -1
  53. package/lib/dataStore.d.ts +62 -0
  54. package/lib/dataStore.d.ts.map +1 -0
  55. package/lib/dataStore.js +130 -0
  56. package/lib/dataStore.js.map +1 -0
  57. package/lib/dataStoreContext.js.map +1 -1
  58. package/lib/dataStores.d.ts +9 -5
  59. package/lib/dataStores.d.ts.map +1 -1
  60. package/lib/dataStores.js +13 -18
  61. package/lib/dataStores.js.map +1 -1
  62. package/lib/garbageCollection.d.ts +47 -21
  63. package/lib/garbageCollection.d.ts.map +1 -1
  64. package/lib/garbageCollection.js +197 -63
  65. package/lib/garbageCollection.js.map +1 -1
  66. package/lib/index.d.ts +3 -2
  67. package/lib/index.d.ts.map +1 -1
  68. package/lib/index.js +2 -1
  69. package/lib/index.js.map +1 -1
  70. package/lib/packageVersion.d.ts +1 -1
  71. package/lib/packageVersion.d.ts.map +1 -1
  72. package/lib/packageVersion.js +1 -1
  73. package/lib/packageVersion.js.map +1 -1
  74. package/lib/runningSummarizer.d.ts +1 -0
  75. package/lib/runningSummarizer.d.ts.map +1 -1
  76. package/lib/runningSummarizer.js +23 -15
  77. package/lib/runningSummarizer.js.map +1 -1
  78. package/lib/summarizerTypes.d.ts +6 -6
  79. package/lib/summarizerTypes.d.ts.map +1 -1
  80. package/lib/summarizerTypes.js.map +1 -1
  81. package/lib/summaryGenerator.d.ts +2 -1
  82. package/lib/summaryGenerator.d.ts.map +1 -1
  83. package/lib/summaryGenerator.js +46 -28
  84. package/lib/summaryGenerator.js.map +1 -1
  85. package/package.json +13 -13
  86. package/src/blobManager.ts +12 -1
  87. package/src/connectionTelemetry.ts +7 -6
  88. package/src/containerRuntime.ts +231 -103
  89. package/src/dataStore.ts +187 -0
  90. package/src/dataStoreContext.ts +1 -1
  91. package/src/dataStores.ts +18 -38
  92. package/src/garbageCollection.ts +283 -105
  93. package/src/index.ts +3 -1
  94. package/src/packageVersion.ts +1 -1
  95. package/src/runningSummarizer.ts +25 -16
  96. package/src/summarizerTypes.ts +6 -8
  97. package/src/summaryGenerator.ts +72 -23
@@ -93,6 +93,7 @@ export class RunningSummarizer implements IDisposable {
93
93
  readonly resultsBuilder: SummarizeResultBuilder;
94
94
  } | undefined;
95
95
  private summarizeCount = 0;
96
+ private totalSuccessfulAttempts = 0;
96
97
 
97
98
  private constructor(
98
99
  baseLogger: ITelemetryLogger,
@@ -107,7 +108,14 @@ export class RunningSummarizer implements IDisposable {
107
108
  { disableHeuristics = false }: Readonly<Partial<ISummarizerOptions>> = {},
108
109
  ) {
109
110
  this.logger = ChildLogger.create(
110
- baseLogger, "Running", { all: { summaryGenTag: () => this.summarizeCount } });
111
+ baseLogger, "Running",
112
+ {
113
+ all: {
114
+ summarizeCount: () => this.summarizeCount,
115
+ summarizerSuccessfulAttempts: () => this.totalSuccessfulAttempts,
116
+ },
117
+ },
118
+ );
111
119
 
112
120
  if (!disableHeuristics) {
113
121
  this.heuristicRunner = new SummarizeHeuristicRunner(
@@ -124,7 +132,7 @@ export class RunningSummarizer implements IDisposable {
124
132
  maxAckWaitTime,
125
133
  () => {
126
134
  this.raiseSummarizingError("summaryAckWaitTimeout");
127
- // Note: summaryGenTag (from ChildLogger definition) may be 0,
135
+ // Note: summarizeCount (from ChildLogger definition) may be 0,
128
136
  // since this code path is hit when RunningSummarizer first starts up,
129
137
  // before this instance has kicked off a new summarize run.
130
138
  this.logger.sendErrorEvent({
@@ -152,6 +160,7 @@ export class RunningSummarizer implements IDisposable {
152
160
  this.heuristicData,
153
161
  this.submitSummaryCallback,
154
162
  this.raiseSummarizingError,
163
+ () => { this.totalSuccessfulAttempts++; },
155
164
  this.summaryWatcher,
156
165
  this.logger,
157
166
  );
@@ -183,8 +192,7 @@ export class RunningSummarizer implements IDisposable {
183
192
  switch (op.type) {
184
193
  case MessageType.ClientLeave:
185
194
  case MessageType.ClientJoin:
186
- case MessageType.Propose:
187
- case MessageType.Reject: {
195
+ case MessageType.Propose: {
188
196
  // Synchronously handle quorum ops like regular ops
189
197
  this.handleOp(undefined, op);
190
198
  return;
@@ -338,27 +346,27 @@ export class RunningSummarizer implements IDisposable {
338
346
  { refreshLatestAck: true, fullTree: true, delaySeconds: 10 * 60 },
339
347
  ];
340
348
  let overrideDelaySeconds: number | undefined;
341
- let totalAttempts = 0;
342
- let attemptPerPhase = 0;
349
+ let summaryAttempts = 0;
350
+ let summaryAttemptsPerPhase = 0;
343
351
 
344
352
  let lastResult: { message: string; error: any; } | undefined;
345
353
 
346
- for (let attemptPhase = 0; attemptPhase < attempts.length;) {
354
+ for (let summaryAttemptPhase = 0; summaryAttemptPhase < attempts.length;) {
347
355
  if (this.cancellationToken.cancelled) {
348
356
  return;
349
357
  }
350
358
 
351
- totalAttempts++;
352
- attemptPerPhase++;
359
+ summaryAttempts++;
360
+ summaryAttemptsPerPhase++;
353
361
 
354
- const { delaySeconds: regularDelaySeconds = 0, ...options } = attempts[attemptPhase];
362
+ const { delaySeconds: regularDelaySeconds = 0, ...options } = attempts[summaryAttemptPhase];
355
363
  const delaySeconds = overrideDelaySeconds ?? regularDelaySeconds;
356
364
 
357
365
  const summarizeProps: ITelemetryProperties = {
358
366
  summarizeReason,
359
- summarizeTotalAttempts: totalAttempts,
360
- summarizeAttemptsPerPhase: attemptPerPhase,
361
- summarizeAttemptPhase: attemptPhase + 1, // make everything 1-based
367
+ summaryAttempts,
368
+ summaryAttemptsPerPhase,
369
+ summaryAttemptPhase: summaryAttemptPhase + 1, // make everything 1-based
362
370
  ...options,
363
371
  };
364
372
 
@@ -377,14 +385,15 @@ export class RunningSummarizer implements IDisposable {
377
385
  const result = await resultSummarize.receivedSummaryAckOrNack;
378
386
 
379
387
  if (result.success) {
388
+ this.totalSuccessfulAttempts++;
380
389
  return;
381
390
  }
382
391
  // Check for retryDelay that can come from summaryNack or upload summary flow.
383
392
  // Retry the same step only once per retryAfter response.
384
393
  overrideDelaySeconds = result.retryAfterSeconds;
385
- if (overrideDelaySeconds === undefined || attemptPerPhase > 1) {
386
- attemptPhase++;
387
- attemptPerPhase = 0;
394
+ if (overrideDelaySeconds === undefined || summaryAttemptsPerPhase > 1) {
395
+ summaryAttemptPhase++;
396
+ summaryAttemptsPerPhase = 0;
388
397
  }
389
398
  lastResult = result;
390
399
  }
@@ -20,14 +20,6 @@ import {
20
20
  import { ISummaryStats } from "@fluidframework/runtime-definitions";
21
21
  import { ISummaryAckMessage, ISummaryNackMessage, ISummaryOpMessage } from "./summaryCollection";
22
22
 
23
- declare module "@fluidframework/core-interfaces" {
24
- export interface IFluidObject {
25
- /** @deprecated - use `FluidObject<ISummarizer>` instead */
26
- readonly ISummarizer?: ISummarizer;
27
-
28
- }
29
- }
30
-
31
23
  /**
32
24
  * @deprecated - This will be removed in a later release.
33
25
  */
@@ -144,8 +136,12 @@ export interface IEnqueueSummarizeOptions extends IOnDemandSummarizeOptions {
144
136
  * only relevant at the root of the tree.
145
137
  */
146
138
  export interface IGeneratedSummaryStats extends ISummaryStats {
139
+ /** The total number of data stores in the container. */
147
140
  readonly dataStoreCount: number;
141
+ /** The number of data stores that were summarized in this summary. */
148
142
  readonly summarizedDataStoreCount: number;
143
+ /** The number of data stores whose GC reference state was updated in this summary. */
144
+ readonly gcStateUpdatedDataStoreCount?: number;
149
145
  }
150
146
 
151
147
  /** Base results for all submitSummary attempts. */
@@ -166,6 +162,8 @@ export interface IGenerateSummaryTreeResult extends Omit<IBaseSummarizeResult, "
166
162
  readonly summaryStats: IGeneratedSummaryStats;
167
163
  /** Time it took to generate the summary tree and stats. */
168
164
  readonly generateDuration: number;
165
+ /** True if the full tree regeneration with no handle reuse optimizations was forced. */
166
+ readonly forcedFullTree: boolean;
169
167
  }
170
168
 
171
169
  /** Results of submitSummary after uploading the tree to storage. */
@@ -153,6 +153,7 @@ export class SummaryGenerator {
153
153
  private readonly heuristicData: ISummarizeHeuristicData,
154
154
  private readonly submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
155
155
  private readonly raiseSummarizingError: (errorCode: string) => void,
156
+ private readonly successfulSummaryCallback: () => void,
156
157
  private readonly summaryWatcher: Pick<IClientSummaryWatcher, "watchSummary">,
157
158
  private readonly logger: ITelemetryLogger,
158
159
  ) {
@@ -193,13 +194,21 @@ export class SummaryGenerator {
193
194
  ): Promise<void> {
194
195
  const { refreshLatestAck, fullTree } = options;
195
196
  const logger = ChildLogger.create(this.logger, undefined, { all: summarizeProps });
197
+
198
+ const timeSinceLastAttempt = Date.now() - this.heuristicData.lastAttempt.summaryTime;
199
+ const timeSinceLastSummary = Date.now() - this.heuristicData.lastSuccessfulSummary.summaryTime;
200
+ let summarizeTelemetryProps: Record<string, string | number | boolean | undefined> = {
201
+ fullTree,
202
+ timeSinceLastAttempt,
203
+ timeSinceLastSummary,
204
+ }
205
+
196
206
  const summarizeEvent = PerformanceEvent.start(logger, {
197
207
  eventName: "Summarize",
198
208
  refreshLatestAck,
199
- fullTree,
200
- timeSinceLastAttempt: Date.now() - this.heuristicData.lastAttempt.summaryTime,
201
- timeSinceLastSummary: Date.now() - this.heuristicData.lastSuccessfulSummary.summaryTime,
209
+ ...summarizeTelemetryProps,
202
210
  });
211
+
203
212
  // Helper functions to report failures and return.
204
213
  const getFailMessage =
205
214
  (errorCode: keyof typeof summarizeErrors) => `${errorCode}: ${summarizeErrors[errorCode]}`;
@@ -231,10 +240,15 @@ export class SummaryGenerator {
231
240
 
232
241
  // Wait to generate and send summary
233
242
  this.summarizeTimer.start();
243
+
234
244
  // Use record type to prevent unexpected value types
235
245
  let summaryData: SubmitSummaryResult | undefined;
236
- let generateTelemetryProps: Record<string, string | number | boolean | undefined> = {};
237
246
  try {
247
+ const generateSummaryEvent = PerformanceEvent.start(logger, {
248
+ eventName: "Summarize",
249
+ ...summarizeTelemetryProps,
250
+ });
251
+
238
252
  summaryData = await this.submitSummaryCallback({
239
253
  fullTree,
240
254
  refreshLatestAck,
@@ -243,29 +257,32 @@ export class SummaryGenerator {
243
257
  });
244
258
 
245
259
  // Cumulatively add telemetry properties based on how far generateSummary went.
246
- const { referenceSequenceNumber: refSequenceNumber } = summaryData;
247
- generateTelemetryProps = {
248
- referenceSequenceNumber: refSequenceNumber,
249
- opsSinceLastAttempt: refSequenceNumber - this.heuristicData.lastAttempt.refSequenceNumber,
250
- opsSinceLastSummary: refSequenceNumber - this.heuristicData.lastSuccessfulSummary.refSequenceNumber,
260
+ const referenceSequenceNumber = summaryData.referenceSequenceNumber;
261
+ const opsSinceLastSummary =
262
+ referenceSequenceNumber - this.heuristicData.lastSuccessfulSummary.refSequenceNumber;
263
+ summarizeTelemetryProps = {
264
+ ...summarizeTelemetryProps,
265
+ referenceSequenceNumber,
266
+ opsSinceLastAttempt: referenceSequenceNumber - this.heuristicData.lastAttempt.refSequenceNumber,
267
+ opsSinceLastSummary,
251
268
  };
252
269
  if (summaryData.stage !== "base") {
253
- generateTelemetryProps = {
254
- ...generateTelemetryProps,
270
+ summarizeTelemetryProps = {
271
+ ...summarizeTelemetryProps,
255
272
  ...summaryData.summaryStats,
256
273
  generateDuration: summaryData.generateDuration,
257
274
  };
258
275
 
259
276
  if (summaryData.stage !== "generate") {
260
- generateTelemetryProps = {
261
- ...generateTelemetryProps,
277
+ summarizeTelemetryProps = {
278
+ ...summarizeTelemetryProps,
262
279
  handle: summaryData.handle,
263
280
  uploadDuration: summaryData.uploadDuration,
264
281
  };
265
282
 
266
283
  if (summaryData.stage !== "upload") {
267
- generateTelemetryProps = {
268
- ...generateTelemetryProps,
284
+ summarizeTelemetryProps = {
285
+ ...summarizeTelemetryProps,
269
286
  clientSequenceNumber: summaryData.clientSequenceNumber,
270
287
  };
271
288
  }
@@ -273,11 +290,33 @@ export class SummaryGenerator {
273
290
  }
274
291
 
275
292
  if (summaryData.stage !== "submit") {
276
- return fail("submitSummaryFailure", summaryData.error, generateTelemetryProps);
293
+ return fail("submitSummaryFailure", summaryData.error, summarizeTelemetryProps);
294
+ }
295
+
296
+ /**
297
+ * With incremental summaries, if the full tree was not summarized, only data stores that changed should
298
+ * be summarized. A data store is considered changed if either or both of the following is true:
299
+ * - It has received an op.
300
+ * - Its reference state changed, i.e., it went from referenced to unreferenced or vice-versa.
301
+ *
302
+ * In the extreme case, every op can be for a different data store and each op can result in the reference
303
+ * state change of multiple data stores. So, the total number of data stores that are summarized should not
304
+ * exceed the number of ops since last summary + number of data store whose reference state changed.
305
+ */
306
+ if (!fullTree && !summaryData.forcedFullTree) {
307
+ const { summarizedDataStoreCount, gcStateUpdatedDataStoreCount = 0 } = summaryData.summaryStats;
308
+ if (summarizedDataStoreCount > gcStateUpdatedDataStoreCount + opsSinceLastSummary) {
309
+ logger.sendErrorEvent({
310
+ eventName: "IncrementalSummaryViolation",
311
+ summarizedDataStoreCount,
312
+ gcStateUpdatedDataStoreCount,
313
+ opsSinceLastSummary,
314
+ });
315
+ }
277
316
  }
278
317
 
279
318
  // Log event here on summary success only, as Summarize_cancel duplicates failure logging.
280
- logger.sendTelemetryEvent({ eventName: "GenerateSummary", ...generateTelemetryProps });
319
+ generateSummaryEvent.reportEvent("generate", {...summarizeTelemetryProps});
281
320
  resultsBuilder.summarySubmitted.resolve({ success: true, data: summaryData });
282
321
  } catch (error) {
283
322
  return fail("submitSummaryFailure", error);
@@ -305,9 +344,10 @@ export class SummaryGenerator {
305
344
  success: true,
306
345
  data: { summarizeOp, broadcastDuration },
307
346
  });
347
+
308
348
  this.heuristicData.lastAttempt.summarySequenceNumber = summarizeOp.sequenceNumber;
309
349
  logger.sendTelemetryEvent({
310
- eventName: "SummaryOp",
350
+ eventName: "Summarize_Op",
311
351
  duration: broadcastDuration,
312
352
  referenceSequenceNumber: summarizeOp.referenceSequenceNumber,
313
353
  summarySequenceNumber: summarizeOp.sequenceNumber,
@@ -327,14 +367,22 @@ export class SummaryGenerator {
327
367
 
328
368
  // Update for success/failure
329
369
  const ackNackDuration = Date.now() - this.heuristicData.lastAttempt.summaryTime;
330
- const telemetryProps: Record<string, number> = {
370
+
371
+ // adding new properties
372
+ summarizeTelemetryProps = {
331
373
  ackWaitDuration: ackNackDuration,
332
- sequenceNumber: ackNackOp.sequenceNumber,
374
+ ackNackSequenceNumber: ackNackOp.sequenceNumber,
333
375
  summarySequenceNumber: ackNackOp.contents.summaryProposal.summarySequenceNumber,
376
+ ...summarizeTelemetryProps,
334
377
  };
335
378
  if (ackNackOp.type === MessageType.SummaryAck) {
336
379
  this.heuristicData.markLastAttemptAsSuccessful();
337
- summarizeEvent.end({ ...telemetryProps, handle: ackNackOp.contents.handle, message: "summaryAck" });
380
+ this.successfulSummaryCallback();
381
+ summarizeEvent.end({
382
+ ...summarizeTelemetryProps,
383
+ handle: ackNackOp.contents.handle,
384
+ message: "summaryAck",
385
+ });
338
386
  resultsBuilder.receivedSummaryAckOrNack.resolve({ success: true, data: {
339
387
  summaryAckOp: ackNackOp,
340
388
  ackNackDuration,
@@ -348,13 +396,14 @@ export class SummaryGenerator {
348
396
 
349
397
  const error = new LoggingError(`summaryNack: ${message}`, { retryAfterSeconds });
350
398
  logger.sendErrorEvent(
351
- { eventName: "SummaryNack", ...generateTelemetryProps, retryAfterSeconds }, error);
399
+ { eventName: "SummaryNack", ...summarizeTelemetryProps, retryAfterSeconds }, error);
400
+
352
401
  assert(getRetryDelaySecondsFromError(error) === retryAfterSeconds, 0x25f /* "retryAfterSeconds" */);
353
402
  // This will only set resultsBuilder.receivedSummaryAckOrNack, as other promises are already set.
354
403
  return fail(
355
404
  "summaryNack",
356
405
  error,
357
- { ...telemetryProps, nackRetryAfter: retryAfterSeconds },
406
+ { ...summarizeTelemetryProps, nackRetryAfter: retryAfterSeconds },
358
407
  { summaryNackOp: ackNackOp, ackNackDuration },
359
408
  );
360
409
  }