@fluidframework/container-runtime 2.0.0-internal.3.0.2 → 2.0.0-internal.3.0.4
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/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +15 -3
- package/dist/containerRuntime.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +4 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +42 -3
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +3 -2
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +15 -3
- package/lib/containerRuntime.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +4 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +42 -3
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +3 -2
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +14 -14
- package/src/containerRuntime.ts +21 -2
- package/src/opLifecycle/opDecompressor.ts +49 -2
- package/src/opLifecycle/outbox.ts +3 -2
- package/src/packageVersion.ts +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
6
|
+
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
7
|
import { IMessageProcessingResult } from "./definitions";
|
|
7
8
|
/**
|
|
8
9
|
* State machine that "unrolls" contents of compressed batches of ops after decompressing them.
|
|
@@ -16,6 +17,9 @@ export declare class OpDecompressor {
|
|
|
16
17
|
private activeBatch;
|
|
17
18
|
private rootMessageContents;
|
|
18
19
|
private processedCount;
|
|
20
|
+
private readonly logger;
|
|
21
|
+
constructor(logger: ITelemetryLogger);
|
|
19
22
|
processMessage(message: ISequencedDocumentMessage): IMessageProcessingResult;
|
|
23
|
+
private isCompressed;
|
|
20
24
|
}
|
|
21
25
|
//# sourceMappingURL=opDecompressor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAGjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAEzD;;;;;;;GAOG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAAK;
|
|
1
|
+
{"version":3,"file":"opDecompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAGjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAEzD;;;;;;;GAOG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAEZ,MAAM,EAAE,gBAAgB;IAI7B,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,wBAAwB;IAyEnF,OAAO,CAAC,YAAY;CAuCvB"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { decompress } from "lz4js";
|
|
6
6
|
import { assert, IsoBuffer, Uint8ArrayToString } from "@fluidframework/common-utils";
|
|
7
|
+
import { ChildLogger } from "@fluidframework/telemetry-utils";
|
|
7
8
|
import { CompressionAlgorithms } from "../containerRuntime";
|
|
8
9
|
/**
|
|
9
10
|
* State machine that "unrolls" contents of compressed batches of ops after decompressing them.
|
|
@@ -14,14 +15,15 @@ import { CompressionAlgorithms } from "../containerRuntime";
|
|
|
14
15
|
* 4. An individually compressed op will have undefined batch metadata and compression set to true
|
|
15
16
|
*/
|
|
16
17
|
export class OpDecompressor {
|
|
17
|
-
constructor() {
|
|
18
|
+
constructor(logger) {
|
|
18
19
|
this.activeBatch = false;
|
|
19
20
|
this.processedCount = 0;
|
|
21
|
+
this.logger = ChildLogger.create(logger, "OpDecompressor");
|
|
20
22
|
}
|
|
21
23
|
processMessage(message) {
|
|
22
24
|
var _a, _b, _c, _d;
|
|
23
25
|
assert(message.compression === undefined || message.compression === CompressionAlgorithms.lz4, 0x511 /* Only lz4 compression is supported */);
|
|
24
|
-
if (((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch) === true && message
|
|
26
|
+
if (((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch) === true && this.isCompressed(message)) {
|
|
25
27
|
// Beginning of a compressed batch
|
|
26
28
|
assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
|
|
27
29
|
if (message.compression) {
|
|
@@ -58,7 +60,7 @@ export class OpDecompressor {
|
|
|
58
60
|
state: "Processed",
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
|
-
if (((_d = message.metadata) === null || _d === void 0 ? void 0 : _d.batch) === undefined && message
|
|
63
|
+
if (((_d = message.metadata) === null || _d === void 0 ? void 0 : _d.batch) === undefined && this.isCompressed(message)) {
|
|
62
64
|
// Single compressed message
|
|
63
65
|
assert(this.activeBatch === false, 0x4ba /* shouldn't receive compressed message in middle of a batch */);
|
|
64
66
|
const contents = IsoBuffer.from(message.contents.packedContents, "base64");
|
|
@@ -75,6 +77,43 @@ export class OpDecompressor {
|
|
|
75
77
|
state: "Skipped",
|
|
76
78
|
};
|
|
77
79
|
}
|
|
80
|
+
isCompressed(message) {
|
|
81
|
+
var _a, _b, _c;
|
|
82
|
+
if (message.compression === CompressionAlgorithms.lz4) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Back-compat self healing mechanism for ADO:3538, as loaders from
|
|
87
|
+
* version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not
|
|
88
|
+
* support adding the proper compression metadata to compressed messages submitted
|
|
89
|
+
* by the runtime. Should be removed after the loader reaches sufficient saturation
|
|
90
|
+
* for a version greater or equal than client_v2.0.0-internal.2.2.0.
|
|
91
|
+
*
|
|
92
|
+
* The condition holds true for compressed messages, regardless of metadata. We are ultimately
|
|
93
|
+
* looking for a message with a single property `packedContents` inside `contents`, of type 'string'
|
|
94
|
+
* with a base64 encoded value.
|
|
95
|
+
*/
|
|
96
|
+
try {
|
|
97
|
+
if (typeof message.contents === "object" &&
|
|
98
|
+
Object.keys(message.contents).length === 1 &&
|
|
99
|
+
((_a = message.contents) === null || _a === void 0 ? void 0 : _a.packedContents) !== undefined &&
|
|
100
|
+
typeof ((_b = message.contents) === null || _b === void 0 ? void 0 : _b.packedContents) === "string" &&
|
|
101
|
+
message.contents.packedContents.length > 0 &&
|
|
102
|
+
IsoBuffer.from(message.contents.packedContents, "base64").toString("base64") ===
|
|
103
|
+
message.contents.packedContents) {
|
|
104
|
+
this.logger.sendTelemetryEvent({
|
|
105
|
+
eventName: "LegacyCompression",
|
|
106
|
+
type: message.type,
|
|
107
|
+
batch: (_c = message.metadata) === null || _c === void 0 ? void 0 : _c.batch,
|
|
108
|
+
});
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
78
117
|
}
|
|
79
118
|
// We should not be mutating the input message nor its metadata
|
|
80
119
|
const newMessage = (originalMessage, contents) => (Object.assign(Object.assign({}, originalMessage), { contents, compression: undefined, metadata: Object.assign({}, originalMessage.metadata) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.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 { decompress } from \"lz4js\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { assert, IsoBuffer, Uint8ArrayToString } from \"@fluidframework/common-utils\";\nimport { CompressionAlgorithms } from \"../containerRuntime\";\nimport { IMessageProcessingResult } from \"./definitions\";\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n */\nexport class OpDecompressor {\n private activeBatch = false;\n private rootMessageContents: any | undefined;\n private processedCount = 0;\n\n public processMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {\n assert(\n message.compression === undefined || message.compression === CompressionAlgorithms.lz4,\n 0x511 /* Only lz4 compression is supported */);\n\n if (message.metadata?.batch === true && message.compression === CompressionAlgorithms.lz4) {\n // Beginning of a compressed batch\n assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);\n if (message.compression) {\n // lz4 is the only supported compression algorithm for now\n assert(message.compression === CompressionAlgorithms.lz4,\n 0x4b9 /* lz4 is currently the only supported compression algorithm */);\n }\n\n this.activeBatch = true;\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = Uint8ArrayToString(decompressedMessage);\n const asObj = JSON.parse(intoString);\n this.rootMessageContents = asObj;\n\n return {\n message: newMessage(message, this.rootMessageContents[this.processedCount++]),\n state: \"Accepted\",\n };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === undefined && this.activeBatch) {\n assert(message.contents === undefined, 0x512 /* Expecting empty message */);\n\n // Continuation of compressed batch\n return {\n message: newMessage(message, this.rootMessageContents[this.processedCount++]),\n state: \"Accepted\",\n };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === false) {\n // End of compressed batch\n const returnMessage = newMessage(message, this.rootMessageContents[this.processedCount++]);\n\n this.activeBatch = false;\n this.rootMessageContents = undefined;\n this.processedCount = 0;\n\n return {\n message: returnMessage,\n state: \"Processed\",\n };\n }\n\n if (message.metadata?.batch === undefined && message.compression === CompressionAlgorithms.lz4) {\n // Single compressed message\n assert(this.activeBatch === false, 0x4ba /* shouldn't receive compressed message in middle of a batch */);\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = new TextDecoder().decode(decompressedMessage);\n const asObj = JSON.parse(intoString);\n\n return {\n message: newMessage(message, asObj[0]),\n state: \"Processed\",\n };\n }\n\n return {\n message,\n state: \"Skipped\",\n };\n }\n}\n\n// We should not be mutating the input message nor its metadata\nconst newMessage = (originalMessage: ISequencedDocumentMessage, contents: any): ISequencedDocumentMessage => ({\n ...originalMessage,\n contents,\n compression: undefined,\n metadata: { ...originalMessage.metadata },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.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 { decompress } from \"lz4js\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { assert, IsoBuffer, Uint8ArrayToString } from \"@fluidframework/common-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { CompressionAlgorithms } from \"../containerRuntime\";\nimport { IMessageProcessingResult } from \"./definitions\";\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n */\nexport class OpDecompressor {\n private activeBatch = false;\n private rootMessageContents: any | undefined;\n private processedCount = 0;\n private readonly logger;\n\n constructor(logger: ITelemetryLogger) {\n this.logger = ChildLogger.create(logger, \"OpDecompressor\");\n }\n\n public processMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {\n assert(\n message.compression === undefined || message.compression === CompressionAlgorithms.lz4,\n 0x511 /* Only lz4 compression is supported */);\n\n if (message.metadata?.batch === true && this.isCompressed(message)) {\n // Beginning of a compressed batch\n assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);\n if (message.compression) {\n // lz4 is the only supported compression algorithm for now\n assert(message.compression === CompressionAlgorithms.lz4,\n 0x4b9 /* lz4 is currently the only supported compression algorithm */);\n }\n\n this.activeBatch = true;\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = Uint8ArrayToString(decompressedMessage);\n const asObj = JSON.parse(intoString);\n this.rootMessageContents = asObj;\n\n return {\n message: newMessage(message, this.rootMessageContents[this.processedCount++]),\n state: \"Accepted\",\n };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === undefined && this.activeBatch) {\n assert(message.contents === undefined, 0x512 /* Expecting empty message */);\n\n // Continuation of compressed batch\n return {\n message: newMessage(message, this.rootMessageContents[this.processedCount++]),\n state: \"Accepted\",\n };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === false) {\n // End of compressed batch\n const returnMessage = newMessage(message, this.rootMessageContents[this.processedCount++]);\n\n this.activeBatch = false;\n this.rootMessageContents = undefined;\n this.processedCount = 0;\n\n return {\n message: returnMessage,\n state: \"Processed\",\n };\n }\n\n if (message.metadata?.batch === undefined && this.isCompressed(message)) {\n // Single compressed message\n assert(this.activeBatch === false, 0x4ba /* shouldn't receive compressed message in middle of a batch */);\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = new TextDecoder().decode(decompressedMessage);\n const asObj = JSON.parse(intoString);\n\n return {\n message: newMessage(message, asObj[0]),\n state: \"Processed\",\n };\n }\n\n return {\n message,\n state: \"Skipped\",\n };\n }\n\n private isCompressed(message: ISequencedDocumentMessage) {\n if (message.compression === CompressionAlgorithms.lz4) {\n return true;\n }\n\n /**\n * Back-compat self healing mechanism for ADO:3538, as loaders from\n * version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not\n * support adding the proper compression metadata to compressed messages submitted\n * by the runtime. Should be removed after the loader reaches sufficient saturation\n * for a version greater or equal than client_v2.0.0-internal.2.2.0.\n *\n * The condition holds true for compressed messages, regardless of metadata. We are ultimately\n * looking for a message with a single property `packedContents` inside `contents`, of type 'string'\n * with a base64 encoded value.\n */\n try {\n if (\n typeof message.contents === \"object\" &&\n Object.keys(message.contents).length === 1 &&\n message.contents?.packedContents !== undefined &&\n typeof message.contents?.packedContents === \"string\" &&\n message.contents.packedContents.length > 0 &&\n IsoBuffer.from(message.contents.packedContents, \"base64\").toString(\"base64\") ===\n message.contents.packedContents\n ) {\n this.logger.sendTelemetryEvent({\n eventName: \"LegacyCompression\",\n type: message.type,\n batch: message.metadata?.batch,\n });\n return true;\n }\n } catch (err) {\n return false;\n }\n\n return false;\n }\n}\n\n// We should not be mutating the input message nor its metadata\nconst newMessage = (originalMessage: ISequencedDocumentMessage, contents: any): ISequencedDocumentMessage => ({\n ...originalMessage,\n contents,\n compression: undefined,\n metadata: { ...originalMessage.metadata },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/outbox.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAU,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IAExD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAClD,QAAQ,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACrC;AAED,qBAAa,MAAM;IAKH,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,iCAAiC,CAAa;gBAElC,MAAM,EAAE,iBAAiB;IAiBtD,IAAW,OAAO,IAAI,OAAO,CAE5B;IAEM,MAAM,CAAC,OAAO,EAAE,YAAY;IAa5B,YAAY,CAAC,OAAO,EAAE,YAAY;IA4BlC,KAAK;IAKZ,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/outbox.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAU,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IAExD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAClD,QAAQ,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACrC;AAED,qBAAa,MAAM;IAKH,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,iCAAiC,CAAa;gBAElC,MAAM,EAAE,iBAAiB;IAiBtD,IAAW,OAAO,IAAI,OAAO,CAE5B;IAEM,MAAM,CAAC,OAAO,EAAE,YAAY;IAa5B,YAAY,CAAC,OAAO,EAAE,YAAY;IA4BlC,KAAK;IAKZ,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IA+BrB;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IA2CjB,OAAO,CAAC,YAAY;IAkBb,UAAU;;;;CAMpB"}
|
|
@@ -76,8 +76,9 @@ export class Outbox {
|
|
|
76
76
|
compressBatch(batch) {
|
|
77
77
|
if (batch.content.length === 0
|
|
78
78
|
|| this.params.config.compressionOptions === undefined
|
|
79
|
-
|| this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes
|
|
80
|
-
|
|
79
|
+
|| this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes
|
|
80
|
+
|| this.params.containerContext.submitBatchFn === undefined) {
|
|
81
|
+
// Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress
|
|
81
82
|
return batch;
|
|
82
83
|
}
|
|
83
84
|
const compressedBatch = this.params.compressor.compressBatch(batch);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/opLifecycle/outbox.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 { IContainerContext } from \"@fluidframework/container-definitions\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport { MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ICompressionRuntimeOptions } from \"../containerRuntime\";\nimport { PendingStateManager } from \"../pendingStateManager\";\nimport { BatchManager } from \"./batchManager\";\nimport { BatchMessage, IBatch } from \"./definitions\";\nimport { OpCompressor } from \"./opCompressor\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport interface IOutboxConfig {\n readonly compressionOptions: ICompressionRuntimeOptions;\n // The maximum size of a batch that we can send over the wire.\n readonly maxBatchSizeInBytes: number;\n readonly enableOpReentryCheck?: boolean;\n};\n\nexport interface IOutboxParameters {\n readonly shouldSend: () => boolean,\n readonly pendingStateManager: PendingStateManager,\n readonly containerContext: IContainerContext,\n readonly config: IOutboxConfig,\n readonly compressor: OpCompressor;\n readonly splitter: OpSplitter;\n readonly logger: ITelemetryLogger;\n}\n\nexport class Outbox {\n private readonly attachFlowBatch: BatchManager;\n private readonly mainBatch: BatchManager;\n private readonly defaultAttachFlowSoftLimitInBytes = 64 * 1024;\n\n constructor(private readonly params: IOutboxParameters) {\n const isCompressionEnabled = this.params.config.compressionOptions.minimumBatchSizeInBytes !== Number.POSITIVE_INFINITY;\n // We need to allow infinite size batches if we enable compression\n const hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;\n const softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;\n\n this.attachFlowBatch = new BatchManager({\n hardLimit,\n softLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n this.mainBatch = new BatchManager({\n hardLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n }\n\n public get isEmpty(): boolean {\n return this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;\n }\n\n public submit(message: BatchMessage) {\n if (!this.mainBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.mainBatch.length,\n limit: this.mainBatch.options.hardLimit,\n });\n }\n }\n\n public submitAttach(message: BatchMessage) {\n if (!this.attachFlowBatch.push(message)) {\n // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged\n // when queue is not empty.\n // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit\n this.flushInternal(this.attachFlowBatch.popBatch());\n if (!this.attachFlowBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.attachFlowBatch.length,\n limit: this.attachFlowBatch.options.hardLimit,\n });\n }\n }\n\n // If compression is enabled, we will always successfully receive\n // attach ops and compress then send them at the next JS turn, regardless\n // of the overall size of the accumulated ops in the batch.\n // However, it is more efficient to flush these ops faster, preferably\n // after they reach a size which would benefit from compression.\n if (this.attachFlowBatch.contentSizeInBytes >= this.params.config.compressionOptions.minimumBatchSizeInBytes) {\n this.flushInternal(this.attachFlowBatch.popBatch());\n }\n }\n\n public flush() {\n this.flushInternal(this.attachFlowBatch.popBatch());\n this.flushInternal(this.mainBatch.popBatch());\n }\n\n private flushInternal(rawBatch: IBatch) {\n const processedBatch = this.compressBatch(rawBatch);\n const clientSequenceNumber = this.sendBatch(processedBatch);\n\n this.persistBatch(clientSequenceNumber, rawBatch.content);\n }\n\n private compressBatch(batch: IBatch): IBatch {\n if (batch.content.length === 0\n || this.params.config.compressionOptions === undefined\n || this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes) {\n // Nothing to do if the batch is empty or if compression is disabled or if we don't need to compress\n return batch;\n }\n\n const compressedBatch = this.params.compressor.compressBatch(batch);\n if (compressedBatch.contentSizeInBytes <= this.params.config.maxBatchSizeInBytes) {\n // If we don't reach the maximum supported size of a batch, it can safely be sent as is\n return compressedBatch;\n }\n\n if (this.params.splitter.isBatchChunkingEnabled) {\n return this.params.splitter.splitCompressedBatch(compressedBatch);\n }\n\n // If we've reached this point, the runtime would attempt to send a batch larger than the allowed size\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: batch.contentSizeInBytes,\n count: batch.content.length,\n limit: this.params.config.maxBatchSizeInBytes,\n compressed: true,\n });\n }\n\n /**\n * Sends the batch object to the container context to be sent over the wire.\n *\n * @param batch - batch to be sent\n * @returns the client sequence number of the last batched op which was sent and\n * -1 if there are no ops or the container cannot send ops.\n */\n private sendBatch(batch: IBatch): number {\n let clientSequenceNumber: number = -1;\n const length = batch.content.length;\n\n // Did we disconnect in the middle of turn-based batch?\n // If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n if (length === 0 || !this.params.shouldSend()) {\n return clientSequenceNumber;\n }\n\n if (this.params.containerContext.submitBatchFn === undefined) {\n // Legacy path - supporting old loader versions. Can be removed only when LTS moves above\n // version that has support for batches (submitBatchFn)\n for (const message of batch.content) {\n // Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways\n if (message.metadata?.compressed) {\n delete message.metadata.compressed;\n }\n\n clientSequenceNumber = this.params.containerContext.submitFn(\n MessageType.Operation,\n message.deserializedContent,\n true, // batch\n message.metadata);\n }\n\n this.params.containerContext.deltaManager.flush();\n } else {\n // returns clientSequenceNumber of last message in a batch\n clientSequenceNumber = this.params.containerContext.submitBatchFn(\n batch.content.map((message) => ({\n contents: message.contents,\n metadata: message.metadata,\n compression: message.compression,\n })));\n }\n\n // Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.\n clientSequenceNumber -= length - 1;\n assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);\n return clientSequenceNumber;\n }\n\n private persistBatch(initialClientSequenceNumber: number, batch: BatchMessage[]) {\n let clientSequenceNumber = initialClientSequenceNumber;\n // Let the PendingStateManager know that a message was submitted.\n // In future, need to shift toward keeping batch as a whole!\n for (const message of batch) {\n this.params.pendingStateManager.onSubmitMessage(\n message.deserializedContent.type,\n clientSequenceNumber,\n message.referenceSequenceNumber,\n message.deserializedContent.contents,\n message.localOpMetadata,\n message.metadata,\n );\n\n clientSequenceNumber++;\n }\n }\n\n public checkpoint() {\n return {\n mainBatch: this.mainBatch.checkpoint(),\n attachFlowBatch: this.attachFlowBatch.checkpoint(),\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/opLifecycle/outbox.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 { IContainerContext } from \"@fluidframework/container-definitions\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport { MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ICompressionRuntimeOptions } from \"../containerRuntime\";\nimport { PendingStateManager } from \"../pendingStateManager\";\nimport { BatchManager } from \"./batchManager\";\nimport { BatchMessage, IBatch } from \"./definitions\";\nimport { OpCompressor } from \"./opCompressor\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport interface IOutboxConfig {\n readonly compressionOptions: ICompressionRuntimeOptions;\n // The maximum size of a batch that we can send over the wire.\n readonly maxBatchSizeInBytes: number;\n readonly enableOpReentryCheck?: boolean;\n};\n\nexport interface IOutboxParameters {\n readonly shouldSend: () => boolean,\n readonly pendingStateManager: PendingStateManager,\n readonly containerContext: IContainerContext,\n readonly config: IOutboxConfig,\n readonly compressor: OpCompressor;\n readonly splitter: OpSplitter;\n readonly logger: ITelemetryLogger;\n}\n\nexport class Outbox {\n private readonly attachFlowBatch: BatchManager;\n private readonly mainBatch: BatchManager;\n private readonly defaultAttachFlowSoftLimitInBytes = 64 * 1024;\n\n constructor(private readonly params: IOutboxParameters) {\n const isCompressionEnabled = this.params.config.compressionOptions.minimumBatchSizeInBytes !== Number.POSITIVE_INFINITY;\n // We need to allow infinite size batches if we enable compression\n const hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;\n const softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;\n\n this.attachFlowBatch = new BatchManager({\n hardLimit,\n softLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n this.mainBatch = new BatchManager({\n hardLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n }\n\n public get isEmpty(): boolean {\n return this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;\n }\n\n public submit(message: BatchMessage) {\n if (!this.mainBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.mainBatch.length,\n limit: this.mainBatch.options.hardLimit,\n });\n }\n }\n\n public submitAttach(message: BatchMessage) {\n if (!this.attachFlowBatch.push(message)) {\n // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged\n // when queue is not empty.\n // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit\n this.flushInternal(this.attachFlowBatch.popBatch());\n if (!this.attachFlowBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.attachFlowBatch.length,\n limit: this.attachFlowBatch.options.hardLimit,\n });\n }\n }\n\n // If compression is enabled, we will always successfully receive\n // attach ops and compress then send them at the next JS turn, regardless\n // of the overall size of the accumulated ops in the batch.\n // However, it is more efficient to flush these ops faster, preferably\n // after they reach a size which would benefit from compression.\n if (this.attachFlowBatch.contentSizeInBytes >= this.params.config.compressionOptions.minimumBatchSizeInBytes) {\n this.flushInternal(this.attachFlowBatch.popBatch());\n }\n }\n\n public flush() {\n this.flushInternal(this.attachFlowBatch.popBatch());\n this.flushInternal(this.mainBatch.popBatch());\n }\n\n private flushInternal(rawBatch: IBatch) {\n const processedBatch = this.compressBatch(rawBatch);\n const clientSequenceNumber = this.sendBatch(processedBatch);\n\n this.persistBatch(clientSequenceNumber, rawBatch.content);\n }\n\n private compressBatch(batch: IBatch): IBatch {\n if (batch.content.length === 0\n || this.params.config.compressionOptions === undefined\n || this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes\n || this.params.containerContext.submitBatchFn === undefined) {\n // Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress\n return batch;\n }\n\n const compressedBatch = this.params.compressor.compressBatch(batch);\n if (compressedBatch.contentSizeInBytes <= this.params.config.maxBatchSizeInBytes) {\n // If we don't reach the maximum supported size of a batch, it can safely be sent as is\n return compressedBatch;\n }\n\n if (this.params.splitter.isBatchChunkingEnabled) {\n return this.params.splitter.splitCompressedBatch(compressedBatch);\n }\n\n // If we've reached this point, the runtime would attempt to send a batch larger than the allowed size\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: batch.contentSizeInBytes,\n count: batch.content.length,\n limit: this.params.config.maxBatchSizeInBytes,\n compressed: true,\n });\n }\n\n /**\n * Sends the batch object to the container context to be sent over the wire.\n *\n * @param batch - batch to be sent\n * @returns the client sequence number of the last batched op which was sent and\n * -1 if there are no ops or the container cannot send ops.\n */\n private sendBatch(batch: IBatch): number {\n let clientSequenceNumber: number = -1;\n const length = batch.content.length;\n\n // Did we disconnect in the middle of turn-based batch?\n // If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n if (length === 0 || !this.params.shouldSend()) {\n return clientSequenceNumber;\n }\n\n if (this.params.containerContext.submitBatchFn === undefined) {\n // Legacy path - supporting old loader versions. Can be removed only when LTS moves above\n // version that has support for batches (submitBatchFn)\n for (const message of batch.content) {\n // Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways\n if (message.metadata?.compressed) {\n delete message.metadata.compressed;\n }\n\n clientSequenceNumber = this.params.containerContext.submitFn(\n MessageType.Operation,\n message.deserializedContent,\n true, // batch\n message.metadata);\n }\n\n this.params.containerContext.deltaManager.flush();\n } else {\n // returns clientSequenceNumber of last message in a batch\n clientSequenceNumber = this.params.containerContext.submitBatchFn(\n batch.content.map((message) => ({\n contents: message.contents,\n metadata: message.metadata,\n compression: message.compression,\n })));\n }\n\n // Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.\n clientSequenceNumber -= length - 1;\n assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);\n return clientSequenceNumber;\n }\n\n private persistBatch(initialClientSequenceNumber: number, batch: BatchMessage[]) {\n let clientSequenceNumber = initialClientSequenceNumber;\n // Let the PendingStateManager know that a message was submitted.\n // In future, need to shift toward keeping batch as a whole!\n for (const message of batch) {\n this.params.pendingStateManager.onSubmitMessage(\n message.deserializedContent.type,\n clientSequenceNumber,\n message.referenceSequenceNumber,\n message.deserializedContent.contents,\n message.localOpMetadata,\n message.metadata,\n );\n\n clientSequenceNumber++;\n }\n }\n\n public checkpoint() {\n return {\n mainBatch: this.mainBatch.checkpoint(),\n attachFlowBatch: this.attachFlowBatch.checkpoint(),\n };\n }\n}\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/container-runtime";
|
|
8
|
-
export declare const pkgVersion = "2.0.0-internal.3.0.
|
|
8
|
+
export declare const pkgVersion = "2.0.0-internal.3.0.4";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
package/lib/packageVersion.js
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export const pkgName = "@fluidframework/container-runtime";
|
|
8
|
-
export const pkgVersion = "2.0.0-internal.3.0.
|
|
8
|
+
export const pkgVersion = "2.0.0-internal.3.0.4";
|
|
9
9
|
//# sourceMappingURL=packageVersion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-runtime\";\nexport const pkgVersion = \"2.0.0-internal.3.0.
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-runtime\";\nexport const pkgVersion = \"2.0.0-internal.3.0.4\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.0.0-internal.3.0.
|
|
3
|
+
"version": "2.0.0-internal.3.0.4",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -65,19 +65,19 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
67
67
|
"@fluidframework/common-utils": "^1.0.0",
|
|
68
|
-
"@fluidframework/container-definitions": ">=2.0.0-internal.3.0.
|
|
69
|
-
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.3.0.
|
|
70
|
-
"@fluidframework/container-utils": ">=2.0.0-internal.3.0.
|
|
71
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.3.0.
|
|
72
|
-
"@fluidframework/datastore": ">=2.0.0-internal.3.0.
|
|
73
|
-
"@fluidframework/driver-definitions": ">=2.0.0-internal.3.0.
|
|
74
|
-
"@fluidframework/driver-utils": ">=2.0.0-internal.3.0.
|
|
75
|
-
"@fluidframework/garbage-collector": ">=2.0.0-internal.3.0.
|
|
68
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
69
|
+
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
70
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
71
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
72
|
+
"@fluidframework/datastore": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
73
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
74
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
75
|
+
"@fluidframework/garbage-collector": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
76
76
|
"@fluidframework/protocol-base": "^0.1038.2000",
|
|
77
77
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
78
|
-
"@fluidframework/runtime-definitions": ">=2.0.0-internal.3.0.
|
|
79
|
-
"@fluidframework/runtime-utils": ">=2.0.0-internal.3.0.
|
|
80
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-internal.3.0.
|
|
78
|
+
"@fluidframework/runtime-definitions": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
79
|
+
"@fluidframework/runtime-utils": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
80
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
81
81
|
"double-ended-queue": "^2.1.0-0",
|
|
82
82
|
"events": "^3.1.0",
|
|
83
83
|
"lz4js": "^0.2.0",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"@fluidframework/build-tools": "^0.8.0",
|
|
90
90
|
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.0.0-internal.3.0.0",
|
|
91
91
|
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
92
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.0.
|
|
93
|
-
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.3.0.
|
|
92
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
93
|
+
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.3.0.4 <2.0.0-internal.4.0.0",
|
|
94
94
|
"@microsoft/api-extractor": "^7.22.2",
|
|
95
95
|
"@rushstack/eslint-config": "^2.5.1",
|
|
96
96
|
"@types/double-ended-queue": "^2.1.0",
|
package/src/containerRuntime.ts
CHANGED
|
@@ -1083,7 +1083,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1083
1083
|
Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
|
|
1084
1084
|
runtimeOptions.maxBatchSizeInBytes,
|
|
1085
1085
|
this.mc.logger);
|
|
1086
|
-
this.remoteMessageProcessor = new RemoteMessageProcessor(
|
|
1086
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(
|
|
1087
|
+
opSplitter,
|
|
1088
|
+
new OpDecompressor(this.mc.logger),
|
|
1089
|
+
);
|
|
1087
1090
|
|
|
1088
1091
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
1089
1092
|
|
|
@@ -1802,7 +1805,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1802
1805
|
case ContainerMessageType.Rejoin:
|
|
1803
1806
|
break;
|
|
1804
1807
|
default:
|
|
1805
|
-
|
|
1808
|
+
if (runtimeMessage) {
|
|
1809
|
+
const error = DataProcessingError.create(
|
|
1810
|
+
// Former assert 0x3ce
|
|
1811
|
+
"Runtime message of unknown type",
|
|
1812
|
+
"OpProcessing",
|
|
1813
|
+
message,
|
|
1814
|
+
{
|
|
1815
|
+
local,
|
|
1816
|
+
type: message.type,
|
|
1817
|
+
contentType: typeof message.contents,
|
|
1818
|
+
batch: message.metadata?.batch,
|
|
1819
|
+
compression: message.compression,
|
|
1820
|
+
},
|
|
1821
|
+
);
|
|
1822
|
+
this.closeFn(error);
|
|
1823
|
+
throw error;
|
|
1824
|
+
}
|
|
1806
1825
|
}
|
|
1807
1826
|
|
|
1808
1827
|
// For back-compat, notify only about runtime messages for now.
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { decompress } from "lz4js";
|
|
7
7
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
8
8
|
import { assert, IsoBuffer, Uint8ArrayToString } from "@fluidframework/common-utils";
|
|
9
|
+
import { ChildLogger } from "@fluidframework/telemetry-utils";
|
|
10
|
+
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
9
11
|
import { CompressionAlgorithms } from "../containerRuntime";
|
|
10
12
|
import { IMessageProcessingResult } from "./definitions";
|
|
11
13
|
|
|
@@ -21,13 +23,18 @@ export class OpDecompressor {
|
|
|
21
23
|
private activeBatch = false;
|
|
22
24
|
private rootMessageContents: any | undefined;
|
|
23
25
|
private processedCount = 0;
|
|
26
|
+
private readonly logger;
|
|
27
|
+
|
|
28
|
+
constructor(logger: ITelemetryLogger) {
|
|
29
|
+
this.logger = ChildLogger.create(logger, "OpDecompressor");
|
|
30
|
+
}
|
|
24
31
|
|
|
25
32
|
public processMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {
|
|
26
33
|
assert(
|
|
27
34
|
message.compression === undefined || message.compression === CompressionAlgorithms.lz4,
|
|
28
35
|
0x511 /* Only lz4 compression is supported */);
|
|
29
36
|
|
|
30
|
-
if (message.metadata?.batch === true && message
|
|
37
|
+
if (message.metadata?.batch === true && this.isCompressed(message)) {
|
|
31
38
|
// Beginning of a compressed batch
|
|
32
39
|
assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
|
|
33
40
|
if (message.compression) {
|
|
@@ -74,7 +81,7 @@ export class OpDecompressor {
|
|
|
74
81
|
};
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
if (message.metadata?.batch === undefined && message
|
|
84
|
+
if (message.metadata?.batch === undefined && this.isCompressed(message)) {
|
|
78
85
|
// Single compressed message
|
|
79
86
|
assert(this.activeBatch === false, 0x4ba /* shouldn't receive compressed message in middle of a batch */);
|
|
80
87
|
|
|
@@ -94,6 +101,46 @@ export class OpDecompressor {
|
|
|
94
101
|
state: "Skipped",
|
|
95
102
|
};
|
|
96
103
|
}
|
|
104
|
+
|
|
105
|
+
private isCompressed(message: ISequencedDocumentMessage) {
|
|
106
|
+
if (message.compression === CompressionAlgorithms.lz4) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Back-compat self healing mechanism for ADO:3538, as loaders from
|
|
112
|
+
* version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not
|
|
113
|
+
* support adding the proper compression metadata to compressed messages submitted
|
|
114
|
+
* by the runtime. Should be removed after the loader reaches sufficient saturation
|
|
115
|
+
* for a version greater or equal than client_v2.0.0-internal.2.2.0.
|
|
116
|
+
*
|
|
117
|
+
* The condition holds true for compressed messages, regardless of metadata. We are ultimately
|
|
118
|
+
* looking for a message with a single property `packedContents` inside `contents`, of type 'string'
|
|
119
|
+
* with a base64 encoded value.
|
|
120
|
+
*/
|
|
121
|
+
try {
|
|
122
|
+
if (
|
|
123
|
+
typeof message.contents === "object" &&
|
|
124
|
+
Object.keys(message.contents).length === 1 &&
|
|
125
|
+
message.contents?.packedContents !== undefined &&
|
|
126
|
+
typeof message.contents?.packedContents === "string" &&
|
|
127
|
+
message.contents.packedContents.length > 0 &&
|
|
128
|
+
IsoBuffer.from(message.contents.packedContents, "base64").toString("base64") ===
|
|
129
|
+
message.contents.packedContents
|
|
130
|
+
) {
|
|
131
|
+
this.logger.sendTelemetryEvent({
|
|
132
|
+
eventName: "LegacyCompression",
|
|
133
|
+
type: message.type,
|
|
134
|
+
batch: message.metadata?.batch,
|
|
135
|
+
});
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
97
144
|
}
|
|
98
145
|
|
|
99
146
|
// We should not be mutating the input message nor its metadata
|
|
@@ -114,8 +114,9 @@ export class Outbox {
|
|
|
114
114
|
private compressBatch(batch: IBatch): IBatch {
|
|
115
115
|
if (batch.content.length === 0
|
|
116
116
|
|| this.params.config.compressionOptions === undefined
|
|
117
|
-
|| this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes
|
|
118
|
-
|
|
117
|
+
|| this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes
|
|
118
|
+
|| this.params.containerContext.submitBatchFn === undefined) {
|
|
119
|
+
// Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress
|
|
119
120
|
return batch;
|
|
120
121
|
}
|
|
121
122
|
|
package/src/packageVersion.ts
CHANGED