@fluidframework/runtime-utils 1.2.1 → 2.0.0-internal.1.0.0.81601

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.
@@ -2,13 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { assert } from "@fluidframework/common-utils";
6
- import { SummaryType, } from "@fluidframework/protocol-definitions";
7
5
  import { channelsTreeName } from "@fluidframework/runtime-definitions";
8
- import { SummaryTreeBuilder } from "../summaryUtils";
9
- const baseSummaryTreeKey = "_baseSummary";
10
- const outstandingOpsBlobKey = "_outstandingOps";
11
- const maxDecodeDepth = 100;
12
6
  /** Path for nodes in a tree with escaped special characters */
13
7
  export class EscapedPath {
14
8
  constructor(path) {
@@ -90,91 +84,6 @@ export class SummaryNode {
90
84
  });
91
85
  }
92
86
  }
93
- /**
94
- * Checks if the snapshot is created by referencing a previous successful
95
- * summary plus outstanding ops. If so, it will recursively "decode" it until
96
- * it gets to the last successful summary (the base summary) and returns that
97
- * as well as a function for fetching the outstanding ops. Also returns the
98
- * full path to the previous base summary for child summarizer nodes to use as
99
- * their base path when necessary.
100
- * @param snapshot - snapshot tree to decode
101
- */
102
- export function decodeSummary(snapshot, logger) {
103
- let baseSummary = snapshot;
104
- const pathParts = [];
105
- const opsBlobs = [];
106
- for (let i = 0;; i++) {
107
- if (i > maxDecodeDepth) {
108
- logger.sendTelemetryEvent({
109
- eventName: "DecodeSummaryMaxDepth",
110
- maxDecodeDepth,
111
- });
112
- }
113
- const outstandingOpsBlob = baseSummary.blobs[outstandingOpsBlobKey];
114
- const newBaseSummary = baseSummary.trees[baseSummaryTreeKey];
115
- if (outstandingOpsBlob === undefined && newBaseSummary === undefined) {
116
- return {
117
- baseSummary,
118
- pathParts,
119
- async getOutstandingOps(readAndParseBlob) {
120
- let outstandingOps = [];
121
- for (const opsBlob of opsBlobs) {
122
- const newOutstandingOps = await readAndParseBlob(opsBlob);
123
- if (outstandingOps.length > 0 && newOutstandingOps.length > 0) {
124
- const latestSeq = outstandingOps[outstandingOps.length - 1].sequenceNumber;
125
- const newEarliestSeq = newOutstandingOps[0].sequenceNumber;
126
- if (newEarliestSeq <= latestSeq) {
127
- logger.sendTelemetryEvent({
128
- eventName: "DuplicateOutstandingOps",
129
- // eslint-disable-next-line max-len
130
- message: `newEarliestSeq <= latestSeq in decodeSummary: ${newEarliestSeq} <= ${latestSeq}`,
131
- });
132
- while (newOutstandingOps.length > 0
133
- && newOutstandingOps[0].sequenceNumber <= latestSeq) {
134
- newOutstandingOps.shift();
135
- }
136
- }
137
- }
138
- outstandingOps = outstandingOps.concat(newOutstandingOps);
139
- }
140
- return outstandingOps;
141
- },
142
- };
143
- }
144
- assert(!!outstandingOpsBlob, 0x1af /* "Outstanding ops blob missing, but base summary tree exists" */);
145
- assert(newBaseSummary !== undefined, 0x1b0 /* "Base summary tree missing, but outstanding ops blob exists" */);
146
- baseSummary = newBaseSummary;
147
- pathParts.push(baseSummaryTreeKey);
148
- opsBlobs.unshift(outstandingOpsBlob);
149
- }
150
- }
151
- /**
152
- * Creates a summary tree which is a handle of the previous successfully acked summary
153
- * and a blob of the outstanding ops since that summary. If there is no acked summary yet,
154
- * it will create with the tree found in the initial attach op and the blob of outstanding ops.
155
- * @param summaryParam - information about last acked summary and paths to encode if from summary,
156
- * otherwise the initial summary from the attach op.
157
- * @param outstandingOps - outstanding ops since last acked summary
158
- */
159
- export function encodeSummary(summaryParam, outstandingOps) {
160
- let additionalPath = EscapedPath.create(baseSummaryTreeKey);
161
- const builder = new SummaryTreeBuilder();
162
- builder.addBlob(outstandingOpsBlobKey, JSON.stringify(outstandingOps));
163
- if (summaryParam.fromSummary) {
164
- // Create using handle of latest acked summary
165
- const summaryNode = summaryParam.summaryNode;
166
- if (summaryNode.additionalPath !== undefined) {
167
- additionalPath = additionalPath.concat(summaryNode.additionalPath);
168
- }
169
- builder.addHandle(baseSummaryTreeKey, SummaryType.Tree, summaryNode.fullPath.path);
170
- }
171
- else {
172
- // Create using initial summary from attach op
173
- builder.addWithStats(baseSummaryTreeKey, summaryParam.initialSummary);
174
- }
175
- const summary = builder.getSummaryTree();
176
- return Object.assign(Object.assign({}, summary), { additionalPath });
177
- }
178
87
  /**
179
88
  * Checks if the summary contains .channels subtree where the children subtrees
180
89
  * would be located if exists.
@@ -1 +1 @@
1
- {"version":3,"file":"summarizerNodeUtils.js","sourceRoot":"","sources":["../../src/summarizerNode/summarizerNodeUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport {\n ISnapshotTree,\n ISequencedDocumentMessage,\n SummaryType,\n ISummaryTree,\n SummaryObject,\n} from \"@fluidframework/protocol-definitions\";\nimport { channelsTreeName, ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { SummaryTreeBuilder } from \"../summaryUtils\";\nimport { ReadAndParseBlob } from \"../utils\";\n\nconst baseSummaryTreeKey = \"_baseSummary\";\nconst outstandingOpsBlobKey = \"_outstandingOps\";\nconst maxDecodeDepth = 100;\n\n/**\n * Return value of refreshSummaryAck function. There can be three different scenarios based on the passed params:\n * 1. The latest summary was not udpated.\n * 2. The latest summary was updated and the summary corresponding to the params was tracked by this client.\n * 3. The latest summary was updated but the summary corresponding to the params was not tracked. In this case, the\n * latest summary is updated based on the downloaded snapshot which is also returned.\n */\nexport type RefreshSummaryResult = {\n latestSummaryUpdated: false;\n} | {\n latestSummaryUpdated: true;\n wasSummaryTracked: true;\n} | {\n latestSummaryUpdated: true;\n wasSummaryTracked: false;\n snapshot: ISnapshotTree;\n};\n\nexport interface ISummarizerNodeRootContract {\n startSummary(referenceSequenceNumber: number, summaryLogger: ITelemetryLogger): void;\n completeSummary(proposalHandle: string): void;\n clearSummary(): void;\n refreshLatestSummary(\n proposalHandle: string | undefined,\n summaryRefSeq: number,\n getSnapshot: () => Promise<ISnapshotTree>,\n readAndParseBlob: ReadAndParseBlob,\n correlatedSummaryLogger: ITelemetryLogger,\n ): Promise<RefreshSummaryResult>;\n}\n\n/** Path for nodes in a tree with escaped special characters */\nexport class EscapedPath {\n private constructor(public readonly path: string) { }\n public static create(path: string): EscapedPath {\n return new EscapedPath(encodeURIComponent(path));\n }\n public static createAndConcat(pathParts: string[]): EscapedPath {\n let ret = EscapedPath.create(pathParts[0] ?? \"\");\n for (let i = 1; i < pathParts.length; i++) {\n ret = ret.concat(EscapedPath.create(pathParts[i]));\n }\n return ret;\n }\n public toString(): string {\n return this.path;\n }\n public concat(path: EscapedPath): EscapedPath {\n return new EscapedPath(`${this.path}/${path.path}`);\n }\n}\n\n/** Information about a summary relevant to a specific node in the tree */\nexport class SummaryNode {\n /** Creates an instance that is valid for the root with specific basePath and localPath */\n public static createForRoot(referenceSequenceNumber: number): SummaryNode {\n return new SummaryNode({\n referenceSequenceNumber,\n basePath: undefined,\n localPath: EscapedPath.create(\"\"), // root hard-coded to \"\"\n });\n }\n\n /** Summary reference sequence number, i.e. last sequence number seen when it was created */\n public get referenceSequenceNumber(): number {\n return this.summary.referenceSequenceNumber;\n }\n /** Full path to parent node, or undefined if this is the root */\n public get basePath(): EscapedPath | undefined {\n return this.summary.basePath;\n }\n /** Relative path to this node from its parent node */\n public get localPath(): EscapedPath {\n return this.summary.localPath;\n }\n /** Relative path from this node to its node innermost base summary */\n public get additionalPath(): EscapedPath | undefined {\n return this.summary.additionalPath;\n }\n public set additionalPath(additionalPath: EscapedPath | undefined) {\n this.summary.additionalPath = additionalPath;\n }\n constructor(private readonly summary: {\n readonly referenceSequenceNumber: number;\n readonly basePath: EscapedPath | undefined;\n readonly localPath: EscapedPath;\n additionalPath?: EscapedPath;\n }) { }\n\n /** Gets the full path to this node, to be used when sending a handle */\n public get fullPath(): EscapedPath {\n return this.basePath?.concat(this.localPath) ?? this.localPath;\n }\n\n /**\n * Gets the full path to this node's innermost base summary.\n * The children nodes can use this as their basePath to determine their path.\n */\n public get fullPathForChildren(): EscapedPath {\n return this.additionalPath !== undefined\n ? this.fullPath.concat(this.additionalPath)\n : this.fullPath;\n }\n\n /**\n * Creates a new node within the same summary for a child of this node.\n * @param id - id of the child node\n */\n public createForChild(id: string): SummaryNode {\n return new SummaryNode({\n referenceSequenceNumber: this.referenceSequenceNumber,\n basePath: this.fullPathForChildren,\n localPath: EscapedPath.create(id),\n });\n }\n}\n\n/** Result from decoding summary which may have been a differential summary. */\ninterface IDecodedSummary {\n /** The innermost base summary which is not itself a differential summary */\n readonly baseSummary: ISnapshotTree;\n /** The entire path name to the innermost base summary */\n readonly pathParts: string[];\n /** Function to fetch all outstanding ops since the innermost base summary */\n getOutstandingOps(readAndParseBlob: ReadAndParseBlob): Promise<ISequencedDocumentMessage[]>;\n}\n\n/**\n * Checks if the snapshot is created by referencing a previous successful\n * summary plus outstanding ops. If so, it will recursively \"decode\" it until\n * it gets to the last successful summary (the base summary) and returns that\n * as well as a function for fetching the outstanding ops. Also returns the\n * full path to the previous base summary for child summarizer nodes to use as\n * their base path when necessary.\n * @param snapshot - snapshot tree to decode\n */\nexport function decodeSummary(\n snapshot: ISnapshotTree,\n logger: Pick<ITelemetryLogger, \"sendTelemetryEvent\">,\n): IDecodedSummary {\n let baseSummary = snapshot;\n const pathParts: string[] = [];\n const opsBlobs: string[] = [];\n\n for (let i = 0; ; i++) {\n if (i > maxDecodeDepth) {\n logger.sendTelemetryEvent({\n eventName: \"DecodeSummaryMaxDepth\",\n maxDecodeDepth,\n });\n }\n const outstandingOpsBlob = baseSummary.blobs[outstandingOpsBlobKey];\n const newBaseSummary = baseSummary.trees[baseSummaryTreeKey];\n if (outstandingOpsBlob === undefined && newBaseSummary === undefined) {\n return {\n baseSummary,\n pathParts,\n async getOutstandingOps(readAndParseBlob: ReadAndParseBlob) {\n let outstandingOps: ISequencedDocumentMessage[] = [];\n for (const opsBlob of opsBlobs) {\n const newOutstandingOps = await readAndParseBlob<ISequencedDocumentMessage[]>(opsBlob);\n if (outstandingOps.length > 0 && newOutstandingOps.length > 0) {\n const latestSeq = outstandingOps[outstandingOps.length - 1].sequenceNumber;\n const newEarliestSeq = newOutstandingOps[0].sequenceNumber;\n if (newEarliestSeq <= latestSeq) {\n logger.sendTelemetryEvent({\n eventName: \"DuplicateOutstandingOps\",\n // eslint-disable-next-line max-len\n message: `newEarliestSeq <= latestSeq in decodeSummary: ${newEarliestSeq} <= ${latestSeq}`,\n });\n while (newOutstandingOps.length > 0\n && newOutstandingOps[0].sequenceNumber <= latestSeq) {\n newOutstandingOps.shift();\n }\n }\n }\n outstandingOps = outstandingOps.concat(newOutstandingOps);\n }\n return outstandingOps;\n },\n };\n }\n\n assert(!!outstandingOpsBlob, 0x1af /* \"Outstanding ops blob missing, but base summary tree exists\" */);\n assert(newBaseSummary !== undefined, 0x1b0 /* \"Base summary tree missing, but outstanding ops blob exists\" */);\n baseSummary = newBaseSummary;\n pathParts.push(baseSummaryTreeKey);\n opsBlobs.unshift(outstandingOpsBlob);\n }\n}\n\n/**\n * Summary tree which is a handle of the previous successfully acked summary\n * and a blob of the outstanding ops since that summary.\n */\ninterface IEncodedSummary extends ISummaryTreeWithStats {\n readonly additionalPath: EscapedPath;\n}\n\n/**\n * Parameter to help encode summary with conditional behavior.\n * When fromSummary is true, it will contain the SummaryNode of\n * its previous summary, which it can use to point to with a handle.\n * When fromSummary is false, it will use an actual summary tree\n * as its base summary in case the first summary is a differential summary.\n */\nexport type EncodeSummaryParam = {\n fromSummary: true;\n summaryNode: SummaryNode;\n} | {\n fromSummary: false;\n initialSummary: ISummaryTreeWithStats;\n};\n\n/**\n * Creates a summary tree which is a handle of the previous successfully acked summary\n * and a blob of the outstanding ops since that summary. If there is no acked summary yet,\n * it will create with the tree found in the initial attach op and the blob of outstanding ops.\n * @param summaryParam - information about last acked summary and paths to encode if from summary,\n * otherwise the initial summary from the attach op.\n * @param outstandingOps - outstanding ops since last acked summary\n */\nexport function encodeSummary(\n summaryParam: EncodeSummaryParam,\n outstandingOps: ISequencedDocumentMessage[],\n): IEncodedSummary {\n let additionalPath = EscapedPath.create(baseSummaryTreeKey);\n\n const builder = new SummaryTreeBuilder();\n builder.addBlob(outstandingOpsBlobKey, JSON.stringify(outstandingOps));\n\n if (summaryParam.fromSummary) {\n // Create using handle of latest acked summary\n const summaryNode = summaryParam.summaryNode;\n if (summaryNode.additionalPath !== undefined) {\n additionalPath = additionalPath.concat(summaryNode.additionalPath);\n }\n builder.addHandle(baseSummaryTreeKey, SummaryType.Tree, summaryNode.fullPath.path);\n } else {\n // Create using initial summary from attach op\n builder.addWithStats(baseSummaryTreeKey, summaryParam.initialSummary);\n }\n\n const summary = builder.getSummaryTree();\n return {\n ...summary,\n additionalPath,\n };\n}\n\n/**\n * Information about the initial summary tree found from an attach op.\n */\nexport interface IInitialSummary {\n sequenceNumber: number;\n id: string;\n summary: ISummaryTreeWithStats | undefined;\n}\n\n/**\n * Represents the details needed to create a child summarizer node.\n */\nexport interface ICreateChildDetails {\n /** Summary from attach op if known */\n initialSummary: IInitialSummary | undefined;\n /** Latest summary from server node data */\n latestSummary: SummaryNode | undefined;\n /** Sequence number of latest known change to the node */\n changeSequenceNumber: number;\n}\n\nexport interface ISubtreeInfo<T extends ISnapshotTree | SummaryObject> {\n /** Tree to use to find children subtrees */\n childrenTree: T;\n /** Additional path part where children are isolated */\n childrenPathPart: string | undefined;\n}\n\n/**\n * Checks if the summary contains .channels subtree where the children subtrees\n * would be located if exists.\n * @param baseSummary - summary to check\n */\nexport function parseSummaryForSubtrees(baseSummary: ISnapshotTree): ISubtreeInfo<ISnapshotTree> {\n // New versions of snapshots have child nodes isolated in .channels subtree\n const channelsSubtree = baseSummary.trees[channelsTreeName];\n if (channelsSubtree !== undefined) {\n return {\n childrenTree: channelsSubtree,\n childrenPathPart: channelsTreeName,\n };\n }\n return {\n childrenTree: baseSummary,\n childrenPathPart: undefined,\n };\n}\n\n/**\n * Checks if the summary contains .channels subtree where the children subtrees\n * would be located if exists.\n * @param baseSummary - summary to check\n */\nexport function parseSummaryTreeForSubtrees(summary: ISummaryTree): ISubtreeInfo<SummaryObject> {\n // New versions of snapshots have child nodes isolated in .channels subtree\n const channelsSubtree = summary.tree[channelsTreeName];\n if (channelsSubtree !== undefined) {\n return {\n childrenTree: channelsSubtree,\n childrenPathPart: channelsTreeName,\n };\n }\n return {\n childrenTree: summary,\n childrenPathPart: undefined,\n };\n}\n"]}
1
+ {"version":3,"file":"summarizerNodeUtils.js","sourceRoot":"","sources":["../../src/summarizerNode/summarizerNodeUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport {\n ISnapshotTree,\n ISummaryTree,\n SummaryObject,\n} from \"@fluidframework/protocol-definitions\";\nimport { channelsTreeName, ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { ReadAndParseBlob } from \"../utils\";\n\n/**\n * Return value of refreshSummaryAck function. There can be three different scenarios based on the passed params:\n * 1. The latest summary was not udpated.\n * 2. The latest summary was updated and the summary corresponding to the params was tracked by this client.\n * 3. The latest summary was updated but the summary corresponding to the params was not tracked. In this case, the\n * latest summary is updated based on the downloaded snapshot which is also returned.\n */\nexport type RefreshSummaryResult = {\n latestSummaryUpdated: false;\n} | {\n latestSummaryUpdated: true;\n wasSummaryTracked: true;\n} | {\n latestSummaryUpdated: true;\n wasSummaryTracked: false;\n snapshot: ISnapshotTree;\n};\n\nexport interface ISummarizerNodeRootContract {\n startSummary(referenceSequenceNumber: number, summaryLogger: ITelemetryLogger): void;\n completeSummary(proposalHandle: string): void;\n clearSummary(): void;\n refreshLatestSummary(\n proposalHandle: string | undefined,\n summaryRefSeq: number,\n getSnapshot: () => Promise<ISnapshotTree>,\n readAndParseBlob: ReadAndParseBlob,\n correlatedSummaryLogger: ITelemetryLogger,\n ): Promise<RefreshSummaryResult>;\n}\n\n/** Path for nodes in a tree with escaped special characters */\nexport class EscapedPath {\n private constructor(public readonly path: string) { }\n public static create(path: string): EscapedPath {\n return new EscapedPath(encodeURIComponent(path));\n }\n public static createAndConcat(pathParts: string[]): EscapedPath {\n let ret = EscapedPath.create(pathParts[0] ?? \"\");\n for (let i = 1; i < pathParts.length; i++) {\n ret = ret.concat(EscapedPath.create(pathParts[i]));\n }\n return ret;\n }\n public toString(): string {\n return this.path;\n }\n public concat(path: EscapedPath): EscapedPath {\n return new EscapedPath(`${this.path}/${path.path}`);\n }\n}\n\n/** Information about a summary relevant to a specific node in the tree */\nexport class SummaryNode {\n /** Creates an instance that is valid for the root with specific basePath and localPath */\n public static createForRoot(referenceSequenceNumber: number): SummaryNode {\n return new SummaryNode({\n referenceSequenceNumber,\n basePath: undefined,\n localPath: EscapedPath.create(\"\"), // root hard-coded to \"\"\n });\n }\n\n /** Summary reference sequence number, i.e. last sequence number seen when it was created */\n public get referenceSequenceNumber(): number {\n return this.summary.referenceSequenceNumber;\n }\n /** Full path to parent node, or undefined if this is the root */\n public get basePath(): EscapedPath | undefined {\n return this.summary.basePath;\n }\n /** Relative path to this node from its parent node */\n public get localPath(): EscapedPath {\n return this.summary.localPath;\n }\n /** Relative path from this node to its node innermost base summary */\n public get additionalPath(): EscapedPath | undefined {\n return this.summary.additionalPath;\n }\n public set additionalPath(additionalPath: EscapedPath | undefined) {\n this.summary.additionalPath = additionalPath;\n }\n constructor(private readonly summary: {\n readonly referenceSequenceNumber: number;\n readonly basePath: EscapedPath | undefined;\n readonly localPath: EscapedPath;\n additionalPath?: EscapedPath;\n }) { }\n\n /** Gets the full path to this node, to be used when sending a handle */\n public get fullPath(): EscapedPath {\n return this.basePath?.concat(this.localPath) ?? this.localPath;\n }\n\n /**\n * Gets the full path to this node's innermost base summary.\n * The children nodes can use this as their basePath to determine their path.\n */\n public get fullPathForChildren(): EscapedPath {\n return this.additionalPath !== undefined\n ? this.fullPath.concat(this.additionalPath)\n : this.fullPath;\n }\n\n /**\n * Creates a new node within the same summary for a child of this node.\n * @param id - id of the child node\n */\n public createForChild(id: string): SummaryNode {\n return new SummaryNode({\n referenceSequenceNumber: this.referenceSequenceNumber,\n basePath: this.fullPathForChildren,\n localPath: EscapedPath.create(id),\n });\n }\n}\n\n/**\n * Parameter to help encode summary with conditional behavior.\n * When fromSummary is true, it will contain the SummaryNode of\n * its previous summary, which it can use to point to with a handle.\n * When fromSummary is false, it will use an actual summary tree\n * as its base summary in case the first summary is a differential summary.\n */\nexport type EncodeSummaryParam = {\n fromSummary: true;\n summaryNode: SummaryNode;\n} | {\n fromSummary: false;\n initialSummary: ISummaryTreeWithStats;\n};\n\n/**\n * Information about the initial summary tree found from an attach op.\n */\nexport interface IInitialSummary {\n sequenceNumber: number;\n id: string;\n summary: ISummaryTreeWithStats | undefined;\n}\n\n/**\n * Represents the details needed to create a child summarizer node.\n */\nexport interface ICreateChildDetails {\n /** Summary from attach op if known */\n initialSummary: IInitialSummary | undefined;\n /** Latest summary from server node data */\n latestSummary: SummaryNode | undefined;\n /** Sequence number of latest known change to the node */\n changeSequenceNumber: number;\n}\n\nexport interface ISubtreeInfo<T extends ISnapshotTree | SummaryObject> {\n /** Tree to use to find children subtrees */\n childrenTree: T;\n /** Additional path part where children are isolated */\n childrenPathPart: string | undefined;\n}\n\n/**\n * Checks if the summary contains .channels subtree where the children subtrees\n * would be located if exists.\n * @param baseSummary - summary to check\n */\nexport function parseSummaryForSubtrees(baseSummary: ISnapshotTree): ISubtreeInfo<ISnapshotTree> {\n // New versions of snapshots have child nodes isolated in .channels subtree\n const channelsSubtree = baseSummary.trees[channelsTreeName];\n if (channelsSubtree !== undefined) {\n return {\n childrenTree: channelsSubtree,\n childrenPathPart: channelsTreeName,\n };\n }\n return {\n childrenTree: baseSummary,\n childrenPathPart: undefined,\n };\n}\n\n/**\n * Checks if the summary contains .channels subtree where the children subtrees\n * would be located if exists.\n * @param baseSummary - summary to check\n */\nexport function parseSummaryTreeForSubtrees(summary: ISummaryTree): ISubtreeInfo<SummaryObject> {\n // New versions of snapshots have child nodes isolated in .channels subtree\n const channelsSubtree = summary.tree[channelsTreeName];\n if (channelsSubtree !== undefined) {\n return {\n childrenTree: channelsSubtree,\n childrenPathPart: channelsTreeName,\n };\n }\n return {\n childrenTree: summary,\n childrenPathPart: undefined,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/runtime-utils",
3
- "version": "1.2.1",
3
+ "version": "2.0.0-internal.1.0.0.81601",
4
4
  "description": "Collection of utility functions for Fluid Runtime",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -62,22 +62,22 @@
62
62
  "dependencies": {
63
63
  "@fluidframework/common-definitions": "^0.20.1",
64
64
  "@fluidframework/common-utils": "^0.32.1",
65
- "@fluidframework/container-definitions": "^1.2.1",
66
- "@fluidframework/container-runtime-definitions": "^1.2.1",
67
- "@fluidframework/core-interfaces": "^1.2.1",
68
- "@fluidframework/datastore-definitions": "^1.2.1",
69
- "@fluidframework/garbage-collector": "^1.2.1",
70
- "@fluidframework/protocol-base": "^0.1036.5000",
71
- "@fluidframework/protocol-definitions": "^0.1028.2000",
72
- "@fluidframework/runtime-definitions": "^1.2.1",
73
- "@fluidframework/telemetry-utils": "^1.2.1"
65
+ "@fluidframework/container-definitions": "2.0.0-internal.1.0.0.81601",
66
+ "@fluidframework/container-runtime-definitions": "2.0.0-internal.1.0.0.81601",
67
+ "@fluidframework/core-interfaces": "2.0.0-internal.1.0.0.81601",
68
+ "@fluidframework/datastore-definitions": "2.0.0-internal.1.0.0.81601",
69
+ "@fluidframework/garbage-collector": "2.0.0-internal.1.0.0.81601",
70
+ "@fluidframework/protocol-base": "^0.1037.1000-0",
71
+ "@fluidframework/protocol-definitions": "^0.1029.1000-0",
72
+ "@fluidframework/runtime-definitions": "2.0.0-internal.1.0.0.81601",
73
+ "@fluidframework/telemetry-utils": "2.0.0-internal.1.0.0.81601"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@fluidframework/build-common": "^0.24.0",
77
- "@fluidframework/build-tools": "^0.2.74327",
77
+ "@fluidframework/build-tools": "^0.3.0-0",
78
78
  "@fluidframework/eslint-config-fluid": "^0.28.2000",
79
- "@fluidframework/mocha-test-setup": "^1.2.1",
80
- "@fluidframework/runtime-utils-previous": "npm:@fluidframework/runtime-utils@1.2.0",
79
+ "@fluidframework/mocha-test-setup": "2.0.0-internal.1.0.0.81601",
80
+ "@fluidframework/runtime-utils-previous": "npm:@fluidframework/runtime-utils@^1.0.0",
81
81
  "@microsoft/api-extractor": "^7.22.2",
82
82
  "@rushstack/eslint-config": "^2.5.1",
83
83
  "@types/mocha": "^9.1.1",
@@ -95,7 +95,7 @@
95
95
  "typescript-formatter": "7.1.0"
96
96
  },
97
97
  "typeValidation": {
98
- "version": "1.2.1",
98
+ "version": "2.0.0",
99
99
  "broken": {}
100
100
  }
101
101
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/runtime-utils";
9
- export const pkgVersion = "1.2.1";
9
+ export const pkgVersion = "2.0.0-internal.1.0.0.81601";
@@ -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
  /**
@@ -334,15 +290,14 @@ export class SummarizerNode implements IRootSummarizerNode {
334
290
 
335
291
  this.refreshLatestSummaryCore(referenceSequenceNumber);
336
292
 
337
- const { baseSummary, pathParts } = decodeSummary(snapshotTree, correlatedSummaryLogger);
338
-
339
293
  this._latestSummary = new SummaryNode({
340
294
  referenceSequenceNumber,
341
295
  basePath,
342
296
  localPath,
343
297
  });
344
298
 
345
- const { childrenTree, childrenPathPart } = parseSummaryForSubtrees(baseSummary);
299
+ const pathParts: string[] = [];
300
+ const { childrenTree, childrenPathPart } = parseSummaryForSubtrees(snapshotTree);
346
301
  if (childrenPathPart !== undefined) {
347
302
  pathParts.push(childrenPathPart);
348
303
  }
@@ -376,14 +331,6 @@ export class SummarizerNode implements IRootSummarizerNode {
376
331
  this.pendingSummaries.delete(key);
377
332
  }
378
333
  }
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
334
  }
388
335
 
389
336
  public loadBaseSummaryWithoutDifferential(snapshot: ISnapshotTree) {
@@ -398,46 +345,22 @@ export class SummarizerNode implements IRootSummarizerNode {
398
345
  public async loadBaseSummary(
399
346
  snapshot: ISnapshotTree,
400
347
  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);
348
+ ): Promise<ISnapshotTree> {
349
+ const pathParts: string[] = [];
350
+ const { childrenPathPart } = parseSummaryForSubtrees(snapshot);
406
351
  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);
352
+ pathParts.push(childrenPathPart);
412
353
  }
413
354
 
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
- );
355
+ if (pathParts.length > 0 && this._latestSummary !== undefined) {
356
+ this._latestSummary.additionalPath = EscapedPath.createAndConcat(pathParts);
422
357
  }
423
358
 
424
- return {
425
- baseSummary: decodedSummary.baseSummary,
426
- outstandingOps,
427
- };
359
+ return snapshot;
428
360
  }
429
361
 
430
362
  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
363
  this.invalidate(op.sequenceNumber);
439
- this.trackingSequenceNumber = op.sequenceNumber;
440
- this.outstandingOps.push(op);
441
364
  }
442
365
 
443
366
  public invalidate(sequenceNumber: number): void {
@@ -460,13 +383,6 @@ export class SummarizerNode implements IRootSummarizerNode {
460
383
  }
461
384
 
462
385
  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
386
 
471
387
  /**
472
388
  * Do not call constructor directly.
@@ -483,11 +399,6 @@ export class SummarizerNode implements IRootSummarizerNode {
483
399
  protected wipSummaryLogger?: ITelemetryLogger,
484
400
  ) {
485
401
  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
402
  }
492
403
 
493
404
  public createChild(
@@ -4,22 +4,14 @@
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:
25
17
  * 1. The latest summary was not udpated.
@@ -137,88 +129,6 @@ export class SummaryNode {
137
129
  }
138
130
  }
139
131
 
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
132
  /**
223
133
  * Parameter to help encode summary with conditional behavior.
224
134
  * When fromSummary is true, it will contain the SummaryNode of
@@ -234,42 +144,6 @@ export type EncodeSummaryParam = {
234
144
  initialSummary: ISummaryTreeWithStats;
235
145
  };
236
146
 
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
147
  /**
274
148
  * Information about the initial summary tree found from an attach op.
275
149
  */