@fluidframework/runtime-utils 1.2.6 → 2.0.0-dev.1.3.0.96595

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 (66) hide show
  1. package/.mocharc.js +12 -0
  2. package/dist/objectstorageutils.d.ts.map +1 -1
  3. package/dist/objectstorageutils.js +2 -12
  4. package/dist/objectstorageutils.js.map +1 -1
  5. package/dist/packageVersion.d.ts +1 -1
  6. package/dist/packageVersion.d.ts.map +1 -1
  7. package/dist/packageVersion.js +1 -1
  8. package/dist/packageVersion.js.map +1 -1
  9. package/dist/requestParser.d.ts.map +1 -1
  10. package/dist/requestParser.js +1 -6
  11. package/dist/requestParser.js.map +1 -1
  12. package/dist/runtimeFactoryHelper.d.ts.map +1 -1
  13. package/dist/runtimeFactoryHelper.js +1 -6
  14. package/dist/runtimeFactoryHelper.js.map +1 -1
  15. package/dist/summarizerNode/summarizerNode.d.ts +18 -16
  16. package/dist/summarizerNode/summarizerNode.d.ts.map +1 -1
  17. package/dist/summarizerNode/summarizerNode.js +63 -87
  18. package/dist/summarizerNode/summarizerNode.js.map +1 -1
  19. package/dist/summarizerNode/summarizerNodeUtils.d.ts +5 -38
  20. package/dist/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  21. package/dist/summarizerNode/summarizerNodeUtils.js +1 -94
  22. package/dist/summarizerNode/summarizerNodeUtils.js.map +1 -1
  23. package/dist/summarizerNode/summarizerNodeWithGc.d.ts +7 -3
  24. package/dist/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  25. package/dist/summarizerNode/summarizerNodeWithGc.js +7 -3
  26. package/dist/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  27. package/dist/summaryUtils.d.ts.map +1 -1
  28. package/dist/summaryUtils.js +4 -13
  29. package/dist/summaryUtils.js.map +1 -1
  30. package/lib/objectstorageutils.d.ts.map +1 -1
  31. package/lib/objectstorageutils.js +2 -12
  32. package/lib/objectstorageutils.js.map +1 -1
  33. package/lib/packageVersion.d.ts +1 -1
  34. package/lib/packageVersion.d.ts.map +1 -1
  35. package/lib/packageVersion.js +1 -1
  36. package/lib/packageVersion.js.map +1 -1
  37. package/lib/requestParser.d.ts.map +1 -1
  38. package/lib/requestParser.js +1 -6
  39. package/lib/requestParser.js.map +1 -1
  40. package/lib/runtimeFactoryHelper.d.ts.map +1 -1
  41. package/lib/runtimeFactoryHelper.js +1 -6
  42. package/lib/runtimeFactoryHelper.js.map +1 -1
  43. package/lib/summarizerNode/summarizerNode.d.ts +18 -16
  44. package/lib/summarizerNode/summarizerNode.d.ts.map +1 -1
  45. package/lib/summarizerNode/summarizerNode.js +64 -88
  46. package/lib/summarizerNode/summarizerNode.js.map +1 -1
  47. package/lib/summarizerNode/summarizerNodeUtils.d.ts +5 -38
  48. package/lib/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  49. package/lib/summarizerNode/summarizerNodeUtils.js +0 -91
  50. package/lib/summarizerNode/summarizerNodeUtils.js.map +1 -1
  51. package/lib/summarizerNode/summarizerNodeWithGc.d.ts +7 -3
  52. package/lib/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  53. package/lib/summarizerNode/summarizerNodeWithGc.js +7 -3
  54. package/lib/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  55. package/lib/summaryUtils.d.ts.map +1 -1
  56. package/lib/summaryUtils.js +4 -13
  57. package/lib/summaryUtils.js.map +1 -1
  58. package/package.json +18 -18
  59. package/src/objectstorageutils.ts +2 -10
  60. package/src/packageVersion.ts +1 -1
  61. package/src/requestParser.ts +1 -5
  62. package/src/runtimeFactoryHelper.ts +1 -5
  63. package/src/summarizerNode/summarizerNode.ts +64 -111
  64. package/src/summarizerNode/summarizerNodeUtils.ts +4 -127
  65. package/src/summarizerNode/summarizerNodeWithGc.ts +7 -3
  66. package/src/summaryUtils.ts +4 -11
