@fluidframework/runtime-utils 1.2.2 → 2.0.0-internal.1.0.0.81589
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.
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summarizerNode/summarizerNode.d.ts +1 -12
- package/dist/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNode.js +13 -81
- package/dist/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summarizerNode/summarizerNodeUtils.d.ts +1 -37
- package/dist/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNodeUtils.js +1 -94
- package/dist/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summarizerNode/summarizerNode.d.ts +1 -12
- package/lib/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNode.js +14 -82
- package/lib/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summarizerNode/summarizerNodeUtils.d.ts +1 -37
- package/lib/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNodeUtils.js +0 -91
- package/lib/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/package.json +14 -14
- package/src/packageVersion.ts +1 -1
- package/src/summarizerNode/summarizerNode.ts +14 -103
- package/src/summarizerNode/summarizerNodeUtils.ts +0 -126
|
@@ -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.
|
|
3
|
+
"version": "2.0.0-internal.1.0.0.81589",
|
|
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": "
|
|
66
|
-
"@fluidframework/container-runtime-definitions": "
|
|
67
|
-
"@fluidframework/core-interfaces": "
|
|
68
|
-
"@fluidframework/datastore-definitions": "
|
|
69
|
-
"@fluidframework/garbage-collector": "
|
|
70
|
-
"@fluidframework/protocol-base": "^0.
|
|
71
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
72
|
-
"@fluidframework/runtime-definitions": "
|
|
73
|
-
"@fluidframework/telemetry-utils": "
|
|
65
|
+
"@fluidframework/container-definitions": "2.0.0-internal.1.0.0.81589",
|
|
66
|
+
"@fluidframework/container-runtime-definitions": "2.0.0-internal.1.0.0.81589",
|
|
67
|
+
"@fluidframework/core-interfaces": "2.0.0-internal.1.0.0.81589",
|
|
68
|
+
"@fluidframework/datastore-definitions": "2.0.0-internal.1.0.0.81589",
|
|
69
|
+
"@fluidframework/garbage-collector": "2.0.0-internal.1.0.0.81589",
|
|
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.81589",
|
|
73
|
+
"@fluidframework/telemetry-utils": "2.0.0-internal.1.0.0.81589"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@fluidframework/build-common": "^0.24.0",
|
|
77
|
-
"@fluidframework/build-tools": "^0.
|
|
77
|
+
"@fluidframework/build-tools": "^0.3.0-0",
|
|
78
78
|
"@fluidframework/eslint-config-fluid": "^0.28.2000",
|
|
79
|
-
"@fluidframework/mocha-test-setup": "
|
|
80
|
-
"@fluidframework/runtime-utils-previous": "npm:@fluidframework/runtime-utils
|
|
79
|
+
"@fluidframework/mocha-test-setup": "2.0.0-internal.1.0.0.81589",
|
|
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": "
|
|
98
|
+
"version": "2.0.0",
|
|
99
99
|
"broken": {}
|
|
100
100
|
}
|
|
101
101
|
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
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<
|
|
402
|
-
const
|
|
403
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
415
|
-
|
|
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
|
*/
|