@fluidframework/container-runtime 2.1.0-281041 → 2.1.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 +4 -0
- package/README.md +4 -4
- package/container-runtime.test-files.tar +0 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +11 -3
- package/dist/containerRuntime.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +1 -1
- package/dist/opLifecycle/batchManager.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.js +1 -1
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +4 -2
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +22 -10
- 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/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +12 -4
- package/lib/containerRuntime.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +1 -1
- package/lib/opLifecycle/batchManager.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.js +1 -1
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +4 -2
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +20 -9
- 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/package.json +24 -24
- package/src/containerRuntime.ts +15 -3
- package/src/opLifecycle/batchManager.ts +4 -1
- package/src/opLifecycle/index.ts +5 -1
- package/src/opLifecycle/outbox.ts +1 -1
- package/src/opLifecycle/remoteMessageProcessor.ts +33 -10
- package/src/packageVersion.ts +1 -1
package/src/containerRuntime.ts
CHANGED
|
@@ -171,6 +171,7 @@ import { IBatchMetadata, ISavedOpMetadata } from "./metadata.js";
|
|
|
171
171
|
import {
|
|
172
172
|
BatchId,
|
|
173
173
|
BatchMessage,
|
|
174
|
+
ensureContentsDeserialized,
|
|
174
175
|
IBatch,
|
|
175
176
|
IBatchCheckpoint,
|
|
176
177
|
OpCompressor,
|
|
@@ -2616,14 +2617,25 @@ export class ContainerRuntime
|
|
|
2616
2617
|
// or something different, like a system message.
|
|
2617
2618
|
const modernRuntimeMessage = messageArg.type === MessageType.Operation;
|
|
2618
2619
|
|
|
2620
|
+
const savedOp = (messageArg.metadata as ISavedOpMetadata)?.savedOp;
|
|
2621
|
+
|
|
2622
|
+
// There is some ancient back-compat code that we'd like to instrument
|
|
2623
|
+
// to understand if/when it is hit.
|
|
2624
|
+
const logLegacyCase = (codePath: string) =>
|
|
2625
|
+
this.logger.sendTelemetryEvent({
|
|
2626
|
+
eventName: "LegacyMessageFormat",
|
|
2627
|
+
details: { codePath, type: messageArg.type },
|
|
2628
|
+
});
|
|
2629
|
+
|
|
2619
2630
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
2620
2631
|
// There might be multiple container instances receiving the same message.
|
|
2621
2632
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2622
2633
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
2623
2634
|
const messageCopy = { ...messageArg };
|
|
2624
|
-
|
|
2635
|
+
// We expect runtime messages to have JSON contents - deserialize it in place.
|
|
2636
|
+
ensureContentsDeserialized(messageCopy, modernRuntimeMessage, logLegacyCase);
|
|
2625
2637
|
if (modernRuntimeMessage) {
|
|
2626
|
-
const processResult = this.remoteMessageProcessor.process(messageCopy);
|
|
2638
|
+
const processResult = this.remoteMessageProcessor.process(messageCopy, logLegacyCase);
|
|
2627
2639
|
if (processResult === undefined) {
|
|
2628
2640
|
// This means the incoming message is an incomplete part of a message or batch
|
|
2629
2641
|
// and we need to process more messages before the rest of the system can understand it.
|
|
@@ -4324,7 +4336,7 @@ export class ContainerRuntime
|
|
|
4324
4336
|
fetchSource: FetchSource.noCache,
|
|
4325
4337
|
});
|
|
4326
4338
|
const id = snapshot.snapshotTree.id;
|
|
4327
|
-
assert(id !== undefined,
|
|
4339
|
+
assert(id !== undefined, 0x9d0 /* id of the fetched snapshot should be defined */);
|
|
4328
4340
|
props.snapshotVersion = id;
|
|
4329
4341
|
snapshotTree = snapshot.snapshotTree;
|
|
4330
4342
|
} else {
|
|
@@ -156,7 +156,10 @@ const addBatchMetadata = (batch: IBatch, batchId?: BatchId): IBatch => {
|
|
|
156
156
|
|
|
157
157
|
const firstMsg = batch.messages[0];
|
|
158
158
|
const lastMsg = batch.messages[batchEnd];
|
|
159
|
-
assert(
|
|
159
|
+
assert(
|
|
160
|
+
firstMsg !== undefined && lastMsg !== undefined,
|
|
161
|
+
0x9d1 /* expected non-empty batch */,
|
|
162
|
+
);
|
|
160
163
|
|
|
161
164
|
const firstMetadata: Partial<IBatchMetadata> = firstMsg.metadata ?? {};
|
|
162
165
|
const lastMetadata: Partial<IBatchMetadata> = lastMsg.metadata ?? {};
|
package/src/opLifecycle/index.ts
CHANGED
|
@@ -16,7 +16,11 @@ export { Outbox, getLongStack } from "./outbox.js";
|
|
|
16
16
|
export { OpCompressor } from "./opCompressor.js";
|
|
17
17
|
export { OpDecompressor } from "./opDecompressor.js";
|
|
18
18
|
export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
|
|
19
|
-
export {
|
|
19
|
+
export {
|
|
20
|
+
ensureContentsDeserialized,
|
|
21
|
+
RemoteMessageProcessor,
|
|
22
|
+
unpackRuntimeMessage,
|
|
23
|
+
} from "./remoteMessageProcessor.js";
|
|
20
24
|
export {
|
|
21
25
|
OpGroupingManager,
|
|
22
26
|
OpGroupingManagerConfig,
|
|
@@ -298,7 +298,7 @@ export class Outbox {
|
|
|
298
298
|
clientSequenceNumber = this.sendBatch(processedBatch);
|
|
299
299
|
assert(
|
|
300
300
|
clientSequenceNumber === undefined || clientSequenceNumber >= 0,
|
|
301
|
-
|
|
301
|
+
0x9d2 /* unexpected negative clientSequenceNumber (empty batch should yield undefined) */,
|
|
302
302
|
);
|
|
303
303
|
}
|
|
304
304
|
|
|
@@ -70,7 +70,10 @@ export class RemoteMessageProcessor {
|
|
|
70
70
|
* @returns all the unchunked, decompressed, ungrouped, unpacked InboundSequencedContainerRuntimeMessage from a single batch
|
|
71
71
|
* or undefined if the batch is not yet complete.
|
|
72
72
|
*/
|
|
73
|
-
public process(
|
|
73
|
+
public process(
|
|
74
|
+
remoteMessageCopy: ISequencedDocumentMessage,
|
|
75
|
+
logLegacyCase: (codePath: string) => void,
|
|
76
|
+
):
|
|
74
77
|
| {
|
|
75
78
|
messages: InboundSequencedContainerRuntimeMessage[];
|
|
76
79
|
batchStartCsn: number;
|
|
@@ -78,8 +81,6 @@ export class RemoteMessageProcessor {
|
|
|
78
81
|
| undefined {
|
|
79
82
|
let message = remoteMessageCopy;
|
|
80
83
|
|
|
81
|
-
ensureContentsDeserialized(message);
|
|
82
|
-
|
|
83
84
|
if (isChunkedMessage(message)) {
|
|
84
85
|
const chunkProcessingResult = this.opSplitter.processChunk(message);
|
|
85
86
|
// Only continue further if current chunk is the final chunk
|
|
@@ -104,10 +105,13 @@ export class RemoteMessageProcessor {
|
|
|
104
105
|
|
|
105
106
|
if (isGroupedBatch(message)) {
|
|
106
107
|
// We should be awaiting a new batch (batchStartCsn undefined)
|
|
107
|
-
assert(
|
|
108
|
+
assert(
|
|
109
|
+
this.batchStartCsn === undefined,
|
|
110
|
+
0x9d3 /* Grouped batch interrupting another batch */,
|
|
111
|
+
);
|
|
108
112
|
assert(
|
|
109
113
|
this.processorBatch.length === 0,
|
|
110
|
-
|
|
114
|
+
0x9d4 /* Processor batch should be empty on grouped batch */,
|
|
111
115
|
);
|
|
112
116
|
return {
|
|
113
117
|
messages: this.opGroupingManager.ungroupOp(message).map(unpack),
|
|
@@ -118,7 +122,7 @@ export class RemoteMessageProcessor {
|
|
|
118
122
|
const batchStartCsn = this.getAndUpdateBatchStartCsn(message);
|
|
119
123
|
|
|
120
124
|
// Do a final unpack of runtime messages in case the message was not grouped, compressed, or chunked
|
|
121
|
-
unpackRuntimeMessage(message);
|
|
125
|
+
unpackRuntimeMessage(message, logLegacyCase);
|
|
122
126
|
this.processorBatch.push(message as InboundSequencedContainerRuntimeMessage);
|
|
123
127
|
|
|
124
128
|
// this.batchStartCsn is undefined only if we have processed all messages in the batch.
|
|
@@ -146,7 +150,7 @@ export class RemoteMessageProcessor {
|
|
|
146
150
|
const batchMetadataFlag = asBatchMetadata(message.metadata)?.batch;
|
|
147
151
|
if (this.batchStartCsn === undefined) {
|
|
148
152
|
// We are waiting for a new batch
|
|
149
|
-
assert(batchMetadataFlag !== false,
|
|
153
|
+
assert(batchMetadataFlag !== false, 0x9d5 /* Unexpected batch end marker */);
|
|
150
154
|
|
|
151
155
|
// Start of a new multi-message batch
|
|
152
156
|
if (batchMetadataFlag === true) {
|
|
@@ -162,7 +166,7 @@ export class RemoteMessageProcessor {
|
|
|
162
166
|
// We are in the middle or end of an existing multi-message batch. Return the current batchStartCsn
|
|
163
167
|
const batchStartCsn = this.batchStartCsn;
|
|
164
168
|
|
|
165
|
-
assert(batchMetadataFlag !== true,
|
|
169
|
+
assert(batchMetadataFlag !== true, 0x9d6 /* Unexpected batch start marker */);
|
|
166
170
|
if (batchMetadataFlag === false) {
|
|
167
171
|
// Batch end? Then get ready for the next batch to start
|
|
168
172
|
this.batchStartCsn = undefined;
|
|
@@ -173,12 +177,27 @@ export class RemoteMessageProcessor {
|
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
/** Takes an incoming message and if the contents is a string, JSON.parse's it in place */
|
|
176
|
-
function ensureContentsDeserialized(
|
|
180
|
+
export function ensureContentsDeserialized(
|
|
181
|
+
mutableMessage: ISequencedDocumentMessage,
|
|
182
|
+
modernRuntimeMessage: boolean,
|
|
183
|
+
logLegacyCase: (codePath: string) => void,
|
|
184
|
+
): void {
|
|
177
185
|
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
178
186
|
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
179
187
|
// Old ops may contain empty string (I assume noops).
|
|
188
|
+
let parsedJsonContents: boolean;
|
|
180
189
|
if (typeof mutableMessage.contents === "string" && mutableMessage.contents !== "") {
|
|
181
190
|
mutableMessage.contents = JSON.parse(mutableMessage.contents);
|
|
191
|
+
parsedJsonContents = true;
|
|
192
|
+
} else {
|
|
193
|
+
parsedJsonContents = false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// We expect Modern Runtime Messages to have JSON serialized contents,
|
|
197
|
+
// and all other messages not to (system messages and legacy runtime messages without outer "op" type envelope)
|
|
198
|
+
// Let's observe if we are wrong about this to learn about these cases.
|
|
199
|
+
if (modernRuntimeMessage !== parsedJsonContents) {
|
|
200
|
+
logLegacyCase("ensureContentsDeserialized_unexpectedContentsType");
|
|
182
201
|
}
|
|
183
202
|
}
|
|
184
203
|
|
|
@@ -214,7 +233,10 @@ function unpack(message: ISequencedDocumentMessage): InboundSequencedContainerRu
|
|
|
214
233
|
*
|
|
215
234
|
* @internal
|
|
216
235
|
*/
|
|
217
|
-
export function unpackRuntimeMessage(
|
|
236
|
+
export function unpackRuntimeMessage(
|
|
237
|
+
message: ISequencedDocumentMessage,
|
|
238
|
+
logLegacyCase: (codePath: string) => void = () => {},
|
|
239
|
+
): boolean {
|
|
218
240
|
if (message.type !== MessageType.Operation) {
|
|
219
241
|
// Legacy format, but it's already "unpacked",
|
|
220
242
|
// i.e. message.type is actually ContainerMessageType.
|
|
@@ -230,6 +252,7 @@ export function unpackRuntimeMessage(message: ISequencedDocumentMessage): boolea
|
|
|
230
252
|
(message.contents as { type?: unknown }).type === undefined
|
|
231
253
|
) {
|
|
232
254
|
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
255
|
+
logLegacyCase("unpackRuntimeMessage_contentsWithAddress");
|
|
233
256
|
} else {
|
|
234
257
|
// new format
|
|
235
258
|
unpack(message);
|
package/src/packageVersion.ts
CHANGED