@fluidframework/container-runtime 2.4.0-299707 → 2.4.0
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/CHANGELOG.md +12 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +10 -4
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +6 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +22 -10
- package/dist/containerRuntime.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +6 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +9 -1
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +4 -4
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +6 -18
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- 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/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +3 -4
- package/dist/summary/summaryCollection.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +10 -4
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +6 -0
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +20 -9
- package/lib/containerRuntime.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +6 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -0
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +4 -4
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +6 -18
- package/lib/opLifecycle/remoteMessageProcessor.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/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +3 -4
- package/lib/summary/summaryCollection.js.map +1 -1
- package/package.json +17 -17
- package/src/blobManager/blobManager.ts +10 -4
- package/src/containerRuntime.ts +24 -8
- package/src/opLifecycle/index.ts +1 -1
- package/src/opLifecycle/outbox.ts +11 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +8 -22
- package/src/packageVersion.ts +1 -1
- package/src/summary/summaryCollection.ts +3 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.4.0
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -129,18 +129,18 @@
|
|
|
129
129
|
"temp-directory": "nyc/.nyc_output"
|
|
130
130
|
},
|
|
131
131
|
"dependencies": {
|
|
132
|
-
"@fluid-internal/client-utils": "2.4.0
|
|
133
|
-
"@fluidframework/container-definitions": "2.4.0
|
|
134
|
-
"@fluidframework/container-runtime-definitions": "2.4.0
|
|
135
|
-
"@fluidframework/core-interfaces": "2.4.0
|
|
136
|
-
"@fluidframework/core-utils": "2.4.0
|
|
137
|
-
"@fluidframework/datastore": "2.4.0
|
|
138
|
-
"@fluidframework/driver-definitions": "2.4.0
|
|
139
|
-
"@fluidframework/driver-utils": "2.4.0
|
|
140
|
-
"@fluidframework/id-compressor": "2.4.0
|
|
141
|
-
"@fluidframework/runtime-definitions": "2.4.0
|
|
142
|
-
"@fluidframework/runtime-utils": "2.4.0
|
|
143
|
-
"@fluidframework/telemetry-utils": "2.4.0
|
|
132
|
+
"@fluid-internal/client-utils": "~2.4.0",
|
|
133
|
+
"@fluidframework/container-definitions": "~2.4.0",
|
|
134
|
+
"@fluidframework/container-runtime-definitions": "~2.4.0",
|
|
135
|
+
"@fluidframework/core-interfaces": "~2.4.0",
|
|
136
|
+
"@fluidframework/core-utils": "~2.4.0",
|
|
137
|
+
"@fluidframework/datastore": "~2.4.0",
|
|
138
|
+
"@fluidframework/driver-definitions": "~2.4.0",
|
|
139
|
+
"@fluidframework/driver-utils": "~2.4.0",
|
|
140
|
+
"@fluidframework/id-compressor": "~2.4.0",
|
|
141
|
+
"@fluidframework/runtime-definitions": "~2.4.0",
|
|
142
|
+
"@fluidframework/runtime-utils": "~2.4.0",
|
|
143
|
+
"@fluidframework/telemetry-utils": "~2.4.0",
|
|
144
144
|
"@tylerbu/sorted-btree-es6": "^1.8.0",
|
|
145
145
|
"double-ended-queue": "^2.1.0-0",
|
|
146
146
|
"lz4js": "^0.2.0",
|
|
@@ -149,16 +149,16 @@
|
|
|
149
149
|
"devDependencies": {
|
|
150
150
|
"@arethetypeswrong/cli": "^0.16.4",
|
|
151
151
|
"@biomejs/biome": "~1.8.3",
|
|
152
|
-
"@fluid-internal/mocha-test-setup": "2.4.0
|
|
153
|
-
"@fluid-private/stochastic-test-utils": "2.4.0
|
|
154
|
-
"@fluid-private/test-pairwise-generator": "2.4.0
|
|
152
|
+
"@fluid-internal/mocha-test-setup": "~2.4.0",
|
|
153
|
+
"@fluid-private/stochastic-test-utils": "~2.4.0",
|
|
154
|
+
"@fluid-private/test-pairwise-generator": "~2.4.0",
|
|
155
155
|
"@fluid-tools/benchmark": "^0.50.0",
|
|
156
156
|
"@fluid-tools/build-cli": "^0.48.0",
|
|
157
157
|
"@fluidframework/build-common": "^2.0.3",
|
|
158
158
|
"@fluidframework/build-tools": "^0.48.0",
|
|
159
159
|
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@~2.3.0",
|
|
160
160
|
"@fluidframework/eslint-config-fluid": "^5.4.0",
|
|
161
|
-
"@fluidframework/test-runtime-utils": "2.4.0
|
|
161
|
+
"@fluidframework/test-runtime-utils": "~2.4.0",
|
|
162
162
|
"@microsoft/api-extractor": "7.47.8",
|
|
163
163
|
"@types/double-ended-queue": "^2.1.0",
|
|
164
164
|
"@types/mocha": "^9.1.1",
|
|
@@ -811,6 +811,13 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
811
811
|
for (const [id, entry] of this.pendingBlobs) {
|
|
812
812
|
if (!localBlobs.has(entry)) {
|
|
813
813
|
localBlobs.add(entry);
|
|
814
|
+
// In order to follow natural blob creation flow we need to:
|
|
815
|
+
// 1 send the blob attach op
|
|
816
|
+
// 2 resolve the blob handle
|
|
817
|
+
// 3 wait for op referencing the blob
|
|
818
|
+
if (!entry.opsent) {
|
|
819
|
+
this.sendBlobAttachOp(id, entry.storageId);
|
|
820
|
+
}
|
|
814
821
|
// Resolving the blob handle to let hosts continue with their operations (it will resolve
|
|
815
822
|
// original createBlob call) and let them attach the blob. This is a lie we told since the upload
|
|
816
823
|
// hasn't finished yet, but it's fine since we will retry on rehydration.
|
|
@@ -843,7 +850,9 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
843
850
|
}
|
|
844
851
|
// Wait for all blobs to be attached. This is important, otherwise serialized container
|
|
845
852
|
// could send the blobAttach op without any op that references the blob, making it useless.
|
|
846
|
-
await Promise.allSettled(attachBlobsP).catch(() => {
|
|
853
|
+
await Promise.allSettled(attachBlobsP).catch(() => {
|
|
854
|
+
return undefined;
|
|
855
|
+
});
|
|
847
856
|
}
|
|
848
857
|
|
|
849
858
|
for (const [id, entry] of this.pendingBlobs) {
|
|
@@ -855,9 +864,6 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
855
864
|
continue;
|
|
856
865
|
}
|
|
857
866
|
assert(entry.attached === true, 0x790 /* stashed blob should be attached */);
|
|
858
|
-
if (!entry.opsent) {
|
|
859
|
-
this.sendBlobAttachOp(id, entry.storageId);
|
|
860
|
-
}
|
|
861
867
|
blobs[id] = {
|
|
862
868
|
blob: bufferToString(entry.blob, "base64"),
|
|
863
869
|
storageId: entry.storageId,
|
package/src/containerRuntime.ts
CHANGED
|
@@ -185,6 +185,7 @@ import {
|
|
|
185
185
|
OpSplitter,
|
|
186
186
|
Outbox,
|
|
187
187
|
RemoteMessageProcessor,
|
|
188
|
+
serializeOpContents,
|
|
188
189
|
} from "./opLifecycle/index.js";
|
|
189
190
|
import { pkgVersion } from "./packageVersion.js";
|
|
190
191
|
import {
|
|
@@ -762,7 +763,7 @@ function lastMessageFromMetadata(metadata: IContainerRuntimeMetadata | undefined
|
|
|
762
763
|
* to understand if/when it is hit.
|
|
763
764
|
* We only want to log this once, to avoid spamming telemetry if we are wrong and these cases are hit commonly.
|
|
764
765
|
*/
|
|
765
|
-
let getSingleUseLegacyLogCallback = (logger: ITelemetryLoggerExt, type: string) => {
|
|
766
|
+
export let getSingleUseLegacyLogCallback = (logger: ITelemetryLoggerExt, type: string) => {
|
|
766
767
|
return (codePath: string) => {
|
|
767
768
|
logger.sendTelemetryEvent({
|
|
768
769
|
eventName: "LegacyMessageFormat",
|
|
@@ -2710,8 +2711,13 @@ export class ContainerRuntime
|
|
|
2710
2711
|
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2711
2712
|
const logLegacyCase = getSingleUseLegacyLogCallback(this.logger, messageCopy.type);
|
|
2712
2713
|
|
|
2713
|
-
|
|
2714
|
-
|
|
2714
|
+
let runtimeBatch: boolean =
|
|
2715
|
+
hasModernRuntimeMessageEnvelope || isUnpackedRuntimeMessage(messageCopy);
|
|
2716
|
+
if (runtimeBatch) {
|
|
2717
|
+
// We expect runtime messages to have JSON contents - deserialize it in place.
|
|
2718
|
+
ensureContentsDeserialized(messageCopy);
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2715
2721
|
if (hasModernRuntimeMessageEnvelope) {
|
|
2716
2722
|
// If the message has the modern message envelope, then process it here.
|
|
2717
2723
|
// Here we unpack the message (decompress, unchunk, and/or ungroup) into a batch of messages with ContainerMessageType
|
|
@@ -2749,7 +2755,6 @@ export class ContainerRuntime
|
|
|
2749
2755
|
}
|
|
2750
2756
|
}
|
|
2751
2757
|
|
|
2752
|
-
let runtimeBatch: boolean = true;
|
|
2753
2758
|
// Reach out to PendingStateManager, either to zip localOpMetadata into the *local* message list,
|
|
2754
2759
|
// or to check to ensure the *remote* messages don't match the batchId of a pending local batch.
|
|
2755
2760
|
// This latter case would indicate that the container has forked - two copies are trying to persist the same local changes.
|
|
@@ -2807,12 +2812,23 @@ export class ContainerRuntime
|
|
|
2807
2812
|
runtimeBatch,
|
|
2808
2813
|
);
|
|
2809
2814
|
} else {
|
|
2815
|
+
if (!runtimeBatch) {
|
|
2816
|
+
// The DeltaManager used to do this, but doesn't anymore as of Loader v2.4
|
|
2817
|
+
// Anyone listening to our "op" event would expect the contents to be parsed per this same logic
|
|
2818
|
+
if (
|
|
2819
|
+
typeof messageCopy.contents === "string" &&
|
|
2820
|
+
messageCopy.contents !== "" &&
|
|
2821
|
+
messageCopy.type !== MessageType.ClientLeave
|
|
2822
|
+
) {
|
|
2823
|
+
messageCopy.contents = JSON.parse(messageCopy.contents);
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2810
2826
|
this.processInboundMessages(
|
|
2811
2827
|
[{ message: messageCopy, localOpMetadata: undefined }],
|
|
2812
2828
|
{ batchStart: true, batchEnd: true }, // Single message
|
|
2813
2829
|
local,
|
|
2814
2830
|
savedOp,
|
|
2815
|
-
|
|
2831
|
+
runtimeBatch,
|
|
2816
2832
|
);
|
|
2817
2833
|
}
|
|
2818
2834
|
|
|
@@ -4212,7 +4228,7 @@ export class ContainerRuntime
|
|
|
4212
4228
|
contents: idRange,
|
|
4213
4229
|
};
|
|
4214
4230
|
const idAllocationBatchMessage: BatchMessage = {
|
|
4215
|
-
contents:
|
|
4231
|
+
contents: serializeOpContents(idAllocationMessage),
|
|
4216
4232
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4217
4233
|
};
|
|
4218
4234
|
this.outbox.submitIdAllocation(idAllocationBatchMessage);
|
|
@@ -4275,13 +4291,13 @@ export class ContainerRuntime
|
|
|
4275
4291
|
contents: schemaChangeMessage,
|
|
4276
4292
|
};
|
|
4277
4293
|
this.outbox.submit({
|
|
4278
|
-
contents:
|
|
4294
|
+
contents: serializeOpContents(msg),
|
|
4279
4295
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4280
4296
|
});
|
|
4281
4297
|
}
|
|
4282
4298
|
|
|
4283
4299
|
const message: BatchMessage = {
|
|
4284
|
-
contents:
|
|
4300
|
+
contents: serializeOpContents(containerRuntimeMessage),
|
|
4285
4301
|
metadata,
|
|
4286
4302
|
localOpMetadata,
|
|
4287
4303
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
package/src/opLifecycle/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ export {
|
|
|
14
14
|
} from "./batchManager.js";
|
|
15
15
|
export { BatchMessage, IBatch, IBatchCheckpoint, IChunkedOp } from "./definitions.js";
|
|
16
16
|
export { DuplicateBatchDetector } from "./duplicateBatchDetector.js";
|
|
17
|
-
export { Outbox, getLongStack } from "./outbox.js";
|
|
17
|
+
export { Outbox, getLongStack, serializeOpContents } from "./outbox.js";
|
|
18
18
|
export { OpCompressor } from "./opCompressor.js";
|
|
19
19
|
export { OpDecompressor } from "./opDecompressor.js";
|
|
20
20
|
export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "@fluidframework/telemetry-utils/internal";
|
|
16
16
|
|
|
17
17
|
import { ICompressionRuntimeOptions } from "../containerRuntime.js";
|
|
18
|
+
import { OutboundContainerRuntimeMessage } from "../messageTypes.js";
|
|
18
19
|
import { PendingMessageResubmitData, PendingStateManager } from "../pendingStateManager.js";
|
|
19
20
|
|
|
20
21
|
import {
|
|
@@ -28,6 +29,8 @@ import { BatchMessage, IBatch, IBatchCheckpoint } from "./definitions.js";
|
|
|
28
29
|
import { OpCompressor } from "./opCompressor.js";
|
|
29
30
|
import { OpGroupingManager } from "./opGroupingManager.js";
|
|
30
31
|
import { OpSplitter } from "./opSplitter.js";
|
|
32
|
+
// eslint-disable-next-line unused-imports/no-unused-imports -- Used by "@link" comment annotation below
|
|
33
|
+
import { ensureContentsDeserialized } from "./remoteMessageProcessor.js";
|
|
31
34
|
|
|
32
35
|
export interface IOutboxConfig {
|
|
33
36
|
readonly compressionOptions: ICompressionRuntimeOptions;
|
|
@@ -54,6 +57,14 @@ export interface IOutboxParameters {
|
|
|
54
57
|
readonly closeContainer: (error?: ICriticalContainerError) => void;
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Before submitting an op to the Outbox, its contents must be serialized using this function.
|
|
62
|
+
* @remarks - The deserialization on process happens via the function {@link ensureContentsDeserialized}.
|
|
63
|
+
*/
|
|
64
|
+
export function serializeOpContents(contents: OutboundContainerRuntimeMessage): string {
|
|
65
|
+
return JSON.stringify(contents);
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
/**
|
|
58
69
|
* Temporarily increase the stack limit while executing the provided action.
|
|
59
70
|
* If a negative value is provided for `length`, no stack frames will be collected.
|
|
@@ -20,6 +20,8 @@ import { asBatchMetadata } from "../metadata.js";
|
|
|
20
20
|
import { OpDecompressor } from "./opDecompressor.js";
|
|
21
21
|
import { OpGroupingManager, isGroupedBatch } from "./opGroupingManager.js";
|
|
22
22
|
import { OpSplitter, isChunkedMessage } from "./opSplitter.js";
|
|
23
|
+
// eslint-disable-next-line unused-imports/no-unused-imports -- Used by "@link" comment annotation below
|
|
24
|
+
import { serializeOpContents } from "./outbox.js";
|
|
23
25
|
|
|
24
26
|
/** Info about the batch we learn when we process the first message */
|
|
25
27
|
export interface BatchStartInfo {
|
|
@@ -236,32 +238,16 @@ export class RemoteMessageProcessor {
|
|
|
236
238
|
}
|
|
237
239
|
|
|
238
240
|
/**
|
|
239
|
-
* Takes an incoming message
|
|
241
|
+
* Takes an incoming runtime message JSON.parse's its contents in place, if needed (old Loader does this for us).
|
|
242
|
+
* Only to be used for runtine messages.
|
|
243
|
+
* @remarks - Serialization during submit happens via {@link serializeOpContents}
|
|
240
244
|
* @param mutableMessage - op message received
|
|
241
|
-
* @param hasModernRuntimeMessageEnvelope - false if the message does not contain the modern op envelop where message.type is MessageType.Operation
|
|
242
|
-
* @param logLegacyCase - callback to log when legacy op is encountered
|
|
243
245
|
*/
|
|
244
|
-
export function ensureContentsDeserialized(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
logLegacyCase: (codePath: string) => void,
|
|
248
|
-
): void {
|
|
249
|
-
// This should become unconditional once (Loader LTS) DeltaManager.processInboundMessage() stops parsing content (ADO #12052)
|
|
250
|
-
// Note: Until that change is made in the loader, this case will never be hit.
|
|
251
|
-
// Then there will be a long time of needing both cases, until LTS catches up to the change.
|
|
252
|
-
let didParseJsonContents: boolean;
|
|
246
|
+
export function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMessage): void {
|
|
247
|
+
// This should become unconditional once Loader LTS reaches 2.4 or later.
|
|
248
|
+
// There will be a long time of needing both cases, until LTS advances to that point.
|
|
253
249
|
if (typeof mutableMessage.contents === "string" && mutableMessage.contents !== "") {
|
|
254
250
|
mutableMessage.contents = JSON.parse(mutableMessage.contents);
|
|
255
|
-
didParseJsonContents = true;
|
|
256
|
-
} else {
|
|
257
|
-
didParseJsonContents = false;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// The DeltaManager parses the contents of the message as JSON if it is a string,
|
|
261
|
-
// so we should never end up parsing it here.
|
|
262
|
-
// Let's observe if we are wrong about this to learn about these cases.
|
|
263
|
-
if (didParseJsonContents) {
|
|
264
|
-
logLegacyCase("ensureContentsDeserialized_foundJsonContents");
|
|
265
251
|
}
|
|
266
252
|
}
|
|
267
253
|
|
package/src/packageVersion.ts
CHANGED
|
@@ -353,11 +353,10 @@ export class SummaryCollection extends TypedEventEmitter<ISummaryCollectionOpEve
|
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
private parseContent(op: ISequencedDocumentMessage) {
|
|
356
|
-
// This should become unconditional once (Loader LTS)
|
|
357
|
-
//
|
|
358
|
-
// Then there will be a long time of needing both cases, until LTS catches up to the change.
|
|
356
|
+
// This should become unconditional once (Loader LTS) reaches 2.4 or later
|
|
357
|
+
// There will be a long time of needing both cases, until LTS catches up to the change.
|
|
359
358
|
// That said, we may instead move to listen for "op" events from ContainerRuntime,
|
|
360
|
-
// and parsing may not be required at all if ContainerRuntime.process()
|
|
359
|
+
// and parsing may not be required at all if ContainerRuntime.process() continues to parse it for all types of ops.
|
|
361
360
|
if (typeof op.contents === "string") {
|
|
362
361
|
op.contents = JSON.parse(op.contents);
|
|
363
362
|
}
|