@@ -25,21 +25,13 @@ export async function listBlobsAtTreePath(inputTree: ITree | undefined, path: st
25
25
  while (tree?.entries !== undefined && pathParts.length > 0) {
26
26
  const part = pathParts.shift();
27
27
  const treeEntry = tree.entries.find((value) => {
28
- if (value.type === "Tree" && value.path === part) {
29
- return true;
30
- } else {
31
- return false;
32
- }
28
+ return value.type === "Tree" && value.path === part ? true : false;
33
29
  });
34
30
 
35
31
  // this check is largely superfluous due to the same check being done
36
32
  // immediately above. the type system, however, is not aware of this.
37
33
  // so we must redundantly determine that the entry's type is "Tree"
38
- if (treeEntry?.type === "Tree") {
39
- tree = treeEntry.value;
40
- } else {
41
- tree = undefined;
42
- }
34
+ tree = treeEntry?.type === "Tree" ? treeEntry.value : undefined;
43
35
  }
44
36
  if (tree?.entries === undefined || pathParts.length !== 0) {
45
37
  throw new Error("path does not exist");
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/runtime-utils";
9
- export const pkgVersion = "1.2.6";
9
+ export const pkgVersion = "2.0.0-dev.1.3.0.96595";
@@ -40,11 +40,7 @@ export class RequestParser implements IRequest {
40
40
 
41
41
  protected constructor(private readonly request: Readonly<IRequest>) {
42
42
  const queryStartIndex = this.request.url.indexOf("?");
43
- if (queryStartIndex >= 0) {
44
- this.query = this.request.url.substring(queryStartIndex);
45
- } else {
46
- this.query = "";
47
- }
43
+ this.query = queryStartIndex >= 0 ? this.request.url.substring(queryStartIndex) : "";
48
44
  }
49
45
 
50
46
  public get url(): string {
@@ -22,11 +22,7 @@ export abstract class RuntimeFactoryHelper<T = IContainerRuntime> implements IRu
22
22
  : existing;
23
23
  const runtime = await this.preInitialize(context, fromExisting);
24
24
 
25
- if (fromExisting) {
26
- await this.instantiateFromExisting(runtime);
27
- } else {
28
- await this.instantiateFirstTime(runtime);
29
- }
25
+ await (fromExisting ? this.instantiateFromExisting(runtime) : this.instantiateFirstTime(runtime));
30
26
 
31
27
  await this.hasInitialized(runtime);
32
28
  return runtime;
@@ -24,9 +24,6 @@ import { assert, unreachableCase } from "@fluidframework/common-utils";
24
24
  import { mergeStats, convertToSummaryTree, calculateStats } from "../summaryUtils";
25
25
  import { ReadAndParseBlob } from "../utils";
26
26
  import {
27
- decodeSummary,
28
- encodeSummary,
29
- EncodeSummaryParam,
30
27
  EscapedPath,
31
28
  ICreateChildDetails,
32
29
  IInitialSummary,
@@ -63,7 +60,6 @@ export class SummarizerNode implements IRootSummarizerNode {
63
60
 
64
61
  protected readonly children = new Map<string, SummarizerNode>();
65
62
  protected readonly pendingSummaries = new Map<string, SummaryNode>();
66
- private readonly outstandingOps: ISequencedDocumentMessage[] = [];
67
63
  private wipReferenceSequenceNumber: number | undefined;
68
64
  private wipLocalPaths: { localPath: EscapedPath; additionalPath?: EscapedPath; } | undefined;
69
65
  private wipSkipRecursion = false;
@@ -112,52 +108,12 @@ export class SummarizerNode implements IRootSummarizerNode {
112
108
  }
113
109
  }
114
110
 
115
- try {
116
- const result = await this.summarizeInternalFn(fullTree, true, telemetryContext);
117
- this.wipLocalPaths = { localPath: EscapedPath.create(result.id) };
118
- if (result.pathPartsForChildren !== undefined) {
119
- this.wipLocalPaths.additionalPath = EscapedPath.createAndConcat(result.pathPartsForChildren);
120
- }
121
- return { summary: result.summary, stats: result.stats };
122
- } catch (error) {
123
- if (this.throwOnError || this.trackingSequenceNumber < this._changeSequenceNumber) {
124
- throw error;
125
- }
126
- const latestSummary = this._latestSummary;
127
- const initialSummary = this.initialSummary;
128
-
129
- let encodeParam: EncodeSummaryParam;
130
- let localPath: EscapedPath;
131
- if (latestSummary !== undefined) {
132
- // Create using handle of latest acked summary
133
- encodeParam = {
134
- fromSummary: true,
135
- summaryNode: latestSummary,
136
- };
137
- localPath = latestSummary.localPath;
138
- } else if (initialSummary?.summary !== undefined) {
139
- // Create using initial summary from attach op
140
- encodeParam = {
141
- fromSummary: false,
142
- initialSummary: initialSummary.summary,
143
- };
144
- localPath = EscapedPath.create(initialSummary.id);
145
- } else {
146
- // No base summary to reference
147
- throw error;
148
- }
149
- this.wipSummaryLogger.sendErrorEvent({
150
- eventName: "SummarizingWithBasePlusOps",
151
- },
152
- error);
153
- const summary = encodeSummary(encodeParam, this.outstandingOps);
154
- this.wipLocalPaths = {
155
- localPath,
156
- additionalPath: summary.additionalPath,
157
- };
158
- this.wipSkipRecursion = true;
159
- return { summary: summary.summary, stats: summary.stats };
111
+ const result = await this.summarizeInternalFn(fullTree, true, telemetryContext);
112
+ this.wipLocalPaths = { localPath: EscapedPath.create(result.id) };
113
+ if (result.pathPartsForChildren !== undefined) {
114
+ this.wipLocalPaths.additionalPath = EscapedPath.createAndConcat(result.pathPartsForChildren);
160
115
  }
116
+ return { summary: result.summary, stats: result.stats };
161
117
  }
162
118
 
163
119
  /**
@@ -210,10 +166,15 @@ export class SummarizerNode implements IRootSummarizerNode {
210
166
  // If there is no latestSummary, clearSummary and return before reaching this code.
211
167
  assert(!!localPathsToUse, 0x1a5 /* "Tracked summary local paths not set" */);
212
168
 
169
+ // DataStore can be realized out-of-order during the summary execution (ex. the mixinSummaryHandler
170
+ // in order to provide search capability to the summaries). If that happens,
171
+ // a child node will not have the handle paths with ".channels".
172
+ // By using the _latestSummary's basePath we have a safe path to compose its pendingSummary.
173
+ // PR: https://github.com/microsoft/FluidFramework/pull/11697
213
174
  const summary = new SummaryNode({
214
175
  ...localPathsToUse,
215
176
  referenceSequenceNumber: this.wipReferenceSequenceNumber,
216
- basePath: parentPath,
177
+ basePath: parentSkipRecursion ? this._latestSummary?.basePath ?? parentPath : parentPath,
217
178
  });
218
179
  const fullPathForChildren = summary.fullPathForChildren;
219
180
  for (const child of this.children.values()) {
@@ -248,11 +209,15 @@ export class SummarizerNode implements IRootSummarizerNode {
248
209
  * it becomes the latest summary. If the current summary is already ahead (e.g., loaded from a service summary),
249
210
  * we skip the update. Otherwise, we get the snapshot by calling `getSnapshot` and update latest
250
211
  * summary based off of that.
212
+ *
251
213
  * @returns A RefreshSummaryResult type which returns information based on the following three scenarios:
252
- * 1. The latest summary was not udpated.
253
- * 2. The latest summary was updated and the summary corresponding to the params was being tracked.
254
- * 3. The latest summary was updated but the summary corresponding to the params was not tracked. In this
255
- * case, the latest summary is updated based on the downloaded snapshot which is also returned.
214
+ *
215
+ * 1. The latest summary was not udpated.
216
+ *
217
+ * 2. The latest summary was updated and the summary corresponding to the params was being tracked.
218
+ *
219
+ * 3. The latest summary was updated but the summary corresponding to the params was not tracked. In this
220
+ * case, the latest summary is updated based on the downloaded snapshot which is also returned.
256
221
  */
257
222
  public async refreshLatestSummary(
258
223
  proposalHandle: string | undefined,
@@ -268,6 +233,17 @@ export class SummarizerNode implements IRootSummarizerNode {
268
233
  this.refreshLatestSummaryFromPending(proposalHandle, maybeSummaryNode.referenceSequenceNumber);
269
234
  return { latestSummaryUpdated: true, wasSummaryTracked: true };
270
235
  }
236
+
237
+ const props = {
238
+ summaryRefSeq,
239
+ pendingSize: this.pendingSummaries.size ?? undefined,
240
+ };
241
+ this.defaultLogger.sendTelemetryEvent({
242
+ eventName: "PendingSummaryNotFound",
243
+ proposalHandle,
244
+ referenceSequenceNumber: this.referenceSequenceNumber,
245
+ details: JSON.stringify(props),
246
+ });
271
247
  }
272
248
 
273
249
  // If we have seen a summary same or later as the current one, ignore it.
@@ -286,7 +262,12 @@ export class SummarizerNode implements IRootSummarizerNode {
286
262
  );
287
263
  return { latestSummaryUpdated: true, wasSummaryTracked: false, snapshot: snapshotTree };
288
264
  }
289
-
265
+ /**
266
+ * Called when we get an ack from the server for a summary we've just sent. Updates the reference state of this node
267
+ * from the state in the pending summary queue.
268
+ * @param proposalHandle - Handle for the current proposal.
269
+ * @param referenceSequenceNumber - reference sequence number of sent summary.
270
+ */
290
271
  protected refreshLatestSummaryFromPending(
291
272
  proposalHandle: string,
292
273
  referenceSequenceNumber: number,
@@ -312,7 +293,6 @@ export class SummarizerNode implements IRootSummarizerNode {
312
293
  this.refreshLatestSummaryCore(referenceSequenceNumber);
313
294
 
314
295
  this._latestSummary = summaryNode;
315
-
316
296
  // Propagate update to all child nodes
317
297
  for (const child of this.children.values()) {
318
298
  child.refreshLatestSummaryFromPending(proposalHandle, referenceSequenceNumber);
@@ -334,15 +314,14 @@ export class SummarizerNode implements IRootSummarizerNode {
334
314
 
335
315
  this.refreshLatestSummaryCore(referenceSequenceNumber);
336
316
 
337
- const { baseSummary, pathParts } = decodeSummary(snapshotTree, correlatedSummaryLogger);
338
-
339
317
  this._latestSummary = new SummaryNode({
340
318
  referenceSequenceNumber,
341
319
  basePath,
342
320
  localPath,
343
321
  });
344
322
 
345
- const { childrenTree, childrenPathPart } = parseSummaryForSubtrees(baseSummary);
323
+ const pathParts: string[] = [];
324
+ const { childrenTree, childrenPathPart } = parseSummaryForSubtrees(snapshotTree);
346
325
  if (childrenPathPart !== undefined) {
347
326
  pathParts.push(childrenPathPart);
348
327
  }
@@ -376,14 +355,6 @@ export class SummarizerNode implements IRootSummarizerNode {
376
355
  this.pendingSummaries.delete(key);
377
356
  }
378
357
  }
379
-
380
- // Clear earlier outstanding ops
381
- while (
382
- this.outstandingOps.length > 0
383
- && this.outstandingOps[0].sequenceNumber <= referenceSequenceNumber
384
- ) {
385
- this.outstandingOps.shift();
386
- }
387
358
  }
388
359
 
389
360
  public loadBaseSummaryWithoutDifferential(snapshot: ISnapshotTree) {
@@ -398,46 +369,22 @@ export class SummarizerNode implements IRootSummarizerNode {
398
369
  public async loadBaseSummary(
399
370
  snapshot: ISnapshotTree,
400
371
  readAndParseBlob: ReadAndParseBlob,
401
- ): Promise<{ baseSummary: ISnapshotTree; outstandingOps: ISequencedDocumentMessage[]; }> {
402
- const decodedSummary = decodeSummary(snapshot, this.defaultLogger);
403
- const outstandingOps = await decodedSummary.getOutstandingOps(readAndParseBlob);
404
-
405
- const { childrenPathPart } = parseSummaryForSubtrees(decodedSummary.baseSummary);
372
+ ): Promise<ISnapshotTree> {
373
+ const pathParts: string[] = [];
374
+ const { childrenPathPart } = parseSummaryForSubtrees(snapshot);
406
375
  if (childrenPathPart !== undefined) {
407
- decodedSummary.pathParts.push(childrenPathPart);
408
- }
409
-
410
- if (decodedSummary.pathParts.length > 0 && this._latestSummary !== undefined) {
411
- this._latestSummary.additionalPath = EscapedPath.createAndConcat(decodedSummary.pathParts);
376
+ pathParts.push(childrenPathPart);
412
377
  }
413
378
 
414
- // Defensive assertion: tracking number should already exceed this number.
415
- // This is probably a little excessive; can remove when stable.
416
- if (outstandingOps.length > 0) {
417
- const newOpsLatestSeq = outstandingOps[outstandingOps.length - 1].sequenceNumber;
418
- assert(
419
- newOpsLatestSeq <= this.trackingSequenceNumber,
420
- 0x1a9 /* "When loading base summary, expected outstanding ops <= tracking sequence number" */,
421
- );
379
+ if (pathParts.length > 0 && this._latestSummary !== undefined) {
380
+ this._latestSummary.additionalPath = EscapedPath.createAndConcat(pathParts);
422
381
  }
423
382
 
424
- return {
425
- baseSummary: decodedSummary.baseSummary,
426
- outstandingOps,
427
- };
383
+ return snapshot;
428
384
  }
429
385
 
430
386
  public recordChange(op: ISequencedDocumentMessage): void {
431
- const lastOp = this.outstandingOps[this.outstandingOps.length - 1];
432
- if (lastOp !== undefined) {
433
- assert(
434
- lastOp.sequenceNumber < op.sequenceNumber,
435
- 0x1aa /* Out of order change recorded */,
436
- );
437
- }
438
387
  this.invalidate(op.sequenceNumber);
439
- this.trackingSequenceNumber = op.sequenceNumber;
440
- this.outstandingOps.push(op);
441
388
  }
442
389
 
443
390
  public invalidate(sequenceNumber: number): void {
@@ -460,13 +407,6 @@ export class SummarizerNode implements IRootSummarizerNode {
460
407
  }
461
408
 
462
409
  private readonly canReuseHandle: boolean;
463
- private readonly throwOnError: boolean;
464
- /**
465
- * Sequence number of latest tracked op. This updates during recordChange,
466
- * but not for invalidate since we don't have the op. If this drifts from
467
- * changeSequenceNumber and we try to create a differential summary we assert.
468
- */
469
- private trackingSequenceNumber: number;
470
410
 
471
411
  /**
472
412
  * Do not call constructor directly.
@@ -483,11 +423,6 @@ export class SummarizerNode implements IRootSummarizerNode {
483
423
  protected wipSummaryLogger?: ITelemetryLogger,
484
424
  ) {
485
425
  this.canReuseHandle = config.canReuseHandle ?? true;
486
- // BUGBUG: Seeing issues with differential summaries.
487
- // this will disable them, and throw instead
488
- // while we continue to investigate
489
- this.throwOnError = true; // config.throwOnFailure ?? false;
490
- this.trackingSequenceNumber = this._changeSequenceNumber;
491
426
  }
492
427
 
493
428
  public createChild(
@@ -517,7 +452,8 @@ export class SummarizerNode implements IRootSummarizerNode {
517
452
  );
518
453
 
519
454
  // There may be additional state that has to be updated in this child. For example, if a summary is being
520
- // tracked, the child's summary tracking state needs to be updated too.
455
+ // tracked, the child's summary tracking state needs to be updated too. Same goes for pendingSummaries we might
456
+ // have outstanding on the parent in case we realize nodes in between Summary Op and Summary Ack.
521
457
  this.maybeUpdateChildState(child);
522
458
 
523
459
  this.children.set(id, child);
@@ -620,6 +556,8 @@ export class SummarizerNode implements IRootSummarizerNode {
620
556
  /**
621
557
  * Updates the state of the child if required. For example, if a summary is currently being tracked, the child's
622
558
  * summary tracking state needs to be updated too.
559
+ * Also, in case a child node gets realized in between Summary Op and Summary Ack, let's initialize the child's
560
+ * pending summary as well.
623
561
  * @param child - The child node whose state is to be updated.
624
562
  */
625
563
  protected maybeUpdateChildState(child: SummarizerNode) {
@@ -628,8 +566,23 @@ export class SummarizerNode implements IRootSummarizerNode {
628
566
  if (this.isTrackingInProgress()) {
629
567
  child.wipReferenceSequenceNumber = this.wipReferenceSequenceNumber;
630
568
  }
569
+ // In case we have pending summaries on the parent, let's initialize it on the child.
570
+ if (child._latestSummary !== undefined) {
571
+ for (const [key, value] of this.pendingSummaries.entries()) {
572
+ const newLatestSummaryNode = new SummaryNode({
573
+ referenceSequenceNumber: value.referenceSequenceNumber,
574
+ basePath: child._latestSummary.basePath,
575
+ localPath: child._latestSummary.localPath,
576
+ });
577
+
578
+ child.addPendingSummary(key, newLatestSummaryNode);
579
+ }
580
+ }
631
581
  }
632
582
 
583
+ protected addPendingSummary(key: string, summary: SummaryNode) {
584
+ this.pendingSummaries.set(key, summary);
585
+ }
633
586
  /**
634
587
  * Tells whether summary tracking is in progress. True if "startSummary" API is called before summarize.
635
588
  */
@@ -4,28 +4,23 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { assert } from "@fluidframework/common-utils";
8
7
  import {
9
8
  ISnapshotTree,
10
- ISequencedDocumentMessage,
11
- SummaryType,
12
9
  ISummaryTree,
13
10
  SummaryObject,
14
11
  } from "@fluidframework/protocol-definitions";
15
12
  import { channelsTreeName, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
16
- import { SummaryTreeBuilder } from "../summaryUtils";
17
13
  import { ReadAndParseBlob } from "../utils";
18
14
 
19
- const baseSummaryTreeKey = "_baseSummary";
20
- const outstandingOpsBlobKey = "_outstandingOps";
21
- const maxDecodeDepth = 100;
22
-
23
15
  /**
24
16
  * Return value of refreshSummaryAck function. There can be three different scenarios based on the passed params:
17
+ *
25
18
  * 1. The latest summary was not udpated.
19
+ *
26
20
  * 2. The latest summary was updated and the summary corresponding to the params was tracked by this client.
21
+ *
27
22
  * 3. The latest summary was updated but the summary corresponding to the params was not tracked. In this case, the
28
- * latest summary is updated based on the downloaded snapshot which is also returned.
23
+ * latest summary is updated based on the downloaded snapshot which is also returned.
29
24
  */
30
25
  export type RefreshSummaryResult = {
31
26
  latestSummaryUpdated: false;
@@ -137,88 +132,6 @@ export class SummaryNode {
137
132
  }
138
133
  }
139
134
 
140
- /** Result from decoding summary which may have been a differential summary. */
141
- interface IDecodedSummary {
142
- /** The innermost base summary which is not itself a differential summary */
143
- readonly baseSummary: ISnapshotTree;
144
- /** The entire path name to the innermost base summary */
145
- readonly pathParts: string[];
146
- /** Function to fetch all outstanding ops since the innermost base summary */
147
- getOutstandingOps(readAndParseBlob: ReadAndParseBlob): Promise<ISequencedDocumentMessage[]>;
148
- }
149
-
150
- /**
151
- * Checks if the snapshot is created by referencing a previous successful
152
- * summary plus outstanding ops. If so, it will recursively "decode" it until
153
- * it gets to the last successful summary (the base summary) and returns that
154
- * as well as a function for fetching the outstanding ops. Also returns the
155
- * full path to the previous base summary for child summarizer nodes to use as
156
- * their base path when necessary.
157
- * @param snapshot - snapshot tree to decode
158
- */
159
- export function decodeSummary(
160
- snapshot: ISnapshotTree,
161
- logger: Pick<ITelemetryLogger, "sendTelemetryEvent">,
162
- ): IDecodedSummary {
163
- let baseSummary = snapshot;
164
- const pathParts: string[] = [];
165
- const opsBlobs: string[] = [];
166
-
167
- for (let i = 0; ; i++) {
168
- if (i > maxDecodeDepth) {
169
- logger.sendTelemetryEvent({
170
- eventName: "DecodeSummaryMaxDepth",
171
- maxDecodeDepth,
172
- });
173
- }
174
- const outstandingOpsBlob = baseSummary.blobs[outstandingOpsBlobKey];
175
- const newBaseSummary = baseSummary.trees[baseSummaryTreeKey];
176
- if (outstandingOpsBlob === undefined && newBaseSummary === undefined) {
177
- return {
178
- baseSummary,
179
- pathParts,
180
- async getOutstandingOps(readAndParseBlob: ReadAndParseBlob) {
181
- let outstandingOps: ISequencedDocumentMessage[] = [];
182
- for (const opsBlob of opsBlobs) {
183
- const newOutstandingOps = await readAndParseBlob<ISequencedDocumentMessage[]>(opsBlob);
184
- if (outstandingOps.length > 0 && newOutstandingOps.length > 0) {
185
- const latestSeq = outstandingOps[outstandingOps.length - 1].sequenceNumber;
186
- const newEarliestSeq = newOutstandingOps[0].sequenceNumber;
187
- if (newEarliestSeq <= latestSeq) {
188
- logger.sendTelemetryEvent({
189
- eventName: "DuplicateOutstandingOps",
190
- // eslint-disable-next-line max-len
191
- message: `newEarliestSeq <= latestSeq in decodeSummary: ${newEarliestSeq} <= ${latestSeq}`,
192
- });
193
- while (newOutstandingOps.length > 0
194
- && newOutstandingOps[0].sequenceNumber <= latestSeq) {
195
- newOutstandingOps.shift();
196
- }
197
- }
198
- }
199
- outstandingOps = outstandingOps.concat(newOutstandingOps);
200
- }
201
- return outstandingOps;
202
- },
203
- };
204
- }
205
-
206
- assert(!!outstandingOpsBlob, 0x1af /* "Outstanding ops blob missing, but base summary tree exists" */);
207
- assert(newBaseSummary !== undefined, 0x1b0 /* "Base summary tree missing, but outstanding ops blob exists" */);
208
- baseSummary = newBaseSummary;
209
- pathParts.push(baseSummaryTreeKey);
210
- opsBlobs.unshift(outstandingOpsBlob);
211
- }
212
- }
213
-
214
- /**
215
- * Summary tree which is a handle of the previous successfully acked summary
216
- * and a blob of the outstanding ops since that summary.
217
- */
218
- interface IEncodedSummary extends ISummaryTreeWithStats {
219
- readonly additionalPath: EscapedPath;
220
- }
221
-
222
135
  /**
223
136
  * Parameter to help encode summary with conditional behavior.
224
137
  * When fromSummary is true, it will contain the SummaryNode of
@@ -234,42 +147,6 @@ export type EncodeSummaryParam = {
234
147
  initialSummary: ISummaryTreeWithStats;
235
148
  };
236
149
 
237
- /**
238
- * Creates a summary tree which is a handle of the previous successfully acked summary
239
- * and a blob of the outstanding ops since that summary. If there is no acked summary yet,
240
- * it will create with the tree found in the initial attach op and the blob of outstanding ops.
241
- * @param summaryParam - information about last acked summary and paths to encode if from summary,
242
- * otherwise the initial summary from the attach op.
243
- * @param outstandingOps - outstanding ops since last acked summary
244
- */
245
- export function encodeSummary(
246
- summaryParam: EncodeSummaryParam,
247
- outstandingOps: ISequencedDocumentMessage[],
248
- ): IEncodedSummary {
249
- let additionalPath = EscapedPath.create(baseSummaryTreeKey);
250
-
251
- const builder = new SummaryTreeBuilder();
252
- builder.addBlob(outstandingOpsBlobKey, JSON.stringify(outstandingOps));
253
-
254
- if (summaryParam.fromSummary) {
255
- // Create using handle of latest acked summary
256
- const summaryNode = summaryParam.summaryNode;
257
- if (summaryNode.additionalPath !== undefined) {
258
- additionalPath = additionalPath.concat(summaryNode.additionalPath);
259
- }
260
- builder.addHandle(baseSummaryTreeKey, SummaryType.Tree, summaryNode.fullPath.path);
261
- } else {
262
- // Create using initial summary from attach op
263
- builder.addWithStats(baseSummaryTreeKey, summaryParam.initialSummary);
264
- }
265
-
266
- const summary = builder.getSummaryTree();
267
- return {
268
- ...summary,
269
- additionalPath,
270
- };
271
- }
272
-
273
150
  /**
274
151
  * Information about the initial summary tree found from an attach op.
275
152
  */
@@ -49,12 +49,16 @@ class SummaryNodeWithGC extends SummaryNode {
49
49
 
50
50
  /**
51
51
  * Extends the functionality of SummarizerNode to manage this node's garbage collection data:
52
+ *
52
53
  * - Adds a new API `getGCData` to return GC data of this node.
54
+ *
53
55
  * - Caches the result of `getGCData` to be used if nothing changes between summaries.
56
+ *
54
57
  * - Manages the used routes of this node. These are used to identify if this node is referenced in the document
55
- * and to determine if the node's used state changed since last summary.
58
+ * and to determine if the node's used state changed since last summary.
59
+ *
56
60
  * - Adds trackState param to summarize. If trackState is false, it bypasses the SummarizerNode and calls
57
- * directly into summarizeInternal method.
61
+ * directly into summarizeInternal method.
58
62
  */
59
63
  export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNodeWithGC {
60
64
  // Tracks the work-in-progress used routes during summary.
@@ -121,7 +125,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
121
125
  }
122
126
 
123
127
  /**
124
- * @deprecated - Renamed to getBaseGCDetails.
128
+ * @deprecated Renamed to {@link SummarizerNodeWithGC.getBaseGCDetails}.
125
129
  */
126
130
  public getGCSummaryDetails(): IGarbageCollectionSummaryDetails {
127
131
  return this.getBaseGCDetails();
@@ -70,11 +70,7 @@ export function utf8ByteLength(str: string): number {
70
70
  }
71
71
 
72
72
  export function getBlobSize(content: ISummaryBlob["content"]): number {
73
- if (typeof content === "string") {
74
- return utf8ByteLength(content);
75
- } else {
76
- return content.byteLength;
77
- }
73
+ return typeof content === "string" ? utf8ByteLength(content) : content.byteLength;
78
74
  }
79
75
 
80
76
  function calculateStatsCore(summaryObject: SummaryObject, stats: ISummaryStats): void {
@@ -202,12 +198,9 @@ export function convertToSummaryTreeWithStats(
202
198
  switch (entry.type) {
203
199
  case TreeEntry.Blob: {
204
200
  const blob = entry.value;
205
- let content: string | Uint8Array;
206
- if (blob.encoding === "base64") {
207
- content = IsoBuffer.from(blob.contents, "base64");
208
- } else {
209
- content = blob.contents;
210
- }
201
+ const content = blob.encoding === "base64"
202
+ ? IsoBuffer.from(blob.contents, "base64")
203
+ : blob.contents;
211
204
  builder.addBlob(entry.path, content);
212
205
  break;
213
206
  }